引言
问什么要使用分词器?
这个问题可能就要牵扯到倒排索引这个概念,那什么是倒排索引呢?
倒排索引(英语:Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构。
有两种不同的反向索引形式:
- 一条记录的水平反向索引(或者反向档案索引)包含每个引用单词的文档的列表。
- 一个单词的水平反向索引(或者完全反向索引)又包含每个单词在一个文档中的位置。
后者的形式提供了更多的兼容性(比如短语搜索),但是需要更多的时间和空间来创建。
以上是维基百科给出的较为官方的解释。
通俗的使用关系型数据库可解释为:将某个字段的部分内容设置为关键词,而这个关键词就是逻辑上的索引(记录所在字段、所在字段位置),通过这个索引可以找到这个字段以及所在字段的具体位置,当然获取到字段完全匹配就可以获取到这条数据。 (此解释存在对倒排索引的扭曲,不可当作官方或准确的定义使用,只为了侧面的解释其含义)
说到这里也许大家已经知道分词器的作用了。为什么要说中文分词器呢?因为 elasticsearch 不是中国创造的,人家默认不支持中文分词(此处中文分词是分为有意义的词语为非单个文字)。
正文
安装方式
github确认版本对应关系 elasticsearch-analysis-ik 确认版本之后在 releases 下载或复制路径。
ik分词存在两种安装方式
- 通过 elasticsearch 的 elasticsearch-plugin 命令安装
复制所要安装的 ik 分词器的链接,使用以下方式安装即可。此方式安装生成的 ik 分词器的配置文件在 elasticsearch 的 config 目录下
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.3/elasticsearch-analysis-ik-6.4.3.zip
- 解压包方式
通过上面所说的地址下载解压在 elasticsearch 的 plgins 目录下即可,此方式 ik 分词器的配置文件就在解压包的下级目录
重启 elasticsearch 验证是否安装完成
curl 192.168.82.92:9200/_cat/plugins
node-1 analysis-ik 6.4.3
读时分词
读时分词:发生在用户查询时,ES 会即时地对用户输入的关键词进行分词,分词结果只存在内存中,当查询结束时,分词结果也会随即消失。
以下将演示读时分词操作,我将在kibana(elastic 旗下的一个组件)操作。
#添加索引
PUT /word-segmenter?pretty=true
#返回值
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "word-segmenter"
}
#查看索引
GET /_cat/indices?v
#返回值
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open word-segmenter 3YonvPYpSKyk4TOX74Ql9A 5 1 0 0 1.1kb 1.1kb
#存储数据
POST /word-segmenter/article
{
"subject" : "王者荣耀"
}
#返回值
{
"_index": "word-segmenter",
"_type": "article",
"_id": "hyL9GGwBQsOoFTCidV78",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
#验证分词器
GET /word-segmenter/_analyze?pretty=true
{
"analyzer" : "ik_max_word",
"text" : "王者荣耀"
}
#返回值
{
"tokens": [
{
"token": "王者",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "荣耀",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
}
]
}
#查询测试
POST /word-segmenter/_search?pretty=true
{
"query" : {
"match": {
"subject": {
"query": "王者荣耀",
"analyzer": "ik_max_word"
}
}
}
}
#返回值
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}
你会发现查询不到结果,明明存入了数据,并且也是也使用了分词,分词的结果“王者”、“荣耀”在数据中也存在,那问题到底出现在哪里?
#查看 word-segmenter 索引的结构
GET /word-segmenter?pretty=true
#返回值
{
"word-segmenter": {
"aliases": {},
"mappings": {
"article": {
"properties": {
"subject": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
},
"settings": {
"index": {
"creation_date": "1563787157307",
"number_of_shards": "5",
"number_of_replicas": "1",
"uuid": "3YonvPYpSKyk4TOX74Ql9A",
"version": {
"created": "6040399"
},
"provided_name": "word-segmenter"
}
}
}
}
在查询索引结构的返回值中可以发现 subject 并没有看到使用分词器(实际上使用了 elasticsearch 的默认分词器)。问题来了,难道使用不同的分词器就不能查找到结果么?下面我们证实以下,修改查询词。
#
POST /word-segmenter/_search?pretty=true
{
"query" : {
"match": {
"subject": {
"query": "王",
"analyzer": "ik_max_word"
}
}
}
}
#返回值
{
"took": 11,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "word-segmenter",
"_type": "article",
"_id": "hyL9GGwBQsOoFTCidV78",
"_score": 0.2876821,
"_source": {
"subject": "王者荣耀"
}
}
]
}
}
可以查询到数据,首先可以排除上面的猜想。那么只能是分词的问题了,查看默认分词,分的到底是什么?
#默认分词器
GET /word-segmenter/_analyze?pretty=true
{
"text" : "王者荣耀"
}
#返回值
{
"tokens": [
{
"token": "王",
"start_offset": 0,
"end_offset": 1,
"type": "",
"position": 0
},
{
"token": "者",
"start_offset": 1,
"end_offset": 2,
"type": "",
"position": 1
},
{
"token": "荣",
"start_offset": 2,
"end_offset": 3,
"type": "",
"position": 2
},
{
"token": "耀",
"start_offset": 3,
"end_offset": 4,
"type": "",
"position": 3
}
]
}
通过上面我们可以确认两个分词器分词的结果,相互不存在,所以就造成了我们无法搜索到结果。这里我们就将引入写时分词
写时分词
写时分词:发生在文档写入时,ES 会对文档进行分词后,将结果存入倒排索引,该部分最终会以文件的形式存储于磁盘上,不会因查询结束或者 ES 重启而丢失。
#删除原有索引 word-segmenter
DELETE /word-segmenter?pretty=true
#返回值
{
"acknowledged": true
}
#新建索引,指定默认分词器为 ik 分词器
PUT /word-segmenter?pretty=true
{
"settings": {
"analysis": {
"analyzer": {
"ik" : {
"tokenizer" : "ik_max_word"
}
}
}
},
"mappings": {
"article" : {
"properties": {
"subject" : {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
然后执行读时分词的数据插入及查询会发现可以查到。就算查询时不手动制定使用的分词器也可以查询数据。
自定义分词器
在上面我们测试了将“王者荣耀”分词为“王者”、“荣耀”两个词,但是我就是想将“王者荣耀”也作为一个词如何操作呢?ik 分词器给我们提供了扩展字典的方式。
#进入 ik 分词器的配置文件夹(路径取决于安装方式与安装路径)
cd /home/software/elasticsearch-6.4.3/config/analysis-ik/
#查创建自己的词库
vim /my_extra_word.dic
#添加数据,每行代表一个词
王者荣耀
#修改 ik 分词器的配置
vim IKAnalyzer.cfg.xml
#将 指向新建的词典,多个词典用 “;” 分号隔开
IK Analyzer 扩展配置
my_extra_word.dic
#重启 elasticsearch
kill -9 9200
cd /home/software/elasticsearch-6.4.3/
./bin/elasticsearch -d
#测试查看
GET /word-segmenter/_analyze?&pretty=true
{
"analyzer": "ik_max_word",
"text" : "王者荣耀"
}
#返回值
{
"tokens": [
{
"token": "王者荣耀",
"start_offset": 0,
"end_offset": 4,
"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
}
]
}
引用
倒排索引-维基百科
掌握 analyze API,一举搞定 Elasticsearch 分词难题
ES创建索引映射指定分词策略
elasticsearch5.5.2手动创建索引并配置ik中文分词器
ES中文分词器+自定义分词
centos7安装elasticsearch7并集成IK中文分词器添加系统服务开机启动
Elasticsearch之中文分词器插件es-ik