[Elasticsearch] 鄰近匹配 (三) - 性能,關聯單詞查詢以及Shingles

提升性能

短語和鄰近度查詢比簡單的match查詢在性能上更昂貴。match查詢只是查看詞條是否存在於倒排索引(Inverted Index)中,而match_phrase查詢則須要計算和比較多個可能重複詞條(Multiple possibly repeated)的位置。html

Lucene Nightly Benchmarks中,顯示了一個簡單的term查詢比一個短語查詢快大概10倍,比一個鄰近度查詢(一個擁有slop的短語查詢)快大概20倍。固然,這個代價是在搜索期間而不是索引期間付出的。算法

TIPapache

一般,短語查詢的額外代價並不像這些數字說的那麼嚇人。實際上,性能上的差別只是說明了一個簡單的term查詢時多麼的快。在標準全文數據上進行的短語查詢一般可以在數毫秒內完成,所以它們在實際生產環境下是徹底可以使用的,即便在一個繁忙的集羣中。json

在某些特定的場景下,短語查詢可能會很耗費資源,可是這種狀況時不常有的。一個典型的例子是DNA序列,此時會在不少位置上出現很是之多的相同重複詞條。使用高slop值會使位置計算髮生大幅度的增加。數據結構

所以,如何可以限制短語和鄰近度查詢的性能消耗呢?一個有用的方法是減小須要使用短語查詢進行檢查的文檔總數。app

結果的分值重計算(Rescoring Results)

在上一節中,咱們討論了使用鄰近度查詢來調整相關度,而不是使用它來將文檔從結果列表中添加或者排除。一個查詢可能會匹配百萬計的結果,可是咱們的用戶極可能只對前面幾頁結果有興趣。elasticsearch

一個簡單的match查詢已經經過排序將含有全部搜索詞條的文檔放在結果列表的前面了。而咱們只想對這些前面的結果進行從新排序來給予那些同時匹配了短語查詢的文檔額外的相關度。ide

search API經過分值重計算(Rescoring)來支持這一行爲。在分值重計算階段,你可以使用一個更加昂貴的分值計算算法 - 好比一個短語查詢 - 來爲每一個分片的前K個結果從新計算其分值。緊接着這些結果就會按其新的分值從新排序。性能

該請求以下所示:測試

match查詢用來決定哪些文檔會被包含在最終的結果集合中,結果經過TF/IDF進行排序。 window_size是每一個分片上須要從新計算分值的數量。

 



 

尋找關聯的單詞(Finding Associated Words)

儘管短語和鄰近度查詢很管用,它們仍是有一個缺點。它們過於嚴格了:全部的在短語查詢中的詞條都必須出如今文檔中,即便使用了slop。

經過slop得到的可以調整單詞順序的靈活性也是有代價的,由於你失去了單詞之間的關聯。儘管你可以識別文檔中的sue,alligator和ate出如今一塊,可是你不能判斷是Sue ate仍是alligator ate。

當單詞結合在一塊兒使用時,它們表達的意思比單獨使用時要豐富。"I’m not happy I’m working"和"I’m happy I’m not working"含有相同的單詞,也擁有相近的鄰近度,可是它們的意思截然不同。

若是咱們索引單詞對,而不是索引獨立的單詞,那麼咱們就可以保留更多關於單詞使用的上下文信息。

對於句子"Sue ate the alligator",咱們不只索引每一個單詞(或者Unigram)爲一個詞條:

["sue", "ate", "the", "alligator"]

咱們同時會將每一個單詞和它的鄰近單詞一塊兒索引成一個詞條:

["sue ate", "ate the", "the alligator"]

這些單詞對(也叫作Bigram)就是所謂的Shingle。

TIP

Shingle不限於只是單詞對;你也能夠索引三個單詞(Word Triplet,也被稱爲Trigram)做爲一個詞條:

["sue ate the", "ate the alligator"]

Trigram可以給你更高的精度,可是也大大地增長了索引的不一樣詞條的數量。在多數狀況下,Bigram就足夠了。

固然,只有當用戶輸入查詢的順序和原始文檔的順序一致,Shingle纔可以起做用;一個針對sue alligator的查詢會匹配單獨的單詞,可是不會匹配任何Shingle。

幸運的是,用戶會傾向於使用和他們正在搜索的數據中類似的結構來表達查詢。可是這是很重要的一點:僅使用Bigram是不夠的;咱們仍然須要Unigram,咱們能夠將匹配Bigram做爲信號(Signal)來增長相關度分值。

產生Shingle

Shingle須要在索引期間,做爲分析過程的一部分被建立。咱們能夠將Unigram和Bigram都索引到一個字段中,可是將它們放在不一樣的字段中會更加清晰,也可以讓它們可以被獨立地查詢。Unigram字段造成了咱們搜索的基礎部分,而Bigram字段則用來提高相關度。

首先,咱們須要使用shingle詞條過濾器來建立解析器:

默認Shingle的min/max值就是2,所以咱們也能夠不顯式地指定它們。 output_unigrams被設置爲false,用來避免將Unigram和Bigram索引到相同字段中。

 

讓咱們使用analyze API來測試該解析器:

不出所料,咱們獲得了3個詞條:

 

  • sue ate
  • ate the
  • the alligator

如今咱們就能夠建立一個使用新解析器的字段了。

多字段(Multifields)

將Unigram和Bigram分開索引會更加清晰,所以咱們將title字段建立成一個多字段(Multifield)(參見字符串排序和多字段(String Sorting and Multifields)):

有了上述映射,JSON文檔中的title字段會以Unigram(title字段)和Bigram(title.shingles字段)的方式索引,從而讓咱們能夠獨立地對這兩個字段進行查詢。

 

最後,咱們能夠索引示例文檔:


搜索Shingles

爲了理解添加的shingles字段的好處,讓咱們首先看看一個針對"The hungry alligator ate Sue"的簡單match查詢的返回結果:

該查詢會返回全部的3份文檔,可是注意文檔1和文檔2擁有相同的相關度分值,由於它們含有相同的單詞:

 

如今讓咱們將shingles字段也添加到查詢中。記住咱們會將shingle字段做爲信號 - 以增長相關度分值 - 咱們仍然須要將主要的title字段包含到查詢中:

 

咱們仍然匹配了3分文檔,可是文檔2如今排在了第一位,由於它匹配了Shingle詞條"ate sue":

 

即便在查詢中包含了沒有在任何文檔中出現的單詞hungry,咱們仍然經過使用單詞鄰近度獲得了最相關的文檔。

 

性能

Shingle不只比短語查詢更靈活,它們的性能也更好。相比每次搜索須要爲短語查詢付出的代價,對Shingle的查詢和簡單match查詢同樣的高效。只是在索引期間會付出一點小代價,由於更多的詞條須要被索引,意味着使用了Shingle的字段也會佔用更多的磁盤空間。可是,多數應用是寫入一次讀取屢次的,所以在索引期間花費一點代價來讓查詢更迅速是有意義的。

這是一個你在ES中常常會碰到的主題:讓你在搜索期間可以作不少事情,而不須要任何預先的設置。一旦更好地瞭解了你的需求,就可以經過在索引期間正確地建模來獲得更好的結果和更好的性能。

相關文章
相關標籤/搜索