明明標題全字段匹配,但是按照默認的相關度排序卻排到了第三四位,第一位怎麼看都不應該排在最上面。。今天ES文章檢索我遇到了這個問題。
我對文章的title和content字段使用了多字段查詢,一開始我認爲是content字段干擾了整體的相關度,後來使用boost字段加大了title的查詢權重,結果還是一樣的;後來單獨查詢title字段還是一樣的結果,全匹配字段還是排在了下面,這不禁讓我懷疑起了人生。
百度下,隨便點着點着就點到了官網上的一篇文章:被破壞的相關度 ,全文如下
在討論更復雜的 多字段搜索 之前,讓我們先快速解釋一下爲什麼只在主分片上 創建測試索引 。
用戶會時不時的抱怨無法按相關度排序並提供簡短的重現步驟:用戶索引了一些文檔,運行一個簡單的查詢,然後發現明顯低相關度的結果出現在高相關度結果之上。
爲了理解爲什麼會這樣,可以設想,我們在兩個主分片上創建了索引和總共 10 個文檔,其中 6 個文檔有單詞
foo
。可能是分片 1 有其中 3 個foo
文檔,而分片 2 有其中另外 3 個文檔,換句話說,所有文檔是均勻分佈存儲的。在 什麼是相關度?中,我們描述了 Elasticsearch 默認使用的相似度算法,這個算法叫做 詞頻/逆向文檔頻率 或 TF/IDF 。詞頻是計算某個詞在當前被查詢文檔裏某個字段中出現的頻率,出現的頻率越高,文檔越相關。 逆向文檔頻率 將 某個詞在索引內所有文檔出現的百分數 考慮在內,出現的頻率越高,它的權重就越低。
但是由於性能原因, Elasticsearch 不會計算索引內所有文檔的 IDF 。相反,每個分片會根據 該分片 內的所有文檔計算一個本地 IDF 。
因爲文檔是均勻分佈存儲的,兩個分片的 IDF 是相同的。相反,設想如果有 5 個
foo
文檔存於分片 1 ,而第 6 個文檔存於分片 2 ,在這種場景下,foo
在一個分片裏非常普通(所以不那麼重要),但是在另一個分片裏非常出現很少(所以會顯得更重要)。這些 IDF 之間的差異會導致不正確的結果。在實際應用中,這並不是一個問題,本地和全局的 IDF 的差異會隨着索引裏文檔數的增多漸漸消失,在真實世界的數據量下,局部的 IDF 會被迅速均化,所以上述問題並不是相關度被破壞所導致的,而是由於數據太少。
爲了測試,我們可以通過兩種方式解決這個問題。第一種是隻在主分片上創建索引,正如
match
查詢 裏介紹的那樣,如果只有一個分片,那麼本地的 IDF 就是 全局的 IDF。第二個方式就是在搜索請求後添加
?search_type=dfs_query_then_fetch
,dfs
是指 分佈式頻率搜索(Distributed Frequency Search) , 它告訴 Elasticsearch ,先分別獲得每個分片本地的 IDF ,然後根據結果再計算整個索引的全局 IDF 。不要在生產環境上使用
dfs_query_then_fetch
。完全沒有必要。只要有足夠的數據就能保證詞頻是均勻分佈的。沒有理由給每個查詢額外加上 DFS 這步。
看完全文,似懂非懂,能明白大概是分片導致的,有一句話很重要:如果只有一個分片,那麼本地的 IDF 就是 全局的 IDF。
我的索引分了5片,如果只是分了一片的話,是不是就能解決問題了?
首先,刪除索引
DELETE {{es-host}}/cosmo_qdlind_top_search
然後重建索引
PUT {{es-host}}/cosmo_qdlind_top_search
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"max_result_window": 1000000
}
}
在這裏把分片和副本數量都設置爲1(原來我的分片數量是5)。
然後重建mapping,最後重新導入數據,測試結果就正常了