本文是對ElasticSearch中文分詞學(xué)習(xí)的一個知識總結(jié),包括如下章節(jié)的內(nèi)容:
- 基本概念
- ik分詞器的安裝
- ik中文分詞器的使用
- ik的自定義詞典
- 文檔的中文分詞使用
參考資料:
1、如果希望先對ElasticSearch組件的基本概念有所了解,可先閱讀《ElasticSearch學(xué)習(xí)筆記》。
一、基本概念
當(dāng)一個文檔被存儲時,ES會使用分詞器從文檔中提取出若干詞元(token)來支持索引的存儲和搜索。
ES內(nèi)置了很多分詞器,但內(nèi)置的分詞器對中文的處理不好。下面通過例子來看內(nèi)置分詞器的處理。在web客戶端發(fā)起如下的一個REST請求,對英文語句進(jìn)行分詞:
POST http://localhost:9200/_analyze
{
"text": "hello world"
}
操作成功后,響應(yīng)的內(nèi)容如下:
{
"tokens": [
{
"token": "hello",
"start_offset": 0,
"end_offset": 5,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "world",
"start_offset": 6,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 1
}
]
}
上面結(jié)果顯示 "hello world"語句被分為兩個單詞,因?yàn)橛⑽奶焐钥崭穹指?,自然就以空格來分詞,這沒有任何問題。
下面我們看一個中文的語句例子,請求REST如下:
POST http://localhost:9200/_analyze
{
"text": "世界如此之大"
}
操作成功后,響應(yīng)的內(nèi)容如下:
{
"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
}
]
}
從結(jié)果可以看出,這種分詞把每個漢字都獨(dú)立分開來了,這對中文分詞就沒有意義了,所以ES默認(rèn)的分詞器對中文處理是有問題的。好在有很多不錯的第三方的中文分詞器,可以很好地和ES結(jié)合起來使用。在ES中,每種分詞器(包括內(nèi)置的、第三方的)都會有個名稱。上面默認(rèn)的操作,其實(shí)用的分詞器的名稱是standard。下面的請求與前面介紹的請求是等價的,如:
POST http://localhost:9200/_analyze
{
"analyzer": "standard",
"text": "世界如此之大"
}
當(dāng)我們換一個分詞器處理分詞時,只需將"analyzer"字段設(shè)置相應(yīng)的分詞器名稱即可。
ES通過安裝插件的方式來支持第三方分詞器,對于第三方的中文分詞器,比較常用的是中科院ICTCLAS的smartcn和IKAnanlyzer分詞器。在本文中,我們介紹IKAnanlyzer分詞器(下面簡稱ik)的使用。
因?yàn)槭窃诰W(wǎng)上搜索資料來進(jìn)行ik的安裝,碰到了很多坑,折騰很長時間才搞定,主要原因是網(wǎng)上的很多資料都是針對舊版本的ES和IK版本,有很多信息在新版本中已經(jīng)有變化。下面的內(nèi)容會介紹那些遇到的坑。
二、ik分詞器的安裝
ES提供了一個腳本elasticsearch-plugin(windows下為elasticsearch-plugin.bat)來安裝插件,腳本位于ES安裝目錄的bin目錄下。elasticsearch-plugin腳本可以有三種命令,靠參數(shù)區(qū)分:
1、 elasticsearch-plugin install 插件地址
install 參數(shù)指定的命令是安裝指定的插件到當(dāng)前ES節(jié)點(diǎn)中。
2、 elasticsearch-plugin list
list參數(shù)指定的命令是顯示當(dāng)前ES節(jié)點(diǎn)已經(jīng)安裝的插件列表。
3、 elasticsearch-plugin remove 插件名稱
remove 參數(shù)指定的命令是刪除已安裝的插件。
使用elasticsearch-plugin install 安裝插件時,插件地址既可以是一個遠(yuǎn)程文件地址(在線安裝),也可以是下載到本地的文件,不管是遠(yuǎn)程文件或本地文件,對于ik插件來說都是一個zip文件。
注意,ik的版本要與ES的版本一致,因?yàn)楸疚腅S用的是5.6.9版本,所以我們ik也用的是5.6.9版本。
遠(yuǎn)程文件安裝命令如下:
elasticsearch-plugin install
https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.6.9/elasticsearch-analysis-ik-5.6.9.zip
因?yàn)槭褂玫腖inux機(jī)器無法直接上外網(wǎng),所以就考慮采用本地文件安裝的方式,這是碰到的第一個坑。從上面地址上下載了elasticsearch-analysis-ik-5.6.9.zip文件到本地,然后執(zhí)行命令如下(實(shí)際下面寫法是錯誤的):
elasticsearch-plugin install
/home/hadoop/elasticsearch-analysis-ik-5.6.9.zip
上面命令是在linux系統(tǒng)下執(zhí)行的,zip文件位于本地的/home/hadoop目錄下。但是無論怎么執(zhí)行,都報錯,報/home/hadoop/elasticsearch-analysis-ik-5.6.9.zip不是插件。在網(wǎng)上搜索了半天,也沒有找到原因,甚至懷疑是下載的二進(jìn)制包有問題,直接下載源代碼自己編譯打包成zip包,結(jié)果還是有問題。后來請教了同事,才知道elasticsearch-plugin install后面跟的路徑,如果是本地的話,需要帶file:///協(xié)議頭。果然,帶上協(xié)議頭無論是在liunx下還是windows下安裝都是成功的。這個事情給的教訓(xùn)是,不要想當(dāng)然,因?yàn)橐詾楸镜刂苯硬捎帽镜芈窂郊纯?,沒想到要加file:///協(xié)議頭;第二,有問題一段時間搞不定時及時向他人請教,省得浪費(fèi)時間。
正確的linux下的本地安裝命令是:
elasticsearch-plugin install
file:///home/hadoop/elasticsearch-analysis-ik-5.6.9.zip
正確的windows下本地安裝的命令是:
elasticsearch-plugin.bat install
file:///D:/hadoop/elasticsearch-analysis-ik-5.6.9.zip
安裝完畢后,發(fā)現(xiàn)在ES的安裝目錄下的plugins目錄下多了一個analysis-ik目錄(內(nèi)容是ik的zip包解壓后根目錄下的所有文件,一共是5個jar文件和1個properties配置文件),另外ES的安裝目錄下的config目錄下多了一個analysis-ik目錄(內(nèi)容是ik的zip包解壓后根目錄下的config目錄下所有文件,用于放置ik的自定義詞庫)。
遇到的第二個坑是,網(wǎng)上很多資料說,安裝ik插件后,需要在ES的配置文件elasticsearch.yml中加上如下一行內(nèi)容:
index.analysis.analyzer.ik.type: "ik"
可是,實(shí)際情況是加上這句話后,ES啟動失敗,從報的錯誤信息也看不到原因。又在網(wǎng)上查了半天,終于在一篇文章中看到對這個問題的解釋:“index.analysis.analyzer.ik.type這個在新版本的ES中已經(jīng)不需要了,添加了啟動時反而會報錯,ES5.X版本不再通過elasitcsearch.yml配置設(shè)置分詞規(guī)則,而是在創(chuàng)建索引時指定”。
下面再介紹遇到的第三個坑,網(wǎng)上給出使用ik的例子,請求命令如下:
POST http://localhost:9200/_analyze
{
"analyzer": "ik",
"text": "世界如此之大"
}
但是總是返回錯誤信息,錯誤如下:
{
"error": {
"root_cause": [
{
"type": "remote_transport_exception",
"reason": "[IefRKZ0][localhost:9300][indices:admin/analyze[s]]"
}
],
"type": "illegal_argument_exception",
"reason": "failed to find global analyzer [ik]"
},
"status": 400
}
錯誤信息還是很明確的,找不到分詞器ik。就以為是配置文件哪里配錯了,折騰試了半天沒找到原因。后來查找資料才知道在新的ik版本中,分詞器的名稱變了(不再叫ik),新版本的ik提供了兩個分詞器,分別是ik_max_word 和ik_smart,用任何一個替換ik,就沒問題了。
三、ik中文分詞器的使用
上面提到,ik提供了兩個分詞器,分別是ik_max_word 和ik_smart,下面我們分別測試下。
先測試ik_max_word,輸入命令如下:
POST http://localhost:9200/_analyze
{
"analyzer": "ik_max_word",
"text": "世界如此之大"
}
響應(yīng)結(jié)果如下:
{
"tokens": [
{
"token": "世界",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "如此之",
"start_offset": 2,
"end_offset": 5,
"type": "CN_WORD",
"position": 1
},
{
"token": "如此",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 2
},
{
"token": "之大",
"start_offset": 4,
"end_offset": 6,
"type": "CN_WORD",
"position": 3
}
]
}
再測試ik_smart,輸入命令如下:
POST http://localhost:9200/_analyze
{
"analyzer": "ik_smart",
"text": "世界如此之大"
}
響應(yīng)結(jié)果如下:
{
"tokens": [
{
"token": "世界",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "如此",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
},
{
"token": "之大",
"start_offset": 4,
"end_offset": 6,
"type": "CN_WORD",
"position": 2
}
]
}
比較兩個分詞器對同一句中文的分詞結(jié)果,ik_max_word比ik_smart得到的中文詞更多(從兩者的英文名含義就可看出來),但這樣也帶來一個問題,使用ik_max_word會占用更多的存儲空間。
四、ik的自定義詞典
有時,可能ik自身提供的分詞詞典無法滿足特定的一些需求(如專用名詞等),ik提供了自定義詞典的功能,也就是用戶可以自己定義一些詞匯,這樣ik就會把它們當(dāng)作詞典中的內(nèi)容來處理。
舉個例子,對于上面例子中的“世界如此之大”這個中文語句,ik詞庫中不會有“界如此”這樣一個單詞,假設(shè)“界如此”就是一個專用名詞,我們希望ik能識別出來。這時就可自定義ik的詞典。具體方法是:
1、新建擴(kuò)展名為dic的文本文件,文件中寫入想增加的詞條,每個詞條單獨(dú)一行,如文件名是test.dic,文件內(nèi)容如下:
界如此
高潛
上面例子中有兩個自定義詞條。
2、將上面的dic文件保存到ES安裝目錄的config目錄下的analysis-ik目錄(安裝ik插件時產(chǎn)生的目錄)下,可以建立子目錄,放在子目錄下。比如文件的路徑如:
** config/analysis-ik/mydic/test.dic**
3、修改ik的配置文件IKAnalyzer.cfg.xml(位于config/analysis-ik目錄下),在配置文件中增加如下條目:
<entry key="ext_dict">mydict/test.dic</entry>
這樣就將自定義的字典文件加到ik的字典中了。
4、重啟ES讓生效。
這時我們發(fā)起如下的REST請求:
POST http://localhost:9200/_analyze
{
"analyzer": "ik_max_word",
"text": "世界如此之大"
}
響應(yīng)結(jié)果如下:
{
"tokens": [
{
"token": "世界",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "界如此",
"start_offset": 1,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
},
{
"token": "如此之",
"start_offset": 2,
"end_offset": 5,
"type": "CN_WORD",
"position": 2
},
{
"token": "如此",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 3
},
{
"token": "之大",
"start_offset": 4,
"end_offset": 6,
"type": "CN_WORD",
"position": 4
}
]
}
可以看出,自定義的“界如此”詞條被分詞出來了。不過如果我們將analyzer改為ik_smart卻發(fā)現(xiàn)“界如此”詞條沒能被識別出來。
五、文檔的中文分詞使用
前面的介紹只是簡單舉例介紹了ik的使用,下面我們來通過一個更完整的例子介紹分詞。ES的分詞在創(chuàng)建索引(index)后,可以通過REST命令來設(shè)置,這樣后續(xù)插入到該索引的數(shù)據(jù)都會被相應(yīng)的分詞器進(jìn)行處理。
為了比較ik的ik_smart和ik_max_word這兩個分詞器及默認(rèn)的分詞器standard,我們創(chuàng)建3個索引來分別使用這3個分詞器。
1、創(chuàng)建索引
PUT http://192.168.226.132:9200/index_ik_s
PUT http://192.168.226.132:9200/index_ik_m
PUT http://192.168.226.132:9200/index_stan
2、設(shè)置分析器
POST http://localhost:9200/index_ik_s/resource/_mapping
{
"properties": {
"content": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
}
}
}
POST http://localhost:9200/index_ik_m/resource/_mapping
{
"properties": {
"content": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
POST http://localhost:9200/index_stan/resource/_mapping
{
"properties": {
"content": {
"type": "text",
"analyzer": "standard",
"search_analyzer": "standard"
}
}
}
3、插入數(shù)據(jù)
為了批量插入,我們使用了linxu的curl命令來執(zhí)行REST操作。
curl -XPOST http://localhost:9200/index_ik_s/resource/1 -d'
{"content":"上海牛人科技有限公司"}'
curl -XPOST http://localhost:9200/index_ik_s/resource/2 -d'
{"content":"上海牛人科技有限公司,牛人聚集的地方"}'
curl -XPOST http://localhost:9200/index_ik_s/resource/3 -d'
{"content":"最牛的人所在的地方,上海牛人科技有限公司"}'
curl -XPOST http://localhost:9200/index_ik_s/resource/4 -d'
{"content":" This is an English test case"}'
curl -XPOST http://localhost:9200/index_ik_s/resource/5 -d'
{"content":" English Test Case"}'
curl -XPOST http://localhost:9200/index_ik_m/resource/1 -d'
{"content":"上海牛人科技有限公司"}'
curl -XPOST http://localhost:9200/index_ik_m/resource/2 -d'
{"content":"上海牛人科技有限公司,牛人聚集的地方"}'
curl -XPOST http://localhost:9200/index_ik_m/resource/3 -d'
{"content":"最牛的人所在的地方,上海牛人科技有限公司"}'
curl -XPOST http://localhost:9200/index_ik_m/resource/4 -d'
{"content":" This is an English test case"}'
curl -XPOST http://localhost:9200/index_ik_m/resource/5 -d'
{"content":" English Test Case"}'
curl -XPOST http://localhost:9200/index_stan/resource/1 -d'
{"content":"上海牛人科技有限公司"}'
curl -XPOST http://localhost:9200/index_stan/resource/2 -d'
{"content":"上海牛人科技有限公司,牛人聚集的地方"}'
curl -XPOST http://localhost:9200/index_stan/resource/3 -d'
{"content":"最牛的人所在的地方,上海牛人科技有限公司"}'
curl -XPOST http://localhost:9200/index_stan/resource/4 -d'
{"content":" This is an English test case"}'
curl -XPOST http://localhost:9200/index_stan/resource/5 -d'
{"content":" English Test Case"}'
4、測試驗(yàn)證和對比
先測下他們對中文標(biāo)準(zhǔn)單詞的支持,查詢“科技”,3種索引效果都一樣的,都能勝任。請求命令如下:
POST http://localhost:9200/_search
{
"query": {
"query_string": {
"query": "科技",
"fields": ["content"]
}
}
}
再測試下非標(biāo)準(zhǔn)的搜索,搜索“海?!?,ik_smart搜索不出結(jié)果,因?yàn)樵趕mart的反向索引中分詞是“上?!薄ⅰ芭H恕?。ik_max_word搜索得到結(jié)果,因?yàn)樵趍axword的反向索引中“上?!薄ⅰ芭H恕?、“海牛”這些都有分詞。Stardard可以搜索的到結(jié)果,因?yàn)閟tartard的反向索引中每個字都拆分成一個分詞“?!?、“牛”,在搜索時又將每個字都拆成一個檢索條件,所以查詢得到。
案例中也給了英文的例子,測試后發(fā)現(xiàn),ik_smart和ik_max_word對英文的分詞不比standard差多少。
對比后我們的結(jié)論是:ik_smart既能滿足英文的要求,又更智能更輕量,占用存儲最小,所以首推ik_smart;standard對英語支持是最好的,但是對中文是簡單暴力每個字建一個反向索引,浪費(fèi)存儲空間而且效果很差;ik_max_word比ik_smart對中文的支持更全面,但是存儲上的開銷實(shí)在太大,不建議使用。