Elasticsearch Query DSL 整理總結(四)—— Multi Match Query
目錄
該做的事情一定要做,決心要做的事情一定要做好
——本傑明·富蘭克林
引言
最近很喜歡使用思維導圖來學習總結知識點,如果你對思維導圖不太瞭解,又非常感興趣,請來看下這篇文章。書歸正傳,這次介紹下 MutiMatch, 正文之前,請先看下本文的思維導圖預熱下:
概要
multi_match
查詢建立在 match
查詢之上,重要的是它允許對多個欄位查詢。
先構建一個例項, multimatch_test
中設定了兩個欄位 subject
和 message
, 使用 fields
引數在兩個欄位上都查詢 multimatch
,從而得到了兩個匹配文件。
PUT multimatchtest { } PUT multimatchtest/_mapping/multimatch_test { "properties": { "subject": { "type": "text" }, "message": { "type": "text" } } } PUT multimatchtest/multimatch_test/1 { "subject": "this is a multimatch test", "message": "blala blalba" } PUT multimatchtest/multimatch_test/2 { "subject": "blala blalba", "message": "this is a multimatch test" } GET multimatchtest/multimatch_test/_search { "query": { "multi_match": { "query": "multimatch", "fields": ["subject", "message"] } } }
下面來講解下 fields
引數的使用
fields 欄位
萬用字元
fields
欄位中的值支援萬用字元 *
, 設定 mess*
依舊可以查詢出 message
欄位中的匹配。
GET multimatchtest/multimatch_test/_search { "query": { "multi_match": { "query": "multimatch", "fields": ["subject", "mess*"] } } }
提升欄位權重
在查詢欄位後使用 ^
符號可以提高欄位的權重,增加欄位的分數 _score
。例如,我們想增加 subject
欄位的權重。
GET multimatchtest/multimatch_test/_search { "query": { "multi_match": { "query": "multimatch", "fields": ["subject^3", "mess*"] } } }
雖然文件 1 和 文件 2 中都含有相同數量的 multimatch
詞條,但可以看出,搜尋結果中 subject
中含有 multimatch
的分數是另一個文件的 3 倍。
"hits": { "total": 2, "max_score": 0.8630463, "hits": [ { "_index": "multimatchtest", "_type": "multimatch_test", "_id": "1", "_score": 0.8630463, "_source": { "subject": "this is a multimatch test", "message": "blala blalba" } }, { "_index": "multimatchtest", "_type": "multimatch_test", "_id": "2", "_score": 0.2876821, "_source": { "subject": "blala blalba", "message": "this is a multimatch test" } } ] } }
如果在 multimatch
查詢中不指定 fields
引數,預設會將文件中的所有欄位都匹配一遍。但不建議這麼做,可能會出現效能問題,也沒有什麼意義。
multi_multich 查詢的型別
multi_match
查詢內部到底如何執行主要取決於它的 type
引數,這個引數的可取得值如下
-
best_fields
是預設型別,會將任何與查詢匹配的文件作為結果返回,但是隻使用最佳欄位的 _score 評分作為評分結果返回。 -
most_fields
將任何與查詢匹配的文件作為結果返回,並所有匹配欄位的評分合並起來 -
phrase
在fields
中的每個欄位上均執行match_phrase
查詢,並將最佳欄位的 _score 作為結果返回 -
phrase_prefix
在fields
中的欄位上均執行match_phrase_prefix
查詢,並將每個欄位的分數進行合併
下面我們來依次檢視寫這些型別的意義和具體使用。
best_fields 型別
要搞懂 best_fields
型別,首先要了解下 dis_max
。
dis_max 分離最大化查詢
dis_max
查詢英文全稱為 Disjunction Max Query 就是分離最大化查詢的意思。
- 分離(Disjunction)的意思是 或(or) ,表示把同一個文件中每個欄位上的查詢都分離開,分別計算出分數。
- 分離最大化查詢(Disjunction Max Query)指的是: 將任何與任一查詢匹配的文件作為結果返回,但 只將最佳匹配的評分作為查詢的評分結果返回
來看一個例子, 我們將上面兩個文件的內容重寫
PUT multimatchtest/multimatch_test/1 { "subject": "food is delicious!", "message": "cook food" } PUT multimatchtest/multimatch_test/2 { "subject": "blabla blala", "message": "I like chinese food" }
這時我們在 subject
和 message
兩個欄位上都查詢 chinese food
,看得到什麼結果?(我們先不使用 multimatch
而是 match
)
GET multimatchtest/multimatch_test/_search { "query": { "dis_max": { "queries": [ { "match": { "subject": "chinese food" } }, { "match": { "message": "chinese food" } } ] } } }
而得到的結果則是
"hits": { "total": 2, "max_score": 0.5753642, "hits": [ { "_index": "multimatchtest", "_type": "multimatch_test", "_id": "2", "_score": 0.5753642, "_source": { "subject": "blabla blala", "message": "I like chinese food" } }, { "_index": "multimatchtest", "_type": "multimatch_test", "_id": "1", "_score": 0.2876821, "_source": { "subject": "food is delicious!", "message": "cook food" } } ] } }
雖然文件 1 中的 subject
和 message
欄位中都含有 food
能夠匹配到,但由於使用的 dis_max
查詢,只會將它們單獨計算得分,而文件 2 中只有 message
匹配到,但是它的分數更高。由此比較,文件 2 的得分當然比文件 1 高,而這就是 best_fields
型別的計算方式。
best_fields
上個小節中的 dis_max
查詢則直接就可以用
best_fields
在查詢多個詞條最佳匹配度方面是最有用的,它和 dis_max 方式是等價的。例如,上節中的 dis_max
查詢就可以寫成下面的形式。而且 best_fields
型別是 multi_match
查詢時的預設型別。
GET multimatchtest/multimatch_test/_search { "query": { "multi_match": { "query": "chinese food", "fields": ["subject", "message"] } } }
按照這種方式,只是最佳匹配語句起作用,其他語句對分數一點貢獻度也沒有了。這樣太純粹了似乎也不太好。有沒有折中的辦法,其他語句也參與評分,只不過要打下折扣,讓它們的貢獻度不那麼高?嗯,還真有,這就是 tie_breaker
引數。
維權使者 tie_breaker
感覺 tie_breaker
引數就是為了維護其他語句的權利而生的,先了解下它的評分方式:
- 先由
best_fields
type 獲得最佳匹配語句的評分_score
。 - 將其他匹配語句的評分結果與
tie_breaker
相乘。 - 對以上評分求和並規範化。
有了 tie_breaker
,世界變得更美好了,在計算時會考慮所有匹配語句,但 tie_breaker
並沒有喧賓奪主, 最佳匹配語句依然是老大,但其他語句在 tie_breaker
的幫助下也有了一定的話語權。
將上節查詢語句新增一個 tie_breaker
引數才來看結果。
GET multimatchtest/multimatch_test/_search { "query": { "multi_match": { "query": "chinese food", "fields": ["subject", "message"], "tie_breaker": 0.3 } } }
結果如下:
"hits": { "total": 2, "max_score": 0.5753642, "hits": [ { "_index": "multimatchtest", "_type": "multimatch_test", "_id": "2", "_score": 0.5753642, "_source": { "subject": "blabla blala", "message": "I like chinese food" } }, { "_index": "multimatchtest", "_type": "multimatch_test", "_id": "1", "_score": 0.37398672, "_source": { "subject": "food is delicious!", "message": "cook food" } } ] }
和上節的文件 1 的評分對比,由於文件 1 中 message
欄位和 subject
都只有一個 "food" 單詞,它們的評分是一樣的,且 tie_breaker
為 0.3,那就相當於 0.2876821x1.3=0.37398672
,正好與結果吻合。
開篇時我們就說到, multi-match
查詢是構建在 match
查詢基礎上的,因此 match
查詢的引數, multi-match
都可以使用,可以參考我之前寫的match query 文件來檢視。
most_fields
most_fields 主要用在多個欄位都包含相同的文字的場合,會將所有欄位的評分合並起來。
GET multimatchtest/multimatch_test/_search { "query": { "multi_match": { "query": "multimatch", "fields": ["subject", "message"], "type": "most_fields" } } }
phrase 和 phrase_prefix
phrase
和 phrase_prefix
型別的行為與 best_fields
引數類似,區別就是
-
phrase
使用match_phrase
&dis_max
實現 -
phrase_prefix
使用match_phrase_prefix
&dis_max
實現 -
best_fields
使用match
&dis_max
實現
GET multimatchtest/multimatch_test/_search { "query": { "multi_match": { "query": "this is", "fields": ["subject", "message"], "type": "phrase" } } }
上面查詢等價於
GET multimatchtest/multimatch_test/_search { "query": { "dis_max": { "queries": [{ "match_phrase": { "subject": "this is" } }, { "match_phrase": { "message": "this is" } }] } } }
cross_fields
像 most_fields
和 best_fields
型別都是詞中心式(field-centric),什麼意思呢?舉個例子,假如要查詢 "blabla like" 字串,並且指定 operator
為 and
,則會在同一個欄位內搜尋整個字串,只有一個欄位內都有這兩個詞,才匹配上。
GET multimatchtest/_search { "query": { "multi_match": { "query": "blabla like", "operator": "and", "fields": [ "subject", "message"], "type": "best_fields" } } }
而 cross_fields 型別則是欄位中心式的,例如,要查詢 "blabla like" 字串,查詢欄位為 "subject" 和 "message"。此時首先分析查詢字串並生成一個詞列表,然後 從所有欄位中依次搜尋每個詞 ,只要查詢到,就算匹配上。
GET multimatchtest/_search { "query": { "multi_match": { "query": "blabla like", "operator": "and", "fields": [ "subject", "message"], "type": "cross_fields" } } }
評分
那麼 cross_fields 的評分是怎麼完成的呢?
cross_fields
也有 tie_breaker
配置,就是由它來控制 cross_fields
的評分。tie_breaker 的取值及意義如下:
0.0 1.0 0.0 < n < 1.0
GET multimatchtest/_search { "query": { "multi_match": { "query": "blabla like", "fields": [ "subject", "message"], "type": "cross_fields", "tie_breaker": 0.5 } } }
小結
Muti-Match 是非常常用的全文搜尋,它構建在 Match 查詢的基礎上,同時又添加了許多型別來符合多欄位搜尋的場景。最後,請在通過思維導圖一起來回顧下本節的知識點吧.
參考
ofollow,noindex" target="_blank">https://www.elastic.co/guide/en/elasticsearch/reference/6.3/query-dsl-multi-match-query.html