1、 中文分詞器
1.1 預設分詞器
先來看看ElasticSearch中預設的standard 分詞器,對英文比較友好,但是對於中文來說就是按照字元拆分,不是那麼友好。
GET /_analyze
{
"analyzer": "standard",
"text": "中華人民共和國"
}
我們想要的效果是什麼:“中華人民共和國”作為一整個詞語。
得到的結果是:
{
"tokens" : [
{
"token" : "中",
"start_offset" : 0,
"end_offset" : 1,
"type" : "<IDEOGRAPHIC>",
"position" : 0
},
{
"token" : "華",
"start_offset" : 1,
"end_offset" : 2,
"type" : "<IDEOGRAPHIC>",
"position" : 1
},
{
"token" : "人",
"start_offset" : 2,
"end_offset" : 3,
"type" : "<IDEOGRAPHIC>",
"position" : 2
},
{
"token" : "民",
"start_offset" : 3,
"end_offset" : 4,
"type" : "<IDEOGRAPHIC>",
"position" : 3
},
{
"token" : "共",
"start_offset" : 4,
"end_offset" : 5,
"type" : "<IDEOGRAPHIC>",
"position" : 4
},
{
"token" : "和",
"start_offset" : 5,
"end_offset" : 6,
"type" : "<IDEOGRAPHIC>",
"position" : 5
},
{
"token" : "國",
"start_offset" : 6,
"end_offset" : 7,
"type" : "<IDEOGRAPHIC>",
"position" : 6
}
]
}
1.2 安裝ik分詞器
安裝我就不詳細說了,教程很多。
1.3 ik分詞器基礎知識
ik_max_word: 會將文字做最細粒度的拆分,比如會將“中華人民共和國人民大會堂”拆分為“中華人民共和國,中華人民,中華,華人,人民共和國,人民大會堂,人民大會,大會堂”,會窮盡各種可能的組合;
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "中華人民共和國人民大會堂"
}
{
"tokens" : [
{
"token" : "中華人民共和國",
"start_offset" : 0,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "中華人民",
"start_offset" : 0,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "中華",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "華人",
"start_offset" : 1,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "人民共和國",
"start_offset" : 2,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "人民",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 5
},
{
"token" : "共和國",
"start_offset" : 4,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 6
},
{
"token" : "共和",
"start_offset" : 4,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 7
},
{
"token" : "國人",
"start_offset" : 6,
"end_offset" : 8,
"type" : "CN_WORD",
"position" : 8
},
{
"token" : "人民大會堂",
"start_offset" : 7,
"end_offset" : 12,
"type" : "CN_WORD",
"position" : 9
},
{
"token" : "人民大會",
"start_offset" : 7,
"end_offset" : 11,
"type" : "CN_WORD",
"position" : 10
},
{
"token" : "人民",
"start_offset" : 7,
"end_offset" : 9,
"type" : "CN_WORD",
"position" : 11
},
{
"token" : "大會堂",
"start_offset" : 9,
"end_offset" : 12,
"type" : "CN_WORD",
"position" : 12
},
{
"token" : "大會",
"start_offset" : 9,
"end_offset" : 11,
"type" : "CN_WORD",
"position" : 13
},
{
"token" : "會堂",
"start_offset" : 10,
"end_offset" : 12,
"type" : "CN_WORD",
"position" : 14
}
]
}
ik_smart: 會做最粗粒度的拆分,比如會將“中華人民共和國人民大會堂”拆分為“中華人民共和國,人民大會堂”。
GET /_analyze
{
"analyzer": "ik_smart",
"text": "中華人民共和國人民大會堂"
}
{
"tokens" : [
{
"token" : "中華人民共和國",
"start_offset" : 0,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "人民大會堂",
"start_offset" : 7,
"end_offset" : 12,
"type" : "CN_WORD",
"position" : 1
}
]
}
1.4 ik分詞器的使用
儲存時,使用ik_max_word,搜尋時,使用ik_smart,原因也很容易想到:儲存時,儘量儲存多的可能性,搜尋時做粗粒度的拆分
例如,建立以下對映
PUT /my_index
{
"mappings": {
"properties": {
"text": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}
}
2、ik配置檔案
ik配置檔案地址:外掛的config目錄下
部分檔案內容如下:
- IKAnalyzer.cfg.xml:用來配置自定義詞庫
- main.dic:ik原生內建的中文詞庫,總共有27萬多條,只要是這些單詞,都會被分在一起,都會按照這個裡面的詞語去分詞,ik原生最重要的兩個配置檔案之一
- preposition.dic: 介詞
- quantifier.dic:放了一些單位相關的詞,量詞
- suffix.dic:放了一些字尾
- surname.dic:中國的姓氏
- stopword.dic:包含了英文的停用詞,a the and at but等。會在分詞的時候,直接被幹掉,不會建立在倒排索引中。ik原生最重要的兩個配置檔案之一
3、自定義詞庫
3.1 自定義分詞詞庫
每年都會湧現一些特殊的流行詞,內卷,耗子尾汁,不講武德等,這些詞一般不會出現在ik的原生詞典裡,分詞的時候也不會把這些詞彙當作整個詞彙來進行分詞。所以需要我們自己補充自己的最新的詞語,到ik的詞庫裡面。
就拿耗子尾汁來說,不做自定義分詞的效果如下。
在實際的搜尋過程中,肯定不希望把它分詞,而是希望把它作為一個整體的詞彙。
(1)首先在IK外掛的config目錄下,有一個IKAnalyzer.cfg.xml檔案。
(2)使用Notepad++開啟該檔案
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴充套件配置</comment>
<!--使用者可以在這裡配置自己的擴充套件字典 -->
<entry key="ext_dict"></entry>
<!--使用者可以在這裡配置自己的擴充套件停止詞字典-->
<entry key="ext_stopwords"></entry>
<!--使用者可以在這裡配置遠端擴充套件字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--使用者可以在這裡配置遠端擴充套件停止詞字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
(3)可以看到上面的提示
(4)於是我們建立一個名為mydict.dic的檔案,內容如下
(5)注意如果多個詞語,就著下一行接著錄入,然後把這個檔案放在與配置檔案的相同目錄下。
(6)然後再把檔名mydict.dic新增在IKAnalyzer.cfg.xml檔案中,然後儲存
(7)然後重啟es,檢視效果
(9)可以看到,耗子尾汁這個詞已經能夠作為一個整體的詞語來做分詞了。
3.2 自定義停用詞庫
比如了,的,啥,麼,我們可能並不想去建立索引,讓人家搜尋。
做法與上面自定義詞庫類似,這裡只是簡單的說一下,比方說建立一個mystop.dic檔案,把不想建立的索引的詞寫進檔案,把檔案與配置檔案放在同一個目錄,然後在把檔名寫進配置檔案對應的位置,如下所示
然後在重啟es,就可以檢視效果了。
這樣做的一個好處就是,已經有了常用的中文停用詞,但是可以補充自己的停用詞。
4、熱更新詞庫
4.1 熱更新
每次都是在es的擴充套件詞典中,手動新增新詞語,很坑
(1)每次新增完,都要重啟es才能生效,非常麻煩
(2)es是分散式的,可能有數百個節點,你不能每次都一個一個節點上面去修改
所以引出熱更新的解決方案。es不停機,直接我們在外部某個地方新增新的詞語,es中立即熱載入到這些新詞語
熱更新的方案
(1)基於ik分詞器原生支援的熱更新方案,部署一個web伺服器,提供一個http介面,通過modified和tag兩個http響應頭,來提供詞語的熱更新,這種方式在官網也提到過。https://github.com/medcl/elasticsearch-analysis-ik
修改了外掛配置之後需要重啟,如果之後對遠端的詞庫.txt檔案修改就不需要再重啟ES了,該外掛支援熱更新分詞。
(2)修改ik分詞器原始碼,然後手動支援從資料庫中每隔一定時間,自動載入新的詞庫
一般來說採用第二種方案,第一種,ik git社群官方都不建議採用,覺得不太穩定
4.2 步驟
1、下載原始碼,https://github.com/medcl/elasticsearch-analysis-ik/releases
ik分詞器,是個標準的java maven工程,直接匯入idea就可以看到原始碼
2、修改源
org.wltea.analyzer.dic.Dictionary類,160行Dictionary單例類的初始化方法,在這裡需要建立一個我們自定義的執行緒,並且啟動它
org.wltea.analyzer.dic.HotDictReloadThread類:就是死迴圈,不斷呼叫Dictionary.getSingleton().reLoadMainDict(),去重新載入詞典
Dictionary類,399行:this.loadMySQLExtDict(); 載入mysql字典。
Dictionary類,609行:this.loadMySQLStopwordDict();載入mysql停用詞
config下jdbc-reload.properties。mysql配置檔案
3、mvn package打包程式碼
target\releases\elasticsearch-analysis-ik-7.3.0.zip
4、解壓縮ik壓縮包
將mysql驅動jar,放入ik的目錄下
5、修改jdbc相關配置
6、重啟es
觀察日誌,日誌中就會顯示我們列印的那些東西,比如載入了什麼配置,載入了什麼詞語,什麼停用詞
7、在mysql中新增詞庫與停用詞
8、分詞實驗,驗證熱更新生效
這裡只是大概的一個步驟,具體情況按照自己的業務邏輯進行開發。