Springデータを使用したElasticsearchクエリ

Springデータを使用したElasticsearchクエリ

1. 前書き

previous articleでは、プロジェクトに対してSpring DataElasticsearchを構成して使用する方法を示しました。 この記事では、Elasticsearchが提供するいくつかのクエリタイプを調べ、フィールドアナライザーとそれらが検索結果に与える影響についても説明します。

2. アナライザー

デフォルトでは、保存されているすべての文字列フィールドはアナライザーによって処理されます。 アナライザーは、1つのトークナイザーと複数のトークンフィルターで構成され、通常、1つ以上の文字フィルターが先行します。

デフォルトのアナライザーは、文字列を一般的な単語区切り文字(スペースや句読点など)で分割し、すべてのトークンを小文字にします。 また、一般的な英語の単語も無視します。

Elasticsearchは、フィールドを同時に分析および分析対象外と見なすように設定することもできます。

たとえば、Articleクラスで、タイトルフィールドを標準の分析フィールドとして格納するとします。 接尾辞verbatimが付いた同じフィールドは、分析されていないフィールドとして格納されます。

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

ここでは、@MultiFieldアノテーションを適用して、このフィールドにいくつかの方法でインデックスを付けたいことをSpringDataに通知します。 メインフィールドはtitleという名前を使用し、上記のルールに従って分析されます。

ただし、2番目の注釈@InnerFieldも提供します。これは、titleフィールドの追加のインデックスを記述します。 FieldType.keywordを使用して、フィールドの追加のインデックス作成を実行するときにアナライザーを使用したくないこと、およびこの値は接尾辞verbatimが付いたネストされたフィールドを使用して格納する必要があることを示します。

2.1. 分析されたフィールド

例を見てみましょう。 「Spring Data Elasticsearch」というタイトルの記事がインデックスに追加されたとします。 デフォルトのアナライザーは、スペース文字で文字列を分割し、小文字のトークン「spring」、「data“,」、および「elasticsearch」を生成します。

これで、これらの用語の任意の組み合わせを使用して、ドキュメントを照合できます。

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

2.2. 分析されていないフィールド

分析されていないフィールドはトークン化されないため、一致クエリまたは用語クエリを使用する場合は、全体としてのみ一致できます。

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

一致クエリを使用すると、完全なタイトルでのみ検索できますが、これも大文字と小文字が区別されます。

3. 一致クエリ

match queryは、テキスト、数値、および日付を受け入れます。

「一致」クエリには次の3つのタイプがあります。

  • ブール値

  • phraseおよび

  • phrase_prefix

このセクションでは、boolean一致クエリについて説明します。

3.1. ブール演算子とのマッチング

booleanは、一致クエリのデフォルトタイプです。使用するブール演算子を指定できます(orがデフォルトです)。

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title","Search engines").operator(AND))
  .build();
List
articles = getElasticsearchTemplate() .queryForList(searchQuery, Article.class);

このクエリは、and演算子を使用してタイトルから2つの用語を指定することにより、「検索エンジン」というタイトルの記事を返します。 しかし、用語の1つだけが一致するときに、デフォルトの(or)演算子を使用して検索するとどうなりますか?

SearchQuery searchQuery = new NativeSearchQueryBuilder()
  .withQuery(matchQuery("title", "Engines Solutions"))
  .build();
List
articles = getElasticsearchTemplate() .queryForList(searchQuery, Article.class); assertEquals(1, articles.size()); assertEquals("Search engines", articles.get(0).getTitle());

Search engines」の記事は引き続き一致しますが、すべての用語が一致したわけではないため、スコアは低くなります。

一致する各用語のスコアの合計は、結果の各ドキュメントの合計スコアになります。

クエリに入力されたまれな用語を含むドキュメントが、いくつかの一般的な用語を含むドキュメントよりもランクが高い場合があります。

3.2. あいまいさ

ユーザーが単語にタイプミスをした場合でも、fuzzinessパラメータを指定することで検索と一致させることができます。これにより、不正確な一致が可能になります。

文字列フィールドの場合、fuzzinessは編集距離を意味します。つまり、別の文字列と同じにするために1つの文字列に加える必要のある1文字の変更の数です。

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

prefix_lengthパラメータは、パフォーマンスを向上させるために使用されます。 この場合、最初の3文字が完全に一致する必要があり、可能な組み合わせの数が減ります。

位相検索はより厳密ですが、slopパラメータで制御できます。 このパラメーターは、フレーズクエリに、ドキュメントを一致と見なしながら、用語の間隔を許可します。

つまり、クエリとドキュメントを一致させるために用語を移動する必要がある回数を表します。

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

ここでは、スロップを1に設定しているため、クエリはタイトル「Spring Data Elasticsearch」のドキュメントと一致します。

6. マルチマッチクエリ

複数のフィールドを検索する場合は、QueryBuilders#multiMatchQuery()を使用して、一致するすべてのフィールドを指定できます。

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

ここでは、titleフィールドとtagsフィールドで一致するものを検索します。

ここでは、「最高のフィールド」スコアリング戦略を使用していることに注意してください。 フィールド間の最大スコアをドキュメントスコアとして取得します。

7. 集計

Articleクラスでは、分析されていないtagsフィールドも定義しました。 集計を使用して、タグクラウドを簡単に作成できます。

フィールドは分析されていないため、タグはトークン化されないことに注意してください。

TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_tags")
  .field("tags")
  .order(Terms.Order.count(false));
SearchResponse response = client.prepareSearch("blog")
  .setTypes("article")
  .addAggregation(aggregation)
  .execute().actionGet();

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. 概要

この記事では、分析されたフィールドと分析されていないフィールドの違い、およびこの違いが検索に与える影響について説明しました。

また、一致クエリ、フレーズ一致クエリ、全文検索クエリ、ブールクエリなど、Elasticsearchが提供するいくつかのタイプのクエリについても学びました。

Elasticsearchは、geoクエリ、スクリプトクエリ、複合クエリなど、他の多くのタイプのクエリを提供します。 これらのクエリをコードで使用するには、Elasticsearch documentationでそれらについて読み、Spring Data ElasticsearchAPIを調べることができます。

この記事で使用されている例を含むプロジェクトは、the GitHub repositoryにあります。