Elasticsearch 電商場景:明明有這個(gè)關(guān)鍵詞,但是搜不出來,怎么辦?
1、實(shí)戰(zhàn)問題
- 場景:電商創(chuàng)業(yè)公司(非傳統(tǒng)巨頭)
- 讀者描述需求:
content是一個(gè)text類型,用的 ik_max_word 分的詞,需要根據(jù)關(guān)鍵詞做精準(zhǔn)匹配,并且按照發(fā)布時(shí)間倒序。
比如我搜:小米6s,搜出來的結(jié)果要精確匹配到:小米6s,并且按照用戶的發(fā)布時(shí)間倒序排序。
現(xiàn)在的問題是用 match_pharse 搜索的時(shí)候,有時(shí)候文檔里明明有這個(gè)關(guān)鍵詞,但是搜不出來,嘗試了好幾種手段......
熟悉咱們公眾號推文的同學(xué)應(yīng)該知道,咱們分別在 2018年、2020年、2022年 都做過多次類似問題的討論。
探究 | 明明存在,怎么搜索不出來呢?
Elasticsearch能檢索出來,但不能正確高亮怎么辦?
由 Elasticsearch 空間換時(shí)間的線上問題說開去......
2、重新梳理一下檢索認(rèn)知
2.1 分詞和詞典的本質(zhì)
數(shù)據(jù)索引化的過程是借助分詞器完成的,如讀者的分詞器是 IK 中文分詞器。
問題來了?IK 中文分詞器能包含全部的詞匯嗎?
大家看 medcl 大佬開源的 IK 分詞器的源碼中能找到 main.dic 大小是 2.92 MB。并且這個(gè)詞典 8 年+ 沒有更新過。
圖片
顯然:IK 默認(rèn)詞典覆蓋不了全部詞匯,尤其諸如“顯眼包”、“小米14”、“奧利給”、“葉氏那拉”等的新詞。如下截圖是我自定義的詞典的詞庫檢索截圖。
圖片
再來一波舉例看看:
PUT my_index_0512
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_smart",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
POST my_index_0512/_bulk
{"index":{"_id":1}}
{"title":"奧利給是一個(gè)網(wǎng)絡(luò)流行詞,第一次出現(xiàn)在一名快手主播直播時(shí)說的正能量語錄里。"}
## 分詞為:“奧利” 和 “給” 兩個(gè)詞
POST my_index_0512/_analyze
{
"text":"奧利給是一個(gè)網(wǎng)絡(luò)流行詞,第一次出現(xiàn)在一名快手主播直播時(shí)說的正能量語錄里。",
"analyzer":"ik_smart"
}
## 檢索不能召回結(jié)果,這里用 term 主要說明問題,合理性待商榷!
POST my_index_0512/_search
{
"profile": true,
"query": {
"term": {
"title": "奧利給"
}
}
}
圖片
結(jié)論:詞典決定分詞,詞典里沒有的詞,極大可能(有一定概率,比如:match_phrase 詞+詞組合的情況)檢索會(huì)檢索不到。
ps: 關(guān)于 term、match、match_phrase 區(qū)別等,推薦閱讀:檢索選型。
2.2 全文檢索的本質(zhì)
全文檢索的本質(zhì)是查詢待檢索的關(guān)鍵詞在寫入所創(chuàng)建的索引中是否存在的過程。
存在,則召回;不存在,則返回空。
2.3 明明有這個(gè)關(guān)鍵詞,但是搜不出來的本質(zhì)
表面上可以看出,之前咱們2018年、2020年、2022年討論的方案用 match、match_phrase、match_phrase_prefix 等再結(jié)合 slop,貌似能解決一些問題,好像有些不召回的情況,可以召回了。
但,依然治標(biāo)不治本。依然會(huì)存在一些“新詞”、“詞典里沒有的詞”等看似明明一段話里存在的詞,就是檢索不到的原因。
3、能不能根治呢?
答案:不完全能!
但,可以嘗試空間換時(shí)間,借助 Ngram 能解決 99% 以上場景的問題。
針對讀者的問題,借助 Ngram 分詞實(shí)操一下:
### 3.1 創(chuàng)建索引
DELETE new_spy_uat2
PUT new_spy_uat2
{
"settings": {
"index.max_ngram_diff": 10,
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "my_tokenizer",
"char_filter": ["my_char_filter"]
}
},
"char_filter": {
"my_char_filter": {
"type": "pattern_replace",
"pattern": "[^\\p{L}\\p{N}\\s]+",
"replacement": ""
}
},
"tokenizer": {
"my_tokenizer": {
"type": "ngram",
"min_gram": 2,
"max_gram": 10
}
}
}
},
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "my_analyzer",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
在提供的 Elasticsearch 配置中,my_tokenizer 是一個(gè)基于 n-gram 的分詞器,配置了從最小 2 個(gè)字符到最大 10 個(gè)字符的 n-gram。
在《一本書講透 Elasticsearch》第6.4 章節(jié) P111 解讀了自定義分詞器的三大核心組成:
- character filter
- tokenizer
- token filter
咱們上面的“my_char_filter”定義了文本在分詞前進(jìn)行預(yù)處理的字符過濾規(guī)則。實(shí)際是使用正則表達(dá)式刪除所有非字母、非數(shù)字、非空格字符,只保留字母、數(shù)字和空白字符,中文字符是可以保留的。
N-gram 是一種分詞方法,通過從文本中提取 n 個(gè)連續(xù)字符的滑動(dòng)窗口來創(chuàng)建詞元(tokens)。這種方法在處理需要部分匹配和模糊搜索的應(yīng)用中非常有用,比如搜索建議和拼寫錯(cuò)誤的容錯(cuò)處理。
在這種配置下,文本會(huì)被分解成所有可能的 2 到 10 個(gè)字符的組合。
例如,要執(zhí)行如下檢索:
POST new_spy_uat2/_analyze
{
"analyzer":"my_analyzer",
"text":"奧利給這幾年才流行"
}
分詞結(jié)果如下:
圖片
這種方法可以大大增加索引的大小因?yàn)槊總€(gè)詞都被分解成多個(gè)子詞,但同時(shí)也提高了搜索的靈活性和準(zhǔn)確性,尤其是在搜索短文本或關(guān)鍵詞片段時(shí)。
這樣的分詞器尤其適合于搜索引擎的自動(dòng)補(bǔ)全功能和處理用戶可能的輸入錯(cuò)誤,因?yàn)樗軌蛟谟脩糨斎氩糠中畔r(shí)就開始匹配相關(guān)的詞條。
3.2 導(dǎo)入數(shù)據(jù)
POST new_spy_uat2/_bulk
{ "index" : { "_index" : "new_spy_uat2", "_id" : "1" } }
{ "content" : "新品豪車??限1000單食物鏈巴氏小仙包犬濕糧360g??16.9,折??8.4/袋人食級鮮肉泥,健體增肌首選(gkqHWGV0r4J)/ AC01" ,"createTime":1715323447000}
{ "index" : { "_index" : "new_spy_uat2", "_id" : "2" } }
{ "content" : "??搶??嬰兒包單??12.3起????搶??嬰兒包單??12.3起????史D??愛貝迪拉包單??忦啦??小跑快沖,錯(cuò)過大腿拍青" ,"createTime":1714323447000}
{ "index" : { "_index" : "new_spy_uat2", "_id" : "3" } }
{ "content" : "盛夏光年gala青春版34/包?單拍好價(jià)???34/包?單拍好價(jià)?34/包?單拍好價(jià)???M-3XL碼 紙褲&拉褲?無卷 88vip疊+3富代" ,"createTime":1716323447000}
{ "index" : { "_index" : "new_spy_uat2", "_id" : "3" } }
{ "content" : "小米6s真便宜?34/包?單拍好價(jià)???M-3XL碼 紙褲&拉褲?無卷 88vip疊+3富代" ,"createTime":1716323447000}
3.3 驗(yàn)證分詞
POST new_spy_uat2/_analyze
{
"analyzer":"my_analyzer",
"text":"小米6s真便宜?34/包?單拍好價(jià)???M-3XL碼 紙褲&拉褲?無卷 88vip疊+3富代"
}
圖片
3.4 執(zhí)行檢索
POST new_spy_uat2/_search
{
"query": {
"match": {
"content": "小米6s"
}
}
}
如上檢索必然能找回結(jié)果。
圖片
3.5 結(jié)合業(yè)務(wù)需求再完善檢索語句
POST new_spy_uat2/_search
{
"query": {
"match": {
"content": "小米6s"
}
},
"sort": [
"_score", // 先按相關(guān)性得分排序
{
"createTime": {
"order": "desc" // 再按發(fā)布時(shí)間倒序排序
}
}
]
}
此處需要結(jié)合業(yè)務(wù)情況具體問題具體分析。
4、小結(jié)
本文是基于實(shí)戰(zhàn)問題的思路探討。要說明的是:Ngram 的本質(zhì)是空間換時(shí)間,可能會(huì)導(dǎo)致寫入的延時(shí)以及存儲(chǔ)的成倍增長,選型一定要慎重。
只有高精準(zhǔn)檢索要求的場景才考慮 Ngram 分詞。
普通業(yè)務(wù)場景實(shí)際是容許一些數(shù)據(jù)明明存在但就是無法召回的情況的,現(xiàn)在主流搜索引擎Google、Bing、baidu等都肯定無法召回全部數(shù)據(jù)的。
所以,還得結(jié)合業(yè)務(wù)場景進(jìn)行探討。