Elasticsearch 分词器 Analysis IK分词器

Analysis

Analysis: 文本分析是把全文本转换一系列单词(term/token)的过程,也叫分词。Analysis是通过Analyzer来实现的

当一个文档被索引时,每个Field都可能会创建一个倒排索引(Mapping可以设置不索引该Field)。

倒排索引的过程就是将文档通过Analyzer分成一个一个的Term,每一个Term都指向包含这个Term的文档集合。

当查询query时,Elasticsearch会根据搜索类型决定是否对query进行analyze,然后和倒排索引中的term进行相关性查询,匹配相应的文档。

Analyzer组成

分析器(analyzer)都由三种构件块组成的:character filterstokenizerstoken filters

1) character filter 字符过滤器
在一段文本进行分词之前,先进行预处理,比如说最常见的就是,过滤html标签(<span>hello<span> --> &lt;hello&gt;
2) tokenizers 分词器

英文分词可以根据空格将单词分开,中文分词比较复杂,可以采用机器学习算法来分词。

3) Token filters Token过滤器

将切分的单词进行加工。大小写转换(例将“Quick”转为小写),去掉词(例如停用词像“a”、“and”、“the”等等),或者增加词(例如同义词像“jump”和“leap”)。

三者顺序Character Filters--->Tokenizer--->Token Filter

三者个数analyzer = CharFilters(0个或多个) + Tokenizer(恰好一个) + TokenFilters(0个或多个)

内置分词器

官网地址

https://www.elastic.co/guide/en/elasticsearch/reference/current/analyzer.html

内置分词器

https://www.elastic.co/guide/en/elasticsearch/reference/7.x/analysis-tokenizers.html

Standard Analyzer

  • 默认分词器
  • 按词分类
POST _analyze
{
  "analyzer": "standard",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

The above sentence would produce the following terms:

[ The, 2, QUICK, Brown, Foxes, jumped, over, the, lazy, dog's, bone ]

Configuration

The standard tokenizer accepts the following parameters:

max_token_length The maximum token length. If a token is seen that exceeds this length then it is split at max_token_length intervals. Defaults to 255.
最大令牌长度。如果看到令牌超过此长度,则将其max_token_length间隔分割。默认为255

我们将standard令牌生成器 配置max_token_length为5。

PUT my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "standard",
          "max_token_length": 5
        }
      }
    }
  }
}

POST my-index-000001/_analyze
{
  "analyzer": "my_analyzer",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

The above example produces the following terms:

[ The, 2, QUICK, Brown, Foxes, jumpe, d, over, the, lazy, dog's, bone ]

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]

Stop Analyzer

  • 停用词过滤(the,a, 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]

Whitespace Analyzer

  • 按空格切分
  • 英文不区分大小写
  • 中文不分词
#stop
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.]

Keyword Analyzer

  • 不分词,当成一整个 term 输出
#keyword
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.]

Patter Analyzer**

  • 通过正则表达式进行分词
  • 默认是 \W+(非字母进行分隔)
GET _analyze
{
  "analyzer": "pattern",
  "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]

Configuration

The pattern tokenizer accepts the following parameters:

pattern A Java regular expression, defaults to \W+.
flags Java正则表达式标志。标志应该用管道分隔,例如"CASE_INSENSITIVE"
group 要提取哪个捕获组作为标记。默认为-1(分割)。

我们将pattern tokenizer程序配置为在遇到逗号时将文本分成令牌:

DELETE my-index-000001

PUT my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "pattern",
          "pattern": ","
        }
      }
    }
  }
}

POST my-index-000001/_analyze
{
  "analyzer": "my_analyzer",
  "text": "comma,separated,values"
}

上面的示例产生以下结果:

[ comma, separated, values ]

Language Analyzer

  • 停用词过滤(the,a, is)

支持语言:arabic, armenian, basque, bengali, bulgarian, catalan, czech, dutch, english, finnish, french, galician, german, hindi, hungarian, indonesian, irish, italian, latvian, lithuanian, norwegian, portuguese, romanian, russian, sorani, spanish, swedish, turkish.

#english
GET _analyze
{
  "analyzer": "english",
  "text": "The 2 a is QUICK Brown-Foxes jumped over the lazy dog's bone."
}

输出:

[2,quick,brown,fox,jump,over,the,lazy,dog,bone]

多分词器

delete /st

PUT /st
{
  "mappings": {
      "properties": {
        "uid": {
          "type": "keyword"
        },
        "addr": {
          "type": "text",
          "analyzer": "standard",
          "search_analyzer": "standard",
           "fields": {
              "standardname": {
                "type": "text", 
                "analyzer": "ik_max_word"
              },
              "simplename": {
                "type": "text", 
                "analyzer": "simple"
              },
              "patternname": {
                "type": "text", 
                "analyzer": "pattern"
              }
           }
        }
    }
  }
}

注意:声明了分词器,插入数据的时候才会根据分词建立倒排序索引。搜索的时候才能命中。

中文分词要比英文分词难,英文都以空格分隔,中文理解通常需要上下文理解才能有正确的理解,比如 [苹果,不大好吃]和
[苹果,不大,好吃],这两句意思就不一样。

中文分词

中文的分词器现在比较推荐的就是 IK分词器,当然也有些其它的比如 smartCNHanLP

hanlp> ansj >结巴>ik>smart chinese analysis

这里只讲如何使用IK做为中文分词。

IK分词器的版本要你安装ES的版本一致。

开源分词器 Ik 的github:

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

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

我下载的是7.0.0,地址为

https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.0.0

Docker安装IK中文分词器

Elasticsearch-analysis-ik是一款中文的分词插件,支持自定义词库,也有默认的词库。

下载IK分词器:
elasticsearch-analysis-ik-7.0.0.zip

进入解压后的包中看一下,主目录下包含一些插件依赖的外部jar包和一个config文件,

config文件里面的内容是分词器分词时读取文件的主要目录,我们大概说说里面的各文件内容,

IKAnalyzer.cfg.xml:用来配置自定义词库
main.dic:ik原生内置的中文词库,总共有27万多条,只要是这些单词,都会被分在一起
quantifier.dic:放了一些单位相关的词
suffix.dic:放了一些后缀
surname.dic:中国的姓氏
stopword.dic:英文停用词

ik原生最重要的两个配置文件
main.dic:包含了原生的中文词语,会按照这个里面的词语去分词
stopword.dic:包含了英文的停用词

进入容器:

docker exec -it elk /bin/bash

姿势1:(速度超慢)

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.0.0/elasticsearch-analysis-ik-7.0.0.zip

姿势2:(手动安装)

将 elasticsearch-analysis-ik-7.0.0.zip上传至服务器。(版本不同,则目录可能为/usr/share/elasticsearch/plugins/ik)

解压到ik目录,并将解压后的文件传入docker。(也可以在里面解压 yum install -y unzip zip)

docker cp ik elk:/opt/elasticsearch/plugins

进入容器后

docker exec -it elk /bin/bash
cd /opt/elasticsearch/plugins

发现已经有了插件。

退出容器:exit

重启

docker restart elk

IK有两种颗粒度的拆分:

ik_smart: 会做最粗粒度的拆分

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

GET _analyze
{
  "analyzer": "ik_smart",
  "text": "我爱北京天安门"
}


GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "我爱北京天安门"
}

会拆解成【我,爱,北京,天安门】

自定义字典的分词器

姿势1:

进入docker

docker exec -it elk /bin/bash
cd /opt/elasticsearch/plugins/ik/config

如果docker没有vi

apt-get update
apt-get install vim

编辑文件: vi IKAnalyzer.cfg.xml

<?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">han.dic</entry>
     <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords"></entry>
    <!--用户可以在这里配置远程扩展字典 -->
    <!-- <entry key="remote_ext_dict">words_location</entry> -->
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
han.dic

老韩
信老韩

<entry key="ext_dict">han.dic</entry>

退出 exit

重新启动 docker restart elk

GET _analyze
{
  "analyzer": "ik_max_word",
  "text":"信老韩得永生"
}

{
  "tokens" : [
    {
      "token" : "信老韩",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "老韩",
      "start_offset" : 1,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "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

在外部更改main.dic,然后将main.dic直接拷贝入docker

docker cp main.dic elk:/opt/elasticsearch/plugins/ik/config

查询中使用分词器

有三种方式可以指定查询分析器:

URL指定

7版本分词,使用postman。

请求地址: http://192.168.2.100:9200/_analyze

请求类型: POST

BODY===>RAW===>JSON(AppliactionJSON)

参数:

{
"analyzer":"standard",
"text":"hello love you"
}

得到结果,换分词器。

{
    "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": 4,
            "type": "CN_WORD",
            "position": 2
        },
        {
            "token": "天安门",
            "start_offset": 4,
            "end_offset": 7,
            "type": "CN_WORD",
            "position": 3
        }
    ]
}

Request Body指定

增加记录

PUT /st/_doc/7
{
  "uname":"高3鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"大门"
}
PUT /st/_doc/6
{
  "uname":"高4鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"我爱北京天安门"
}
PUT /st/_doc/8
{
  "uname":"高5鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"天天向上"
}

查询

get /st/_search  
{
  "query":{
    "match": {
      "addr": {
        "query" : "天安门",
         "analyzer" : "ik_max_word"
      }
    }
  }
}
则只会命中【大门,我爱北京天安门】,而不会命中【天天向上】,原因是
GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "天安门"
}
的分词结果【天安门,天安,门】不会包含【天,安】

mapping指定

