Elasticsearch 中文分词器插件

ES 中默认的分词器是 Standard Analyzer,会对文本内容按单词分类并进行小写处理,但是主要是用于处理英文的,对中文的分词处理就非常不友好了。理解了分词,自己去做搜索的时候就会少一些为什么搜索的结果和预期不符的疑惑。

1、为什么需要中文分词器插件

先使用 Kibana 测试 ES 默认分词器对英文的处理:

GET _analyze
{
  "text": "Hello World"
}

结果如下:


所以 ES 默认分词器对英文的处理是符合预期的,同时大写字母也被转为小写。

但是对中文的处理呢?

GET _analyze
{
  "text": "你好世界"
}

很显然无法对中文正确的分词。那这样又会导致什么问题呢?

我们先创建一个test索引,并添加几条文档数据:

PUT test

POST test/_doc
{
  "content": "Hello World"
}

POST test/_doc
{
  "content": "你好世界"
}

POST test/_doc
{
  "content": "好好学习"
}

操作完成后,可以在 head 工具中看到如下数据:


我们先通过hello来查询数据:

GET test/_search
{
  "query": {
    "match": {
      "content": "hello"
    }
  }
}

查询结果如下,可以得到预期的数据:


再查询你好试试:

GET test/_search
{
  "query": {
    "match": {
      "content": "你好"
    }
  }
}

结果如下,我们期望只查出你好世界,但好好学习也被查出来了,原因是就查询时你好被分词成了两个字,而不是一个完整的词。由于我们没有设置分词器,使用的是默认的分词器,所以在保存文档数据时content字段的中文内容也会被分词成单个字,并生成索引,查询时会使用被分词后的关键字去content生成的索引中匹配,自然会匹配出多条文档数据:

所以问题很明显了,由于使用了 ES 默认的分词器,导致查询中文时不能按照我们的预期得到想要的结果,所以我们一般都会单独安装中文分词器插件,ES 中使用比较多的中文分词器插件是elasticsearch-analysis-ik,简称 IK 分词器。

2、安装 IK 分词器

它的原码托管在 GitHub 上,主页地址 https://github.com/medcl/elasticsearch-analysis-ik。需要注意的是,不同版本的 IK 分词器对应的 ES 版本是不同的 ,我们的 ES 使用的是7.9.3版本,下载对应版本的 IK 分词器即可。安装比较简单可以参考如下步骤:

  • 在每个 ES 节点安装目录的 plugins 文件夹下创建名为 ik 的文件夹,并将下载好的分词器压缩包解压到里边
  • 重启 ES 服务

IK 分词器有如下两种分词模式:

  • ik_max_word,会对文本做最细粒度的拆分,尽可能拆分出多的词。一个字段的值需要被全文检索式,可以在创建索引时设置字段的分词模式指定为ik_max_word,这样字段内容会被最大化的分词进而生成对应的索引,这样对应的文档能更准确的被检索到。
  • ik_smart,会对文本做最粗粒度的拆分,拆分出的词相对会少些。一般检索时可以设置关键字的分词模式为ik_smart,这样能更准确的检索到预期的结果。

3、测试

首先我们测试使用 IK 分词器后的分词效果:

GET _analyze
{
  "analyzer": "ik_smart",
  "text": "你好世界"
}

可以看到 IK 分词器已经可以按照我们的预期分词了:


在测试查询之前,我们先删掉之前的test索引,重新创建索引,并指定字段索引时的分词模式(analyzer),以及检索式关键字的分词模式(search_analyzer):

PUT test
{
  "mappings": {
    "properties": {
      "content":{
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      }
    }
  }
}

添加的数据和之前一样,我们再尝试查询你好

GET test/_search
{
  "query": {
    "match": {
      "content": "你好"
    }
  }
}

结果符合预期:


3、自定义扩展字典

一些新的词汇或者特殊词汇,IK 分词器可能并不会识别,还是会给我们拆分成单个字,例如这两个词唐家三少南派三叔,先测试下 IK 分词器对唐家三少的分词效果:

GET _analyze
{
  "analyzer": "ik_smart",
  "text": "唐家三少"
}

很显然 IK 分词器并不会把它当做一个完整词去处理(南派三叔的情况也是类似的):

我们再给索引添加如下两条数据:

POST test/_doc
{
  "content": "唐家三少"
}

POST test/_doc
{
  "content": "南派三叔"
}

试着查询一下唐家三少

GET test/_search
{
  "query": {
    "match": {
      "content": "唐家三少"
    }
  }
}

我们希望的是查询唐家三少时,只查询出唐家三少所在的文档数据,让 IK 分词器把它当做一个词去处理,而不是直接拆分成单个字。我们可以自定义扩展字典来解决这个问题。

3.1、本地字典

定义本地扩展字典的步骤如下:

  • 在分词器插件目录的 config 目录下创建 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": "唐家三少"
    }
  }
}

实现了我们预期的效果:


3.3、远程字典

如果每次修扩展字典都要重启 ES 服务也是挺麻烦的,我们可以通过定义远程扩展字典来解决这个问题。

我们要做的就是提供一个接口,返回扩展字典文件即可。我们这里创建一个 Spring Boot 项目,将自定义的词典放在静态资源目录即可:


这样我们就可以通过接口 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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容