[这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战]
一、什么是文本分析(Text analysis)
ElasticSearch中的文本分析是指将非结构化文本
转换为针对搜索进行优化的结构化数据
的过程。
Elasticsearch 在索引或搜索text
类型字段时,执行文本分析。如果原生JSON数据不包含text
类型的字段,则不需要考虑文本搜索的配置与优化。如果需要搜索text
类型:
- 构建搜索引擎
- 挖掘非结构化数据
- 优化特定语言的搜索
- 优化特定专业的搜索
相关概念
- token:表示
分词过程产生的对象
,包括词语、词语在文本中的开始、结束位置以及词语类型。在Lucene 2.9后,官方不再建议使用Token,而推荐使用 Attuibutes。 - term:是一个最小搜索单元,包含文本中的词语以及对应属性名
- tokenization(分词):用于语句划分;例如:
这是一个es的测试语句->【'这是', '一个', 'es','的','测试','语句'】
- Normalization(
tokenization normalization
):将意思相同但是形式不同的词转换成同一个词(token)
分词(tokenization)
文本分析通过分词
使全文搜索成为可能:将文本分解为更小的块,称为词语。在大多数情况下,这些词语是单独的词。如果不进行分词,假如文本是测试搜索语句
,那么测试语句
就无法匹配这个文本。
文本正规化(Normalization)
分词支持对单个term的匹配,但是每个token依旧按字面值进行匹配的,这意味着:
- 搜索
Quick
不会匹配quick
- 虽然
fox
和foxes
共享相同的词根,但搜索foxes
不会匹配fox
- 搜索
jumps
将不匹配leaps
;尽管不共享一个词根,但它们是同义词并具有相似的含义
为了解决这些问题,文本分析可以将这些token规范化为标准格式。
自定义文本分析
文本分析由分析器执行,分析器是一组控制整个过程的规则。Elasticsearch 包含一个默认分析器standard analyzer
,适用于大多数开箱即用的用例。
自定义分析器可让您控制分析过程的每个步骤,包括:
- 分词前对文本的更改
- 文本如何转换为
Token
- 在索引或搜索前,对
Token
所做的正规化处理
二、相关概念
分析器
无论内置的还是自定义的分析器(analyzer)都由三部分组成,即
- 字符过滤器(character filters):接收字符流,可以添加、移除或改变字符来转换流
- 例如:去除字符里的HTML元素
- 分词器(tokenizers):接收处理后的字符流,分解为单独的 token(通常是单个单词)。
- 例如:
whitespace
分词器会在遇到空格时对文本进行拆分; - 分词器还负责记录每个term的
顺序或位置
以及该term所代表的原始单词的开始和结束字符偏移量
; - 一个分析器有且只有一个分词器。
- 例如:
- token过滤器(token filters):token过滤器在遇到token流时,可以添加、删除或改变Token
- 例如,lowercase令牌过滤器将所有令牌转换为小写;stop令牌过滤器从令牌流中删除常用词(停用词)the;synonym令牌过滤器将同义词引入令牌流。
- Token过滤器不允许更改每个Token的位置或字符偏移量
- 分析器可能有零个或多个 token过滤器,按序生效
内置分析器将这些构建块预先打包成适用于不同语言和文本类型的分析器。Elasticsearch 还公开了各个构建块,以便将它们组合起来定义新的自定义分析器。
索引与搜索
在使用时会触发文本分析
两次:
- 索引时间:对文档编制索引时,会分析任何
text类型
的字段值 - 搜索时间(查询时间):在字段上运行全文搜索时,会
先分析查询字符串
(用户正在搜索的文本)
每次使用的分析器或分析规则集分别称为索引分析器或搜索分析器。在大多数情况下,应该在索引和搜索时使用相同的分析器。这样可确保字段的值和查询字符串会转换为相同形式的Token。但是,有时候也有使用指定分析器的需求,例如:
# 指定分析器
GET my-index-000001/_search
{
"query": {
"match": {
"message": {
"query": "Quick foxes",
"analyzer": "stop"
}
}
}
}
复制代码
词干
词干提取是将单词简化为其词根形式的过程,确保了搜索时单词能匹配所有的变体。例如,walking
和walked
可以词干到相同的词根: walk
。一旦词干化,出现的任何一个词都会在搜索中与另一个词匹配。
在 Elasticsearch 中,词干提取由词干分词过滤器
处理。这些Token过滤器可以根据它们的词干方式进行分类:
- 算法词干分析器,词干分析基于一组规则
- 词典词干分析器,通过在词典中查找词干
词干提取是英文语料预处理的一个步骤,但是中文分词并不需要,暂时略过。
Token Graphs
当分词器将字符流转换为token流时,会记录:
- token的顺序
- token的开始和结束字符偏移量
使用这些数据,可以生成一个有向无环图。令牌图中,每个位置代表一个节点。每个标记代表一条边或弧,指向下一个位置。
某些Token过滤器
可以向现有token流添加新token,例如同义词。这些同义词通常跨越与现有标记相同的位置。例如:
一些Token过滤器
可以添加跨越多个位置的令牌,可以包括多个词的同义词标记:
- synonym_graph
- word_delimiter_graph
以下令牌过滤器可以添加跨越多个位置的token,但只能添加positionLength=1
的token:
- synonym
- word_delimiter
三、文本分析器
默认情况下,Elasticsearch 使用standard分析器
进行所有文本分析,提供基于语法
的分词器(基于 Unicode 文本分割算法)并且适用于大多数语言。如果标准分析器不符合您的需求,可以尝试 Elasticsearch 的其他内置分析器
。
内置分析器
standard Analyzer
standard
分析器将文本分为在字边界条件,如通过Unicode文本分割算法定义。它删除了大多数标点符号、小写术语,并支持删除停用词。
例如:
POST /_analyze?pretty
{
"analyzer": "standard",
"text": "测试搜索语句"
}
{
"tokens" : [
{
"token" : "测",
"start_offset" : 0,
"end_offset" : 1,
"type" : "<IDEOGRAPHIC>",
"position" : 0
},
{
"token" : "试",
"start_offset" : 1,
"end_offset" : 2,
"type" : "<IDEOGRAPHIC>",
"position" : 1
},
{
"token" : "搜",
"start_offset" : 2,
"end_offset" : 3,
"type" : "<IDEOGRAPHIC>",
"position" : 2
},
{
"token" : "索",
"start_offset" : 3,
"end_offset" : 4,
"type" : "<IDEOGRAPHIC>",
"position" : 3
},
{
"token" : "语",
"start_offset" : 4,
"end_offset" : 5,
"type" : "<IDEOGRAPHIC>",
"position" : 4
},
{
"token" : "句",
"start_offset" : 5,
"end_offset" : 6,
"type" : "<IDEOGRAPHIC>",
"position" : 5
}
]
}
复制代码
停用词是指在信息检索中,为节省存储空间和提高搜索效率,在处理自然语言数据(或文本)之前或之后会自动过滤掉某些字或词,这些字或词即被称为Stop Words(停用词)。
Simple Analyzer
simple
分析仪在任何非字母字符Token,如数字,空格,连字符和撇号,丢弃非字母字符,并将大写为小写
。
例如:
# 测试简单分析器
POST /_analyze?pretty
{
"analyzer": "simple",
"text": "测试搜索语句AAA"
}
{
"tokens" : [
{
"token" : "测试搜索语句aaa",
"start_offset" : 0,
"end_offset" : 9,
"type" : "word",
"position" : 0
}
]
}
复制代码
Whitespace Analyzer
Whitespace
分析器在遇到空白字符时,进行分词。
例如:
# 测试空白分析器
POST /_analyze?pretty
{
"analyzer": "whitespace",
"text": "This is a text"
}
{
"tokens" : [
{
"token" : "This",
"start_offset" : 0,
"end_offset" : 4,
"type" : "word",
"position" : 0
},
{
"token" : "is",
"start_offset" : 5,
"end_offset" : 7,
"type" : "word",
"position" : 1
},
{
"token" : "a",
"start_offset" : 8,
"end_offset" : 9,
"type" : "word",
"position" : 2
},
{
"token" : "text",
"start_offset" : 10,
"end_offset" : 14,
"type" : "word",
"position" : 3
}
]
}
复制代码
Stop Analyzer
Stop
分析器类似于Whitespace
分析器,还可以去除停止词。
例如:
# 测试stop分析器
POST /_analyze?pretty
{
"analyzer": "stop",
"text": "This is a TEXT"
}
{
"tokens" : [
{
"token" : "text",
"start_offset" : 10,
"end_offset" : 14,
"type" : "word",
"position" : 3
}
]
}
复制代码
Keyword Analyzer
keyword
分析器是一个“空操作”分析仪,它返回整个输入串作为一个单一Token。该keyword
分析仪是不可配置。
例如:
# 测试Keyword分析器
POST /_analyze?pretty
{
"analyzer": "keyword",
"text": "This is a TEXT"
}
{
"tokens" : [
{
"token" : "This is a TEXT",
"start_offset" : 0,
"end_offset" : 14,
"type" : "word",
"position" : 0
}
]
}
复制代码
Pattern analyzer
pattern
分析器使用正则表达式分割文本,匹配的是标记分隔符 而不是Token本身,默认为\W+
(或所有非单词字符)。
写得不好的正则表达式可能会运行得很慢,甚至会抛出 StackOverflowError 并导致它正在运行的节点突然退出。
pattern
分析器接受以下参数:
pattern |
Java的正则表达式,则默认为\W+ 。 |
---|---|
flags |
Java 正则表达式标志 |
lowercase |
术语是否应该小写。默认为true |
stopwords |
预定义的停用词列表,如_english_ 或包含停用词列表的数组。默认为_none_ |
stopwords_path |
包含停用词的文件的路径 |
POST _analyze
{
"analyzer": "pattern",
"text": "This is a TEXT."
}
{
"tokens" : [
{
"token" : "this",
"start_offset" : 0,
"end_offset" : 4,
"type" : "word",
"position" : 0
},
{
"token" : "is",
"start_offset" : 5,
"end_offset" : 7,
"type" : "word",
"position" : 1
},
{
"token" : "a",
"start_offset" : 8,
"end_offset" : 9,
"type" : "word",
"position" : 2
},
{
"token" : "text",
"start_offset" : 10,
"end_offset" : 14,
"type" : "word",
"position" : 3
}
]
}
复制代码
其它
除此之外,还有:
Language analyzers
即语言分析器,可以根据特定语言进行分词;Fingerprint Analyzer
即指纹分析器,实现了指纹识别算法,输入文本被小写、规范化以移除扩展字符、排序、去重并连接成单个标记。如果配置了停用词列表,停用词也将被删除。
字符过滤器
字符过滤器用于在将字符流传递给Token前对其进行预处理。
Elasticsearch 有许多内置的字符过滤器,可用于构建 自定义分析器,包括
- HTML Strip 字符过滤器:用于过滤HTML元素,比如
<b>
以及&
; - Mapping 字符过滤器:可以设置键值对,当遇到与键一致的字符串时,可以替换该值
- Pattern Replace 字符过滤器:使用正则表达式进行匹配,匹配后替换
分词器
分词器(tokenizers)会接收处理后的字符流,分解为单独的 token(通常是单个单词),还负责记录以下信息:
- term的顺序或位置(用于短语和单词邻近查询)
- 原始单词的开始和结束字符的偏移量(用于突出显示搜索片段)
- token分类,例如
<ALPHANUM>
、<HANGUL>
、 或<NUM>
面向词的分词器:
- Standard Tokenizer:该
standard
分词器将文本分为单词边界条件,由Unicode文本分割算法定义 - Letter Tokenizer:按字母进行分词
- Lowercase Tokenizer:遇到非字母的字符时将文本分成词,但也会将所有词小写
- Whitespace Tokenizer:按照空白字符进行分词
- UAX URL Email Tokenizer:类似于
standard
分词器,不同之处在于它将 URL 和电子邮件地址识别为单个标记。 - Classic Tokenizer:基于语法的英文分词器
- Thai Tokenizer:用于将泰语文本分割成单词
部分单词分词器(Partial Word Tokenizers)
这些分词器用于将文本或单词分解为小片段,用于部分单词匹配
- N-Gram Tokenizer:
ngram
分词器可以分解文本成单词,能根据文本的步长逐步对写入的文本内容进行约束切割。例如:quick
→[qu, ui, ic, ck]
- Edge N-Gram Tokenizer:可以分解文本成单词,只是解析逻辑不同,便于
输入即搜索
的业务场景。例如:quick
→[q, qu, qui, quic,quick]
结构化文本分词器(Structured Text Tokenizersedit)
通常用于结构化文本,如标识符、电子邮件地址、邮政编码和路径
- Keyword Tokenizer:“noop”分词器,将输入作为一个单独的token
- Pattern Tokenizer:使用正则表达式,与单词分隔符匹配时将文本拆分为多个token,或将匹配的文本捕获为token
- Simple Pattern Tokenizer:使用正则表达式,将匹配文本作为term
- Simple Pattern Split Tokenizer:使用与simple_pattern分词器相同的受限正则表达式子集,但是匹配的部分用来拆分输入,而不是将匹配部分作为结果返回。
- Char Group Tokenizer:通过字符集进行分词
- Path Tokenizer:用于分割路径