1. 序章

前の記事では、プロジェクトにSpringDataElasticsearchを構成して使用する方法を示しました。 この記事では、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 も提供します。これは、タイトルフィールドの追加のインデックスを記述します。 FieldType.keyword を使用して、フィールドの追加のインデックス作成を実行するときにアナライザーを使用したくないこと、およびこの値を接尾辞verbatim[のネストされたフィールドを使用して格納する必要があることを示します。 X225X]。

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

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

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

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

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

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

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

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

3. 一致クエリ

一致クエリは、テキスト、数値、および日付を受け入れます。

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

  • ブール値
  • フレーズ
  • phrase_prefix

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

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

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

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

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

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

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

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

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

3.2. あいまいさ

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

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

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

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

5. フレーズ検索

フェーズ検索はより厳密ですが、slopパラメーターで制御できます。 このパラメータは、ドキュメントが一致していると見なしながら、用語がどれだけ離れているかをフレーズクエリに通知します。

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

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

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

6. マルチマッチクエリ

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

NativeSearchQuery 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));
SearchSourceBuilder builder = new SearchSourceBuilder().aggregation(aggregation);

SearchRequest searchRequest = 
  new SearchRequest().indices("blog").types("article").source(builder);
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

Map<String, Aggregation> results = response.getAggregations().asMap();
StringTerms topTags = (StringTerms) results.get("top_tags");

List<String> keys = topTags.getBuckets()
  .stream()
  .map(b -> b.getKeyAsString())
  .collect(toList());
assertEquals(asList("elasticsearch", "spring data", "search engines", "tutorial"), keys);

8. 概要

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

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

Elasticsearchは、geoクエリ、スクリプトクエリ、複合クエリなど、他の多くのタイプのクエリを提供します。 これらについては、 Elasticsearchのドキュメントで読むことができ、コードでこれらのクエリを使用するためにSpring DataElasticsearchAPIを調べることができます。

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