Kueri Elasticsearch dengan Spring Data

1. Perkenalan

Di artikel sebelumnya, kami mendemonstrasikan cara mengonfigurasi dan menggunakan Spring Data Elasticsearch untuk sebuah proyek. Dalam artikel ini, kami akan memeriksa beberapa jenis kueri yang ditawarkan oleh Elasticsearch dan kami juga akan membahas tentang penganalisis bidang dan dampaknya pada hasil pencarian.

2. Alat analisis

Semua bidang string yang disimpan, secara default, diproses oleh penganalisis. Penganalisis terdiri dari satu tokenizer dan beberapa filter token, dan biasanya didahului oleh satu atau lebih filter karakter.

Penganalisis default membagi string dengan pemisah kata umum (seperti spasi atau tanda baca) dan meletakkan setiap token dalam huruf kecil. Itu juga mengabaikan kata-kata umum bahasa Inggris.

Elasticsearch juga dapat dikonfigurasi untuk menganggap bidang sebagai dianalisis dan tidak dianalisis pada saat yang sama.

Misalnya, dalam kelas Artikel , misalkan kita menyimpan bidang judul sebagai bidang analisis standar. Bidang yang sama dengan sufiks kata demi kata akan disimpan sebagai bidang yang tidak dianalisis:

@MultiField( mainField = @Field(type = Text, fielddata = true), otherFields = { @InnerField(suffix = "verbatim", type = Keyword) } ) private String title;

Di sini, kami menerapkan anotasi @MultiField untuk memberi tahu Spring Data bahwa kami ingin bidang ini diindeks dengan beberapa cara. Bidang utama akan menggunakan judul nama dan akan dianalisis sesuai dengan aturan yang dijelaskan di atas.

Tapi kami juga menyediakan anotasi kedua, @InnerField , yang menjelaskan pengindeksan tambahan untuk kolom judul . Kami menggunakan FieldType.keyword untuk menunjukkan bahwa kami tidak ingin menggunakan penganalisis saat melakukan pengindeksan tambahan pada bidang, dan nilai ini harus disimpan menggunakan bidang bersarang dengan sufiks kata demi kata .

2.1. Bidang yang dianalisis

Mari kita lihat contohnya. Misalkan artikel dengan judul "Spring Data Elasticsearch" ditambahkan ke indeks kami. Penganalisis default akan memecah string pada karakter spasi dan menghasilkan token huruf kecil: " spring ", " data", dan " elasticsearch ".

Sekarang kita dapat menggunakan kombinasi apapun dari istilah-istilah ini untuk mencocokkan dokumen:

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchQuery("title", "elasticsearch data")) .build();

2.2. Bidang yang tidak dianalisis

Bidang yang tidak dianalisis tidak diberi token, jadi hanya bisa dicocokkan secara keseluruhan saat menggunakan kueri pencocokan atau istilah:

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchQuery("title.verbatim", "Second Article About Elasticsearch")) .build();

Dengan menggunakan kueri pencocokan, kami hanya dapat menelusuri berdasarkan judul lengkap, yang juga peka huruf besar / kecil.

3. Query Pertandingan

Sebuah permintaan pertandingan menerima teks, angka dan tanggal.

Ada tiga jenis kueri "cocok":

  • boolean
  • frase dan
  • frase_prefix

Di bagian ini, kita akan menjelajahi kueri pencocokan boolean .

3.1. Mencocokkan Dengan Operator Boolean

boolean adalah tipe default dari kueri pencocokan; Anda dapat menentukan operator boolean mana yang akan digunakan ( atau defaultnya):

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchQuery("title","Search engines").operator(Operator.AND)) .build(); SearchHits articles = elasticsearchTemplate() .search(searchQuery, Article.class, IndexCoordinates.of("blog"));

Kueri ini akan mengembalikan artikel dengan judul "Mesin telusur" dengan menentukan dua istilah dari judul dengan operator dan . Tetapi apa yang akan terjadi jika kita menelusuri dengan operator default ( atau ) ketika hanya satu istilah yang cocok?

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchQuery("title", "Engines Solutions")) .build(); SearchHits articles = elasticsearchTemplate() .search(searchQuery, Article.class, IndexCoordinates.of("blog")); assertEquals(1, articles.getTotalHits()); assertEquals("Search engines", articles.getSearchHit(0).getContent().getTitle());

