《深刻理解Elasticsearch(原書第2版)》一第2章 查詢DSL進階

第2章

查詢DSL進階
在上一章,咱們瞭解了什麼是Apache Lucene,它的總體架構,以及文本分析過程是如何完成的。以後,咱們還介紹了Lucene的查詢語言及其用法。除此以外,咱們也討論了Elasticsearch,討論了它的架構,以及一些核心概念。在本章,咱們將深刻研究Elasticsearch的查詢DSL(Domain Specific Language)。在瞭解那些高級查詢以前,咱們將先了解Lucene評分公式的工做原理。到本章結束,將涵蓋如下內容:html

  • Lucene默認評分公式是如何工做的
  • 什麼是查詢重寫
  • 什麼是查詢模板以及如何使用查詢模板
  • 如何優化複雜的Boolean查詢
  • 複雜Boolean查詢的性能奧祕
  • 如何爲特定場景選擇合適的查詢類型

2.1 Apache Lucene默認評分公式解釋

評分是Apache Lucene查詢處理過程的一個重要環節。評分是指針對給定查詢計算某個文檔的score屬性的過程。什麼是文檔得分?它是一個刻畫文檔與查詢匹配程度的參數。在本節,咱們將瞭解Apache Lucene的默認評分機制:TF/IDF(詞頻/逆文檔頻率)算法以及它是如何影響文檔查詢結果的。瞭解評分公式的工做原理對構造複雜查詢以及分析查詢中因子的重要性都是頗有價值的。同時,掌握Lucene評分機制的基礎知識有助於咱們更好地優化查詢來獲取符合咱們使用場景的結果。算法

2.1.1 什麼時候文檔被匹配上

