ElasticSearch 之 dis_max tie_break的應用

1. 插入數據

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."
}

2. 搜索

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

3. 結果

{
  "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."
        }
     }
  ]
}

  理論上 2 的文檔實際上是咱們須要的,可是搜索的結果不是。要理解緣由,須要知道 bool 查詢時如何計算獲得其分值的:ui

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

  文檔1 在兩個字段中都包含了 brown,所以兩個 match 查詢都匹配成功並擁有了一個分值。文檔2 在 body 字段中包含了 brown 以及 fox,可是在 title 字段中沒有出現任何搜索的單詞。所以對 body 字段查詢獲得的高分加上對 title字段查詢獲得的零分,而後再乘以匹配的查詢子句數量1,最後除以總的查詢子句數量2,致使總體分值比文檔1 的低。具體計算過程以下:spa

score_1 = (1 + 1) * 2 / 2 = 2
score_2 = (0 + 2) * 1 / 2 = 1

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

  若是咱們不是合併來自每一個字段的分值,而是使用最佳匹配字段的分值做爲整個查詢的總體分值呢?這就會讓包含有咱們尋找的兩個單詞的字段有更高的權重,而不是在不一樣的字段中重複出現的相同單詞。code

4. dis_max 查詢

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

{
    "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."
        }
     }
  ]
}

5. 最佳字段查詢調優

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

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

查詢語句:
{
    "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."
        }
     }
   ]
}

  能夠發現,兩份文檔的分值是如出一轍的。class

  咱們指望的是同時匹配了title字段 和 body字段 的文檔可以擁有更高的排名,可是結果並不是如此。須要記住:dis_max 查詢只是簡單的使用最佳匹配查詢子句獲得的_score搜索

6. 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的一種折中。它會經過下面的方式改變分值計算過程:

  1. 取得最佳匹配查詢子句的_score。

  2. 將其它每一個匹配的子句的分值乘以tie_breaker。

  3. 將以上獲得的分值進行累加並規範化。

經過tie_breaker參數,全部匹配的子句都會起做用,只不過最佳匹配子句的做用更大。

相關文章
相關標籤/搜索