今日はクエリ周りについて学んだ。
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を参照すること。