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