multi_match
查詢提供了一個簡便的方法用來對多個字段執行相同的查詢。
NOTE
存在幾種類型的
multi_match
查詢,其中的3種正好和在"了解你的數據"一節中提到的幾種類型相同:best_fields
,most_fields
以及cross_fields
。
默認情況下,該查詢以best_fields
類型執行,它會為每個字段生成一個match
查詢,然後將這些查詢包含在一個dis_max
查詢中。下面的dis_max
查詢:
{ "dis_max": { "queries": [ { "match": { "title": { "query": "Quick brown fox", "minimum_should_match": "30%" } } }, { "match": { "body": { "query": "Quick brown fox", "minimum_should_match": "30%" } } }, ], "tie_breaker": 0.3 } }
可以通過multi_match
簡單地重寫如下:
{ "multi_match": { "query": "Quick brown fox", "type": "best_fields", "fields": [ "title", "body" ], "tie_breaker": 0.3, "minimum_should_match": "30%" } }
注意到以上的type
屬性為best_fields
。 minimum_should_match
和operator
參數會被傳入到生成的match
查詢中。
字段名可以通過通配符指定:任何匹配了通配符的字段都會被包含在搜索中。你可以通過下面的查詢來匹配book_title
,chapter_title
以及section_title
字段:
{ "multi_match": { "query": "Quick brown fox", "fields": "*_title" } }
個別字段可以通過caret語法(^
)進行提升:僅需要在字段名後添加^boost
,其中的boost
是一個浮點數:
{ "multi_match": { "query": "Quick brown fox", "fields": [ "*_title", "chapter_title^2" ] } }
chapter_title
字段的boost
值為2
,而book_title
和section_title
字段的boost
值為默認的1
。
全文搜索是一場召回率(Recall) - 返回所有相關的文檔,以及准確率(Precision) - 不返回無關文檔,之間的戰斗。目標是在結果的第一頁給用戶呈現最相關的文檔。
為了提高召回率,我們會廣撒網 - 不僅包括精確匹配了用戶搜索詞條的文檔,還包括了那些我們認為和查詢相關的文檔。如果一個用戶搜索了"quick brown fox",一份含有fast foxes的文檔也可以作為一個合理的返回結果。
如果我們擁有的相關文檔僅僅是含有fast foxes的文檔,那麼它會出現在結果列表的頂部。但是如果我們有100份含有quick brown fox的文檔,那麼含有fast foxes的文檔的相關性就會變低,我們希望它出現在結果列表的後面。在包含了許多可能的匹配後,我們需要確保相關度高的文檔出現在頂部。
一個用來調優全文搜索相關性的常用技術是將同樣的文本以多種方式索引,每一種索引方式都提供了不同相關度的信號(Signal)。主要字段(Main field)中含有的詞條的形式是最寬泛的(Broadest-matching),用來盡可能多的匹配文檔。比如,我們可以這樣做:
但是,如果我們有兩份文檔,其中之一含有jumped,而另一份含有jumping,那麼用戶會希望第一份文檔的排序會靠前,因為它含有用戶輸入的精確值。
我們可以通過將相同的文本索引到其它字段來提供更加精確的匹配。一個字段可以包含未被提取詞干的版本,另一個則是含有變音符號的原始單詞,然後第三個使用了shingles,用來提供和單詞鄰近度相關的信息。這些其它字段扮演的角色就是信號(Signals),它們用來增加每個匹配文檔的相關度分值。能夠匹配的字段越多,相關度就越高。
如果一份文檔能夠匹配具有最寬泛形式的主要字段(Main field),那麼它就會被包含到結果列表中。如果它同時也匹配了信號字段,它會得到一些額外的分值用來將它移動到結果列表的前面。
我們會在本書的後面討論同義詞,單詞鄰近度,部分匹配以及其他可能的信號,但是我們會使用提取了詞干和未提取詞干的字段的簡單例子來解釋這個技術。
第一件事就是將我們的字段索引兩次:一次是提取了詞干的形式,一次是未提取詞干的形式。為了實現它,我們會使用多字段(Multifields),在字符串排序和多字段中我們介紹過:
DELETE /my_index PUT /my_index { "settings": { "number_of_shards": 1 }, "mappings": { "my_type": { "properties": { "title": { "type": "string", "analyzer": "english", "fields": { "std": { "type": "string", "analyzer": "standard" } } } } } } }
title字段使用了english解析器進行詞干提取。 title.std字段則使用的是standard解析器,因此它沒有進行詞干提取。
下一步,我們會索引一些文檔:
PUT /my_index/my_type/1 { "title": "My rabbit jumps" } PUT /my_index/my_type/2 { "title": "Jumping jack rabbits" }
以下是一個簡單的針對title字段的match查詢,它查詢jumping rabbits:
GET /my_index/_search { "query": { "match": { "title": "jumping rabbits" } } }
它會變成一個針對兩個提干後的詞條jump和rabbit的查詢,這要得益於english解析器。兩份文檔的title字段都包含了以上兩個詞條,因此兩份文檔的分值是相同的:
{ "hits": [ { "_id": "1", "_score": 0.42039964, "_source": { "title": "My rabbit jumps" } }, { "_id": "2", "_score": 0.42039964, "_source": { "title": "Jumping jack rabbits" } } ] }
如果我們只查詢title.std字段,那麼只有文檔2會匹配。但是,當我們查詢兩個字段並將它們的分值通過bool查詢進行合並的話,兩份文檔都能夠匹配(title字段也匹配了),而文檔2的分值會更高一些(匹配了title.std字段):
GET /my_index/_search { "query": { "multi_match": { "query": "jumping rabbits", "type": "most_fields", "fields": [ "title", "title.std" ] } } }
在上述查詢中,由於我們想合並所有匹配字段的分值,因此使用的類型為most_fields。這會讓multi_match查詢將針對兩個字段的查詢子句包含在一個bool查詢中,而不是包含在一個dis_max查詢中。
{ "hits": [ { "_id": "2", "_score": 0.8226396, "_source": { "title": "Jumping jack rabbits" } }, { "_id": "1", "_score": 0.10741998, "_source": { "title": "My rabbit jumps" } } ] }
此時,文檔2的分值比文檔1的高許多。
我們使用了擁有寬泛形式的title字段來匹配盡可能多的文檔 - 來增加召回率(Recall),同時也使用了title.std字段作為信號來讓最相關的文檔能夠擁有更靠前的排序(譯注:增加了准確率(Precision))。
每個字段對最終分值的貢獻可以通過指定boost值進行控制。比如,我們可以提升title字段來讓該字段更加重要,這也減小了其它信號字段的影響:
GET /my_index/_search { "query": { "multi_match": { "query": "jumping rabbits", "type": "most_fields", "fields": [ "title^10", "title.std" ] } } }