一個文檔被Lucene返回,意味着該文檔與用戶提交的查詢是匹配的。在這種狀況下,每一個被返回文檔會有一個得分。在某些場景下,全部文檔的得分都同樣(好比使用constant_score查詢),不過通常狀況下,各個文檔的得分是不同的。得分越高,文檔更相關,至少從Apache Lucene及其評分公式的角度來看是這樣的。得分還取決於匹配的文檔、查詢和索引內容,所以,很顯然同一個文檔對不一樣查詢的得分是不一樣的。讀者須要注意,同一文檔在不一樣查詢中的得分不具有可比較性,不一樣查詢返回文檔中的最高得分也不具有可比較性。這是由於文檔得分依賴多個因子,除了權重和查詢自己的結構,還依賴被匹配的詞項數目、詞項所在字段,以及用於查詢規範化的匹配類型,如此等等。在一些比較極端的狀況下,同一個文檔在類似查詢中的得分很是懸殊,僅僅是由於使用了自定義得分查詢或者命中詞項數的急劇變化。
如今,讓咱們再回到評分過程。爲了計算文檔得分,咱們須要考慮如下這些因子。apache

  • 文檔權重(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打分公式的一些細節。請記住,爲了調節查詢相關性,你並不須要深刻理解這個公式的前因後果,可是瞭解它的工做原理卻很是重要,由於這有助於簡化相關度調優過程。
1. Lucene的理論評分公式
TF/IDF公式的理論形式以下:
image緩存

上面的公式融合了布爾檢索模型向量空間檢索模型。咱們不打算在此討論理論評分公式,而是直接跳到實踐中使用的評分公式,看看Lucene內部是如何實現和使用評分公式的。
 關於布爾檢索模型和向量空間檢索模型的知識遠遠超出了本書的討論範圍,想了解更多相關知識,請參考http://en.wikipedia.org/wiki/Standard_Boolean_model 和http://en.wikipedia.org/ wiki/Vector_Space_Model。
2. Lucene的實際評分公式
如今讓咱們看看Lucene實際使用的評分公式:
image架構

也許你已經看到了,評分公式是一個關於查詢q和文檔d的函數,正如咱們以前提到的同樣。有兩個因子並不直接依賴查詢詞項,它們是coord和queryNorm,這兩個因子與查詢詞項的一個求和公式相乘。
求和公式中每一個加數由如下因子連乘所得:詞頻,逆文檔頻率,詞項權重,範數。範數就是以前咱們提到過的長度範數。
這個公式聽起來很複雜。請別擔憂,你並不用記住全部的細節,你只須要意識到哪些因素是與評分有關的便可。從前面的公式咱們能夠導出一些基本的規則:分佈式

  • 越罕見的詞項被匹配上,文檔得分越高。Lucene認爲包含獨特單詞的文檔比包含常見單詞的文檔更重要。
  • 文檔字段越短(包含更少的詞項),文檔得分越高。一般,Lucene更加劇視較短的文檔,由於這些短文檔更有可能和咱們查詢的主題高度吻合。
  • 權重越高(不管是索引期或是查詢期賦予的權重值),文檔得分越高。由於更高的權重意味着特定數據(文檔、詞項、短語等)具備更高的重要性。 正如你所見,Lucene將最高得分賦予同時知足如下條件的文檔:包含多個罕見查詢詞項,詞項所在字段較短(該字段索引了較少的詞項)。該公式更「喜歡」包含罕見詞項的文檔。  若是你想了解更多關於Apache Lucene TF/IDF評分公式的信息,請參考Apache lucene 中TFIDFSimilarity類的文檔:http://lucene.apache.org/core/4_9_0/core/org/ apache/lucene/search/similarities/TFIDFSimilarity.html.

2.1.3 Elasticsearch如何看評分

總而言之,Elasticsearch使用了Lucene的評分功能,幸運的是Elasticsearch容許咱們挑選可用的similarity類實現,或者自定義similarity類,來替換默認的評分算法。不過請記住,Elasticsearch不只僅是Lucene的簡單封裝,由於它雖然使用了Lucene的評分功能,但不只限於Lucene的評分功能。
用戶可使用各類不一樣的查詢類型,以精確控制文檔評分的計算。例如使用function_score查詢時,能夠經過使用腳本(scripting)來改變文檔得分,也可使用Elasticsearch 0.90中出現的二次評分功能,經過在返回文檔集之上執行另一個查詢,從新計算top-N文檔的得分。
 想了解更多Apache Lucene查詢類型,請參考http://lucene.apache.org/core/4_9_0/queries/org/apache/lucene/queries/package-summary.html上的相關文檔。函數

2.1.4 一個例子

如今,咱們已經瞭解評分的工做原理。接下來咱們看一個在現實生活中應用評分的簡單例子。首先咱們須要建立一個名爲scoring的新索引。使用以下命令建立這個索引:
image性能

簡單起見,咱們使用了只有一個物理分片和0個副本的索引(咱們不須要在這個例子中關心分佈式文檔頻率)。咱們須要索引一個簡單的文檔,代碼以下:
image優化

接着咱們執行一個簡單的匹配(match)查詢,查詢的詞項是「document」。
imagespa

Elasticsearch返回的結果以下:
image

顯然,剛纔索引的這個文檔被匹配上了,而且被賦予了得分。咱們能夠經過下面這條命令來查看得分的計算過程:
image

Elasticsearch返回的結果以下:
image
image

能夠看出,Elasticsearch給出了針對給定文檔和查詢的詳細的得分計算過程。同時能夠看出,得分等於詞項頻率(本例中是1)和逆文檔頻率(0.30685282)以及字段範數(0.625)的乘積。
如今,咱們再把另外一個文檔加入索引。
image

此時,若是執行最開始的查詢,咱們將看到以下響應:
image
image

如今,能夠對比一下TF/IDF評分公式在現實場景中的工做了。在把第2個文檔索引到相同分片後(請記住咱們建立的索引只有一個分片且沒有副本),得分發生了變化,儘管此時的查詢和剛纔的同樣。這是由於一些影響得分的因子已經改變了。好比,逆文檔頻率變了,所以得分也會跟着改變。咱們還須要注意對比一下兩個文檔的得分。咱們查詢了一個單詞「document」,查詢匹配上了兩個文檔的相同字段的相同詞項。第2個文檔的得分爲何較低,是由於和第1個文檔相比,它的name字段多了一個詞項。根據先前的知識儲備,咱們知道,文檔越短,Lucene給出的得分越高。
但願這個簡短的介紹會讓你對評分工做機制認識得更清楚,在你須要優化查詢時理解目標查詢的工做過程。

http://blog.csdn.net/molong1208/article/details/50623948

2.2 查詢改寫

以前咱們探討了評分機制,這些知識很是珍貴,特別是當你嘗試改進查詢相關性時。咱們還認爲,在對查詢進行調試時,也頗有必要搞清楚查詢是如何執行的。所以咱們決定在本節介紹一下查詢改寫是如何工做的,爲何須要查詢改寫,以及咱們應該如何控制它。
若是你以前使用過諸如前綴查詢或通配符查詢之類的查詢類型,那麼你會了解這些都是基於多詞項的查詢,它們都涉及查詢改寫。Elasticsearch使用查詢改寫是出於對性能的考慮。從Lucene的角度來看,所謂的查詢改寫操做,就是把費時的原始查詢類型實例改寫成一組性能更高的查詢類型實例,從而加快查詢執行速度。查詢改寫過程對客戶端不可見,不過最好可以知道咱們能夠修改查詢改寫過程。舉個例子,讓咱們看看Elasticsearch是如何處理前綴查詢的。

2.2.1 前綴查詢示例

演示查詢改寫過程的最好方式莫過於經過範例深刻了解該過程的內部實現機制,尤爲是要去了解原始查詢中的詞項是如何被改寫成目標查詢中那些詞項的。假設咱們索引了下面這些文檔中的數據:
image
image

如今咱們想找出索引中全部name字段以字母j開頭的文檔。簡單起見,咱們在clients索引中執行如下查詢:
image

這裏使用了一個簡單的前綴查詢,想檢索出全部name字段以字母j開頭的文檔。咱們同時也設置了查詢改寫屬性以肯定執行查詢改寫的具體方法,不過如今咱們跳過該參數,具體的參數值將在本章的後續部分討論。
執行前面的查詢之後,咱們將獲得下面的結果:
image
image

如你所見,返回結果中有3個文檔,這些文檔的name字段以字母j開頭。咱們並無顯式設置待查詢索引的映射,所以Elasticsearch探測出了name字段的映射,並將其設置爲字符串類型並進行文本分析。可以使用下面的命令進行檢查:

image

2.2.2 回到Apache Lucene

如今咱們回到Lucene。若是你還記得Lucene倒排索引是如何構建的,你會指出倒排索引中包含了詞項、詞頻以及文檔指針(若是忘了,請從新閱讀1.1節)。如今咱們看看以前存儲到clients索引中的數據大概是如何組織的。

image


Term這一列很是重要。若是咱們去探究Elasticsearch和Lucene的內部實現,將會發現前綴查詢被改寫爲下面這種查詢:
image

咱們能夠用Elasticsearch API來檢查重寫片斷。首先,使用Explain API執行以下命令:
image

執行結果以下:
image

能夠看到,Elasticsearch對name字段使用了一個詞項是joe的constant_score查詢。固然,這一步發生在Lucene中,Elasticsearch實際上只是從緩存中獲取這些詞項。這一點能夠用Validate查詢API來驗證。
image
image

Elasticsearch返回的結果以下:
image

2.2.3 查詢改寫的屬性

固然,多詞項查詢的rewrite屬性也能夠支持除了「constant_score_boolean」以外的其餘取值。咱們能夠經過這個屬性來控制查詢在Lucene內部的改寫方式。咱們能夠將rewrite參數存放在表明實際查詢的JSON對象中,例如,像下面的代碼這樣:
image

如今讓咱們來看看rewrite參數有哪些選項能夠配置。
scoring_boolean:該選項將每一個生成的詞項轉化爲布爾查詢中的一個或從句(Boolean should clause)。這種改寫方法須要針對每一個文檔都計算得分。所以,這種方法比較耗費CPU(由於要計算和保存每一個詞項的得分),並且有些查詢生成了太多的詞項,以致於超出了布爾查詢默認的1024個從句的限制。默認的布爾查詢限制能夠經過設置Elasticsearch.yml文件的index.query.bool.max_clause_count屬性來修改。用戶需謹記,改寫後的布爾查詢的從句數越多,查詢性能越低。
constant_score_boolean:該選項與前面提到過的scoring_boolean相似,可是CPU耗費更少,這是由於並不計算每一個從句的得分,而是每一個從句獲得一個與查詢權重相同的一個常數得分,默認狀況下等於1,咱們也能夠經過設置查詢權重來改變這個默認值。與scoring_boolean相似,該選項也有布爾從句數的限制。
constant_score_filter:正如Lucene的Javadocs描述的那樣,該選項按以下方式改寫原始查詢—經過順序遍歷每一個詞項來建立一個私有的過濾器,標記全部包含這個詞項的文檔。命中的文檔被賦予一個與查詢權重相同的常量得分。當命中詞項數或文檔數較大時,該方法比scoring_boolean 和constant_score_boolean執行速度更快。
top_terms_N:該選項將每一個生成的詞項轉化爲布爾查詢中的一個或從句,並保存計算出來的查詢得分。與scoring_boolean不一樣之處在於,該方法只保留最佳的N個詞項,以免觸及布爾從句數的限制,並提高查詢總體性能。
top_terms_boost_N:該選項與top_terms_N相似,不一樣之處在於它的文檔得分不是經過計算得出的,而是被設置爲跟查詢權重(boost)一致,默認值爲1。
 當rewrite屬性設置爲constant_score_auto或者沒有設置時,Elasticsearch會根據查詢的類型及其構造方式來決定是使用constant_score_filter仍是constant_score_boolean。
如今,讓咱們再看一個例子。若是咱們想在範例查詢中使用top_terms_N選項,而且N的值設置爲2,那麼查詢看起來與下面的代碼相似:
image

從Elasticsearch返回的結果中能夠看出,和咱們以前使用的查詢不一樣,這裏的文檔得分都不等於1.0。
image
image

這是由於top_terms_N須要保留得分最高的N個詞項。 結束本節以前,讀者應該會產生一個疑問,咱們如何決定什麼時候採用何種查詢改寫方法?該問題的答案更多地取決於您的應用場景。簡單來講,若是您能接受較低的精度和相關性(可是追求更高的性能),那麼能夠採用top-N查詢改寫方法。若是您須要更高的查詢精度和更好的相關性(同時能夠接受較低的性能),那麼應該採用布爾方法。

相關文章
相關標籤/搜索