Elasticsearch分词器

因为Elasticsearch中默认的标准分词器分词器对中文分词不是很友好,会将中文词语拆分成一个一个中文的汉子,因此引入中文分词器。

在ES中一个Analyzer 由下面三种组件组合而成:

  • character filter :字符过滤器,对文本进行字符过滤处理,如处理文本中的html标签字符。处理完后再交给tokenizer进行分词。一个analyzer中可包含0个或多个字符过滤器,多个按配置顺序依次进行处理。
  • tokenizer:分词器,对文本进行分词。一个analyzer必需且只可包含一个tokenizer。
  • token filter:词项过滤器,对tokenizer分出的词进行过滤处理。如转小写、停用词处理、同义词处理。一个analyzer可包含0个或多个词项过滤器,按配置顺序进行过滤。

    测试默认的分词

    1
    2
    3
    4
    5
    GET /_analyze 
    {
    "analyzer":"standard",
    "text":"中国china"
    }
    #或 http://127.0.0.1:9200/_analyze
    1
    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
    参数:
    {
    "analyzer":"standard",
    "text":"中国china"
    }
    结果:
    {
    "tokens": [
    {
    "token": "中",
    "start_offset": 0,
    "end_offset": 1,
    "type": "<IDEOGRAPHIC>",
    "position": 0
    },
    {
    "token": "国",
    "start_offset": 1,
    "end_offset": 2,
    "type": "<IDEOGRAPHIC>",
    "position": 1
    },
    {
    "token": "china",
    "start_offset": 2,
    "end_offset": 7,
    "type": "<ALPHANUM>",
    "position": 2
    }
    ]
    }

    position:第几个词
    offset:词的偏移位置

    character filter

    • HTML Strip Character Filter **
      1
      2
      3
      4
      5
      6
      HTML Strip Character Filter
        html_strip :过滤html标签,解码HTML entities like &.
      Mapping Character Filter
        mapping :用指定的字符串替换文本中的某字符串。
      Pattern Replace Character Filter
        pattern_replace :进行正则表达式替换。
      测试:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      GET _analyze
      {
      "tokenizer": "keyword",
      "char_filter": [ "html_strip" ],
      "text": "<p> I <br/> Love <br/> You!</p>"
      }
      结果:
      {
      "tokens" : [
      {
      "token" : """

      I
      Love
      You!

      """,
      "start_offset" : 0,
      "end_offset" : 31,
      "type" : "word",
      "position" : 0
      }
      ]
      }
      在索引中配置:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      PUT my_index
      {
      "settings": {
      "analysis": {
      "analyzer": {
      "my_analyzer": {
      "tokenizer": "keyword",
      "char_filter": ["my_char_filter"]
      }
      },
      "char_filter": {
      "my_char_filter": {
      "type": "html_strip",
      "escaped_tags": ["b"]
      }
      }
      }
      }
      }

      #escaped_tags 用来指定例外的标签。
    • Mapping character filter **
      创建一个:
      1
      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
      PUT demo_index
      {
      "settings": {
      "analysis": {
      "analyzer": {
      "my_analyzer": {
      "tokenizer": "keyword",
      "char_filter": [
      "my_char_filter"
      ]
      }
      },
      "char_filter": {
      "my_char_filter": {
      "type": "mapping",
      "mappings": [
      "零 => 0",
      "一 => 1",
      "二 => 2",
      "三 => 3"
      ]
      }
      }
      }
      }
      }
      测试:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      POST demo_index/_analyze
      {
      "analyzer": "my_analyzer",
      "text": "一二三四五六零"
      }
      结果:
      {
      "tokens" : [
      {
      "token" : "123四五六0",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "word",
      "position" : 0
      }
      ]
      }
    • Pattern Replace Character Filter **
      创建:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      PUT demo_index1
      {
      "settings": {
      "analysis": {
      "analyzer": {
      "my_analyzer": {
      "tokenizer": "standard",
      "char_filter": [
      "my_char_filter"
      ]
      }
      },
      "char_filter": {
      "my_char_filter": {
      "type": "pattern_replace",
      "pattern": "(\\d+)-(?=\\d)",
      "replacement": "$1_"
      }
      }
      }
      }
      }
      测试:
      1
      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
      POST demo_index1/_analyze
      {
      "analyzer": "my_analyzer",
      "text": "My credit card is 123-456-789"
      }
      结果:
      {
      "tokens" : [
      {
      "token" : "My",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "<ALPHANUM>",
      "position" : 0
      },
      {
      "token" : "credit",
      "start_offset" : 3,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 1
      },
      {
      "token" : "card",
      "start_offset" : 10,
      "end_offset" : 14,
      "type" : "<ALPHANUM>",
      "position" : 2
      },
      {
      "token" : "is",
      "start_offset" : 15,
      "end_offset" : 17,
      "type" : "<ALPHANUM>",
      "position" : 3
      },
      {
      "token" : "123_456_789",
      "start_offset" : 18,
      "end_offset" : 29,
      "type" : "<NUM>",
      "position" : 4
      }
      ]
      }

      Tokenizer

      Standard Tokenizer
      1
      2
      3
      4
      5
      6
      7
      8
      Letter Tokenizer
      Lowercase Tokenizer
      Whitespace Tokenizer
      UAX URL Email Tokenizer
      Classic Tokenizer
      Thai Tokenizer
      NGram Tokenizer
      Edge NGram Tokenizer
      Keyword Tokenizer
      1
      2
      3
      4
      5
      6
      7
      8
      9
      Pattern Tokenizer
      Simple Pattern Tokenizer
      Simple Pattern Split Tokenizer
      Path Hierarchy Tokenizer
      Keyword Tokenizer
      Pattern Tokenizer
      Simple Pattern Tokenizer
      Simple Pattern Split Tokenizer
      Path Hierarchy Tokenizer
      测试:
      1
      2
      3
      4
      5
      POST _analyze
      {
      "tokenizer": "ik_smart",
      "text": "中国china"
      }

