Elasticsearch自定義分詞,從一個問題說開去

一、問題拋出
來自星友的一個真實業務場景問題:
我如今的業務需求是這樣的。有一個做者字段,好比是這樣的Li,LeiLei;Han,MeiMei;還有一些是LeiLei Li...。正則表達式

如今要精確匹配。 個人想法是:用自定義分詞經過分號分詞。可是這樣我檢索Li,LeiLei那麼LeiLei Li就不能搜索到,我但願的結果是LeiLei Li也被搜索到。算法

並且這種分詞,Li,LeiLei不加逗號,也不能匹配到。可是不知道爲何我在mapping裏面添加停用詞也無論用?app

二、本文思路
從問題出發,由淺入深逐步探討ide

爲何須要分詞?spa

文檔轉換爲倒排索引,發生了什麼?翻譯

Elasticsearch自帶的分詞器設計

自定義分詞器的模板code

針對問題,實踐一把blog

三、爲何須要分詞?
中文分詞是天然語言處理的基礎。教程

語義維度:單字不少時候表達不了語義,詞每每能表達。分詞至關於預處理,能使後面和語義有關的分析更準確。

存儲維度:若是全部文章按照單字來索引,須要的存儲空間和搜索計算時間就要多的多。

時間維度:經過倒排索引,咱們能以o(1) 的時間複雜度,經過詞組找到對應的文章。

同理的,英文或者其餘語種也須要分詞。

設計索引的Mapping階段,要根據業務用途肯定是否須要分詞,若是不須要分詞,建議設置keyword類型;須要分詞,設置爲text類型並指定分詞器。

推薦閱讀:乾貨 | 論Elasticsearch數據建模的重要性

分詞使用的時機:
1)建立或更新文檔時,會對文檔作分詞處理。
2)查詢時,會對查詢語句進行分詞處理。

四、文檔轉換爲倒排索引,發生了什麼?
注意:以下文檔中部分關鍵詞的翻譯後反而很差理解,部分關鍵詞我會使用和官方一致的英文關鍵詞。
文檔被髮送並加入倒排索引以前,Elasticsearch在其主體上的操做稱爲分析(analysis)。

而analysis的實現能夠是Elasticsearch內置分詞器(analyzer)或者是自定義分詞器。

Analyzer的由以下三部分組成:

Elasticsearch自定義分詞,從一個問題說開去
4.1 character filters 字符過濾
字符過濾器將原始文本做爲字符流接收,並能夠經過添加,刪除或更改字符來轉換字符流。

字符過濾分類以下:

HTML Strip Character Filter.
用途:刪除HTML元素,如<b>,並解碼HTML實體,如&amp 。

Mapping Character Filter
用途:替換指定的字符。

Pattern Replace Character Filter
用途:基於正則表達式替換指定的字符。

4.2 tokenizers 文本切分爲分詞
接收字符流(若是包含了4.1字符過濾,則接收過濾後的字符流;不然,接收原始字符流),將其分詞。
同時記錄分詞後的順序或位置(position),以及開始值(start_offset)和偏移值(end_offset-start_offset)。

tokenizers分類以下:

Standard Tokenizer

Letter Tokenizer

Lowercase Tokenizer
…..

詳細需參考官方文檔。

4.3 token filters分詞後再過濾
針對tokenizers處理後的字符流進行再加工,好比:轉小寫、刪除(刪除停用詞)、新增(添加同義詞)等。

是否是看着很拗口,甚至不知所云。

不要緊,但,腦海中的這張三部分組成的圖以及三部分的執行順序必定要加深印象。

五、Elasticsearch自帶的Analyzer
5.1 Standard Analyzer
標準分析器是默認分詞器,若是未指定,則使用該分詞器。
它基於Unicode文本分割算法,適用於大多數語言。

5.2 Whitespace Analyzer
基於空格字符切詞。

5.3 Stop Analyzer
在simple Analyzer的基礎上,移除停用詞。

5.4 Keyword Analyzer
不切詞,將輸入的整個串一塊兒返回。
…….
更多分詞器參考官方文檔。

六、自定義分詞器的模板
自定義分詞器的在Mapping的Setting部分設置。

PUT my_custom_index
{
"settings":{
"analysis":{
"char_filter":{},
"tokenizer":{},
"filter":{},
"analyzer":{}
}
}
}

腦海中仍是上面的三部分組成的圖示。
其中:

"char_filter":{},——對應字符過濾部分;

"tokenizer":{},——對應文本切分爲分詞部分;

"filter":{},——對應分詞後再過濾部分;

"analyzer":{}——對應分詞器組成部分,其中會包含:1. 2. 3。

七、針對問題,實踐一把
7.1 問題拆解
核心問題1:實際檢索中,名字不帶","。
逗號須要字符過濾掉。在char_filter階段實現。

核心問題2:思考基於什麼進行分詞?
Li,LeiLei;Han,MeiMei;的構成中,只能採用基於「;"分詞方式。

核心問題3:支持姓名顛倒後的查詢。
即:LeileiLi也能被檢索到。
須要結合同義詞實現。

在分詞後再過濾階段,將:LiLeiLei和LeiLeiLi設定爲同義詞。

7.2 實踐
基於問題的答案以下:

PUT my_index
{
"settings": {
"analysis": {
"char_filter": {
"my_char_filter": {
"type": "mapping",
"mappings": [
", => "
]
}
},
"filter": {
"my_synonym_filter": {
"type": "synonym",
"expand": true,
"synonyms": [
"lileilei => leileili",
"hanmeimei => meimeihan"
]
}
},
"analyzer": {
"my_analyzer": {
"tokenizer": "my_tokenizer",
"char_filter": [
"my_char_filter"
],
"filter": [
"my_synonym_filter"
]
}
},
"tokenizer": {
"my_tokenizer": {
"type": "pattern",
"pattern": "\;"
}
}
}
},
"mappings": {
"_doc": {
"properties": {
"text": {
"type": "text",
"analyzer": "my_analyzer"
}
}
}
}
}

7.3 analyze API的妙處
用途:

實際業務場景中,檢驗分詞的正確性。

排查檢索結果和預期不一致問題的利器。

用法1:直接驗證分詞結果。

GET my_index/_analyze
{
"analyzer": "my_analyzer",
"text": "Li,LeiLei"
}

用法2:基於索引字段驗證分詞結果。

GET my_index/_analyze
{
"field": "my_text",
"text": "Li,LeiLei"
}

八、小結
自定義分詞這塊,我認爲不大好理解。光是:1)"char_filter":2)"tokenizer" 3)"filter" 4)"analyzer"就很容易把人繞進去。

網上中文文檔的各類翻譯不徹底一致,很容易誤導,官方英文文檔的解讀會更準確。

要牢記圖中三部分的組成,結合實際業務場景具體分析+實踐會加深自定義分詞的理解。

參考:一、官方文檔二、rockybean教程

相關文章
相關標籤/搜索