Pertanyaan Elasticsearch dengan Spring Data

1. Pengenalan

Dalam artikel sebelumnya, kami menunjukkan cara mengkonfigurasi dan menggunakan Spring Data Elasticsearch untuk projek. Dalam artikel ini, kami akan memeriksa beberapa jenis pertanyaan yang ditawarkan oleh Elasticsearch dan kami juga akan membincangkan mengenai penganalisis lapangan dan kesannya terhadap hasil carian.

2. Penganalisis

Semua medan rentetan yang disimpan, secara lalai, diproses oleh penganalisis. Penganalisis terdiri daripada satu tokenizer dan beberapa penapis token, dan biasanya didahului oleh satu atau lebih penapis watak.

Penganalisis lalai membelah rentetan dengan pemisah kata umum (seperti spasi atau tanda baca) dan meletakkan setiap token dengan huruf kecil. Ia juga mengabaikan perkataan Inggeris yang biasa.

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

Sebagai contoh, dalam kelas Artikel , anggaplah kita menyimpan medan tajuk sebagai medan analisis standard. Medan yang sama dengan kata kerja akhiran akan disimpan sebagai medan 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 memberitahu Spring Data bahawa kami mahu bidang ini diindeks dengan beberapa cara. Bidang utama akan menggunakan tajuk nama dan akan dianalisis mengikut peraturan yang dinyatakan di atas.

Tetapi kami juga memberikan anotasi kedua, @InnerField , yang menerangkan pengindeksan tambahan pada tajuk . Kami menggunakan kata kunci FieldType.key untuk menunjukkan bahawa kami tidak mahu menggunakan penganalisis ketika melakukan pengindeksan tambahan bidang, dan nilai ini harus disimpan menggunakan bidang bersarang dengan akhiran kata kerja .

2.1. Bidang yang dianalisis

Mari lihat contohnya. Anggaplah artikel dengan judul "Spring Data Elasticsearch" ditambahkan ke indeks kami. Penganalisis lalai akan memecahkan rentetan pada watak spasi dan menghasilkan token huruf kecil: " spring ", " data", dan " elasticsearch ".

Sekarang kita boleh menggunakan gabungan istilah ini untuk mencocokkan dokumen:

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

2.2. Bidang yang tidak dianalisis

Medan yang tidak dianalisis tidak diberi tanda, jadi hanya dapat dipadankan secara keseluruhan ketika menggunakan pertanyaan padanan atau istilah:

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

Dengan menggunakan pertanyaan padanan, kami hanya boleh mencari dengan tajuk penuh, yang juga peka huruf besar kecil.

3. Pertanyaan Pertandingan

A Pertanyaan Perlawanan menerima teks, nombor dan tarikh.

Terdapat tiga jenis pertanyaan "padanan":

  • boolean
  • frasa dan
  • frasa_prefix

Di bahagian ini, kita akan meneroka pertanyaan padanan boolean .

3.1. Memadankan Dengan Pengendali Boolean

boolean adalah jenis pertanyaan padanan lalai; anda boleh menentukan operator boolean mana yang akan digunakan ( atau lalai):

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

Pertanyaan ini akan mengembalikan artikel dengan judul "Search engine" dengan menetapkan dua istilah dari judul dengan dan operator. Tetapi apa yang akan berlaku jika kita mencari dengan operator lalai ( atau ) apabila hanya satu istilah yang sesuai?

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 sesuai, tetapi akan memiliki skor yang lebih rendah kerana tidak semua istilah cocok.

Jumlah skor setiap istilah yang sepadan menambah jumlah skor setiap dokumen yang dihasilkan.

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

3.2. Kekaburan

When the user makes a typo in a word, it is still possible to match it with a search by specifying a fuzziness parameter, which allows inexact matching.

For string fields, fuzziness means the edit distance: the number of one-character changes that need to be made to one string to make it the same as another string.

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

The prefix_length parameter is used to improve performance. In this case, we require that the first three characters should match exactly, which reduces the number of possible combinations.

5. Phrase Search

Phase search is stricter, although you can control it with the slop parameter. This parameter tells the phrase query how far apart terms are allowed to be while still considering the document a match.

In other words, it represents the number of times you need to move a term in order to make the query and document match:

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

Here the query will match the document with the title “Spring Data Elasticsearch” because we set the slop to one.

6. Multi Match Query

When you want to search in multiple fields then you could use QueryBuilders#multiMatchQuery() where you specify all the fields to match:

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

Here we search the title and tags fields for a match.

Notice that here we use the “best fields” scoring strategy. It will take the maximum score among the fields as a document score.

7. Aggregations

In our Article class we have also defined a tags field, which is non-analyzed. We could easily create a tag cloud by using an aggregation.

Remember that, because the field is non-analyzed, the tags will not be tokenized:

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. Summary

In this article, we discussed the difference between analyzed and non-analyzed fields, and how this distinction affects search.

We also learned about several types of queries provided by Elasticsearch, such as the match query, phrase match query, full-text search query, and boolean query.

Elasticsearch provides many other types of queries, such as geo queries, script queries and compound queries. You can read about them in the Elasticsearch documentation and explore the Spring Data Elasticsearch API in order to use these queries in your code.

Anda boleh mendapatkan projek yang mengandungi contoh yang digunakan dalam artikel ini di repositori GitHub.