# /

# 文本分析(Text analysis)

文本分析Text analysis是将非结构化文本(如电子邮件正文或产品描述)转换为针对搜索进行优化的结构化格式的过程。
在本节中
Configure text analysis //配置文本分析
Built-in analyzer //内置分析器
Tokenizer //分词器
Token filter //令牌过滤器
Character filters //字符过滤器
Normalizers //规范化器
1
2
3
4
5
6
7
8

# 分析器的组成(Composition of analyzer)

Anatomy of an analyzer 分析器的解剖
分析器 — 无论是内置的还是自定义的 — 只是一个包含三个较低级别构建块的包:
字符过滤器filters、
标记器tokenizers和
标记过滤器token filters。
1
2
3
4
5
# Character filters
字符过滤器用于在将字符流传递给标记器之前对其进行预处理。
字符过滤器接收作为字符流的原始文本,并可以通过添加、删除或更改字符来转换流。例如,字符过滤器可以用于转换印度-阿拉伯数字 (٠‎١٢٣٤٥٦٧٨‎٩‎) 转换为阿拉伯语-拉丁语等价物(0123456789),或者从流中剥离HTML元素(如<b>)。
Elasticsearch有许多内置的字符过滤器,可用于构建自定义分析器。

HTML Strip Character Filter:去除html元素,如< b >,并解码HTML实体,如& amp。
Mapping Character Filter:用指定的替换项替换任何出现的指定字符串。
Pattern Replace Character Filter:用指定的替换替换任何匹配正则表达式的字符。
1
2
3
4
5
6
7
# HTML Strip Character Filter
GET /_analyze
{
  "tokenizer": "keyword",
  "char_filter": [
    "html_strip"
  ],
  "text": "<p>I&apos;m so <b>happy</b>!</p>"
}
1
2
3
4
5
6
7
8
# Mapping Character Filter
GET /_analyze
{
  "tokenizer": "keyword",
  "char_filter": [
    {
      "type": "mapping",
      "mappings": [
        "٠ => 0",
        "١ => 1",
        "٢ => 2",
        "٣ => 3",
        "٤ => 4",
        "٥ => 5",
        "٦ => 6",
        "٧ => 7",
        "٨ => 8",
        "٩ => 9"
      ]
    }
  ],
  "text": "My license plate is ٢٥٠١٥"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Pattern Replace Character Filter
PUT my-index-00001
{
  "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_"
        }
      }
    }
  }
}

POST my-index-00001/_analyze
{
  "analyzer": "my_analyzer",
  "text": "My credit card is 123-456-789"
}
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
# Tokenizer
断词器Tokenizer接收字符流,将其分解为单独的标记(通常是单独的单词),并输出标记流。例如,空白标记器在看到任何空白时都会将文本分解为标记。它会将文本“Quick brown fox!”转换为术语[Quick,brown,fox!]。

令牌化器还负责记录以下内容:
每个术语的顺序或位置(用于短语和单词接近度查询)
术语所代表的原始单词的开始和结束字符偏移量(用于突出显示搜索片段)。
令牌类型,生成的每个术语的分类,如、或。更简单的分析器只生成单词标记类型。

Elasticsearch有许多内置的标记器,可用于构建自定义分析器。
Standard Tokenizer:根据Unicode文本分割算法的定义,将文本划分为单词边界上的术语。它删除了大多数标点符号。它是大多数语言的最佳选择。
Letter Tokenizer:每当遇到非字母字符时,字母标记器就会将文本划分为多个术语。
Lowercase Tokenizer:小写标记器与字母标记器一样,每当遇到不是字母的字符时,都会将文本划分为多个术语,但它也会降低所有术语的大小写。
Whitespace Tokenizer:每当遇到任何空白字符时,空白标记器都会将文本划分为多个术语。
UAX URL Email Tokenizer:与标准令牌化器类似,只是它将url和电子邮件地址识别为单个令牌。
Classic Tokenizer:经典标记器是一种基于语法的英语标记器。
Thai Tokenizer:泰语标记器将泰语文本分割成单词。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Standard Tokenizer
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 ]
1
2
3
4
5
6
7
8
# Token filter
令牌过滤器Token filter接受来自令牌化器的令牌流,并可以修改令牌(例如lowercasing)、删除令牌(例如stopwords)或添加令牌(例如synonyms)。
Elasticsearch有许多内置的令牌过滤器,可以用来构建自定义分析器。
1
2
# Lowercase Token Filter
PUT my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type": "custom", 
          "tokenizer": "standard",
          "char_filter": [
            "html_strip"
          ],
          "filter": [
            "lowercase",//Lowercase Token Filter
            "asciifolding"//ASCII-Folding Token Filter
          ]
        }
      }
    }
  }
}

