天天看點

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

本節書摘來自華章出版社《深入了解elasticsearch(原書第2版)》一書中的第2章,第2.2節,作者[美]拉斐爾·酷奇(rafal ku) 馬雷克·羅戈任斯基(marek rogoziski),更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視

之前我們探讨了評分機制,這些知識非常珍貴,特别是當你嘗試改進查詢相關性時。我們還認為,在對查詢進行調試時,也很有必要搞清楚查詢是如何執行的。是以我們決定在本節介紹一下查詢改寫是如何工作的,為什麼需要查詢改寫,以及我們應該如何控制它。

如果你之前使用過諸如字首查詢或通配符查詢之類的查詢類型,那麼你會了解這些都是基于多詞項的查詢,它們都涉及查詢改寫。elasticsearch使用查詢改寫是出于對性能的考慮。從lucene的角度來看,所謂的查詢改寫操作,就是把費時的原始查詢類型執行個體改寫成一組性能更高的查詢類型執行個體,進而加快查詢執行速度。查詢改寫過程對用戶端不可見,不過最好能夠知道我們可以修改查詢改寫過程。舉個例子,讓我們看看elasticsearch是如何處理字首查詢的。

2.2.1 字首查詢示例

示範查詢改寫過程的最好方式莫過于通過範例深入了解該過程的内部實作機制,尤其是要去了解原始查詢中的詞項是如何被改寫成目标查詢中那些詞項的。假設我們索引了下面這些文檔中的資料:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫
《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

現在我們想找出索引中所有name字段以字母j開頭的文檔。簡單起見,我們在clients索引中執行以下查詢:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

這裡使用了一個簡單的字首查詢,想檢索出所有name字段以字母j開頭的文檔。我們同時也設定了查詢改寫屬性以确定執行查詢改寫的具體方法,不過現在我們跳過該參數,具體的參數值将在本章的後續部分讨論。

執行前面的查詢以後,我們将得到下面的結果:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫
《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

如你所見,傳回結果中有3個文檔,這些文檔的name字段以字母j開頭。我們并沒有顯式設定待查詢索引的映射,是以elasticsearch探測出了name字段的映射,并将其設定為字元串類型并進行文本分析。可使用下面的指令進行檢查:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

elasticsearch将傳回類似下面的結果:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

2.2.2 回到apache lucene

現在我們回到lucene。如果你還記得lucene反向索引是如何建構的,你會指出反向索引中包含了詞項、詞頻以及文檔指針(如果忘了,請重新閱讀1.1節)。現在我們看看之前存儲到clients索引中的資料大概是如何組織的。

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

term這一列非常重要。如果我們去探究elasticsearch和lucene的内部實作,将會發現字首查詢被改寫為下面這種查詢:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

我們可以用elasticsearch api來檢查重寫片段。首先,使用explain api執行如下指令:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

執行結果如下:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

可以看到,elasticsearch對name字段使用了一個詞項是joe的constant_score查詢。當然,這一步發生在lucene中,elasticsearch實際上隻是從緩存中擷取這些詞項。這一點可以用validate查詢api來驗證。

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫
《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

elasticsearch傳回的結果如下:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

2.2.3 查詢改寫的屬性

當然,多詞項查詢的rewrite屬性也可以支援除了“constant_score_boolean”之外的其他取值。我們可以通過這個屬性來控制查詢在lucene内部的改寫方式。我們可以将rewrite參數存放在代表實際查詢的json對象中,例如,像下面的代碼這樣:

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

現在讓我們來看看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(原書第2版)》一2.2 查詢改寫

從elasticsearch傳回的結果中可以看出,和我們之前使用的查詢不同,這裡的文檔得分都不等于1.0。

《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫
《深入了解Elasticsearch(原書第2版)》一2.2 查詢改寫

這是因為top_terms_n需要保留得分最高的n個詞項。

結束本節之前,讀者應該會産生一個疑問,我們如何決定何時采用何種查詢改寫方法?該問題的答案更多地取決于您的應用場景。簡單來說,如果您能接受較低的精度和相關性(但是追求更高的性能),那麼可以采用top-n查詢改寫方法。如果您需要更高的查詢精度和更好的相關性(同時可以接受較低的性能),那麼應該采用布爾方法。

繼續閱讀