es like模糊匹配_es 基于match_phrase/fuzzy的模糊匹配原理及使用
[版權(quán)聲明]:本文章由danvid發(fā)布于http://danvid.cnblogs.com/,如需轉(zhuǎn)載或部分使用請注明出處
在業(yè)務(wù)中經(jīng)常會遇到類似數(shù)據(jù)庫的"like"的模糊匹配需求,而es基于分詞的全文檢索也是有類似的功能,這個就是短語匹配match_phrase,但往往業(yè)務(wù)需求都不是那么簡單,他想要有l(wèi)ike的功能,又要允許有一定的容錯(就是我搜索"東方賓館"時,"廣州花園賓館酒店"也要出來,這個就不是單純的"like"),下面就是我需要解析的問題(在此吐槽一下業(yè)務(wù)就是這么變態(tài)。。)
描述一個問題時首先需要描述業(yè)務(wù)場景:假設(shè)es中有一索引字段name存儲有以下文本信息:
doc[1]:{"name":"廣州東方賓館酒店"}
doc[2]:{"name":"廣州花園賓館酒店"}
doc[3]:{"name":"東方公園賓館"}
需求要求在輸入:"東方賓館"的時候doc[1]排最前面doc[3]排第二doc[2]排第三,對于這個需求從簡單的全文檢索match來說,doc[3]:{"name":"東方公園賓館"}應(yīng)該是第一位(注意:為了簡化原理分析,分詞我們使用standard即按單個字分詞)
業(yè)務(wù)分析:顯然對于上面的業(yè)務(wù)場景如果單獨使用match的話,顯然是不合適,因為按照standard分詞,doc[3]的詞條長度要比doc[1]的詞條長度短,而詞頻又是都出現(xiàn)了[東][方][賓][館]4個詞,使用match匹配的話就會吧doc[3]排到最前面,顯然業(yè)務(wù)希望把輸入的文字順序匹配度最高的數(shù)據(jù)排前面,因為我確實要找的是"廣州東方賓館酒店"而不是"東方公園賓館"你不能把doc[3]給我排前面,OK業(yè)務(wù)邏輯好像是對的那么怎么解決問題;
解決問題前介紹一哈match_phrase原理(match的原理我就不說了自己回去看文檔),簡單點說match_phrase就是高級"like"。api如下:
GET test_index/_search
{"query": {"match_phrase": {"message": {"query" : "東方賓館","slop" : 2}
}
}
}
es在給文本分詞的時候,除了分詞之外還有一個詞條標(biāo)記,就是position,例如我按照standard對以上三個doc的name進行分詞會變成這樣:
doc[1]:廣[0],州[1],東[2],方[3],賓[4],館[5],酒[6],店[7];
doc[2]:廣[0],州[1],花[2],園[3],賓[4],館[5],酒[6],店[7];
doc[3]:東[0],方[1],公[2],園[3],賓[4],館[5];
query文本分詞:東[0],方[1],賓[2],館[3];
使用match_phrase時:
1.es會先過濾掉不符合的query條件的doc,即doc[2]中沒有"東方"兩個詞匯,會被過濾掉
2.es會根據(jù)分詞的position對分詞進行過濾和評分,這個是就slop參數(shù),默認(rèn)是0,意思是查詢分詞只需要經(jīng)過距離為0的轉(zhuǎn)換就可以變成跟doc一樣的文檔數(shù)據(jù),例如:對于doc[1]來說slop就是0了,對于doc[3]slop就是2,即"賓"和"館"最大位移這兩個分詞只需要最多移動2個位置就可以變成"東方賓館"(反過來也一樣,query的文本中的"賓"和"館"只需要移動2個位置就可以變成"東方**賓館"),用數(shù)學(xué)的理解就是doc[3]的賓[4]-東[0]=4,query文本中的賓[2]-東[0]=2,那么轉(zhuǎn)換距離slop就是4-2=2,同理doc[3]的館[5]-東[0]=5,query的是3,slop結(jié)果也是2,那么"賓"和"館"最大的slop就是2,則query時設(shè)置slop等于2就能把doc[3]匹配出來,當(dāng)設(shè)置為0時就是我們數(shù)據(jù)庫的"like"
原理解析完了,就知道使用match只能匹配相關(guān)度即tf/idf,而分詞之間的位置關(guān)系卻無法保證,而match_phrase能保證分詞間的鄰近關(guān)系,那么就可以利用兩者優(yōu)勢,結(jié)合搜索進行評分
GET test_index/_search
{"query": {"bool": {"must": {"match": {"name": {"query": "東方賓館"}
}
},"should": {"match_phrase": {"name": {"query": "東方賓館","slop": 0}
}
}
}
}
}
這樣就的結(jié)果就是相當(dāng)于match_phrase幫match進行了相關(guān)度分?jǐn)?shù)的加分,當(dāng)然你也可以通過修改slop的參數(shù)來進行步控制分?jǐn)?shù),這個就根據(jù)用戶需求來了;
性能問題:其實使用match_phrase性能是要比單純的全文檢索性能低的,因為他要計算位置嘛,那么想提高性能可以通過先使用match進行過濾數(shù)據(jù),然后利用rescore api對已經(jīng)match的結(jié)果進行加分,這樣就減少了部分不必要的非match過濾:
GET test_index/_search
{"query": {"match": {"name":"東方賓館"}
}, "rescore": {"window_size": 30,"query": {"rescore_query": {"match_phrase": {"name": {"query": "東方賓館","slop": 0}
}
}
}
}
}
#window_size 是每一分片進行重新評分的頂部文檔數(shù)量這個只要大于你可能分頁的總數(shù)*每頁的數(shù)量即可(pageNumber*pageSize)實際上不需要這么多因為翻頁不可能很深,這個根據(jù)業(yè)務(wù)調(diào)整即可。
總結(jié)及注意點:
1.rescore其實跟bool結(jié)合一樣是評分的相加,評分不在這里細說了;
2.雖然可以提高相關(guān)度評分,但是還是存在可能match很低+一個很低的match_phrase結(jié)果沒有單獨只匹配了一個match的分?jǐn)?shù)高的情況,但是這是很極限了,也是符合相關(guān)度評分原理的;
3.由于match_phrase是在搜索階段進行的計算,會影響搜索效率,據(jù)說比term查詢慢20倍,所以不要進行大文本量的字段搜索,盡量進行標(biāo)題,名字這種類型的搜索才使用這個;
4.本文章沒有討論在文本數(shù)據(jù)重復(fù)時的情況,即文本中有多個"[東][方][賓][館]"和query文本中有多個"[東][方][賓][館]"分詞的情況,但是原理是一樣的還是取距離轉(zhuǎn)換的最小值;
5.文中使用了standard分詞,實際上可能會用不同的分詞器,但是建議使用match_phrase時使用標(biāo)準(zhǔn)的一個個分詞,這樣是方便進行鄰近搜索的控制的,如果使用ik等分詞,執(zhí)行match_phrase時分詞是不可控的,所以結(jié)果也是不可控。match使用ik,match_phrase用standard結(jié)合一起使用也是可以的;
6.鄰近搜索效率較低,其實可以通過增加詞庫的方式進行單純使用match匹配效率是最高的,前提是你知道客戶會搜索什么,這又是另一個研究話題了
更新[2019-05-22]:
補充:短語匹配match_phrase必須要滿足下面的要求才能認(rèn)定和["東方賓館"]這個詞條匹配(以standard分析器為例)
1.搜索的詞必須有且僅有["東","方","賓","館"]這幾個詞(對于中文是字)的一個或者多個,如果有其他的詞(對于中文是字)是不會匹配到的,slop不是完全等同于萊文斯坦距離,可以理解成字符的偏移
2.查詢詞中偏移量應(yīng)該跟文檔中詞的偏移量一樣,或者在slop的偏差范圍內(nèi),就是上文解析的意思。
這里講解一下fuzzy和match_phrase的區(qū)別
1.fuzzy是詞/項級別的模糊匹配,match_phrase是基于短語級別的
例如對于英文(standard分析器)來說"dog cat bird"來說"dog"就是一個詞/詞項,而"dog cat"就是一個短語,因此作用范圍不一樣
2.fuzzy是基于萊文斯坦距離的,所以fuzzy是可以容錯的例如你輸入"dcg" 你也可以匹配到"dog cat bird",但是這里注意的是你的查詢只能是單詞條的查詢,不能"dcg cat",如果你需要查詢短語里面的拼寫錯誤,可以使用match的fuzziness參數(shù),match_phrase是不允許出現(xiàn)不存在的詞條的。
下面是對于fuzzy和match_phrase和match 添加fuzziness參數(shù)進行對比
文檔內(nèi)容是{"name":"dog cat bird"} 分析器是standard
--------------------------------------------------------------------------------------------------
1.使用拼寫錯誤的"cot"可以使用fuzzy匹配但是,如果是下面這種,短語是不可以的,輸入應(yīng)當(dāng)是詞條,而不是短語
GET test_save/_search
{"query": {"fuzzy": {"name":{"value": "bird cot","fuzziness": 1}
}
}
}
--------------------------------------------------------------------------------------------------
2.這里可以匹配到因為match先分解成bird 和 cot 其中bird可以匹配到,同時cot也是可以匹配到,只不過分?jǐn)?shù)要比輸入"bird cat"要低
GET test_save/_search
{"query": {"match": {"name":{"query": "bird cot","fuzziness":1}
}
}
}
-----------------------------------------------------------------------------------------------
3.這里由于cot和文本中的cat不是同一個詞,所以是無法匹配到的
GET test_save/_search
{"query": {"match_phrase": {"name": {"query": "bird cot","slop": 1}
}
}
}
以上是對于英文的單詞的解析,對于中文其實也是一樣,只是由于中文如果使用standard一個詞項就是一個字,因此使用因此分詞后的數(shù)據(jù)對于fuzzy模糊匹配來說意義不大,但是可以使用keyword進行
GET test_save/_search
{"query": {"fuzzy": {"name.keyword":{"value": "東日賓館","fuzziness": 1}
}
}
}
這樣是可以匹配到"東方賓館"的數(shù)據(jù)的,但是無法匹配"廣州東方賓館"因為萊文斯坦距離已經(jīng)不止1了
其實短語匹配應(yīng)該叫臨近查詢更適合些
以上就是對模糊查詢和短語匹配的解析和補充~
[說明]:elasticsearch版本5.6.4
總結(jié)
以上是生活随笔為你收集整理的es like模糊匹配_es 基于match_phrase/fuzzy的模糊匹配原理及使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: python降序排列说true不存在_P
- 下一篇: load xml error什么意思_X
