Penerapan Tagging Sederhana dengan Elasticsearch

Ketekunan teratas

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> PERIKSA KURSUS Artikel ini adalah bagian dari seri: • Penerapan Pemberian Tag Sederhana dengan Elasticsearch (artikel saat ini) • Penerapan Pemberian Tag Sederhana dengan JPA

• Penerapan Pemberian Tag Lanjutan dengan JPA

• Penerapan Tagging Sederhana dengan MongoDB

1. Ikhtisar

Pemberian tag adalah pola desain umum yang memungkinkan kita untuk mengkategorikan dan memfilter item dalam model data kita.

Di artikel ini, kami akan menerapkan pemberian tag menggunakan Spring dan Elasticsearch. Kami akan menggunakan Spring Data dan Elasticsearch API.

Pertama-tama, kami tidak akan membahas dasar-dasar mendapatkan Elasticsearch dan Spring Data - Anda dapat menjelajahinya di sini.

2. Menambahkan Tag

Penerapan pemberian tag yang paling sederhana adalah larik string. Kita dapat menerapkan ini dengan menambahkan bidang baru ke model data kita seperti ini:

@Document(indexName = "blog", type = "article") public class Article { // ... @Field(type = Keyword) private String[] tags; // ... }

Perhatikan penggunaan jenis bidang Kata Kunci . Kami hanya ingin tag yang sama persis untuk memfilter hasil. Ini memungkinkan kita untuk menggunakan tag yang serupa tetapi terpisah seperti elasticsearchIsAwesome dan elasticsearchIsTerrible .

Bidang yang dianalisis akan mengembalikan klik parsial yang merupakan perilaku salah dalam kasus ini.

3. Membangun Pertanyaan

Tag memungkinkan kami untuk memanipulasi kueri kami dengan cara yang menarik. Kita dapat menelusuri mereka seperti bidang lainnya, atau kita dapat menggunakannya untuk memfilter hasil kita pada kueri match_all . Kami juga dapat menggunakannya dengan kueri lain untuk memperketat hasil kami.

3.1. Pencarian Tag

Bidang tag baru yang kami buat pada model kami sama seperti bidang lainnya di indeks kami. Kami dapat mencari entitas apa pun yang memiliki tag khusus seperti ini:

@Query("{\"bool\": {\"must\": [{\"match\": {\"tags\": \"?0\"}}]}}") Page findByTagUsingDeclaredQuery(String tag, Pageable pageable);

Contoh ini menggunakan Spring Data Repository untuk membuat kueri kami, tetapi kami dapat dengan cepat menggunakan Templat Istirahat untuk membuat kueri klaster Elasticsearch secara manual.

Demikian pula, kita dapat menggunakan API Elasticsearch:

boolQuery().must(termQuery("tags", "elasticsearch"));

Asumsikan kami menggunakan dokumen-dokumen berikut dalam indeks kami:

[ { "id": 1, "title": "Spring Data Elasticsearch", "authors": [ { "name": "John Doe" }, { "name": "John Smith" } ], "tags": [ "elasticsearch", "spring data" ] }, { "id": 2, "title": "Search engines", "authors": [ { "name": "John Doe" } ], "tags": [ "search engines", "tutorial" ] }, { "id": 3, "title": "Second Article About Elasticsearch", "authors": [ { "name": "John Smith" } ], "tags": [ "elasticsearch", "spring data" ] }, { "id": 4, "title": "Elasticsearch Tutorial", "authors": [ { "name": "John Doe" } ], "tags": [ "elasticsearch" ] }, ]

Sekarang kita bisa menggunakan query ini:

Page articleByTags = articleService.findByTagUsingDeclaredQuery("elasticsearch", PageRequest.of(0, 10)); // articleByTags will contain 3 articles [ 1, 3, 4] assertThat(articleByTags, containsInAnyOrder( hasProperty("id", is(1)), hasProperty("id", is(3)), hasProperty("id", is(4))) );

3.2. Memfilter Semua Dokumen

Pola desain yang umum adalah membuat Tampilan Daftar yang Difilter di UI yang menampilkan semua entitas, tetapi juga memungkinkan pengguna untuk memfilter berdasarkan kriteria yang berbeda.

Katakanlah kita ingin mengembalikan semua artikel yang difilter oleh tag apa pun yang dipilih pengguna:

@Query("{\"bool\": {\"must\": " + "{\"match_all\": {}}, \"filter\": {\"term\": {\"tags\": \"?0\" }}}}") Page findByFilteredTagQuery(String tag, Pageable pageable);

Sekali lagi, kami menggunakan Spring Data untuk membuat kueri yang kami nyatakan.

Akibatnya, kueri yang kami gunakan terpecah menjadi dua bagian. Kueri penilaian adalah istilah pertama, dalam hal ini, match_all . Kueri filter berikutnya dan memberi tahu Elasticsearch hasil mana yang akan dibuang.

Berikut adalah cara kami menggunakan kueri ini:

Page articleByTags = articleService.findByFilteredTagQuery("elasticsearch", PageRequest.of(0, 10)); // articleByTags will contain 3 articles [ 1, 3, 4] assertThat(articleByTags, containsInAnyOrder( hasProperty("id", is(1)), hasProperty("id", is(3)), hasProperty("id", is(4))) );

Penting untuk disadari bahwa meskipun ini mengembalikan hasil yang sama seperti contoh di atas, kueri ini akan berkinerja lebih baik.

3.3. Memfilter Kueri

Terkadang penelusuran menghasilkan terlalu banyak hasil untuk dapat digunakan. Dalam hal ini, bagus untuk mengekspos mekanisme penyaringan yang dapat menjalankan kembali pencarian yang sama, hanya dengan hasil yang dipersempit.

Berikut adalah contoh saat kami mempersempit artikel yang telah ditulis oleh seorang penulis, menjadi hanya artikel dengan tag tertentu:

@Query("{\"bool\": {\"must\": " + "{\"match\": {\"authors.name\": \"?0\"}}, " + "\"filter\": {\"term\": {\"tags\": \"?1\" }}}}") Page findByAuthorsNameAndFilteredTagQuery( String name, String tag, Pageable pageable);

Sekali lagi, Spring Data melakukan semua pekerjaan untuk kami.

Mari kita lihat juga bagaimana membuat kueri ini sendiri:

QueryBuilder builder = boolQuery().must( nestedQuery("authors", boolQuery().must(termQuery("authors.name", "doe")), ScoreMode.None)) .filter(termQuery("tags", "elasticsearch"));

Kita dapat, tentu saja, menggunakan teknik yang sama ini untuk memfilter bidang lain dalam dokumen. Tapi tag cocok untuk kasus penggunaan ini.

Berikut adalah cara menggunakan kueri di atas:

SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder) .build(); List articles = elasticsearchTemplate.queryForList(searchQuery, Article.class); // articles contains [ 1, 4 ] assertThat(articleByTags, containsInAnyOrder( hasProperty("id", is(1)), hasProperty("id", is(4))) );

