ES本身提供了很多分词器,但是这些默认的分词器对于中文的处理并不是很理想。例如使用standard
分词器对下面的内容进行分词测试。
POST /_analyze
{
"tokenizer": "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
}
]
}
从分词结果可以看出,standard
默认分词器将中文汉字拆分成一个个词。这样分词将导致我们搜索结果关联性太低,不太适合中文分词。
ik分词器
IK分词器是一个开源的基于Java语言开发的轻量级的中文分词工具包,这也是大家采用比较多的一种分词器。在ES中分析器通过第三方插件的方式来使用,其项目地址如下:
https://github.com/medcl/elasticsearch-analysis-ik
安装分词器
通常安装ik分词器有两种方式,其一是通过elasticsearch-plugin
安装,另外一种是自己通过下载对应版本然后解压即可。
elasticsearch-plugin安装
在ES_HOME
的bin
通过ES插件安装,执行如下命令:
./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/${version}/elasticsearch-analysis-ik-${version}.zip
其中${version}
代表当前ES对应的版本,特别注意IK分词器版本要和ES版本一致,否则会出错。
手动解压
如果上面方式安装不成功可以通过手动下载对应版本压缩包然后解压安装,资源地址如下:
https://github.com/medcl/elasticsearch-analysis-ik/releases
找到对应的版本下载插件解压等待备用。
-
在
ES_HOME
的plugins
创建目录ik
。 -
将解压内容拷贝到刚刚创建的
ik
目录中即可。
通过上面两种方式安装完成之后,从新启动ES服务,在日志中可以看到如下内容:
[2022-11-08T12:17:56,907][INFO ][o.e.p.PluginsService ] [node-0] loaded plugin [analysis-ik]
...省略部分日志
[2022-11-08T12:18:20,392][INFO ][o.w.a.d.Monitor ] [node-0] try load config from E:ES7.1node1configanalysis-ikIKAnalyzer.cfg.xml
[2022-11-08T12:18:20,393][INFO ][o.w.a.d.Monitor ] [node-0] try load config from E:ES7.1node1pluginsikconfigIKAnalyzer.cfg.xml
说明IK分词器安装成功了。
如果是集群环境,需要每个节点都安装该插件。
测试使用
通过前面的步骤我们已经安装好了IK分词器,准确的说IK为我们提供了ik_smart
和ik_max_word
两种类型的分析器和分词器。
关于分析器和分词器是什么关系关注公众号,发送”分词器”关键字会推送你相关内容。
下面我们来试试IK分词器和standard
分词器分词效果有啥不同。
POST /_analyze
{
"tokenizer": "ik_smart",
"text": "苹果真甜"
}
上面我们测试使用ik_smart
分词器效果,其分词结果如下所示:
{
"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
}
]
}
从分词效果可以看出,它的处理逻辑是将文本拆分成词,而不是像standard
分词器一样拆分成一个个字。
另外我们再试试ik_max_word
模式与ik_smart
模式有什么区别。
POST /_analyze
{
"tokenizer": "ik_max_word",
"text": "苹果真甜"
}
其结果如下:
{
"tokens" : [
{
"token" : "苹果",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "果真",
"start_offset" : 1,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "真甜",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 2
}
]
}
相比于ik_smart
模式,ik_max_word
分词粒度更细致,它列举出了所有词的可能性。
添加新词
IK分词器内部有一个词典,然后根据词典匹配来分词。但是如果如果存在一个新词,IK分词器的词典没有该怎么办呢?例如下面的分词语句:
POST /_analyze
{
"tokenizer": "ik_smart",
"text": "北鼎养生壶"
}
其分词效果如下:
{
"tokens" : [
{
"token" : "北",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "鼎",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "养生",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "壶",
"start_offset" : 4,
"end_offset" : 5,
"type" : "CN_CHAR",
"position" : 3
}
]
}
例如文中的北鼎
是一个品牌名,它并不在我们的词典中,所以IK
分词器会将它按字拆分,实际上我们想得到一个词北鼎
。
幸运的是IK分词器提供了扩展词典,我们只需要往词典中添加新词即可实现扩展新词的功能。
本地扩展
IK提供了扩展本地词典的用来添加新词,在IK分词的安装目录即${ES_HOME}/plugins/ik/config
目录中存在一个IKAnalyzer.cfg.xml
配置文件,通过该配置文件,文件配置内容如下:
<?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>
我这里修改如下:
<?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">custom/mydict.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
然后在当前目录中创建custom
目录,然后再目录中创建文件mydict.dic
,文件中的内容中将北鼎
作为一个新词添加在文件中。如果有多个词,每个词单独形成一行写入即可,然后重启ES服务。在ES中可以看到如下日志:
[2022-11-08T13:38:03,835][INFO ][o.w.a.d.Monitor ] [node-0] [Dict Loading] E:ES7.1node1pluginsikconfigcustommydict.dic
从日志中可以看到IK加载了我们的扩展字典,接下来我们再次运行之前的分词测试示例,得到的结果如下:
{
"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" : 5,
"type" : "CN_CHAR",
"position" : 2
}
]
}
对比两次分词结果可以看出,我们添加的新词北鼎
被当做一个新词了。
如果你添加了本地词典,并且重启了ES服务器但是新词并没有被识别出来。请检查一下你的本地扩展词典的编码是否是UTF-8,大多数情况下分词不生效是因为本地扩展词典的编码格式不正确导致的。
远程词典加载
通过上面的方式我们了解了如何扩展本地词典,但是该方式有如下缺点:
-
对于集群环境每次添加新词都需要修改每个ES节点
-
本地扩展词典不支持热加载,每次修改本地扩展词典都需要重启ES服务器。
针对本地扩展词典方式的弊端,IK提供了远程热加载词典特性。该特性只需要修改IKAnalyzer.cfg.xml
文件中remote_ext_dict
属性,提供一个url
用来加载热加载词典。该请求需要满足两个点即可完成分词热加载。
-
该http请求需要返回两个头部,一个是
Last-Modified
,另一个是Etag
,这两个值只要有一个发生改变该插件就会去抓取新的分词进而更新词库。 -
该http请求返回的内容格式是一行一个分词,换行符用
n
即可。
下面我们使用nginx对扩展词典做静态映射来实现远程加载扩展词典。
配置nginx
首先在nginx.conf
文件中增加如下配置:
location /ik_dict/ {
root ik_dict;
rewrite ^/ik_dict/(.*)$ /$1 break;
}
然后在nginx_home
目录中创建^/ik_di
目录,在新建的目录中创建文件mydict.dic
。然后启动nginx或者重新加载nginx配置。接下来我们可以尝试访问如下地址:
http://${hostname}:${port}/ik_dict/mydict.dic
具体根据你的实际情况来,我这里是本地则直接使用如下地址即可:
http://localhost/ik_dict/mydict.dic
修改IKAnalyzer.cfg.xml文件
如果上一步nginx配置的没问题现在只要修改IKAnalyzer.cfg.xml
配置文件即可,修改如下:
<?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">custom/mydict.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<entry key="remote_ext_dict">http://localhost/ik_dict/mydict.dic</entry>
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
同样修改之后重启ES服务。与本地扩展词典不同的是,本地词典每次添加新词是需要重启的,而远程词典只有修改远程地址时是需要重启的,后续添加新词不需要。
ES重启之后日志可以看到如下内容:
[2022-11-08T14:37:00,902][INFO ][o.w.a.d.Monitor ] [node-0] [Dict Loading] http://localhost/ik_dict/mydict.dic
这说明我们配置的远程词典生效了。
测试
例如我们现在对下面内容进行分词:
POST /_analyze
{
"tokenizer": "ik_smart",
"text": "小明是个社牛"
}
其分词结果如下:
{
"tokens" : [
{
"token" : "小明",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "是",
"start_offset" : 2,
"end_offset" : 3,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "个",
"start_offset" : 3,
"end_offset" : 4,
"type" : "CN_CHAR",
"position" : 2
},
{
"token" : "社",
"start_offset" : 4,
"end_offset" : 5,
"type" : "CN_CHAR",
"position" : 3
},
{
"token" : "牛",
"start_offset" : 5,
"end_offset" : 6,
"type" : "CN_CHAR",
"position" : 4
}
]
}
很显然社牛
是一个新词,IK的词典中是没有的。我们此时只需要修改nginx中配置的词典文件,在该文件增加社牛
一词等待IK重新加载该词即可。
IK默认情况下60s重新加载一次远程词典,我们稍等片刻。如果IK热加载词典后会打印如下日志:
[2022-11-08T14:54:00,775][INFO ][o.w.a.d.Monitor ] [node-0] try load config from E:ES7.1node1configanalysis-ikIKAnalyzer.cfg.xml
[2022-11-08T14:54:00,780][INFO ][o.w.a.d.Monitor ] [node-0] try load config from E:ES7.1node1pluginsikconfigIKAnalyzer.cfg.xml
[2022-11-08T14:54:00,865][INFO ][o.w.a.d.Monitor ] [node-0] [Dict Loading] E:ES7.1node1pluginsikconfigcustommydict.dic
[2022-11-08T14:54:00,866][INFO ][o.w.a.d.Monitor ] [node-0] [Dict Loading] http://localhost/ik_dict/mydict.dic
[2022-11-08T14:54:00,873][INFO ][o.w.a.d.Monitor ] [node-0] 社牛
[2022-11-08T14:54:00,874][INFO ][o.w.a.d.Monitor ] [node-0] 重新加载词典完毕...
再次运行上面的分词示例,得到如下内容:
{
"tokens" : [
{
"token" : "小明",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "是",
"start_offset" : 2,
"end_offset" : 3,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "个",
"start_offset" : 3,
"end_offset" : 4,
"type" : "CN_CHAR",
"position" : 2
},
{
"token" : "社牛",
"start_offset" : 4,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 3
}
]
}
从分词结果可以看出,我们新增加的社牛
一词已经被IK当做是一个新词加载到字典中了。
如果在文档索引前新词没有加载到IK中,那么之前索引的文档时无法应用到新词的。简单的说就是新增加的词,不会导致之前索引的内容发生变化。如果有需要请重新索引文档。
欢迎关注微信公众号,后续ES相关文章会持续更新,关注之后及时接受后续文章内容更新。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72855.html