Elasticsearch 的相似度算法被定義爲檢索詞頻率/反向文檔頻率, TF/IDF 。
一. 相關概念:
- 檢索詞頻率:tf
詞 t 在文檔 d 的詞頻( tf )是該詞在文檔中出現次數的平方根。
tf(t in d) = √frequency
檢索詞在該字段出現的頻率?出現頻率越高,相關性也越高。 字段中出現過 5 次要比只出現過 1 次的相關性高。
- 反向文檔頻率:idf
詞 t 的逆向文檔頻率( idf )是:索引中文檔數量除以所有包含該詞的文檔數,然後求其對數。
idf(t) = 1 + log ( numDocs / (docFreq + 1))
每個檢索詞在索引中出現的頻率?頻率越高,相關性越低。檢索詞出現在多數文檔中會比出現在少數文檔中的權重更低。
二、計算公式爲:
score(q,d) = queryNorm(q) · coord(q,d) · ∑ (tf(t,d) · idf(t)² · t.getBoost() · norm(t,d))
其它參數定義:
- 字段長度準則:norm
字段長度歸一值( norm )是字段中詞數平方根的倒數。
norm(d) = 1 / √numTerms
字段的長度是多少?長度越長,相關性越低。 檢索詞出現在一個短的 title 要比同樣的詞出現在一個長的 content 字段權重更大。
- 查詢歸一因子
查詢歸一因子 ( queryNorm
)試圖將查詢 歸一化 ,這樣就能將兩個不同的查詢結果相比較。
這個因子是在查詢過程的最前面計算的,具體的計算依賴於具體查詢
queryNorm = 1 / √sumOfSquaredWeights
sumOfSquaredWeights
是查詢裏每個詞的 IDF 的平方和。
以上是對於一個詞項檢索時的相關度計算,當查詢多個詞項時,得出多個相關度,則需要按照向量空間模型來計算整體的相似度:
向量空間模型:vector
向量空間模型(vector space model) 提供一種比較多詞查詢的方式,單個評分代表文檔與查詢的匹配程度
在向量空間模型裏,向量空間模型裏的每個數字都代表一個詞的 權重 ,與 詞頻/逆向文檔頻率(term frequency/inverse document frequency) 計算方式類似。
3、控制相關度
一般來說,控制相關度的需求,分爲兩種:
-
忽略TF/IDF
即不需要評分,可以使用constant_score來達成,在constant_score
查詢中,它可以包含查詢或過濾,爲任意一個匹配的文檔指定評分1
,忽略 TF/IDF 信息。 -
定製評分
function_score
查詢 是用來控制評分過程的終極武器,它允許爲每個與主查詢匹配的文檔應用一個函數,以達到改變甚至完全替換原始查詢評分_score
的目的。本文主要介紹使用script_score
函數。
使用腳本計算評
script_score
自定義腳本可以完全控制評分計算:
{
"function_score": {
"functions": {
"script_score": {
"script": "doc['price'].value + doc['margin'].value"
}
}
}
}
4、Painless
es腳本引擎,簡單安全,無痛使用,Painless使用白名單來限制函數與字段的訪問,針對es的場景來進行優化,只做es數據的操作,更加輕量級。
Painless中變量可以聲明爲基本數據類型、引用類型、字符串、void
(不返回值)、數組以及動態類型。其支持下面基本類型:
byte, short, char, int, long, float, double, boolean.聲明變量與java類似:
int i = 0; double a; boolean g = true;
數組類型支持一維和多維,初始值爲null。與引用類型一樣,使用new關鍵字,併爲每個維度設置中括號
int[] x = new int[2];
x[0] = 3;
x[1] = 4;
int[] b = new int[] {1,2,3,4,5};
painless支持動態類型,elasticsearch會自動推斷類型
def a = 1;
def b = "foo";
def[][] h = new def[2][2];
條件語句和運算符
Painless包含完整的操作符列表,除了它們的優先級和結合性之外,這些操作符與其他高級語言幾乎兼容。
if (doc['foo'].value = 5) {
doc['foo'].value *= 10;
}
else {
doc['foo'].value += 10;
}
Painless支持if else
,但不支持else if
或switch
循環
def total = 0;
for (def i = 0; i < doc['scores'].length; i++) {
total += doc['scores'][i];
}
return total;
5、控制相關度實踐
該實例中將使用script_score,將評分設置爲:
doc['download_cnt'].value * 2.5 +doc['replication_cnt'].value * 1.2
{
"query": {
"function_score": {
"query": {
"match": {
"name": "1"
}
},
"functions": [
{
"script_score": {
"script": {
"params": {
"download_ratio": 2.5,
"replication_ratio": 1.2
},
"lang": "painless",
"inline": "doc['download_cnt'].value * params.download_ratio + doc['replication_cnt'].value * params.replication_ratio"
}
}
}
]
}
}
}
_search操作中所有的返回值,都可以通過一個
map
類型變量doc獲取。和所有其他腳本語言一樣,用[]獲取map
中的值。這裏要強調的是,doc只可以在_search中訪問到