查詢DSL進階
在上一章,咱們瞭解了什麼是Apache Lucene,它的總體架構,以及文本分析過程是如何完成的。以後,咱們還介紹了Lucene的查詢語言及其用法。除此以外,咱們也討論了Elasticsearch,討論了它的架構,以及一些核心概念。在本章,咱們將深刻研究Elasticsearch的查詢DSL(Domain Specific Language)。在瞭解那些高級查詢以前,咱們將先了解Lucene評分公式的工做原理。到本章結束,將涵蓋如下內容:html
評分是Apache Lucene查詢處理過程的一個重要環節。評分是指針對給定查詢計算某個文檔的score屬性的過程。什麼是文檔得分?它是一個刻畫文檔與查詢匹配程度的參數。在本節,咱們將瞭解Apache Lucene的默認評分機制:TF/IDF(詞頻/逆文檔頻率)算法以及它是如何影響文檔查詢結果的。瞭解評分公式的工做原理對構造複雜查詢以及分析查詢中因子的重要性都是頗有價值的。同時,掌握Lucene評分機制的基礎知識有助於咱們更好地優化查詢來獲取符合咱們使用場景的結果。算法
一個文檔被Lucene返回,意味着該文檔與用戶提交的查詢是匹配的。在這種狀況下,每一個被返回文檔會有一個得分。在某些場景下,全部文檔的得分都同樣(好比使用constant_score查詢),不過通常狀況下,各個文檔的得分是不同的。得分越高,文檔更相關,至少從Apache Lucene及其評分公式的角度來看是這樣的。得分還取決於匹配的文檔、查詢和索引內容,所以,很顯然同一個文檔對不一樣查詢的得分是不一樣的。讀者須要注意,同一文檔在不一樣查詢中的得分不具有可比較性,不一樣查詢返回文檔中的最高得分也不具有可比較性。這是由於文檔得分依賴多個因子,除了權重和查詢自己的結構,還依賴被匹配的詞項數目、詞項所在字段,以及用於查詢規範化的匹配類型,如此等等。在一些比較極端的狀況下,同一個文檔在類似查詢中的得分很是懸殊,僅僅是由於使用了自定義得分查詢或者命中詞項數的急劇變化。
如今,讓咱們再回到評分過程。爲了計算文檔得分,咱們須要考慮如下這些因子。apache
從Lucene 4.0版本起,Lucene引入了多種不一樣的打分公式,這一點或許你已經有所瞭解了。不過,咱們仍是但願在此探索一下默認的TF/IDF打分公式的一些細節。請記住,爲了調節查詢相關性,你並不須要深刻理解這個公式的前因後果,可是瞭解它的工做原理卻很是重要,由於這有助於簡化相關度調優過程。
1. Lucene的理論評分公式
TF/IDF公式的理論形式以下:
緩存
上面的公式融合了布爾檢索模型和向量空間檢索模型。咱們不打算在此討論理論評分公式,而是直接跳到實踐中使用的評分公式,看看Lucene內部是如何實現和使用評分公式的。
關於布爾檢索模型和向量空間檢索模型的知識遠遠超出了本書的討論範圍,想了解更多相關知識,請參考http://en.wikipedia.org/wiki/Standard_Boolean_model 和http://en.wikipedia.org/ wiki/Vector_Space_Model。
2. Lucene的實際評分公式
如今讓咱們看看Lucene實際使用的評分公式:
架構
也許你已經看到了,評分公式是一個關於查詢q和文檔d的函數,正如咱們以前提到的同樣。有兩個因子並不直接依賴查詢詞項,它們是coord和queryNorm,這兩個因子與查詢詞項的一個求和公式相乘。
求和公式中每一個加數由如下因子連乘所得:詞頻,逆文檔頻率,詞項權重,範數。範數就是以前咱們提到過的長度範數。
這個公式聽起來很複雜。請別擔憂,你並不用記住全部的細節,你只須要意識到哪些因素是與評分有關的便可。從前面的公式咱們能夠導出一些基本的規則:分佈式
總而言之,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上的相關文檔。函數
如今,咱們已經瞭解評分的工做原理。接下來咱們看一個在現實生活中應用評分的簡單例子。首先咱們須要建立一個名爲scoring的新索引。使用以下命令建立這個索引:
性能
簡單起見,咱們使用了只有一個物理分片和0個副本的索引(咱們不須要在這個例子中關心分佈式文檔頻率)。咱們須要索引一個簡單的文檔,代碼以下:
優化
接着咱們執行一個簡單的匹配(match)查詢,查詢的詞項是「document」。
spa
顯然,剛纔索引的這個文檔被匹配上了,而且被賦予了得分。咱們能夠經過下面這條命令來查看得分的計算過程:
能夠看出,Elasticsearch給出了針對給定文檔和查詢的詳細的得分計算過程。同時能夠看出,得分等於詞項頻率(本例中是1)和逆文檔頻率(0.30685282)以及字段範數(0.625)的乘積。
如今,咱們再把另外一個文檔加入索引。
如今,能夠對比一下TF/IDF評分公式在現實場景中的工做了。在把第2個文檔索引到相同分片後(請記住咱們建立的索引只有一個分片且沒有副本),得分發生了變化,儘管此時的查詢和剛纔的同樣。這是由於一些影響得分的因子已經改變了。好比,逆文檔頻率變了,所以得分也會跟着改變。咱們還須要注意對比一下兩個文檔的得分。咱們查詢了一個單詞「document」,查詢匹配上了兩個文檔的相同字段的相同詞項。第2個文檔的得分爲何較低,是由於和第1個文檔相比,它的name字段多了一個詞項。根據先前的知識儲備,咱們知道,文檔越短,Lucene給出的得分越高。
但願這個簡短的介紹會讓你對評分工做機制認識得更清楚,在你須要優化查詢時理解目標查詢的工做過程。
http://blog.csdn.net/molong1208/article/details/50623948
以前咱們探討了評分機制,這些知識很是珍貴,特別是當你嘗試改進查詢相關性時。咱們還認爲,在對查詢進行調試時,也頗有必要搞清楚查詢是如何執行的。所以咱們決定在本節介紹一下查詢改寫是如何工做的,爲何須要查詢改寫,以及咱們應該如何控制它。
若是你以前使用過諸如前綴查詢或通配符查詢之類的查詢類型,那麼你會了解這些都是基於多詞項的查詢,它們都涉及查詢改寫。Elasticsearch使用查詢改寫是出於對性能的考慮。從Lucene的角度來看,所謂的查詢改寫操做,就是把費時的原始查詢類型實例改寫成一組性能更高的查詢類型實例,從而加快查詢執行速度。查詢改寫過程對客戶端不可見,不過最好可以知道咱們能夠修改查詢改寫過程。舉個例子,讓咱們看看Elasticsearch是如何處理前綴查詢的。
演示查詢改寫過程的最好方式莫過於經過範例深刻了解該過程的內部實現機制,尤爲是要去了解原始查詢中的詞項是如何被改寫成目標查詢中那些詞項的。假設咱們索引了下面這些文檔中的數據:
如今咱們想找出索引中全部name字段以字母j開頭的文檔。簡單起見,咱們在clients索引中執行如下查詢:
這裏使用了一個簡單的前綴查詢,想檢索出全部name字段以字母j開頭的文檔。咱們同時也設置了查詢改寫屬性以肯定執行查詢改寫的具體方法,不過如今咱們跳過該參數,具體的參數值將在本章的後續部分討論。
執行前面的查詢之後,咱們將獲得下面的結果:
如你所見,返回結果中有3個文檔,這些文檔的name字段以字母j開頭。咱們並無顯式設置待查詢索引的映射,所以Elasticsearch探測出了name字段的映射,並將其設置爲字符串類型並進行文本分析。可以使用下面的命令進行檢查:
如今咱們回到Lucene。若是你還記得Lucene倒排索引是如何構建的,你會指出倒排索引中包含了詞項、詞頻以及文檔指針(若是忘了,請從新閱讀1.1節)。如今咱們看看以前存儲到clients索引中的數據大概是如何組織的。
Term這一列很是重要。若是咱們去探究Elasticsearch和Lucene的內部實現,將會發現前綴查詢被改寫爲下面這種查詢:
咱們能夠用Elasticsearch API來檢查重寫片斷。首先,使用Explain API執行以下命令:
能夠看到,Elasticsearch對name字段使用了一個詞項是joe的constant_score查詢。固然,這一步發生在Lucene中,Elasticsearch實際上只是從緩存中獲取這些詞項。這一點能夠用Validate查詢API來驗證。
固然,多詞項查詢的rewrite屬性也能夠支持除了「constant_score_boolean」以外的其餘取值。咱們能夠經過這個屬性來控制查詢在Lucene內部的改寫方式。咱們能夠將rewrite參數存放在表明實際查詢的JSON對象中,例如,像下面的代碼這樣:
如今讓咱們來看看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,那麼查詢看起來與下面的代碼相似:
從Elasticsearch返回的結果中能夠看出,和咱們以前使用的查詢不一樣,這裏的文檔得分都不等於1.0。
這是由於top_terms_N須要保留得分最高的N個詞項。 結束本節以前,讀者應該會產生一個疑問,咱們如何決定什麼時候採用何種查詢改寫方法?該問題的答案更多地取決於您的應用場景。簡單來講,若是您能接受較低的精度和相關性(可是追求更高的性能),那麼能夠採用top-N查詢改寫方法。若是您須要更高的查詢精度和更好的相關性(同時能夠接受較低的性能),那麼應該採用布爾方法。