本來沒有這篇文章,在公司分享ES的時候遇到一個問題,使用boost的時候,怎么從評分score中知道boost的影響。
雖然我們從查詢結果可以直觀看到,boost起了應有的作用,但是在explain的時候,找了很久也不明白,boost去哪了?
這個問題花了點時間,不過還是挺值得。由於沒有直接用過lucene,也從沒想過到lucene網站上去看文檔。在Elastic的文檔中發現這樣一段描述
In fact, reading the explain output is a little more complex than that. You won’t see the boost value or t.getBoost() mentioned in the explanation at all. Instead, the boost is rolled into the queryNorm that is applied to a particular term. Although we said that the queryNorm is the same for every term, you will see that the queryNorm for a boosted term is higher than the queryNorm for an unboosted term.
大概是說:從explain中尋找boost是有點復雜的,因為它被放到queryNorm的計算當中了
queryNorm是怎么計算的?
首先我們應該很容易在explain計算idf的部分看到queryNorm。而計算queryNorm的方式是在底部序號為1的lucene的參考資料中。
計算公式如下:
從這里可以看出我們還需要一個公式
有了這兩個公式,我們就可以計算queryNorm,而queryNorm中融合了t.getBoost()這就是我們所關心Boost。顯然Boost不是簡單的乘上了某個倍數,所以我們很難直觀的從score中看到,評分被乘了10或者20,也是我們在分享的時候,找了半天,也沒有找到一個整數倍數的原因。
在Elasticsearch 中queryNorm是怎么計算的?
雖然我們有了公式,不過利用公式帶入到我們的查詢參數中會發現,數值還是有點偏差,有一些細節在公式中並沒有體現。通過實驗(非源碼)我大概能了解計算方式,這里我就舉一個實際例子來看ES怎么計算queryNorm。
首先設計一條查詢語句,這里不討論idf的計算,設計的查詢中idf都是1
{
"size":30,
"query":{
"bool": {
"should": [
{
"match": {
"name": {
"query": "便宜了",
"boost": 1
}
}
},
{
"match": {
"server": {
"query": "電信",
"boost": 1
}
}
}
]
}
}
}
這是一個bool查詢,(Lucene (and thus Elasticsearch) uses the Boolean model to find matching documents,我們的很多查詢其實都被看做bool查詢,ES只是提供了比較友好的其他查詢方式,比如terms查詢就是一種bool的should查詢,或者直觀一點,就是or條件查詢)。boost都是1,默認的boost也是1。根據公式,我們按照每個字一個詞進行分詞的情況下,一共搜索了5個字,計算queryNorm的方式如下:
1/Math.sqrt(5) = 0.4472136
符合我們的預期,如果我們修改boost呢?
{
"size":30,
"query":{
"bool": {
"should": [
{
"match": {
"name": {
"query": "便宜了",
"boost": 4
}
}
},
{
"match": {
"server": {
"query": "電信",
"boost": 1
}
}
}
]
}
}
}
按照公式t.getBoost() 在計算“便宜了” 三個字的時候,應該要乘以4,計算公式應該是
1/Math.sqrt((1 * 4)^2 * 3 + 2 * 1) =0.1414213
但實際情況並非如此,其實ES在處理這個時候,如果較大的boost命中,es將小的那個值變成了0.25,也就是4分之一
1/Math.sqrt(3 + 2 * (1 * 0.25)^2) = 0.5656854
反過來如果,較大的boost沒有命中,就會放大較大的boost的影響,采用第一個算法取用 0.1414213,所以如果有兩個文檔分別命中:
- name命中, 無論server是否命中,采用0.5656854
- name無命中, server命中,采用,0.1414213
兩個數相除正好是4倍左右,可以看到,如果字數差距再大一些,倍數可能不是4,會有一定偏差
note:以上分析結果基於試驗,ES和lucene源碼不一定是這樣實現,畢竟公式可以各種變化計算來達到4倍差值。
參考資料: