$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

今日のElasticsearch学び Vol.3 - クエリ編

今日はクエリ周りについて学んだ。

Query contextとFilter context

Query and filter context | Elasticsearch Guide [8.0] | Elastic

 ElasticSearchのクエリにはQuery contextとFilter contextというのがある。簡単にいえばQuery contextで解釈されたクエリはスコアに影響を及ぼし、Filter contextで解釈されたクエリはスコアに影響を及ばさない。


 これらのcontextはBool Queryで概念として出てくる。こちらのドキュメントの例を見てみると、boolクエリのmustやshouldはQuery contextで解釈され、filterはFilter contextで解釈されるように見える。つまりmustやshouldに指定したクエリはスコアに関係し、filterに指定したクエリはスコアに関係しない。

 あとおそらくだが、filterの中でさらにBool Queryを利用してその中でmustとかshouldとかを使ったら、それはFilter context内になるので、スコアには影響しないのではないかと思う。このあたりはちゃんと調べていない。


 パフォーマンスのことを考慮すると、スコアにあんまり関係しないものならFilter contextを利用するほうが良い。この詳細はQueries and Filters | Elasticsearch: The Definitive Guide [2.x] | Elasticに書かれている。まあスコアを計算しない分速いというのは直感的であるし、単なるfilterであればキャッシュもしやすいからだろう。


 使い分けはQueries and Filters | Elasticsearch: The Definitive Guide [2.x] | Elasticに書かれているとおりで

When to Use Which
As a general rule, use query clauses for full-text search or for any condition that should affect the relevance score, and use filters for everything else.

  • 全文検索やスコアに関係するものならQuery contextで
  • それ以外はFilter contextで

というのがわかりやすいだろう。

よく使うQuery DSL

 ElasticsearchにはとにかくQuery DSLが多い。Query DSLのドキュメントをみてもらうと、その多さが分かる。これをいろいろ読んでいくのはしんどい。ということでとりあえず最近よく使ったQuery DSLだけまとめておく。

Bool Query

Boolean query | Elasticsearch Guide [8.0] | Elastic

  • ANDやOR, NOT検索をするときの要
  • これだけ使えばとりあえずAND, OR, NOTを使えるし、そのネストも出来る
  • mustとfilterはAND検索
  • must_notはNOT検索
  • shouldかつminimum_should_matchが1ならOR検索

 これの使い方のいい例は Elasticsearchのbool queryを利用してAND OR NOTを書いてみる - Qiita にあったので、こちらを参照。

Match Query

Match query | Elasticsearch Guide [8.0] | Elastic

  • 全文検索の要
  • あるフィールドに対して指定した文字列で検索をかける
  • 渡した文字列は適切にAnalyzeされ、termに分割されて検索される
  • ブログのタイトルとか本文検索とかはMatch Queryを使ったらいい

Term Query

Term query | Elasticsearch Guide [8.0] | Elastic

  • termを指定して、そのtermを持つドキュメントに絞り込むもの
  • Analyzeはされないので、例えばMySQLでIDとして扱われているカラムをElasticsearchに入れたものとか、booleanなfieldとか、stringでnot_analyzedなfieldに使うと便利

Range Query

Range query | Elasticsearch Guide [8.0] | Elastic

  • 範囲を指定して、絞り込みをするクエリ
  • 数字の範囲を絞り込んだり、日付で絞り込んだりするのに便利

Phrase検索

 実際にMatch Queryとかを利用して全文検索をしていると、MySQLのLIKE検索っぽいことしたいけどうまくいかなくて困ることがある。例えば「子狐」みたいに検索をかけたら、「子狐の物語」みたいな文書はヒットして欲しいけど、「狐と人間の子の物語」にはヒットしてほしくない。しかし普通にMatch Queryを利用すると、「子狐」は「子」と「狐」の二つのtermに分割されてしまい、先ほどの二つの例も両方共「子」と「狐」が含まれるため、両方共ヒットしてしまう。

 この場合、「子狐」を含むドキュメントを検索したい場合は、Phrase Matchingという仕組みを使うのが便利。これはtermの出現の順序や位置も一致した場合にヒットするモードである。


 例えば「子狐」は「子」「狐」という順序でtermを作る。「子狐の物語」は「子」「狐」「の」「物語」という順序でtermを作る。「狐と人間の子の物語」は「狐」「と」「人間」「の」「子」「の」「物語」という順序でtermを作る。

 この場合「子狐」を使ってPhrase検索をすると「子」のtermの後にすぐ「狐」のtermが来た場合のみマッチするようになる。そのため前者の「子狐の物語」のみにマッチさせることが出来る。他にも「犬と子狐」はマッチするし、「狐の子の物語」はマッチしない。

ドキュメントをsortする

 通常はスコアの降順でソートされた結果が返ってくるが、自分でsortする方法を指定することもできる。詳細はSortを参照。

 例えばブログエントリの文字数とかでsortしたい場合は、search APIに対して、sortというオプションを付ける感じ。

{
    "query" : { "match_all": {} },
    "sort"  : [{ "character_count": "desc" }]
}


 さらに、あるフィールドの中身を使ったsortだけではなくて、もう少しいろいろ計算した内容を入れたい場合はScript Based Sortingというのが利用できる。例えばブログのコメント数といいね数でsortしたい場合は以下のようにする。

{
    "query" : { "match_all": {} },
    "sort"  : {
        "_script": {
            "type": "number",
            "script": {
                "lang": "expression",
                "inline": "doc['comment_count'].value + doc['like_count'].value"
            },
            "order": "desc"
        }
    }
}

 ここで気をつけたいのが、現在はscriptのlangオプションのデフォルトがgroovyだが、groovy自体はデフォルトの設定では利用できないということである。そのため、簡単なものならデフォルトでも利用できるexpressionを明示的に指定するようにしている。詳細はScripting | Elasticsearch Guide [8.0] | Elasticを参照すること。