前言

本文我们来学习有关elasticsearch的核心概念及如何集成IK分词器实现中文搜索,以及扩展词、停用词、同义词的使用。

核心概念

Elasticsearch与关系型数据库中的概念的关系对比如下:

关系型数据库(比如Mysql)非关系型数据库(Elasticsearch)
数据库Database索引Index
表Table索引Index类型(原为Type)
数据行Row文档Document
数据列Column字段Field
约束 Schema映射Mapping

索引(index)

类似的数据放在一个索引,非类似的数据放不同索引, 一个索引也可以理解成一个关系型数据库。

类型(type)

代表document属于index中的哪个类别(type)也有一种说法一种type就像是数据库的表,比如dept表,user表。 注意ES每个大版本之间区别很大:

ES 5.x中一个index可以有多种type。

ES 6.x中一个index只能有一种type。

ES 7.x以后要逐渐移除type这个概念。

映射(mapping)

mapping定义了每个字段的类型等信息。相当于关系型数据库中的表结构。

常用数据类型:text、keyword、number、array、range、boolean、date、geo_point、ip、 nested、object


介绍完elasticsearch里面的相关概念之后,我们需要进行elasticsearch相关api的练习使用,为了方便我们学习,我们需要安装一些工具来更直观的展示效果,这里我们选用kibana。

安装配置Kibana

简介

Kibana是一个基于Node.js的Elasticsearch索引库数据统计工具,可以利用Elasticsearch的聚合功 能,生成各种图表,如柱形图,线状图,饼图等。 而且还提供了操作Elasticsearch索引数据的控制台,并且提供了一定的API提示,非常有利于我们学习 Elasticsearch的语法。

安装

第一步,下载kibana-7.4.2-linux-x86_64.tar.gz并上传至服务器;

第二步,解压安装:

1
2
tar -zxvf kibana-7.4.0-linux-x86_64.tar.gz
mv /root/kibana-7.4.0-linux-x86_64 /usr/kibana/

第三步,改变目录权限为我们之前设置的estest账户:

1
chown -R estest /usr/kibana/

设置访问权限:

1
chmod -R 777 /usr/kibana/

第四步,修改配置文件:

1
vim /usr/kibana/config/kibana.yml

这里需要修改的有三处,端口、IP、es的访问地址:

1
2
3
4
5
6
7
#访问kibana的端口号
server.port: 5601
#0.0.0.0表示任何IP都可以访问kibana
server.host: "0.0.0.0"
# The URLs of the Elasticsearch instances to use for all your queries.
#此处应改为elasticsearch的访问地址
elasticsearch.hosts: ["http://192.168.211.136:9200"]

第五步,启动kibana:

1
2
3
4
#切换用户
su estest
#启动
./bin/kibana(路径:/usr/kibana)

测试

浏览器输入ip:5601,进入Kibana的主界面:

说明kibana安装成功。

我们以后在kibana中执行请求,操作elasticsearch,步骤如下图:


elasticsearch自带的分词器对中文分词效果不好,我们常用的是IK分词器,下面我们来安装IK分词器。

集成IK分词器

简介

IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版 开始,IKAnalyzer已经推出 了3个大版本。最初,它是以开源项目Lucene为应用主体的,结合词典分词 和文法分析算法的中文分词组件。新版本的IKAnalyzer3.0则发展为 面向Java的公用分词组件,独立于 Lucene项目,同时提供了对Lucene的默认优化实现。

特性

IK分词器3.0的特性如下:

  1. 采用了特有的“正向迭代最细粒度切分算法“,具有60万字/秒的高速处理能力。
  2. 采用了多子处理器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数 量词,罗马数字,科学计数法),中文词汇(姓名、地名处理)等分词处理。
  3. 支持个人词条的优化的词典存储,更小的内存占用。
  4. 支持用户词典扩展定义。
  5. 针对Lucene全文检索优化的查询分析器IKQueryParser;采用歧义分析算法优化查询关键字的搜索 排列组合,能极大的提高Lucene检索的命中率。

下载安装

下载地址:https://github.com/medcl/elasticsearch-analysisik/releases/download/v7.4.0/

第一步,在elasticsearch的bin目录下执行以下命令,es插件管理器会自动帮我们安装,然后等待安装完成:

1
/usr/elasticsearch/bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.0/elasticsearch-analysis-ik-7.4.0.zip

第二步,下载完成后会提示 Continue with installation?输入 y 即可完成安装

第三步,重启Elasticsearch 和Kibana

简单测试

Ik分词器有两种分词模式:ik_max_wordik_smart

ik_max_wordik_smart
对文本做最细粒度的拆分对文本做最粗粒度的拆分
常用不常用

来测试一下:

ik_max_word模式

1
2
3
4
5
POST _analyze
{
"analyzer": "ik_max_word",
"text": "南京市长江大桥"
}

ik_max_word模式下的拆分结果:

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
40
41
42
43
44
45
46
{
"tokens" : [
{
"token" : "南京市",
"start_offset" : 0,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "南京",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "市长",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "长江大桥",
"start_offset" : 3,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "长江",
"start_offset" : 3,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "大桥",
"start_offset" : 5,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 5
}
]
}

ik_smart模式

1
2
3
4
5
POST _analyze
{
"analyzer": "ik_smart",
"text": "南京市长江大桥"
}

