本文旨在快速入门Elasticsearch的分词,包括分词分析器的创建和介绍对比等,请确保在阅读前已经搭建好完备的集群。文章基于es7.0+,与稍旧版本的主要区别是没有type。
分词过程
在讨论分词前,我们先看一下es整体创建倒排的分词过程:

我们常说的分词器指的其实是“分析器”analyzer,es将以上常用的逻辑封装起来成为analyzer,但是语义上的分词器是指上面的tokenizer。
经过了三层处理后拿到了terms数组建立最终的倒排索引:
character filter:一般不会用到这个filter,是在分词前对原有的文档字段内容做转换,例如去除html的标签提取出正文内容,按正则清除和替换某些内容,你可以指定及自定义0个到多个character filter,他们将共同存在,一个文本流在经过character filter处理后,依然是文本流;
tokenizer:分词器,是Analyzer的核心,要求有且只有一个,它规定了语句的切分规则,利用tokenizer拆分后的文本可被称作token,只使用单一的tokenizer就可以构造最简单的analyzer了;
token filter:在获取token集合后,再利用token filter进行过滤和转化从而得到最终的分词倒排集合(我们称结果集合中的元素为term),例如转换大小写,处理停用词(一些被忽略并当做分隔符的词汇)等。
特别地,对于不可分词的keyword,也有其专属的处理器,被称作normalizer。
一般说来,指定的既有分析器和一些开源的分析器(例如ik分析器)已经能够处理大部分搜索情况,在使用时直接创建相应的field即可。有时根据具体业务形态,可能也要对分词方式做出改变,此时才需要自己定制一套分词规则。
API——创建指定分词的字段、测试分词
本段记录相关的API。
创建mapping和index
在创建mapping/type时,我们可以手动的指定一个字段使用哪些分词方式,同个field如果使用多个分词方式其在使用上视为多个字段。接下来,让我们先创建一个名为test_index的index,因为是单机测试,所以此处只额外的备份数为0,使用5个分片,刷新间隔30s:(index这一层级其实省略也可)
curl -X PUT "localhost:9200/test_index" -H 'Content-Type: application/json' -d'
{
"settings": {
"index":{
"refresh_interval": "30s",
"number_of_shards": "5",
"number_of_replicas": "0"
}
}
}
'
接下来创建一个测试的mapping,其实可以在创建index时直接指定,我们指定了两个数据类型为text的field,此时并未定义它的分词方式,es会为他分配默认的standard解析器(url已经指定了type为_doc,这也是7.0后固定的type),因为已经创建完了index,所以只能在_doc端点执行修改的POST请求:
curl -X POST "localhost:9200/test_index/_mapping/_doc" -H 'Content-Type: application/json' -d'
{
"properties": {
"name": { "type": "text" },
"email": { "type": "keyword" },
"content": { "type": "text" ,"analyzer":"simple"},
"tweeted_at": { "type": "date" }
}
}
'
其中content使用了simple的analyzer,而name字段使用了默认的standard的analyzer。
为index指定自定义analyzer或normalizer
对于已经建立了索引的index,需要先将其关闭才能新增自定义analyzer,当然可以在创建index时直接指定:
curl -X POST "localhost:9200/test_index/_close"
然后可以创建自定义分词器和过滤器的analyzer,这里创建了一个名为my_analyzer的分析器,并为其指定了tokenizer和filters,其中tokenizer为必选,其余均可为0到多个:
curl -X PUT "localhost:9200/test_index/_settings" -H 'Content-Type: application/json' -d'
{
"analysis": {
"analyzer": {
"my_analyzer":{
"tokenizer":"standard",
"filter":["standard", "lowercase", "stop"],
"char_filter": [ "html_strip" ]
}
}
}
}
'
也可以指定一个normalizer:
curl -X PUT "localhost:9200/test_index/_settings" -H 'Content-Type: application/json' -d'
{
"analysis": {
"normalizer": {
"my_normalizer": {
"filter": ["lowercase", "asciifolding"]
}
}
}
}
'
其后记得再打开index:
curl -X POST "localhost:9200/test_index/_open"
新增field并指定分析器
我们可以添加新的字段,也可以在原有字段映射新的分析器,但是已有的字段映射是不能修改的 包括分析器和数据类型,因为倒排索引是已经建好的,我们为content指定了一个新的whitespace的分析器映射,名也为whitespace(使用时为content.whitespace):
curl -X PUT "localhost:9200/test_index/_mapping/_doc" -H 'Content-Type: application/json' -d'
{
"properties": {
"content": {
"type": "text",
"analyzer":"simple",
"fields":{
"whitespace":{
"type":"text",
"analyzer":"whitespace"
}
}
}
}
}
测试分析、分词器
可以测试各种分词、分析器的效果:
curl -X GET "localhost:9200/{index}/_analyze" -H 'Content-Type: application/json' -d'
{
"analyzer" : "standard",
"text" : "Quick Brown Foxes!"
}
'
上面指定了一个analyzer,其中的index是可选的,如果选了index,就可以使用对应index具体字段的分词器。也指定了要分词的content(text),除去text外,其余参数都为选填:
text:待分词的语句,必填;
analyzer:指定使用的分析器,优先级高于tokenizer,如果没有任何除去text的额外参数,则有一个隐性的analyzer:standard;
char_filter:像定义分析器那样指定charfilter;
filter:指定token filter;
field:在指定了index的前提下,可以指定一个字段使用该字段的分词器;
tokenizer:指定分词器;
normalizer:指定规范器,与tokenizer和analyzer相悖。
会返回相应的分词结果,例如:
curl -X GET "localhost:9200/{index}/_analyze" -H 'Content-Type: application/json' -d'
{
"analyzer" : "english",
"text" : "Brown Foxes jump into the hole!"
}
'
返回:
{
"tokens": [
{
"token": "brown",
"start_offset": 0,
"end_offset": 5,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "fox",
"start_offset": 6,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "jump",
"start_offset": 12,
"end_offset": 16,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "hole",
"start_offset": 26,
"end_offset": 30,
"type": "<ALPHANUM>",
"position": 5
}
]
}
也可以看一下上文中自定义的normalizer的效果:
curl -X GET "localhost:9200/test_index/_analyze" -H 'Content-Type: application/json' -d'
{
"normalizer" : "my_normalizer",
"text" : "Brown Foxes jump into the hole!"
}
'
{
"tokens": [
{
"token": "brown foxes jump into the hole!",
"start_offset": 0,
"end_offset": 31,
"type": "word",
"position": 0
}
]
}
会发现除去过滤效果,其整体是一个token。

