# 分词

返回:elastic

ik-analyzer、结巴中文分词、THULAC、NLPIR和阿里的aliws

https://github.com/medcl/elasticsearch-analysis-ik

# version

IK version ES version
master 7.x -> master
6.x 6.x
5.x 5.x
1.10.6 2.4.6
1.9.5 2.3.5
1.8.1 2.2.1
1.7.0 2.1.1
1.5.0 2.0.0
1.2.6 1.0.0
1.2.5 0.90.x
1.1.3 0.20.x
1.0.0 0.16.2 -> 0.19.0

# 安装

C:\Green\elasticsearch-6.4.3\bin> .\elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/r
eleases/download/v6.4.3/elasticsearch-analysis-ik-6.4.3.zip
1
2

TIP

版本对应,不要弄错

启动日志将会有以下信息

[2020-12-25T14:19:03,332][INFO ][o.e.p.PluginsService     ] [cFIDIIy] loaded plugin [analysis-ik]
1

# 基础知识

IK分词器包含两种analyzer,一般用ik_max_word

  • ik_max_word:会将文本做最细粒度的拆分
  • ik_smart:会做最粗粒度的拆分

# 配置文件

ik插件安装完成后,可以在elasticsearch-6.4.3/config/analysis-ik看到ik的配置文件IKAnalyzer.cfg.xml

该目录下带有许多文件,含义如下:

  • main.dic ik 原生内置的中文词库,里面有275909条现成的词语
  • quantifier.dic 量词和单位名称,如个,斤,克,米之类的
  • suffix.dic 常见后缀词,如江,村,省,市,局等
  • surname.dic 中国姓氏
  • stopword.dic 停用词,目前默认的是写的几个英文单词,如and, a, the等
  • preposition.dic 副词、语气助词,连接词等无实际含义的词语,如却,也,是,否则之类的

还提供了额外的词库补充文件,extra开头的那几个就是,如extra_main.dic,共收录398716条现有的词语,默认没有使用,有需要可以在配置文件IKAnalyzer.cfg.xml上添加

最重要的字典

最重要的是main.dicstopword.dic。stopword(停用词),分词时会直接被干掉,不会建立在倒排索引中。

# 自定义词库

  • 创建自定义词库文件mydic.dic,并在IKAnalyzer.cfg.xml的ext_dict属性里加上该文件名,可以在mydic.dic文件里补充自己的词汇,如网络流行词:跪族篮孩。

添加前的分词效果:

GET /forum/_analyze
{
  "text": "跪族篮孩",
  "analyzer": "ik_max_word"
}

响应结果:
{
  "tokens": [
    {
      "token": "跪",
      "start_offset": 0,
      "end_offset": 1,
      "type": "CN_WORD",
      "position": 0
    },
    {
      "token": "族",
      "start_offset": 1,
      "end_offset": 2,
      "type": "CN_CHAR",
      "position": 1
    },
    {
      "token": "篮",
      "start_offset": 2,
      "end_offset": 3,
      "type": "CN_WORD",
      "position": 2
    },
    {
      "token": "孩",
      "start_offset": 3,
      "end_offset": 4,
      "type": "CN_CHAR",
      "position": 3
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

添加词库后:

{
  "tokens": [
    {
      "token": "跪族篮孩", //能看到完整的“跪族篮孩”,能看到完整的语词出现。
      "start_offset": 0,
      "end_offset": 4,
      "type": "CN_WORD",
      "position": 0
    },
    {
      "token": "跪",
      "start_offset": 0,
      "end_offset": 1,
      "type": "CN_WORD",
      "position": 1
    },
    {
      "token": "族",
      "start_offset": 1,
      "end_offset": 2,
      "type": "CN_CHAR",
      "position": 2
    },
    {
      "token": "篮",
      "start_offset": 2,
      "end_offset": 3,
      "type": "CN_WORD",
      "position": 3
    },
    {
      "token": "孩",
      "start_offset": 3,
      "end_offset": 4,
      "type": "CN_CHAR",
      "position": 4
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  • 自己建立停用词库,如了,的,哈,啥,这几个字不想去建立索引 在配置文件IKAnalyzer.cfg.xml下ext_stopwords标签添加:extra_stopword.dic,并加几个词,修改后同样要重启es。

加一个"啥"字在ext_stopword中 修改前:

GET /forum/_analyze
{
  "text": "啥都好",
  "analyzer": "ik_max_word"
}

响应结果:
{
  "tokens": [
    {
      "token": "啥",
      "start_offset": 0,
      "end_offset": 1,
      "type": "CN_WORD",
      "position": 0
    },
    {
      "token": "都好",
      "start_offset": 1,
      "end_offset": 3,
      "type": "CN_WORD",
      "position": 1
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

添加停用词后

{
    // 那个啥字直接没有了,结果符合预期。
  "tokens": [
    {
      "token": "都好",
      "start_offset": 1,
      "end_offset": 3,
      "type": "CN_WORD",
      "position": 0
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12

# 热更新方案

上面自定义词库有一个致命问题:必须要重启ES,新增的词库才能生效。

  • 基于id分词器原生支持的更新方案,部署一个web服务器,提供一个http接口,通过modified和try两个http响应头,来提供词语的热更新操作。
  • 修改ik分词器源码,然后手动支持从mysql中每隔一定时间,自动加载新的词库。

推荐方案2

  • 下载源码
  • 修改org.wltea.analyzer.dic.Dictionary
    • 主要思路是在这个类的initial()方法内增加一个入口,反复去调用reLoadMainDict()方法,此方法如下:
public void reLoadMainDict() {
    logger.info("重新加载词典...");
    // 新开一个实例加载词典,减少加载过程对当前词典使用的影响
    Dictionary tmpDict = new Dictionary(configuration);
    tmpDict.configuration = getSingleton().configuration;
    tmpDict.loadMainDict();
    tmpDict.loadStopWordDict();
    _MainDict = tmpDict._MainDict;
    _StopWords = tmpDict._StopWords;
    logger.info("重新加载词典完毕...");
}
1
2
3
4
5
6
7
8
9
10
11