ik_smart模式下的拆分结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"tokens" : [
{
"token" : "南京市",
"start_offset" : 0,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "长江大桥",
"start_offset" : 3,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 1
}
]
}

可以看到两种分词模式下的拆分结果是完全不同的。


提出一个问题:"南京市长江大桥"这句话,如果现在假如江大桥是一个人名,是南京市市长,那么上面的分词显然是不合理的,该怎么办?

这就需要扩展词了。

关于扩展词及停用词,我们在 Lucene应用实战(三)——分词器的使用 一文中对于Lucene有过简单的使用。

扩展词典

所谓扩展词,就是不想让哪些词被分开,让他们分成一个词。比如上面的**江大桥 **

具体步骤

编写lagou_ext_dict.dic扩展字典文件,步骤如下:

执行命令:

1
cd /usr/elasticsearch/config/analysis-ik
1
vim lagou_ext_dict.dic

编写内容:

1
江大桥

wq!保存退出。

1
vim IKAnalyzer.cfg.xml

lagou_ext_dict.dic配置在xml文件中,如下:

重启elasticsearch。

测试

执行之前的分词用例:

1
2
3
4
5
POST _analyze
{
"analyzer": "ik_max_word",
"text": "南京市长江大桥"
}

运行结果:

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
"tokens" : [
{
"token" : "南京市",
"start_offset" : 0,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "南京",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "市长",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "长江大桥",
"start_offset" : 3,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "长江",
"start_offset" : 3,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "江大桥",
"start_offset" : 4,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 5
},
{
"token" : "大桥",
"start_offset" : 5,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 6
}
]
}

可以看到多了个自定义的“江大桥”在分词结果当中,说明我们的自定义扩展词生效了。

停用词典

所谓扩展词,就是不想让哪些词被分开,让他们分成一个词。比如上面的江大桥

具体步骤

编写lagou_stop_dict.dic扩展字典文件,步骤如下:

执行命令:

1
cd /usr/elasticsearch/config/analysis-ik
1
vim lagou_stop_dict.dic

编写内容:

1
2
3
南京


wq!保存退出。

1
vim IKAnalyzer.cfg.xml

lagou_ext_dict.dic配置在xml文件中,如下:

测试

执行之前的分词用例:

1
2
3
4
5
POST _analyze
{
"analyzer": "ik_max_word",
"text": "南京市长江大桥"
}

运行结果:

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
40
41
42
43
44
45
46
{
"tokens" : [
{
"token" : "南京市",
"start_offset" : 0,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "市长",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "长江大桥",
"start_offset" : 3,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "长江",
"start_offset" : 3,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "江大桥",
"start_offset" : 4,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "大桥",
"start_offset" : 5,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 5
}
]
}

可以看到“南京”关键词已经在分词结果中消失了,说明咱们配置的停用词典生效了。

同义词典

语言博大精深,有很多相同意思的词,我们称之为同义词,比如“番茄”和“西红柿”,“馒头”和“馍”等。在搜索的时候,我们输入的可能是“番茄”,但是应该把含有“西红柿”的数据一起查询出来,这种情况叫做同义词查询。 注意:扩展词和停用词是在索引的时候使用,而同义词是检索时候使用。

具体步骤

Elasticsearch 自带一个名为 synonym 的同义词 filter。为了能让 IK 和 synonym 同时工作,我们需要定义新的 analyzer,用 IK 做 tokenizer,synonym 做 filter。听上去很复杂,实际上要做的只是加一段配置。

先创建/config/analysis-ik/synonym.txt 文件,输入一些同义词并存为utf-8 格式。例如:

1
2
lagou,拉勾
china,中国

然后创建索引时使用同义词配置,如下:

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
PUT /lagou-es-synonym
{
"settings":{
"analysis":{
"filter":{
"word_sync":{
"type":"synonym",
"synonyms_path":"analysis-ik/synonym.txt"
}
},
"analyzer":{
"ik_sync_max_word":{
"filter":[
"word_sync"
],
"type":"custom",
"tokenizer":"ik_max_word"
},
"ik_sync_smart":{
"filter":[
"word_sync"
],
"type":"custom",
"tokenizer":"ik_smart"
}
}
}
},
"mappings":{
"properties":{
"name":{
"type":"text",
"analyzer":"ik_sync_max_word",
"search_analyzer":"ik_sync_max_word"
}
}
}
}

以上配置定义了ik_sync_max_word和ik_sync_smart这两个新的 analyzer,对应 IK 的 ik_max_word 和 ik_smart 两种分词策略。ik_sync_max_word和 ik_sync_smart都会使用 synonym filter 实现同义词转 换 。

测试

插入数据:

1
2
3
4
POST /lagou-es-synonym/_doc/1
{
"name":"拉勾是中国专业的互联网招聘平台"
}

搜索"lagou"或者"china"都能匹配出"拉勾是中国专业的互联网招聘平台"这条记录:

1
2
3
4
5
6
7
8
POST /lagou-es-synonym/_doc/_search
{
"query": {
"match": {
"name": "lagou"
}
}
}

结果:

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
{
"took" : 989,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.41688064,
"hits" : [
{
"_index" : "lagou-es-synonym",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.41688064,
"_source" : {
"name" : "拉勾是中国专业的互联网招聘平台"
}
}
]
}
}

总结

我们学习了elasticsearch中索引、类型、映射,集成可视化界面kibana的安装使用,最后学习了elasticsesarch集成IK分词器实现中文分词的搜索。