1. Analysis
Analysis,译为“分析”,在es中指的是对文本(text)的分析,所谓文本分析就是把一段文本转换为一系列单词(term/token)的过程,也叫分词。Analysis是通过Analyzer来实现的。
2. Analyzer
分析器(analyzer)都由三部分组成的:character filters, tokenizers , token filters
我们可以看出,存入文档数据时,检索文档数据时,都会用到分词器!
3. 内置分词器
https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-tokenizers.html
3.1 Standard Analyzer
The standard tokenizer provides grammar based tokenization (based on the Unicode Text Segmentation algorithm, as specified in Unicode Standard Annex #29) and works well for most languages.
POST _analyze
{
"tokenizer": "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 ]
3.2 Letter tokenizer
The letter tokenizer breaks text into terms whenever it encounters a character which is not a letter. It does a reasonable job for most European languages, but does a terrible job for some Asian languages, where words are not separated by spaces.
POST _analyze
{
"tokenizer": "letter",
"text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
[ The, QUICK, Brown, Foxes, jumped, over, the, lazy, dog, s, bone ]
3.3 Simple Analyzer
按照非字母切分,非字母则会被去除,英文大写转小写
GET _analyze
{
"analyzer": "simple",
"text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
[the,quick,brown,foxes,jumped,over,the,lazy,dog,s,bone]
3.4 Stop Analyzer
停用词过滤(the,a, an, is),按照非字母切分,非字母则会被去除
GET _analyze
{
"analyzer": "stop",
"text": "The 2 a is 3 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
[quick,brown,foxes,jumped,over,lazy,dog,s,bone]
3.5 Whitespace Analyzer
按空格切分,英文不区分大小写,中文不分词
GET _analyze
{
"analyzer": "whitespace",
"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.]
3.6 Keyword Analyzer
不分词,当成一整个 term 输出
GET _analyze
{
"analyzer": "keyword",
"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.]
3.8 多分词器
在es默认创建索引时(比如添加文档的同时,就创建不存在的索引),其中的text类型的字段,总会自动再创建出keyword类型的索引。比如在bank中的address和address.keyword。
当我们自己手动创建索引的时候,把一个字段定成什么类型,那就只有什么类型,不会额外生产什么keyword类型的索引的。
当然,我们也可以自己指定索引中的一个字段同时建立多个不同的索引,这多个不同的索引可以使用不同的类型和分词器,如下:
DELETE foo
PUT foo
{
"mappings": {
"properties": {
"name": {
"type": "keyword"
},
"addr": {
"type": "text",
"analyzer": "standard",
"search_analyzer": "standard",
"fields": {
"sd": {
"type": "text",
"analyzer": "standard"
},
"kw": {
"type": "keyword"
}
}
}
}
}
}
PUT foo/_doc/1
{
刘德华",
中国陕西省西安市"
}
GET foo/_search
{
"query": {
"match": {
中国陕西省"
}
}
}
4. 中文分词器
汉语中,句子是单词的组合。除标点符号外,汉语词语之间不存在分隔符。这就给中文分词带来了挑战。
比如,“首饰和服装”,“苹果不大好吃”。幸运的是,无论中文分词是有多么的复杂,与我们都没有关系,我们直接使用中文分词器即可。
4.1 问题
当我们在kibana中,发起如下请求时,会发现es对中文处理得很简单:
4.2 解决
为了让es能更好地处理中文,我们需要下载es的ik分词器插件,下载地址如下:
https://github.com/medcl/elasticsearch-analysis-ik/releases
特别注意,下载的ik分词器的版本,必须与你所安装的es的版本一致!比如目前我安装的es版本是7.10.2,那么下载的ik分词器的版本也必须是7.10.2
切换到root用户,将ik分词器插件上传给linux,上传目录为:/home/elk/elasticsearch-7.10.2/plugins
rz -y
然后在/home/elk/elasticsearch-7.10.2/plugins目录中,再创建一个ik目录,注意目录的名字必须是ik
mkdir ik
将ik分词器考入到ik目录中:
mv elasticsearch-analysis-ik-7.10.2.zip ik/
进入ik目录,解压ik分词器文件
unzip elasticsearch-analysis-ik-7.10.2.zip
记得将ik分词器的拥有者改为elk用户:
chown -R elk ik
否则在启动es服务的时候,总是会显示这个错误:java.io.FileNotFoundException: /usr/es/logs/my-application.log (权限不够)
然后以elk用户,重启es,再此进行测试,此时就可以使用ik_smart和ik_max_word分词器了!
ik_smart和ik_max_word,这两个分词器的区别就是分词粒度不同:
5. 指定查询分词器
指定查询分词器的时机,有三个:
1. 访问_anaylze时,通过请求体中的参数analyzer指定
2. 定义索引时,通过mapping指定
3. 检索文档时,通过request body指定
在访问_analyze时,通过
GET _analyze
{
"analyzer": "ik_max_word",
好好学习天天向上"
}
创建索引时,在mapping中指定分词器
DELETE foo
PUT foo
{
"mappings": {
"properties": {
"name": {
"type": "keyword"
},
"addr": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
PUT foo/_doc/1
{
刘德华",
西安"
}
再观察一下不同分词器的效果:
GET _analyze { "analyzer": "ik_smart", "text": "西安市" } | |
GET _analyze { "analyzer": "ik_max_word", "text": "西安市" } |
查询时,在request body中指定分词器
无结果 | 有结果 |
6. 设置索引的默认分词器
es默认的分词器是standard,我们在创建索引时,指定当前索引的默认分词器
delete foo
PUT foo
{
"settings" : {
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
7. 配置词典
7.1 问题
当我们输入一些特有的中文名词,尤其是一些网络用语时,ik分词器也会将这些特有的中文名词分隔
我们以“小星辰”为例,我们想让ik分词器把“小星辰”当做一个整体的词语来对待,但结果却不是这样:
7.2 配置本地词典
以root用户,进入ik分词器的配置文件夹中:/home/elk/elasticsearch-7.10.2/plugins/ik/config,创建my.dic
touch my.dic
在my.dic中,写入“小星辰”
确保my.dic文件的编码为:utf-8
file -i my.dic
然后修改 IKAnalyzer.cfg.xml,配置好自己创建的my.dic
<?xml versinotallow="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
扩展配置</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>
以elk,重启es,再次测试
我们发现,“小星辰”这个词,已经被当做一个完整的词了。
7.3 配置远程词典
使用IK分词器,对网络词汇“洛天依 ”进行分词
GET _analyze
{
"analyzer": "ik_max_word",
洛天依"
}
并没有把“洛天依”当成一个完整的词
["洛", "天" ,"依"]
在tomcat中,发布一个web应用,在该web应用中,发布一个文件:words.dic,内容如下:
如果有多个词的话,则一个词占一行
访问地址为:
http://192.168.1.100:8080/mydic/words.dic
修改ik分词器的配置:IKAnalyzer.cfg.xml
<?xml versinotallow="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
扩展配置</comment>
<entry key="ext_dict"></entry>
<entry key="ext_stopwords"></entry>
<entry key="remote_ext_dict">http://192.168.1.100:8080/mydic/words.dic</entry>
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
以elk用户,重启es,然后再次进行测试
我们发现,洛天依被当做一个词汇了。配置远程词典的方式,还支持热更新,比如,我们使用IK分词器对网络词语:“乔碧萝”进行分词:
发现,IK分词器并没有把“乔碧萝”当做一个词,此时我们修改tomcat下的words.dic文件:
不用重启es,但是要稍等一下,我们再次测试: