ES 中默認的分詞器是 Standard Analyzer柠衅,會對文本內容按單詞分類并進行小寫處理烤黍,但是主要是用于處理英文的漠吻,對中文的分詞處理就非常不友好了赵誓。理解了分詞打毛,自己去做搜索的時候就會少一些為什么搜索的結果和預期不符的疑惑。
1俩功、為什么需要中文分詞器插件
先使用 Kibana 測試 ES 默認分詞器對英文的處理:
GET _analyze
{
"text": "Hello World"
}
結果如下:
所以 ES 默認分詞器對英文的處理是符合預期的幻枉,同時大寫字母也被轉為小寫。
但是對中文的處理呢诡蜓?
GET _analyze
{
"text": "你好世界"
}
很顯然無法對中文正確的分詞熬甫。那這樣又會導致什么問題呢?
我們先創(chuàng)建一個test
索引蔓罚,并添加幾條文檔數(shù)據(jù):
PUT test
POST test/_doc
{
"content": "Hello World"
}
POST test/_doc
{
"content": "你好世界"
}
POST test/_doc
{
"content": "好好學習"
}
操作完成后椿肩,可以在 head 工具中看到如下數(shù)據(jù):
我們先通過hello
來查詢數(shù)據(jù):
GET test/_search
{
"query": {
"match": {
"content": "hello"
}
}
}
查詢結果如下,可以得到預期的數(shù)據(jù):
再查詢你好
試試:
GET test/_search
{
"query": {
"match": {
"content": "你好"
}
}
}
結果如下豺谈,我們期望只查出你好世界
郑象,但好好學習
也被查出來了,原因是就查詢時你好
被分詞成了你
茬末、好
兩個字厂榛,而不是一個完整的詞。由于我們沒有設置分詞器,使用的是默認的分詞器噪沙,所以在保存文檔數(shù)據(jù)時content
字段的中文內容也會被分詞成單個字炼彪,并生成索引,查詢時會使用被分詞后的關鍵字去content
生成的索引中匹配正歼,自然會匹配出多條文檔數(shù)據(jù):
所以問題很明顯了辐马,由于使用了 ES 默認的分詞器,導致查詢中文時不能按照我們的預期得到想要的結果局义,所以我們一般都會單獨安裝中文分詞器插件喜爷,ES 中使用比較多的中文分詞器插件是elasticsearch-analysis-ik,簡稱 IK 分詞器萄唇。
2檩帐、安裝 IK 分詞器
它的原碼托管在 GitHub 上,主頁地址 https://github.com/medcl/elasticsearch-analysis-ik另萤。需要注意的是湃密,不同版本的 IK 分詞器對應的 ES 版本是不同的 ,我們的 ES 使用的是7.9.3版本四敞,下載對應版本的 IK 分詞器即可泛源。安裝比較簡單可以參考如下步驟:
- 下載和 ES 版本對應的分詞器壓縮包 https://github.com/medcl/elasticsearch-analysis-ik/releases
- 在每個 ES 節(jié)點安裝目錄的 plugins 文件夾下創(chuàng)建名為 ik 的文件夾,并將下載好的分詞器壓縮包解壓到里邊
- 重啟 ES 服務
IK 分詞器有如下兩種分詞模式:
-
ik_max_word
忿危,會對文本做最細粒度的拆分达箍,盡可能拆分出多的詞。一個字段的值需要被全文檢索式铺厨,可以在創(chuàng)建索引時設置字段的分詞模式指定為ik_max_word
缎玫,這樣字段內容會被最大化的分詞進而生成對應的索引,這樣對應的文檔能更準確的被檢索到解滓。 -
ik_smart
赃磨,會對文本做最粗粒度的拆分,拆分出的詞相對會少些洼裤。一般檢索時可以設置關鍵字的分詞模式為ik_smart
煞躬,這樣能更準確的檢索到預期的結果。
3逸邦、測試
首先我們測試使用 IK 分詞器后的分詞效果:
GET _analyze
{
"analyzer": "ik_smart",
"text": "你好世界"
}
可以看到 IK 分詞器已經(jīng)可以按照我們的預期分詞了:
在測試查詢之前,我們先刪掉之前的test
索引在扰,重新創(chuàng)建索引缕减,并指定字段索引時的分詞模式(analyzer
),以及檢索式關鍵字的分詞模式(search_analyzer
):
PUT test
{
"mappings": {
"properties": {
"content":{
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}
}
添加的數(shù)據(jù)和之前一樣芒珠,我們再嘗試查詢你好
:
GET test/_search
{
"query": {
"match": {
"content": "你好"
}
}
}
結果符合預期:
3桥狡、自定義擴展字典
一些新的詞匯或者特殊詞匯,IK 分詞器可能并不會識別,還是會給我們拆分成單個字裹芝,例如這兩個詞唐家三少
部逮、南派三叔
,先測試下 IK 分詞器對唐家三少
的分詞效果:
GET _analyze
{
"analyzer": "ik_smart",
"text": "唐家三少"
}
很顯然 IK 分詞器并不會把它當做一個完整詞去處理(南派三叔
的情況也是類似的):
我們再給索引添加如下兩條數(shù)據(jù):
POST test/_doc
{
"content": "唐家三少"
}
POST test/_doc
{
"content": "南派三叔"
}
試著查詢一下唐家三少
:
GET test/_search
{
"query": {
"match": {
"content": "唐家三少"
}
}
}
我們希望的是查詢唐家三少
時嫂易,只查詢出唐家三少
所在的文檔數(shù)據(jù)兄朋,讓 IK 分詞器把它當做一個詞去處理,而不是直接拆分成單個字怜械。我們可以自定義擴展字典來解決這個問題颅和。
3.1、本地字典
定義本地擴展字典的步驟如下:
-
在分詞器插件目錄的 config 目錄下創(chuàng)建 my.dic 文件(文件名不做限制缕允,但后綴名必須是
.dic
)峡扩,然后添加要擴展的詞匯:
定義好了擴展字典,接下來需要在分詞器插件目錄的 config 目錄下的configIKAnalyzer.cfg.xml 文件中配置定義的字典:
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--用戶可以在這里配置自己的擴展字典 -->
<entry key="ext_dict">my.dic</entry>
<!--用戶可以在這里配置自己的擴展停止詞字典-->
<entry key="ext_stopwords"></entry>
<!--用戶可以在這里配置遠程擴展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用戶可以在這里配置遠程擴展停止詞字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
- 重啟 ES 服務
3.2障本、測試
完成了上述配置教届,我們再嘗試查詢唐家三少
:
GET test/_search
{
"query": {
"match": {
"content": "唐家三少"
}
}
}
實現(xiàn)了我們預期的效果:
3.3、遠程字典
如果每次修擴展字典都要重啟 ES 服務也是挺麻煩的驾霜,我們可以通過定義遠程擴展字典來解決這個問題案训。
我們要做的就是提供一個接口,返回擴展字典文件即可寄悯。我們這里創(chuàng)建一個 Spring Boot 項目萤衰,將自定義的詞典放在靜態(tài)資源目錄即可:
這樣我們就可以通過接口 http://localhost:8080/my.dic 訪問到遠程字典,然后在 configIKAnalyzer.cfg.xml 中配置遠程擴展字典即可:
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--用戶可以在這里配置自己的擴展字典 -->
<!--<entry key="ext_dict">my.dic</entry> -->
<!--用戶可以在這里配置自己的擴展停止詞字典-->
<entry key="ext_stopwords"></entry>
<!--用戶可以在這里配置遠程擴展字典 -->
<entry key="remote_ext_dict">http://localhost:8080/my.dic</entry>
<!--用戶可以在這里配置遠程擴展停止詞字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>