Token Filter

ES中内建了很多Token filter

1
2
3
4
5
Lowercase Token Filter :lowercase 转小写
Stop Token Filter :stop 停用词过滤器
Synonym Token Filter: synonym 同义词过滤器

说明:中文分词器Ikanalyzer中自带有停用词过滤功能。

** Synonym Token Filter 同义词过滤器 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
PUT /demo_index1
{
"settings": {
"index": {
"analysis": {
"analyzer": {
"my_ik_synonym": {
"tokenizer": "ik_smart",
"filter": [
"synonym"
]
}
},
"filter": {
"synonym": {
"type": "synonym",
"synonyms_path": "analysis/synonym.txt"
}
}
}
}
}
}

#synonyms_path 指定同义词文件(相对config的位置)

ES同义词格式支持 solr、 WordNet 两种格式。

1
2
3
张三,李四
电饭煲,电饭锅 => 电饭煲
电脑 => 计算机,computer

Analyzer

集成的中文分词器Ikanalyzer中提供的Analyzer:ik_smart 、 ik_max_word

内建的和集成的analyzer可以直接使用。如果它们不能满足我们的需要,则我们可自己组合字符过滤器、分词器、词项过滤器来定义自定义的analyzer

自定义 Analyzer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PUT demo_index3{
"settings": {
"analysis": {
"analyzer": {
"my_ik_analyzer": {
"type": "custom",
"tokenizer": "ik_smart",
"char_filter": [
"html_strip"
],
"filter": [
"synonym"
]
}
},
"filter": {
"synonym": {
"type": "synonym",
"synonyms_path": "analysis/synonym.txt"
}
}
}
}
}

为字段指定分词器

1
2
3
4
5
6
7
8
9
PUT demo_index4/_mapping/user
{
"properties": {
"name": {
"type": "text",
"analyzer": "my_ik_analyzer"
}
}
}

如果该字段的查询需要使用不同的analyzer

1
2
3
4
5
6
7
8
9
10
PUT demo_index4/_mapping/user
{
"properties": {
"name": {
"type": "text",
"analyzer": "my_ik_analyzer",
"search_analyzer": "other_analyzer"
}
}
}

索引定义default分词器

1
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
PUT /demo_index4
{
"settings": {
"analysis": {
"analyzer": {
"default": {
"tokenizer": "ik_smart",
"filter": [
"synonym"
]
}
},
"filter": {
"synonym": {
"type": "synonym",
"synonyms_path": "analysis/synonym.txt"
}
}
}
},
"mappings": {
"user": {
"properties": {
"name": {
"type": "text"
}
}
}
}
}

** Analyzer的使用顺序 **

可以为每个查询、每个字段、每个索引指定分词器。

在索引阶段ES将按如下顺序来选用分词:

1
2
3
4
5
首先选用字段mapping定义中指定的analyzer
字段定义中没有指定analyzer,则选用 index settings中定义的名字为default 的analyzer。
如index setting中没有定义default分词器,则使用 standard analyzer.
``
查询阶段ES将按如下顺序来选用分词:

The analyzer defined in a full-text query.
The search_analyzer defined in the field mapping.
The analyzer defined in the field mapping.
An analyzer named default_search in the index settings.
An analyzer named default in the index settings.
The standard analyzer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17












# 中文分词器IK

下载es的IK插件,解压重命名为ik,放到/usr/local/elasticsearch/plugins目录中,重启elasticsearch即可。

测试上面的例子得到:

{
“tokens”: [
{
“token”: “中国”,
“start_offset”: 0,
“end_offset”: 2,
“type”: “CN_WORD”,
“position”: 0
},
{
“token”: “china”,
“start_offset”: 2,
“end_offset”: 7,
“type”: “ENGLISH”,
“position”: 1
}
]
}

1
2

ik_max_word 和 ik_smart 区别:

IK 5.0 以后,移除名为 ik 的analyzer和tokenizer 只能使用 ik_smart 和 ik_max_word
ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。

1
2
3

# 扩展IK分词
扩展配置文件:ik\config\IKAnalyzer.cfg.xml
IK Analyzer 扩展配置
1
修改配置:
IK Analyzer 扩展配置 ./custom/new_word.dic
1
就可以在对应位置写一些分词。vi ./custom/new_word.dic 如:
# cat ./custom/new_word.dic 我爱你 中国china
1
2
3
4

例子:

请求体不变
{ "analyzer":"ik_smart", "text":"中国china" }
1
结果:
{ "tokens": [ { "token": "中国china", "start_offset": 0, "end_offset": 7, "type": "CN_WORD", "position": 0 } ] }
1
2
3
4
5
6
7
新增扩展词后让历史数据生效:

POST /索引,,,/_update_by_query?conflicts=proceed

# 项目中使用IK扩展分词器
1、把包引入pom.xml
2、在项目的src根目录下建立IKAnalyzer.cfg.xml文件,文件中配置扩展词库和停用词库的路径
IK Analyzer 扩展配置
1
2
3
4
5
6
在配置文件中,用户可一次配置多个词典文件。文件名使用;号分隔,文件路径为相对 java 包的起始根路径。

配置文件和字典文件都放在java包的根目录,上述配置文件的字典路径应该去掉开头的”/”,否则扩展无法生效。

3、通过代码扩展
IK 分词器支持使用 API 编程模型扩充您的词典和停止词典。API 如下:
类 org.wltea.analyzer.dic.Dictionary 说明: IK 分词器的词典对象。它负责中文词汇的加载,内存管理和匹配检索。 public static Dictionary initial(Configuration cfg) //初始化字典实例。字典采用单例模式,只执行一次。参数 1:Configuration cfg,词典路径配置 返回值:Dictionary IK 词典单例

 public static Dictionary getSingleton() //获取初始化完毕的字典单例 返回值:Dictionary IK 词典单例
 public void addWords(Collection words) //加载用户扩展的词汇列表到 IK 的主词典中,增加分词器的可识别词语。 参数 1:Collection words , 扩展的词汇列表
 public void disableWords(Collection words) //屏蔽词典中的词元 参数 1:Collection words, 待删除的词列表

1
2
3
4
5
6
7
8
9
示例:
```java
Configuration cfg=DefaultConfig.getInstance();
System.out.println(cfg.getMainDictionary());
Dictionary.initial(cfg);
Dictionary dic=Dictionary.getSingleton();
HashSet<String> set=new HashSet<>();
set.add("中国china");
dic.addWords(set);

** 通过java看分词 **

1
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
//构造一个IKAnalyzer对象,构造器参数为true说明使用智能分词,默认为最细粒度切分
IKAnalyzer analyzer=new IKAnalyzer(true);
TokenStream ts=null;
try{
ts=analyzer.tokenStream("title", "中国china");
CharTermAttribute cta=ts.addAttribute(CharTermAttribute.class);
//IKAnalyzer.tokenStream(fieldName,text)返回TokenStream对象,Token指分词后的词元;
//TokenStream对象中包含了text的分词结果,fieldName指文档的域名,一片文档包含多个域,如title,abstract,content等等,这里任意指定即可。
//TokenStream.incrementToken()的功能相当于迭代器,用来遍历每个词元。
//CharTermAttribute是具体的词元对象,每次调用incrementToken()方法后,该对象所持有的词元会更新。
ts.reset();
while (ts.incrementToken()) {
System.out.println("term: " + cta.toString());
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(ts!=null)
try{
ts.close();
}catch(Exception e){
e.printStackTrace();
}
if(analyzer!=null){
try{
analyzer.close();
}catch(Exception e){
e.printStackTrace();
}
}
}