Artikel " Mesin pencari " masih cocok, tetapi akan memiliki skor lebih rendah karena tidak semua istilah cocok.

Jumlah skor dari setiap istilah yang cocok dijumlahkan dengan skor total setiap dokumen yang dihasilkan.

Mungkin ada situasi di mana dokumen yang berisi istilah langka yang dimasukkan dalam kueri akan memiliki peringkat lebih tinggi daripada dokumen yang berisi beberapa istilah umum.

3.2. Ketidakjelasan

Saat pengguna salah ketik pada sebuah kata, masih mungkin untuk mencocokkannya dengan pencarian dengan menentukan parameter fuzziness , yang memungkinkan pencocokan tidak tepat.

Untuk bidang string, ketidakjelasan berarti jarak edit: jumlah perubahan satu karakter yang perlu dilakukan pada satu string agar sama dengan string lainnya.

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchQuery("title", "spring date elasticsearch") .operator(Operator.AND) .fuzziness(Fuzziness.ONE) .prefixLength(3)) .build();

The prefix_length parameter digunakan untuk meningkatkan kinerja. Dalam kasus ini, kami mengharuskan tiga karakter pertama harus sama persis, yang mengurangi jumlah kemungkinan kombinasi.

5. Pencarian Frase

Pencarian fase lebih ketat, meskipun Anda dapat mengontrolnya dengan parameter slop . Parameter ini memberi tahu kueri frase seberapa jauh istilah diperbolehkan sementara masih mempertimbangkan dokumen yang cocok.

Dengan kata lain, ini mewakili berapa kali Anda perlu memindahkan istilah agar kueri dan dokumen cocok:

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchPhraseQuery("title", "spring elasticsearch").slop(1)) .build();

Di sini kueri akan mencocokkan dokumen dengan judul " Spring Data Elasticsearch " karena kami menyetel slop ke satu.

6. Query Multi Match

Saat Anda ingin mencari di beberapa bidang, Anda dapat menggunakan QueryBuilders # multiMatchQuery () di mana Anda menentukan semua bidang untuk dicocokkan:

NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(multiMatchQuery("tutorial") .field("title") .field("tags") .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)) .build();

Di sini kami mencari bidang judul dan tag untuk kecocokan.

Perhatikan bahwa di sini kami menggunakan strategi penilaian "bidang terbaik". Ini akan mengambil skor maksimum di antara bidang-bidang sebagai skor dokumen.

7. Agregasi

Di kelas Artikel kami, kami juga mendefinisikan bidang tag , yang tidak dianalisis. Kita bisa dengan mudah membuat tag cloud dengan menggunakan agregasi.

Ingatlah bahwa, karena kolom tidak dianalisis, tag tidak akan diberi token:

TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags") .field("tags") .order(Terms.Order.count(false)); SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation); SearchRequest searchRequest = new SearchRequest().indices("blog").types("article").source(builder); SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); Map results = response.getAggregations().asMap(); StringTerms topTags = (StringTerms) results.get("top_tags"); List keys = topTags.getBuckets() .stream() .map(b -> b.getKeyAsString()) .collect(toList()); assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys);

8. Ringkasan

Dalam artikel ini, kami membahas perbedaan antara bidang yang dianalisis dan tidak dianalisis, dan bagaimana perbedaan ini memengaruhi penelusuran.

Kami juga mempelajari tentang beberapa jenis kueri yang disediakan oleh Elasticsearch, seperti kueri pencocokan, kueri pencocokan frasa, kueri penelusuran teks lengkap, dan kueri boolean.

Elasticsearch menyediakan banyak jenis kueri lainnya, seperti kueri geografis, kueri skrip, dan kueri gabungan. Anda dapat membacanya di dokumentasi Elasticsearch dan menjelajahi Spring Data Elasticsearch API untuk menggunakan kueri ini di kode Anda.

Anda dapat menemukan proyek yang berisi contoh yang digunakan dalam artikel ini di repositori GitHub.