delete /st
PUT /st
{
  "mappings": {
      "properties": {
        "uid": {
          "type": "keyword"
        },
        "addr": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_max_word"
        }
    }
  }
}
GET /st/_analyze
{
  "field": "addr",
  "text": "天安门"
}
PUT /st/_doc/7
{
  "uname":"高3鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"大门"
}
PUT /st/_doc/6
{
  "uname":"高4鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"我爱北京天安门"
}
PUT /st/_doc/8
{
  "uname":"高5鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"天天向上"
}
get /st/_search  
{
  "query":{
    "match": {
      "addr": {
        "query" : "天安门"
      }
    }
  }
}
则和
get /st/_search  
{
  "query":{
    "match": {
      "addr": {
        "query" : "天安门",
         "analyzer" : "ik_max_word"
      }
    }
  }
}
完全一个效果

更改默认的分词器

ES默认的分词器为standard, 想要改变这个, 可以设置成自定义的analyzer.
 
想要改变成配置好的ik分词器, 在config/elasticsearch.yml文件中添加如下配置即可:

index.analysis.analyzer.default.type:ik  

前提当然是你已经安装了ik分词。
Since elasticsearch 5.x index level settings can NOT be set on the nodes 
configuration like the elasticsearch.yaml, in system properties or command line 
arguments.In order to upgrade all indices the settings must be updated via the 
/${index}/_settings API. Unless all settings are dynamic all indices must be closed 
in order to apply the upgradeIndices created in the future should use index templates 
to set default values. 

Please ensure all required values are updated on all indices by executing: 

curl -XPUT 'http://localhost:9200/_all/_settings?preserve_existing=true' -d '{
  "index.analysis.analyzer.default.type" : "ik_max_word"
}'

5.x 版本后,不能写在配置文件了,使用restApi来配置

delete st

PUT /st
{
    "settings" : {
        "index" : {
            "analysis.analyzer.default.type": "ik_max_word"
        }
    }
}
GET /st/_analyze
{
  "field": "addr",
  "text": "天安门"
}

PUT /st/_doc/7
{
  "uname":"高3鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"大门"
}
PUT /st/_doc/6
{
  "uname":"高4鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"我爱北京天安门"
}
PUT /st/_doc/8
{
  "uname":"高5鹏",
  "sex":"male",
  "age":12,
  "bith":"1998-09-09",
  "addr":"天天向上"
}


get /st/_search  
{
  "query":{
    "match": {
      "addr": {
        "query" : "天安门"
      }
    }
  }
}

ik分词器热更新

目前该插件支持热更新 IK 分词,通过上文在 IK 配置文件中提到的如下配置 vi IKAnalyzer.cfg.xml

一定注意编码字符集

    <!--用户可以在这里配置远程扩展字典 -->
    <entry key="remote_ext_dict">location</entry>
    <!--用户可以在这里配置远程扩展停止词字典-->
    <entry key="remote_ext_stopwords">location</entry>
docker cp IKAnalyzer.cfg.xml elk:/opt/elasticsearch/plugins/ik/config

其中 location 是指一个 url,比如 http://yoursite.com/getCustomDict,该请求只需满足以下两点即可完成分词热更新。

  1. 该 http 请求需要返回两个头部(header),一个是 Last-Modified,一个是 ETag,这两者都是字符串类型,只要有一个发生变化,该插件就会去抓取新的分词进而更新词库。
  2. 该 http 请求返回的内容格式是一行一个分词,换行符用 \n 即可。

满足上面两点要求就可以实现热更新分词了,不需要重启 ES 实例。

可以将需自动更新的热词放在一个 UTF-8 编码的 .txt 文件里,放在 nginx 或其他简易 http server 下,当 .txt 文件修改时,http server 会在客户端请求该文件时自动返回相应的 Last-Modified 和 ETag。可以另外做一个工具来从业务系统提取相关词汇,并更新这个 .txt 文件。

热更新文件

tomcat中发布文件 han.dic

老韩
爱老虎
爱老韩
人定胜天
胜天

地址为

http://172.16.4.102:8080/han.dic

配置

编辑IKAnalyzer.cfg.xml文件

<?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://172.16.4.102:8080/han.dic</entry>
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

拷贝

在docker内部编辑或者拷贝文件到docker

 docker cp IKAnalyzer.cfg.xml elk:/opt/elasticsearch/plugins/ik/config

测试

因为/st已经设置了默认的分词器

PUT /st
{
    "settings" : {
        "index" : {
            "analysis.analyzer.default.type": "ik_max_word"
        }
    }
}
GET /st/_analyze
{
  "field": "addr",
  "text": "老韩爱潘峰"
}
#分词为【老韩,爱,潘,峰】
GET /st/_analyze
{
  "field": "addr",
  "text": "我人定胜天"
}
#分词为【我,人定胜天,人定,胜天】
GET /st/_analyze
{
  "field": "addr",
  "text": "爱老韩爱老虎"
}
#分词为【爱老韩,老韩,爱老虎,老虎】

再测试

GET /st/_analyze
{
  "field": "addr",
  "text": "明强吃东座"
}
#则分词为【明,强,吃,东,座】

修改han.dic为

老韩
爱老虎
爱老韩
人定胜天
胜天
明强
吃东座

则访问

GET /st/_analyze
{
  "field": "addr",
  "text": "明强吃东座"
}
#则分词为【明强,吃东座】
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容