(二)ES安装IK分词器

410 阅读7分钟

2、安装IK中文分词器

下载地址: github.com/medcl/elast… 选择对应版本下载 image.png

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中分析器通过第三方插件的方式来使用

  • ES_HOMEplugins创建目录ik
  • 将解压内容拷贝到刚刚创建的ik目录中即可。

image.png

通过上面两种方式安装完成之后,重新启动ES服务,在日志中可以看到如下内容:

image.png 说明IK分词器安装成功了。

如果是集群环境,需要每个节点都安装该插件

通过前面的步骤我们已经安装好了IK分词器,准确的说IK为我们提供了ik_smartik_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分词的安装目录即/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中可以看到如下:

image.png 从日志中可以看到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_dict目录,在新建的目录中创建文件mydict.dic。然后启动nginx或者重新加载nginx配置。接下来我们可以尝试访问如下地址:

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/myLocalDict.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重启之后日志可以看到如下内容:

[2023-08-30T17:02:03,176][INFO ][o.w.a.d.Dictionary       ] [DESKTOP-H954LHA] [Dict Loading] http://localhost/ik_dict/mydict.dic

这说明我们配置的远程词典生效了。

如果启动控制台出现中文乱码, 修改es的conf目录下的jvm.options文件的文件编码配置

image.png 新增如下配置

-Dfile.encoding=GBK

测试

例如我们现在对下面内容进行分词:

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热加载词典后会打印如下日志:

[2023-08-30T16:44:48,660][INFO ][o.w.a.d.Dictionary       ] [DESKTOP-H954LHA] try load config from D:\elasticsearch-7.15.2\config\analysis-ik\IKAnalyzer.cfg.xml
[2023-08-30T16:44:48,667][INFO ][o.w.a.d.Dictionary       ] [DESKTOP-H954LHA] try load config from D:\elasticsearch-7.15.2\plugins\ik\config\IKAnalyzer.cfg.xml
[2023-08-30T16:44:48,745][INFO ][o.w.a.d.Dictionary       ] [DESKTOP-H954LHA] [Dict Loading] D:\elasticsearch-7.15.2\plugins\ik\config\custom\myLocalDict.dic
[2023-08-30T16:44:48,745][INFO ][o.w.a.d.Dictionary       ] [DESKTOP-H954LHA] [Dict Loading] http://localhost/ik_dict/mydict.dic

再次运行上面的分词示例,得到如下内容:

{  
  "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中,那么之前索引的文档时无法应用到新词的。简单的说就是新增加的词,不会导致之前索引的内容发生变化。如果有需要请重新索引文档。