概要
以前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は区別がしにくいため、きちんと用途を把握した上で使用すると良いです。