Elasticsearch-分词
# 前言
我们前面学习Elasticsearch一直说分词,好像很熟悉,但是未作深入了解,分词到底是什么?下面来学习。
# 分词
分词是Elasticsearch全文检索的核心,分词是将一大段完整的句子,分成一个个单词,然后利用单词进行相关性匹配,最终完成全文检索的功能。
# 分词器(tokenizer)
在ES中,是用分词器(tokenizer
)来进行分词,一个tokenizer
(分词器)接收一个字符流,将之分割为独立的tokens
(词元,通常是独立的单词),然后输出tokens
流。
例如:whitesapce tokenizer
遇到空白字符时分割文本。它会将文本“Quick brown fox!
”分割为[Qucik
,brown
,fox!
]。
该tokenizer
(分词器)还负责记录各个term
(词条)的顺序或者position
(位置)(用于phrase
短语和word proximity
词近邻查询),以及term
(词条)所代表的的原始word
(单词)的start
和end
的chaaracter offsets
(字符偏移量,用于高亮显示索索的内容)。
Elasticsearch
提供了很多内置的分词器,可以用来构建custom analyzers
(自定义分词器)。
Elasticsearch
默认使用的分词器是标准分词器 (opens new window)。
# 标准分词器
The
standard
analyzer is the default analyzer which is used if none is specified. It provides grammar based tokenization (based on the Unicode Text Segmentation algorithm, as specified in Unicode Standard Annex #29 (opens new window)) and works well for most languages.
# 官方例子
# 分词请求
POST _analyze
{
"analyzer": "standard",
"text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
# 返回
[ the, 2, quick, brown, foxes, jumped, over, the, lazy, dog's, bone ]
2
3
4
5
6
7
8
# 例子2
# 分词请求
POST _analyze
{
"analyzer": "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
},
{
"token" : "人",
"start_offset" : 4,
"end_offset" : 5,
"type" : "<IDEOGRAPHIC>",
"position" : 4
},
{
"token" : "我",
"start_offset" : 6,
"end_offset" : 7,
"type" : "<IDEOGRAPHIC>",
"position" : 5
},
{
"token" : "想",
"start_offset" : 7,
"end_offset" : 8,
"type" : "<IDEOGRAPHIC>",
"position" : 6
},
{
"token" : "吃",
"start_offset" : 8,
"end_offset" : 9,
"type" : "<IDEOGRAPHIC>",
"position" : 7
},
{
"token" : "重",
"start_offset" : 9,
"end_offset" : 10,
"type" : "<IDEOGRAPHIC>",
"position" : 8
},
{
"token" : "庆",
"start_offset" : 10,
"end_offset" : 11,
"type" : "<IDEOGRAPHIC>",
"position" : 9
},
{
"token" : "火",
"start_offset" : 11,
"end_offset" : 12,
"type" : "<IDEOGRAPHIC>",
"position" : 10
},
{
"token" : "锅",
"start_offset" : 12,
"end_offset" : 13,
"type" : "<IDEOGRAPHIC>",
"position" : 11
}
]
}
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
发现它居然把每个汉字都分成一个词,显然不符合我们的预想,按我的想法肯定会把中国人、火锅放在一个词里面。
显然标准分词器只能满足英文的分词,中文的分词还需要安装一个开源的ik分词器
。
# 安装ik分词器
# 安装步骤
- 进入es容器内部的
plugins
目录
#b7e是容器id前三位
docker exec -it b7e /bin/bash
# 进入到目录
cd plugins/
# 创建一个专用目录
mkdir ik
# 进入到专用目录
cd ik/
# 下载跟当前es同版本的分词器如果没安装`wget`,输入`yum install wget`回车安装
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
# 解压 输入前几位就可以自动补全,如果没有安装`unzip`,输入`yum install unzip`
unzip #下载的文件
# 查看文件夹权限,发现是文件夹蓝色
ll
# 更改ik文件夹权限
chmod -R 777 ik/
# 再次查看文件夹权限,已经变成绿色 drwxrwxrwx
ll
# 进入docker内容内部
docker exec -it b7e /bin/bash
# 查看容器内部挂载,到底同步了没
cd plugins/
# 查看文件夹,发现已经安装上
ll
# 回到上一级目录
cd ../
# 进入bin目录
cd bin/
# 查看bin下的文件,有一个elasticsearch-plugin,可以执行安装插件
ls
# 执行插件
elasticsearch-plugin
# 会提示命令
elasticsearch-plugin -h
# 根据提示命令,查看一下已经安装的插件,回显ik,说明已经安装好了
elasticsearch-plugin list
# 退出容器
exit
# 重启容器
docker restart ee1 #不写容器id也可以写elasticsearch
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
54
55
56
57
58
59
60
# 测试分词
The IK Analysis plugin integrates Lucene IK analyzer (http://code.google.com/p/ik-analyzer/) into elasticsearch, support customized dictionary.
Analyzer:
ik_smart
,ik_max_word
, Tokenizer:ik_smart
,ik_max_word
通过github官方 (opens new window),我们来测试一下刚才“我是中国人,我想吃重庆火锅”的分词有什么区别
# ik_smart
# 请求
POST _analyze
{
"analyzer": "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" : 5,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "我",
"start_offset" : 6,
"end_offset" : 7,
"type" : "CN_CHAR",
"position" : 3
},
{
"token" : "想吃",
"start_offset" : 7,
"end_offset" : 9,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "重庆火锅",
"start_offset" : 9,
"end_offset" : 13,
"type" : "CN_WORD",
"position" : 5
}
]
}
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
# ik_max_word
# 请求
POST _analyze
{
"analyzer": "ik_max_word",
"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" : 5,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "中国",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "国人",
"start_offset" : 3,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "我",
"start_offset" : 6,
"end_offset" : 7,
"type" : "CN_CHAR",
"position" : 5
},
{
"token" : "想吃",
"start_offset" : 7,
"end_offset" : 9,
"type" : "CN_WORD",
"position" : 6
},
{
"token" : "吃重",
"start_offset" : 8,
"end_offset" : 10,
"type" : "CN_WORD",
"position" : 7
},
{
"token" : "重庆火锅",
"start_offset" : 9,
"end_offset" : 13,
"type" : "CN_WORD",
"position" : 8
},
{
"token" : "重庆",
"start_offset" : 9,
"end_offset" : 11,
"type" : "CN_WORD",
"position" : 9
},
{
"token" : "火锅",
"start_offset" : 11,
"end_offset" : 13,
"type" : "CN_WORD",
"position" : 10
}
]
}
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
这就是ik分词器分词的效果。但是似乎有些不如人意。
比如:
# 请求
POST _analyze
{
"analyzer": "ik_max_word",
"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" : 6,
"type" : "CN_WORD",
"position" : 3
}
]
}
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
电商这个词没有出现,没有达到我预想的结果。
再换成其他词,比如:乔碧萝殿下,再比如一些其他网络流行短词。都不能很好的识别。
# 自定义词库
在很多情况下,分词,并不一定符合预想,不过没关系,可以自定义分词规则。
拓展词库,在原来的基础上,添加新的规则。
拓展词库,通常是修改配置文件,向远程请求,获取最新的词
网络上的。
自己维护一个nginx,自己维护词库。
# 安装nginx
# 拉去镜像(当然可以不拉取,启动实例自动检测是否有,会自动拉取)
docker pull nginx
# 随便启动一个nginx实例,只是为了复制出配置
docker run -p 80:80 --name nginx -d nginx
# 创建一个nginx配置文件夹
mkdir /mydata/nginx
# 进入目录
cd /mydata
# 修改nginx文件夹权限
chmod -R 777 nginx/
# 将容器内的配置文件拷贝到当前目录,注意后面有一个点
docker container cp nginx:/etc/nginx .
# 拷贝成功,进入目录查看一下是否复制成功
cd nginx/
ls
# 停止nginx
docker stop nginx
# 删除nginx
docker rm nginx
# 回去上一级目录,修改nginx文件夹名称为conf
cd ../
mv nginx conf
ls
# 创建一个新文件夹nginx
mkdir nginx
# 将conf文件夹移动到nginx下
mv conf nginx/
ls
cd nginx/
ls
# 修改nginx文件夹权限
cd ../
chmod -R 777 nginx/
ls
# 启动nginx容器挂载到虚拟机的nginx文件夹
docker run -p 80:80 --name nginx \
-v /mydata/nginx/html:/usr/share/nginx/html \
-v /mydata/nginx/logs:/var/log/nginx \
-v /mydata/nginx/conf:/etc/nginx \
--restart=always \
-d nginx
# 进入nginx/目录下,检查html,log,conf,这三个文件夹是否创建好
cd nginx/
ls
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
54
55
56
57
58
访问http://192.168.56.10,自动访问80端口,页面是403,因为没有任何页面。
# 进入nginx下的html文件
cd /mydata/nginx/html/
# 自己创建一个html
vi index.html
# 按i进入编辑模式
<h1>我是nginx的主页,默认访问</h1>
# 按ESC退出编辑模式,进入命令模式
# 保存退出
:wq
2
3
4
5
6
7
8
9
10
11
12
13
重新访问nginx主页,发现主页已经有信息了。
# 在html目录下创建一个目录专门来放置分词
mkdir es
ls
# 创建一个文件专门来管理分词
vi expendWord.txt
# i编辑模式
电商
乔碧萝
# ESC
:wq
2
3
4
5
6
7
8
9
10
11
12
13
访问这个文件,http://192.168.56.10/es/expandWord.txt,页面上也出现相关内容。
记得这个txt文件是UTF-8 With BOM格式,不然nginx访问是乱码
# 添加短语
# 进入ES的ik分词器插件
cd /mydata/elasticsearch/plugins/ik/config/
ls
# 修改配置
vi IKAnalyzer.cfg.xml
2
3
4
5
6
进入配置配件,发现远程拓展字典注释了,取消注释,把自己字典的地址放进去。
<?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">http://192.168.56.10/es/expandWord.txt</entry>
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
2
3
4
5
6
7
8
9
10
11
12
13
修改后保存,重启ES
docker restart elasitcsearch
重新访问热词,已经可以按自己预想的词划分了。
POST _analyze
{
"analyzer": "ik_max_word",
"text": "乔碧萝殿下"
}
2
3
4
5
至此,自定义词库成功!
以后还有什么拓展,去到/mydata/nginx/html/es下的expandWord.txt
自行拓展,然后重启ES就能自动识别了!