一、基本概念
当一个文档被存储时,ES会使用分词器从文档中提取出若干词元(token)来支持索引的存储和搜索。
ES内置了很多分词器,但内置的分词器对中文的处理不好。下面通过例子来看内置分词器的处理。在web客户端发起如下的一个REST请求,对英文语句进行分词:
POST http://localhost:9200/_analyze { "text": "hello world" }
操作成功后,响应的内容如下:
{ "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 } ] }
上面结果显示 "hello world"语句被分为两个单词,因为英文天生以空格分隔,自然就以空格来分词,这没有任何问题。
下面我们看一个中文的语句例子,请求REST如下:
POST http://localhost:9200/_analyze { "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 } ] }
POST http://localhost:9200/_analyze { "analyzer": "standard", "text": "世界如此之大" }
ES通过安装插件的方式来支持第三方分词器,对于第三方的中文分词器,比较常用的是中科院ICTCLAS的smartcn和IKAnanlyzer分词器。在本文中,我们介绍IKAnanlyzer分词器(下面简称ik)的使用。
二、ik分词器的安装
ES提供了一个脚本elasticsearch-plugin(windows下为elasticsearch-plugin.bat)来安装插件,脚本位于ES安装目录的bin目录下。elasticsearch-plugin脚本可以有三种命令,靠参数区分:
1、 elasticsearch-plugin install 插件地址
install 参数指定的命令是安装指定的插件到当前ES节点中。
2、 elasticsearch-plugin list
list参数指定的命令是显示当前ES节点已经安装的插件列表。
3、 elasticsearch-plugin remove 插件名称
remove 参数指定的命令是删除已安装的插件。
使用elasticsearch-plugin install 安装插件时,插件地址既可以是一个远程文件地址(在线安装),也可以是下载到本地的文件,不管是远程文件或本地文件,对于ik插件来说都是一个zip文件。
注意,ik的版本要与ES的版本一致,因为本文ES用的是7.8.0版本,所以我们ik也用的是7.8.0版本。
远程文件安装命令如下:
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.8.0/elasticsearch-analysis-ik-7.8.0.zip
如果不能下载,也可以从本地文件安装,elasticsearch-plugin install后面跟的路径,如果是本地的话,需要带file:///协议头
linux下的本地安装命令是:
./bin/elasticsearch-plugin install file:///home/hadoop/elasticsearch-analysis-ik-7.8.0.zip
windows下本地安装的命令是:
elasticsearch-plugin.bat install file:///D:/hadoop/elasticsearch-analysis-ik-7.8.0.zip
安装完毕后,发现在ES的安装目录下的plugins目录下多了一个analysis-ik目录(内容是ik的zip包解压后根目录下的所有文件,一共是5个jar文件和1个properties配置文件),另外ES的安装目录下的config目录下多了一个analysis-ik目录(内容是ik的zip包解压后根目录下的config目录下所有文件,用于放置ik的自定义词库)。
如果要使用插件,需要重启es.
新版本的ik提供了两个分词器,分别是ik_max_word 和ik_smart
三、ik中文分词器的使用
输入命令如下:
POST http://localhost:9200/_analyze { "analyzer": "ik_max_word", "text": "世界如此之大" }
响应结果如下:
{ "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": "世界如此之大" }
响应结果如下:
{ "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 } ] }
比较两个分词器对同一句中文的分词结果,ik_max_word比ik_smart得到的中文词更多(从两者的英文名含义就可看出来),但这样也带来一个问题,使用ik_max_word会占用更多的存储空间。
四、ik的自定义词典
有时,可能ik自身提供的分词词典无法满足特定的一些需求(如专用名词等),ik提供了自定义词典的功能,也就是用户可以自己定义一些词汇,这样ik就会把它们当作词典中的内容来处理。
举个例子,对于上面例子中的“世界如此之大”这个中文语句,ik词库中不会有“界如此”这样一个单词,假设“界如此”就是一个专用名词,我们希望ik能识别出来。这时就可自定义ik的词典。具体方法是:
1、新建扩展名为dic的文本文件,文件中写入想增加的词条,每个词条单独一行,如文件名是test.dic,文件内容如下:
界如此
高潜
上面例子中有两个自定义词条。
2、将上面的dic文件保存到ES安装目录的config目录下的analysis-ik目录(安装ik插件时产生的目录)下,可以建立子目录,放在子目录下。比如文件的路径如:
** 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让生效。
这时我们发起如下的REST请求:
POST http://localhost:9200/_analyze { "analyzer": "ik_max_word", "text": "世界如此之大" }
响应结果如下:
{ "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却发现“界如此”词条没能被识别出来。
设置默认分词器:
PUT http://192.168.110.200:9200/elk1
{ "settings" : { "index" : { "analysis.analyzer.default.type": "ik_smart", "analysis.search_analyzer.default.type":"ik_smart" } } }
查询设置:
GET http://192.168.110.200:9200/elk1/_settings
{ "elk1": { "settings": { "index": { "number_of_shards": "1", "provided_name": "elk1", "creation_date": "1627284013377", "analysis": { "analyzer": { "default": { "type": "ik_smart" } }, "search_analyzer": { "default": { "type": "ik_smart" } } }, "number_of_replicas": "1", "uuid": "oe84kROnSMKk8OtiQX1Ihg", "version": { "created": "7080099" } } } } }
参考