概要
以前Bool Query と Dis Max Query の違いについて書きました。
今回はその中で出てきたMulti Match Query
のtypeの違いについて書きます。
どれも複数のフィールドに対して実行するクエリですが、それぞれ用途が異なります。
環境
- Elasticsearch 6.6.0
データ投入
curl -s -H "Content-Type: application/json" \ -XPOST localhost:9200/my_index/my_type/_bulk -d ' {"index": {"_id": "1"}} {"title": "Quick brown rabbits", "body": "Brown rabbits are commonly seen."} {"index": {"_id": "2"}} {"title": "Keeping pets healthy", "body": "My quick brown fox eats rabbits on a regular basis."} {"index": {"_id": "3"}} {"first_name": "Will", "last_name": "Smith"} {"index": {"_id": "4"}} {"first_name": "Smith", "last_name": "Jones"} '
Multi Match Query
種類
主に以下の3つを使い分けると思います。
type | 説明 | 使いドコロ |
---|---|---|
best_fields | 各フィールドに対して検索し、最も一致するフィールドのスコアを返す | 複数のフィールドからある特定のフレーズとマッチしたものが欲しい時 |
most_fields | 各フィールドに対して検索し、それぞれのスコアを加算したスコアを返す | 複数のフィールドを横断して検索したい時 |
cross_fields | 複数のフィールドを結合して1つのフィールドかのように検索できる | 姓名、読みがな、など1つにまとめたいがフィールドが分かれている時 |
best_fields
クエリ
best_fields
はdis_max
としてラップされるので
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_search?pretty -d ' { "query": { "multi_match": { "query": "brown fox", "type": "best_fields", "fields": ["title", "body"] } }, "size": 5, "from": 0 } '
というクエリは
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_search?pretty -d ' { "query": { "dis_max": { "queries": [ { "match": { "title": "brown fox" } }, { "match": { "body": "brown fox" } } ] } }, "size": 5, "from": 0 } '
として実行されます。
結果
{ "took" : 8, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 0.5753642, "hits" : [ { "_index" : "my_index", "_type" : "my_type", "_id" : "2", "_score" : 0.5753642, "_source" : { "title" : "Keeping pets healthy", "body" : "My quick brown fox eats rabbits on a regular basis." } }, { "_index" : "my_index", "_type" : "my_type", "_id" : "1", "_score" : 0.2876821, "_source" : { "title" : "Quick brown rabbits", "body" : "Brown rabbits are commonly seen." } } ] } }
dis_max
なのでマッチ率が高いフィールドを持つドキュメントを上位に持ってきてくれますね。
operatorにandを使うとどうなる?
上記dis_max
クエリで分かるように、各フィールド毎にbrown fox
で検索されます。
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_search?pretty -d ' { "query": { "multi_match": { "query": "brown fox", "type": "best_fields", "fields": ["title", "body"], "operator": "and" } }, "size": 5, "from": 0 } '
結果はbrown fox
というフレーズが入ったdocument2
のみ返ってきます。
{ "took" : 179, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 0.5753642, "hits" : [ { "_index" : "my_index", "_type" : "my_type", "_id" : "2", "_score" : 0.5753642, "_source" : { "title" : "Keeping pets healthy", "body" : "My quick brown fox eats rabbits on a regular basis." } } ] } }
most_fields
クエリ
most_fields
はbool
クエリとしてラップされるので
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_search?pretty -d ' { "query": { "multi_match": { "query": "brown fox", "type": "most_fields", "fields": ["title", "body"] } }, "size": 5, "from": 0 } '
というクエリは
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_search?pretty -d ' { "query": { "bool": { "should": [ { "match": { "title": "brown fox" } }, { "match": { "body": "brown fox" } } ] } }, "size": 5, "from": 0 } '
として実行されます。
結果
document1
とdocument2
の両方が返りました。
今回は偶然スコアが同じになりましたね。
{ "took" : 15, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 0.5753642, "hits" : [ { "_index" : "my_index", "_type" : "my_type", "_id" : "2", "_score" : 0.5753642, "_source" : { "title" : "Keeping pets healthy", "body" : "My quick brown fox eats rabbits on a regular basis." } }, { "_index" : "my_index", "_type" : "my_type", "_id" : "1", "_score" : 0.5753642, "_source" : { "title" : "Quick brown rabbits", "body" : "Brown rabbits are commonly seen." } } ] } }
operatorにandを使うとどうなる?
上記bool
クエリで分かるように、各フィールド毎にbrown fox
で検索されます。
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_search?pretty -d ' { "query": { "multi_match": { "query": "brown fox", "type": "most_fields", "fields": ["title", "body"], "operator": "and" } }, "size": 5, "from": 0 } '
best_fields
と同様に、brown fox
というフレーズが入ったdocument2
のみ返ってきます。
{ "took" : 10, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 0.5753642, "hits" : [ { "_index" : "my_index", "_type" : "my_type", "_id" : "2", "_score" : 0.5753642, "_source" : { "title" : "Keeping pets healthy", "body" : "My quick brown fox eats rabbits on a regular basis." } } ] } }
cross_fields
best_fields
やmost_fields
は有用な一方で、operator: and
の時に期待しないケースがあります。
例えばfirst_name
、last_name
というフィールドがあり、検索クエリにfirst_name last_name
とした時にbest_fields
やmost_fields
では検索結果に出てこなくなります。
best_fields
やmost_fields
は各フィールドに対してAND検索を行うためです。
will smith
と検索した時に
+(first_name:will last_name:will) +(first_name:smith last_name:smith)
のようにフィールドをまたいでAND
検索をしたい時にこのcross_fields
を使います。
クエリ
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_search?pretty -d ' { "query": { "multi_match": { "query": "will smith", "type": "cross_fields", "fields": ["first_name", "last_name"] } }, "size": 5, "from": 0 } '
結果
document4
のスコアが高いです。
default_operatorはOR
のため、smith
の影響でdocument3
も一応返っています。
{ "took" : 13, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 0.5753642, "hits" : [ { "_index" : "my_index", "_type" : "my_type", "_id" : "3", "_score" : 0.5753642, "_source" : { "first_name" : "Will", "last_name" : "Smith" } }, { "_index" : "my_index", "_type" : "my_type", "_id" : "4", "_score" : 0.2876821, "_source" : { "first_name" : "Smith", "last_name" : "Jones" } } ] } }
クエリのexplain
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_validate/query?explain-d ' { "query": { "multi_match": { "query": "will smith", "type": "cross_fields", "fields": ["first_name", "last_name"] } } } '
結果
+(blended(terms:[first_name:will, last_name:will]) blended(terms:[first_name:smith, last_name:smith]))
これを見て分かるように、first_name
とlast_name
が結合されて1つのフィールドとして扱われ、それに対して各ワードが検索されています。
operatorにandを使うとどうなる?
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_search?pretty -d ' { "query": { "multi_match": { "query": "will smith", "type": "cross_fields", "fields": ["first_name", "last_name"], "operator": "and" } }, "size": 5, "from": 0 } '
document4
だけが返るようになりました。
{ "took" : 10, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 0.5753642, "hits" : [ { "_index" : "my_index", "_type" : "my_type", "_id" : "3", "_score" : 0.5753642, "_source" : { "first_name" : "Will", "last_name" : "Smith" } } ] } }
クエリのexplain
curl -s -H "Content-Type: application/json" \ localhost:9200/my_index/my_type/_validate/query?explain -d ' { "query": { "multi_match": { "query": "will smith", "type": "cross_fields", "fields": ["first_name", "last_name"], "operator": "and" } } } '
結果
+(+blended(terms:[first_name:will, last_name:will]) +blended(terms:[first_name:smith, last_name:smith]))
先ほどと同様にfirst_name
とlast_name
が結合されて1つのフィールドとして扱われ、その上でAND検索されています。
document3
はWill Smith
という1つのフィールドにdocuemnt4
はSmith Jones
という1つのフィールドに
なるため、will smith
でAND検索した場合document3
が返ることになります。
まとめ
Multi Match Queryは区別がしにくいため、きちんと用途を把握した上で使用すると良いです。