從我們在elasticsearch復合框輸入搜索語句到結果顯示,展現給我們的是一個按score得分從高到底排好序的結果集。下面就來學習下elasticsearch怎樣計算得分。
Lucene(或 Elasticsearch)使用 布爾模型(Boolean model) 查找匹配文檔, 並用一個名為 實用評分函數(practical scoring function) 的公式來計算相關度。這個公式借鑒了 詞頻/逆向文檔頻率(term frequency/inverse document frequency) 和 向量空間模型(vector space model),同時也加入了一些現代的新特性,如協調因子(coordination factor),字段長度歸一化(field length normalization),以及詞或查詢語句權重提升。
Lucene計算評分的公式:
這個評分公式有6個部分組成:
coord(q,d) 評分因子,基於文檔中出現查詢項的個數。越多的查詢項在一個文檔中,說明文檔的匹配程度越高。
queryNorm(q)查詢的標准查詢
tf(t in d) 指項t在文檔d中出現的次數frequency。具體值為次數的開根號。
idf(t) 反轉文檔頻率, 出現項t的文檔數docFreq
t.getBoost 查詢時候查詢項加權
norm(t,d) 長度相關的加權因子
1、tf(t in d) 詞頻
tf(t in d) = √frequency
即出現的個數進行開方,這個沒什么可以講述的,實際打分也是如此。
2、idf(t)反轉文檔頻率
這個的意思是出現的逆詞頻數,即召回的文檔在總文檔中出現過多少次,這個的計算在ES中與lucene中有些區別,只有在分片數為1的情況下,與lucene的計算是一致的,如果不唯一,那么每一個分片都有一個不同的idf的值,它的計算方式如下所示:
idf(t) = 1 + log ( numDocs / (docFreq + 1))
其中,log是以e為底的,不是以10或者以2為底,這點需要注意,numDocs是指所有的文檔個數,如果有分片的話,就是指的是在當前分片下總的文檔個數,docFreq是指召回文檔的個數,如果有分片對應的也是在當前分片下召回的個數,這點是計算的時候與lucene不同之處,如果想驗證是否正確,只需將分片shard的個數設置為1即可
3、queryNorm(q)查詢的標准查詢
queryNorm(q) = 1 / √sumOfSquaredWeights
上述公式是ES官網的公式,這是在默認query boost為1,並且在默認term boost為1 的情況下的打分,其中
sumOfSquaredWeights =idf(t1)*idf(t1)+idf(t2)*idf(t2)+...+idf(tn)*idf(tn)
其中n為在query里面切成term的個數,但是上面全部是在默認為1的情況下的計算,實際上的計算公式如下所示:
4、coord(q,d)協調因子
coord(q,d)=overlap / maxoverlap
其中overlap是檢索命中query中term的個數,maxoverlap是query中總共的term個數
5、t.getboost()
對於每一個term的權值,沒仔細研究這個項,個人理解的是,如果對一個field設置boost,那么如果在這個boost召回的話,每一個term的boost都是該field的boost
6、norm(t,d)
對於field的標准化因子,在官方給的解釋是field越短,如果召回的話權重越大
其中d.getboost表明如果該文檔權重越大那么久越重要
f.getboost表明該field的權值越大,越重要
lengthnorm表示該field越長,越不重要,越短,越重要,在官方文檔給出的公式中,默認boost全部為1,在此給出官方文檔的打分公式:
norm(d) = 1 / √numTerms
如查詢解析語句得分:
_search?explain
{
"query": {
"multi_match": {
"query": "居夷集第三卷",
"fields": [
"title",
"keywords",
"author"
]
}
}
}
multi_match默認選擇fields中得分最高的作為最終的得分相當於max(field_score),如下圖:紅框1的得分是屬性keywords中居在文檔id=51時的得分(紅框1= 紅框2 * 紅框3)
具體的計算公式:紅框2處的得分由 queryWeight * fieldWeight 兩部分的乘積組成。詞頻tf(t),反向文檔頻率idf(t)
如:queryWeight = idf(t) * queryNorm(d)
idf(t) = ( 1+ln( maxDocs / (docFreq +1 ) ) ) = (1 + ln ( 3091 / ( 2 +1 )) 注意這個是自然對數
再來看一個只有一個分片的索引來加深剛才的計算,其實在es的head插件中顯示的得分的計算公式實際可以對應的就是:
sumScore = partScore1 +partScore2 +partScore3 + ...
partScore = queryWeight * fieldWeight
queryWeight = idf(t) * queryNorm(d)
fieldweight = idf * tf * fieldnorm
總得分:0.5216244 = 0.119818024 + 0.119818024 + 0.119818024 + 0.119818024 + 0.04235228
partScore 1 : 0.119818024 = 0.4792721 * 0.25
queryWeight : 0.4792721 =( 1 + ln( 2 / (1+1 ) ) ) * (1/ √ (1*1+1*1+1*1+1*1 + 0.5945349*0.5945349))
fieldweight : 0.25 = 1 * 1 * 0.25
Reference:
[1] http://blog.csdn.net/molong1208/article/details/50623948
[2] http://www.cnblogs.com/forfuture1978/archive/2010/03/07/1680007.html
[3] https://www.elastic.co/guide/cn/elasticsearch/guide/current/scoring-theory.html