Elasticsearch 8.X 復雜分詞搞不定,怎么辦?
1、實戰(zhàn)問題
球友提問:我想停用所有純數(shù)字的分詞 , 官網(wǎng)上的這個方法好像對ik分詞器無效!
有沒有什么別的方法啊, chart gpt 說分詞可以用正則匹配 但是測試好像是不行的 我的es版本是 8.5.3。
2、進一步溝通后,得到問題最精準描述
我的查詢內(nèi)容可能是:"北京市海淀區(qū)清華園10棟105",ik_smart 中文分詞結(jié)果為:“北京市”、“海淀區(qū)”、“清華園”、“10棟”、105。
用戶期望:只想把分詞后,是純數(shù)字的排除掉。也就是說:期望最終分詞結(jié)果為:“北京市”、“海淀區(qū)”、“清華園”、“10棟”。
更進一步說:10棟是個分詞,用戶期望檢索分詞結(jié)果:“10棟”。但是105的意義不大,用戶期望分詞階段把類似“105”的純數(shù)字分詞單元去掉。
3、解決方案探討
有沒有現(xiàn)成分詞器可以滿足用戶的需求呢?目前看,沒有!
那怎么辦?只能自定義分詞器。咱們之前講過,自定義分詞器核心就如下圖三部分組成。
三部分含義 如下,結(jié)合上面的圖會更好理解。
| 部分 | 含義 |
|---|---|
| Character Filter | 在分詞之前對原始文本進行處理,例如去除 HTML 標簽,或替換特定字符。 |
| Tokenizer | 定義如何將文本切分為詞條或 token。例如,使用空格或標點符號將文本切分為單詞。 |
| Token Filter | 對 Tokenizer 輸出的詞條進行進一步的處理,例如轉(zhuǎn)為小寫、去除停用詞或添加同義詞。 |
Character Filter 和 Token Filter 的區(qū)別如下:
它倆在 Elasticsearch 中都是文本預處理的組件,但它們的處理時機和目標略有不同:
| 屬性 | Character Filter | Token Filter |
|---|---|---|
| 處理時機 | 在 Tokenizer 之前 | 在 Tokenizer 之后 |
| 作用對象 | 原始字符序列 | 詞條或 token |
| 主要功能 | 預處理文本,如去除 HTML、轉(zhuǎn)換特定字符 | 對詞條進行處理,如轉(zhuǎn)為小寫、去除停用詞、應用同義詞、生成詞干等 |
| 輸出 | 修改后的字符序列 | 處理后的詞條列表 |
本質(zhì)區(qū)別:Character Filter 針對原始的字符級別進行處理,而 Token Filter 針對分詞后的詞項級別進行處理。
到此為止,再看用戶的需求,期望分詞后去掉“數(shù)字”。那也就是在分詞后的 Token filter 處理為上乘方案。
Token filter 怎么處理呢?考慮數(shù)字級別統(tǒng)一處理的正則表達式,數(shù)字的正則為:“^[0-9]+$”。
^[0-9]+$ 可以被分解為幾個部分來解讀:
- ^:這個符號表示匹配的起始位置。也就是說,匹配的內(nèi)容必須從目標字符串的開頭開始。
- [0-9]:這是一個字符類。它匹配從 0 到 9 的任何一個數(shù)字字符。
- +:這是一個量詞。它表示前面的內(nèi)容(在這里是 [0-9] 字符類)必須出現(xiàn)一次或多次。
- $:這個符號表示匹配的結(jié)束位置。也就是說,匹配的內(nèi)容必須直到目標字符串的結(jié)尾。
所以,整體上,這個正則表達式的含義是:字符串的開頭到結(jié)尾之間只包含一到多個數(shù)字字符,并且沒有其他任何字符。
例如:
- "123" 符合該正則。
- "0123" 也符合。
- "abc"、"123a" 或 "a123" 都不符合。
一句話,該正則表達式基本達到用戶的需求。
實際實現(xiàn)的時候我們發(fā)現(xiàn),對應 filter 環(huán)節(jié)的:"pattern_replace-tokenfilter"過濾器。該過濾會實現(xiàn)字符級別的替換,我們可以將正則匹配的數(shù)字替換為某個字符,比如“”空格字符。
但,還沒有達到要求,空格字符用戶期望是剔除。這時候,我們又得考慮“”空格如何剔除。
查閱 filter 官方文檔知道,有個“analysis-length-tokenfilter”的過濾器,將最小長度設置為1,就能過濾掉長度為0的空格字符。
自此,方案初步敲定。
4、敲定和初步驗證解決方案
經(jīng)過上述的討論。我們分三步走戰(zhàn)略。
- step 0: 分詞器依然選擇 ik_smart,和用戶需求高度一致。
- step 1:找出數(shù)值數(shù)據(jù),使用正則過濾 "pattern_replace filter” 實現(xiàn)。==> 正則表達式 ^[0-9]+$ 替換為特定的字符==> ""。
- Step 2: 刪除空格,借助 length filter 實現(xiàn)。==> lenth > 1 小范圍驗證一下:
GET /_analyze
{
"tokenizer": "ik_smart",
"filter": [
{
"type": "pattern_replace",
"pattern": "^[0-9]+$",
"replacement": ""
},
{
"type": "length",
"min": 1
}
],
"text": "11111111北京市10522222海淀區(qū)1053333清華園10棟105"
}
在將輸入文本復雜化處理后,分詞結(jié)果依然能達到預期。
5、實操實現(xiàn)自定義分詞
有了前面的初步實現(xiàn),自定義分詞就變得容易。
DELETE my-index-20230811-000002
PUT my-index-20230811-000002
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"tokenizer": "ik_smart",
"filter": [
"regex_process",
"remove_length_lower_1"
]
}
},
"filter": {
"regex_process": {
"type": "pattern_replace",
"pattern": "^[0-9]+$",
"replacement": ""
},
"remove_length_lower_1": {
"type": "length",
"min": 1
}
}
}
},
"mappings": {
"properties": {
"address":{
"type":"text",
"analyzer": "my_custom_analyzer"
}
}
}
}
POST my-index-20230811-000002/_analyze
{
"text": [
"1111北京市3333海淀區(qū)444444清華園10棟105"
],
"analyzer": "my_custom_analyzer"
}
索引定義解讀如下:
| 部分 | 子部分 | 名稱 | 描述 |
|---|---|---|---|
| Settings | Analyzer | my_custom_analyzer |
使用的分詞器: ik_smart - 使用的過濾器: regex_process , remove_length_lower_1 |
| Settings | Filter | regex_process |
類型: pattern_replace 匹配全數(shù)字的模式,并替換為空字符串 |
| Settings | Filter | remove_length_lower_1 |
類型: length 確保僅保留長度大于或等于1的詞條 |
| Mappings | Properties | address |
類型: text 使用的分析器: my_custom_analyzer |
上 述配置的主要目的是:創(chuàng)建一個自定義的analyzer,該analyzer可以處理中文文本,將純數(shù)字的token替換為空,并確保分析結(jié)果中不包含空token。
最終結(jié)果如下,達到預期效果。
6、小結(jié)
當傳統(tǒng)默認分詞不能達到我們特定的、復雜的需求的時候,記得還有一招:自定義分詞。
自定義分詞記住三部分組成后,拆解一下復雜問題的需求,問題就會迎刃而解。
視頻解讀如下:
歡迎大家關注下我的 視頻號 ,不定期分享 Elasticsearch 實戰(zhàn)進階干貨!
7、參考
https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-overview.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-length-tokenfilter.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-pattern_replace-tokenfilter.html
推薦閱讀
更短時間更快習得更多干貨!
和全球 近2000+ Elastic 愛好者一起精進!
大模型時代 , 搶先 一步學習進階干貨 !
