[Elasticsearch] 多欄位搜尋 (二) - 最佳欄位查詢及其調優(轉)

weixin_33912246發表於2018-04-08

最佳欄位(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查詢具有的最佳匹配性質。

 

原文地址 https://blog.csdn.net/dm_vincent/article/details/41820537

相關文章