Elasticsearchのmax clause countについて
普通につかっていると気にせず済むことは多いですが、たまに検索時のエラーの原因になるmax clause countについてまとめます。
max clause countの何が問題なのか
max clause countを超えるクエリが生成された場合、検索時にtoo_many_clausesエラーが発生します。
そもそもmax clause countってなに?
LuceneのBooleanQueryに含めることのできる句(clause)の最大数。
句(clause)にはmust, should, filter, must_notの4つの種類があります。 github.com
Luceneの実装では、BooleanQueryクラスにBooleanClauseクラスを追加していくようになっています。
max clause countの制限は、検索が大きくなりすぎてCPUやメモリを多く消費することを防ぐために設けられています。
Luceneの実装でdefaultは1024が設定されています。
Boolean Queryに条件をいれすぎなければいいだけでは?
半分正解。
ElasticsearchのBoolean QueryはLuceneのBooleanQueryに展開されます。
したがって、Boolean Queryに大量の条件を追加しないというのがひとつの対策です。
Boolean query | Elasticsearch Guide [8.6] | Elastic
ですが、 Elasticsearch では Boolean Queryだけでなく他のクエリでも内部的に Lucene の BooleanQuery を組み立てます。
事例を見てみましょう。
以下の設定でtest_indexを作成します。
{ "settings": { "analysis": { "tokenizer": { "bigram_tokenizer": { "type": "ngram", "min_gram": 2, "max_gram": 2 } }, "analyzer": { "bigram_analyzer": { "tokenizer": "bigram_tokenizer" } } } }, "mappings": { "properties": { "title": { "type": "text", "analyzer": "bigram_analyzer" } } } }
次に2つドキュメントを追加します
POST test_index/_doc/1 { "title": "宇宙兄弟" } POST test_index/_doc/2 { "title": "ワールドトリガー" }
単純にmatchクエリで検索してみるとこの2つのドキュメントがヒットします。
{ "query": { "match": { "title": "宇宙ワールド" } } }
が、この際内部でどういうクエリが生成されるかsearch_profilerで見てみましょう。
今回使ったElasticsearchのMatch Queryはテキスト解析した結果からクエリを組み立てます。 今回はbigramを使っているため、2文字ずつに分割されたtokenの数だけtermQueryを作成してBooleanQueryでまとめています。
ngramの設定でmin_gramとmax_gramの数が違う、kuromoji tokenizerでnbest_costを変える、検索する文字列が長いなど、クエリ展開時に生成されるtokenが多くなるような設定にしている場合、うっかり超えてしまうことはあるので注意が必要です。