POST my-index-000001/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": "Is this <b>déjà vu</b>?"
}
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
# Normalization token filters
规范化令牌筛选器
有几个可用的令牌过滤器,试图规范特定语言的特殊字符。

arabic_normalization 阿拉伯语
german_normalization 德国
hindi_normalization 印地语
indic_normalization 印度
sorani_normalization 库尔德语(索拉尼语)
persian_normalization 波斯语
scandinavian_normalization, scandinavian_folding 斯堪的纳维亚
serbian_normalization 塞尔维亚语
1
2
3
4
5
6
7
8
9
10
11
# 自定义分词器(Create a custom analyzer)
Elasticsearch有许多内置的令牌过滤器token filters,可以用来构建自定义分析器。
1
# my_custom_analyzer
PUT my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type": "custom", 
          "tokenizer": "standard",
          "char_filter": [
            "html_strip"
          ],
          "filter": [
            "lowercase",
            "asciifolding"
          ]
        }
      }
    }
  }
}

POST my-index-000001/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": "Is this <b>déjà vu</b>?"
}


上面的例子产生了以下术语:
[ is, this, deja, vu ]
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

# 内置分析器(Built-in analyzer)

Built-in analyzer
Elasticsearch提供了广泛的内置分析器,无需进一步配置即可用于任何索引:

Standard Analyzer 标准分析器
Simple Analyzer 简单分析器
Whitespace Analyzer 空白分析器
Stop Analyzer 停止分析器
Keyword Analyzer 关键字分析器
Pattern Analyzer 模式分析器
Language Analyzers 语言分析器
Fingerprint Analyzer 指纹分析器
1
2
3
4
5
6
7
8
9
10
11

# 规范化器(Normalizers)

规范化器Normalizers 与分析器analyzers 类似,不同之处在于它们只能发出单个令牌。因此,它们没有标记化器,只接受可用的字符过滤器和标记过滤器的子集。只允许使用按字符工作的筛选器。例如,允许使用小写过滤器,但不允许使用词干过滤器,因为词干过滤器需要将关键字作为一个整体来查看。可以在规范化器中使用的过滤器的当前列表如下:
arabic_normalization, 
asciifolding, 
bengali_normalization, 
cjk_width, 
decimal_digit, 
elision, 
german_normalization, 
hindi_normalization, 
indic_normalization, 
lowercase, 
persian_normalization, 
scandinavian_folding, 
serbian_normalization, 
sorani_normalization, 
uppercase.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# IK中文分词器(IK Chinese Analyzer)

# 下载安装
#下载
https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.10.0/elasticsearch-analysis-ik-7.10.0.zip

#安装
解压到/plugins/ik/
重启elasticsearch
1
2
3
4
5
6
# IK分词器介绍

主词库main.dic、停用词库stopword.dic、特殊词库、自定义词库。

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

ik_max_word 和 ik_smart 什么区别?
ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,,,共和国,共和,,国国,国歌”,会穷尽各种可能的组合,适合 Term Query;
ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询。
1
2
3
4
5
GET easyes_document/_analyze
{
  "analyzer": "ik_smart",
  "text": ["测试内容1"]
}
1
2
3
4
5
{
  "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
    },
    {
      "token" : "1",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "ARABIC",
      "position" : 2
    }
  ]
}
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
# 热更新分词库
# 基于远程文件词库(官方)

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

 	<!--用户可以在这里配置远程扩展字典 -->
	<entry key="remote_ext_dict">location</entry>
 	<!--用户可以在这里配置远程扩展停止词字典-->
	<entry key="remote_ext_stopwords">location</entry>
1
2
3
4

其中 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 文件。

# 基于Mysql词库

1、在ik初始化词库源码处,会加载文件词库,新增加载Mysql词库。(这不是热更新)
2、热更新还要参考远程文件词库热更新。