最佳欄位(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
查詢是如何計算得到其分值的:
- 執行
should
子句中的兩個查詢 - 相加查詢返回的分值
- 將相加得到的分值乘以匹配的查詢子句的數量
- 除以總的查詢子句的數量
文件1在兩個欄位中都包含了brown
,因此兩個match
查詢都匹配成功並擁有了一個分值。文件2在body
欄位中包含了brown
以及fox
,但是在title
欄位中沒有出現任何搜尋的單詞。因此對body
欄位查詢得到的高分加上對title
欄位查詢得到的零分,然後在乘以匹配的查詢子句數量1,最後除以總的查詢子句數量2,導致整體分值比文件1的低。
在這個例子中,title
和body
欄位是互相競爭的。我們想要找到一個最佳匹配(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_max
和bool
的一種折中。它會通過下面的方式改變分值計算過程:
- 取得最佳匹配查詢子句的
_score
。 - 將其它每個匹配的子句的分值乘以
tie_breaker
。 - 將以上得到的分值進行累加並規範化。
通過tie_breaker
引數,所有匹配的子句都會起作用,只不過最佳匹配子句的作用更大。
NOTE
tie_breaker
的取值範圍是0
到1
之間的浮點數,取0
時即為僅使用最佳匹配子句(譯註:和不使用tie_breaker
引數的dis_max
查詢效果相同),取1
則會將所有匹配的子句一視同仁。它的確切值需要根據你的資料和查詢進行調整,但是一個合理的值會靠近0
,(比如,0.1
-0.4
),來確保不會壓倒dis_max
查詢具有的最佳匹配性質。
原文地址 https://blog.csdn.net/dm_vincent/article/details/41820537