4. Filter Konteks

Saat membuat kueri, kami perlu membedakan antara Konteks Kueri dan Konteks Filter. Setiap kueri di Elasticsearch memiliki Konteks Kueri, jadi kita harus terbiasa melihatnya.

Not every query type supports the Filter Context. Therefore if we want to filter on tags, we need to know which query types we can use.

The bool query has two ways to access the Filter Context. The first parameter, filter, is the one we use above. We can also use a must_not parameter to activate the context.

The next query type we can filter is constant_score. This is useful when uu want to replace the Query Context with the results of the Filter and assign each result the same score.

The final query type that we can filter based on tags is the filter aggregation. This allows us to create aggregation groups based on the results of our filter. In other words, we can group all articles by tag in our aggregation result.

5. Advanced Tagging

So far, we have only talked about tagging using the most basic implementation. The next logical step is to create tags that are themselves key-value pairs. This would allow us to get even fancier with our queries and filters.

For example, we could change our tag field into this:

@Field(type = Nested) private List tags;

Then we'd just change our filters to use nestedQuery types.

Setelah kita memahami cara menggunakan key-value pair, ini adalah langkah kecil untuk menggunakan objek kompleks sebagai tag kita. Tidak banyak implementasi yang membutuhkan objek lengkap sebagai tag, tetapi ada baiknya mengetahui bahwa kami memiliki opsi ini jika kami memerlukannya.

6. Kesimpulan

Pada artikel ini, kami telah membahas dasar-dasar penerapan pemberian tag menggunakan Elasticsearch.

Seperti biasa, contoh dapat ditemukan di GitHub.

Berikutnya » Penerapan Tag Sederhana dengan JPA Persistence paling bawah

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya