[Elasticsearch] 多字段搜索 (二) - 最佳字段查詢及其調優


最佳字段(Best Fields)

 

假設我們有一個讓用戶搜索博客文章的網站,就像這兩份文檔一樣:

PUT /my_index/my_type/1
{
    "title": "Quick brown rabbits", "body": "Brown rabbits are commonly seen." } PUT /my_index/my_type/2 { "title": "Keeping pets healthy", "body": "My quick brown fox eats rabbits on a regular basis." }

用戶輸入了"Brown fox",然后按下了搜索鍵。我們無法預先知道用戶搜索的詞條會出現在博文的title或者body字段中,但是用戶是在搜索和他輸入的單詞相關的內容。以上的兩份文檔中,文檔2似乎匹配的更好一些,因為它包含了用戶尋找的兩個單詞。

讓我們運行下面的bool查詢:

{
    "query": { "bool": { "should": [ { "match": { "title": "Brown fox" }}, { "match": { "body": "Brown fox" }} ] } } }

然后我們發現文檔1的分值更高:

{
  "hits": [ { "_id": "1", "_score": 0.14809652, "_source": { "title": "Quick brown rabbits", "body": "Brown rabbits are commonly seen." } }, { "_id": "2", "_score": 0.09256032, "_source": { "title": "Keeping pets healthy", "body": "My quick brown fox eats rabbits on a regular basis." } } ] }

要理解原因,想想bool查詢是如何計算得到其分值的:

  1. 運行should子句中的兩個查詢
  2. 相加查詢返回的分值
  3. 將相加得到的分值乘以匹配的查詢子句的數量
  4. 除以總的查詢子句的數量

文檔1在兩個字段中都包含了brown,因此兩個match查詢都匹配成功並擁有了一個分值。文檔2在body字段中包含了brown以及fox,但是在title字段中沒有出現任何搜索的單詞。因此對body字段查詢得到的高分加上對title字段查詢得到的零分,然后在乘以匹配的查詢子句數量1,最后除以總的查詢子句數量2,導致整體分值比文檔1的低。

在這個例子中,titlebody字段是互相競爭的。我們想要找到一個最佳匹配(Best-matching)的字段。

如果我們不是合並來自每個字段的分值,而是使用最佳匹配字段的分值作為整個查詢的整體分值呢?這就會讓包含有我們尋找的兩個單詞的字段有更高的權重,而不是在不同的字段中重復出現的相同單詞。

dis_max查詢

相比使用bool查詢,我們可以使用dis_max查詢(Disjuction Max Query)。Disjuction的意思"OR"(而Conjunction的意思是"AND"),因此Disjuction Max Query的意思就是返回匹配了任何查詢的文檔,並且分值是產生了最佳匹配的查詢所對應的分值:

{
    "query": { "dis_max": { "queries": [ { "match": { "title": "Brown fox" }}, { "match": { "body": "Brown fox" }} ] } } }

它會產生我們期望的結果:

{
  "hits": [ { "_id": "2", "_score": 0.21509302, "_source": { "title": "Keeping pets healthy", "body": "My quick brown fox eats rabbits on a regular basis." } }, { "_id": "1", "_score": 0.12713557, "_source": { "title": "Quick brown rabbits", "body": "Brown rabbits are commonly seen." } } ] }

 

 

最佳字段查詢的調優

 

如果用戶搜索的是"quick pets",那么會發生什么呢?兩份文檔都包含了單詞quick,但是只有文檔2包含了單詞pets。兩份文檔都沒能在一個字段中同時包含搜索的兩個單詞。

一個像下面那樣的簡單dis_max查詢會選擇出擁有最佳匹配字段的查詢子句,而忽略其他的查詢子句:

{
    "query": { "dis_max": { "queries": [ { "match": { "title": "Quick pets" }}, { "match": { "body": "Quick pets" }} ] } } }
{
  "hits": [ { "_id": "1", "_score": 0.12713557, "_source": { "title": "Quick brown rabbits", "body": "Brown rabbits are commonly seen." } }, { "_id": "2", "_score": 0.12713557, "_source": { "title": "Keeping pets healthy", "body": "My quick brown fox eats rabbits on a regular basis." } } ] }

可以發現,兩份文檔的分值是一模一樣的。

我們期望的是同時匹配了title字段和body字段的文檔能夠擁有更高的排名,但是結果並非如此。需要記住:dis_max查詢只是簡單的使用最佳匹配查詢子句得到的_score

tie_breaker

但是,將其它匹配的查詢子句考慮進來也是可能的。通過指定tie_breaker參數:

{
    "query": { "dis_max": { "queries": [ { "match": { "title": "Quick pets" }}, { "match": { "body": "Quick pets" }} ], "tie_breaker": 0.3 } } }

它會返回以下結果:

{
  "hits": [ { "_id": "2", "_score": 0.14757764, "_source": { "title": "Keeping pets healthy", "body": "My quick brown fox eats rabbits on a regular basis." } }, { "_id": "1", "_score": 0.124275915, "_source": { "title": "Quick brown rabbits", "body": "Brown rabbits are commonly seen." } } ] }

現在文檔2的分值比文檔1稍高一些。

tie_breaker參數會讓dis_max查詢的行為更像是dis_maxbool的一種折中。它會通過下面的方式改變分值計算過程:

  1. 取得最佳匹配查詢子句的_score
  2. 將其它每個匹配的子句的分值乘以tie_breaker
  3. 將以上得到的分值進行累加並規范化。

通過tie_breaker參數,所有匹配的子句都會起作用,只不過最佳匹配子句的作用更大。

NOTE

tie_breaker的取值范圍是01之間的浮點數,取0時即為僅使用最佳匹配子句(譯注:和不使用tie_breaker參數的dis_max查詢效果相同),取1則會將所有匹配的子句一視同仁。它的確切值需要根據你的數據和查詢進行調整,但是一個合理的值會靠近0,(比如,0.1 -0.4),來確保不會壓倒dis_max查詢具有的最佳匹配性質。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM