本節書摘來自華章出版社《深入了解elasticsearch(原書第2版)》一書中的第2章,第2.1節,作者[美]拉斐爾·酷奇(rafal ku) 馬雷克·羅戈任斯基(marek rogoziski),更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視
在上一章,我們了解了什麼是apache lucene,它的整體架構,以及文本分析過程是如何完成的。之後,我們還介紹了lucene的查詢語言及其用法。除此之外,我們也讨論了elasticsearch,讨論了它的架構,以及一些核心概念。在本章,我們将深入研究elasticsearch的查詢dsl(domain specific language)。在了解那些進階查詢之前,我們将先了解lucene評分公式的工作原理。到本章結束,将涵蓋以下内容:
lucene預設評分公式是如何工作的
什麼是查詢重寫
什麼是查詢模闆以及如何使用查詢模闆
如何優化複雜的boolean查詢
複雜boolean查詢的性能奧秘
如何為特定場景選擇合适的查詢類型
評分是apache lucene查詢處理過程的一個重要環節。評分是指針對給定查詢計算某個文檔的score屬性的過程。什麼是文檔得分?它是一個刻畫文檔與查詢比對程度的參數。在本節,我們将了解apache lucene的預設評分機制:tf/idf(詞頻/逆文檔頻率)算法以及它是如何影響文檔查詢結果的。了解評分公式的工作原理對構造複雜查詢以及分析查詢中因子的重要性都是很有價值的。同時,掌握lucene評分機制的基礎知識有助于我們更好地優化查詢來擷取符合我們使用場景的結果。
2.1.1 何時文檔被比對上
一個文檔被lucene傳回,意味着該文檔與使用者送出的查詢是比對的。在這種情況下,每個被傳回文檔會有一個得分。在某些場景下,所有文檔的得分都一樣(比如使用constant_score查詢),不過一般情況下,各個文檔的得分是不一樣的。得分越高,文檔更相關,至少從apache lucene及其評分公式的角度來看是這樣的。得分還取決于比對的文檔、查詢和索引内容,是以,很顯然同一個文檔對不同查詢的得分是不同的。讀者需要注意,同一文檔在不同查詢中的得分不具備可比較性,不同查詢傳回文檔中的最高得分也不具備可比較性。這是因為文檔得分依賴多個因子,除了權重和查詢本身的結構,還依賴被比對的詞項數目、詞項所在字段,以及用于查詢規範化的比對類型,如此等等。在一些比較極端的情況下,同一個文檔在相似查詢中的得分非常懸殊,僅僅是因為使用了自定義得分查詢或者命中詞項數的急劇變化。
現在,讓我們再回到評分過程。為了計算文檔得分,我們需要考慮以下這些因子。
文檔權重(document boost):索引期賦予某個文檔的權重值。
字段權重(field boost):查詢期賦予某個字段的權重值。
協調因子(coord):基于文檔中詞項個數的協調因子,一個文檔命中了查詢中的詞項越多,得分越高。
逆文檔頻率(inverse document frequency):一個基于詞項的因子,用來告訴評分公式該詞項有多麼罕見。逆文檔頻率越高,詞項就越罕見。評分公式利用該因子,為包含罕見詞項的文檔權重。
長度範數(length norm):每字段的基于詞項個數的歸一化因子(在索引期被計算并存儲在索引中)。一個字段包含的詞項數越多,該因子的權重越低,這意味着apache lucene評分公式更“喜歡”包含更少詞項的字段。
詞頻(term frequency):一個基于詞項的因子,用來表示一個詞項在某個文檔中出現了多少次。詞頻越高,文檔得分越高。
查詢範數(query norm):一個基于查詢的歸一化因子,它等于查詢中詞項的權重平方和。查詢範數使不同查詢的得分能互相比較,盡管這種比較通常是困難和不可行的。
2.1.2 tf/idf評分公式
從lucene 4.0版本起,lucene引入了多種不同的打分公式,這一點或許你已經有所了解了。不過,我們還是希望在此探索一下預設的tf/idf打分公式的一些細節。請記住,為了調節查詢相關性,你并不需要深入了解這個公式的來龍去脈,但是了解它的工作原理卻非常重要,因為這有助于簡化相關度調優過程。
lucene的理論評分公式
tf/idf公式的理論形式如下:

上面的公式融合了布爾檢索模型和向量空間檢索模型。我們不打算在此讨論理論評分公式,而是直接跳到實踐中使用的評分公式,看看lucene内部是如何實作和使用評分公式的。
現在讓我們看看lucene實際使用的評分公式:
也許你已經看到了,評分公式是一個關于查詢q和文檔d的函數,正如我們之前提到的一樣。有兩個因子并不直接依賴查詢詞項,它們是coord和querynorm,這兩個因子與查詢詞項的一個求和公式相乘。
求和公式中每個加數由以下因子連乘所得:詞頻,逆文檔頻率,詞項權重,範數。範數就是之前我們提到過的長度範數。
這個公式聽起來很複雜。請别擔心,你并不用記住所有的細節,你隻需要意識到哪些因素是與評分有關的即可。從前面的公式我們可以導出一些基本的規則:
越罕見的詞項被比對上,文檔得分越高。lucene認為包含獨特單詞的文檔比包含常見單詞的文檔更重要。
文檔字段越短(包含更少的詞項),文檔得分越高。通常,lucene更加重視較短的文檔,因為這些短文檔更有可能和我們查詢的主題高度吻合。
權重越高(不論是索引期或是查詢期賦予的權重值),文檔得分越高。因為更高的權重意味着特定資料(文檔、詞項、短語等)具有更高的重要性。
正如你所見,lucene将最高得分賦予同時滿足以下條件的文檔:包含多個罕見查詢詞項,詞項所在字段較短(該字段索引了較少的詞項)。該公式更“喜歡”包含罕見詞項的文檔。
2.1.3 elasticsearch如何看評分
總而言之,elasticsearch使用了lucene的評分功能,幸運的是elasticsearch允許我們挑選可用的similarity類實作,或者自定義similarity類,來替換預設的評分算法。不過請記住,elasticsearch不僅僅是lucene的簡單封裝,因為它雖然使用了lucene的評分功能,但不僅限于lucene的評分功能。
使用者可以使用各種不同的查詢類型,以精确控制文檔評分的計算。例如使用function_score查詢時,可以通過使用腳本(scripting)來改變文檔得分,也可以使用elasticsearch 0.90中出現的二次評分功能,通過在傳回文檔集之上執行另外一個查詢,重新計算top-n文檔的得分。
2.1.4 一個例子
現在,我們已經了解評分的工作原理。接下來我們看一個在現實生活中應用評分的簡單例子。首先我們需要建立一個名為scoring的新索引。使用如下
簡單起見,我們使用了隻有一個實體分片和0個副本的索引(我們不需要在這個例子中關心分布式文檔頻率)。我們需要索引一個簡單的文檔,代碼如下:
接着我們執行一個簡單的比對(match)查詢,查詢的詞項是“document”。
elasticsearch傳回的結果如下:
顯然,剛才索引的這個文檔被比對上了,并且被賦予了得分。我們可以通過下面這條指令來檢視得分的計算過程:
可以看出,elasticsearch給出了針對給定文檔和查詢的詳細的得分計算過程。同時可以看出,得分等于詞項頻率(本例中是1)和逆文檔頻率(0.30685282)以及字段範數(0.625)的乘積。
現在,我們再把另一個文檔加入索引。
此時,如果執行最開始的查詢,我們将看到如下響應:
現在,可以對比一下tf/idf評分公式在現實場景中的工作了。在把第2個文檔索引到相同分片後(請記住我們建立的索引隻有一個分片且沒有副本),得分發生了變化,盡管此時的查詢和剛才的一樣。這是因為一些影響得分的因子已經改變了。比如,逆文檔頻率變了,是以得分也會跟着改變。我們還需要注意對比一下兩個文檔的得分。我們查詢了一個單詞“document”,查詢比對上了兩個文檔的相同字段的相同詞項。第2個文檔的得分為什麼較低,是因為和第1個文檔相比,它的name字段多了一個詞項。根據先前的知識儲備,我們知道,文檔越短,lucene給出的得分越高。
希望這個簡短的介紹會讓你對評分工作機制認識得更清楚,在你需要優化查詢時了解目标查詢的工作過程。