文本分析使Elasticsearch能够执行全文搜索,其中搜索返回所有相关结果,而不仅仅是精确匹配。
如果搜索Quick fox jumps,则可能需要包含A quick brown fox jumps over the lazy dog的文档,也可能需要包含相关单词(如fast fox或foxes leap)的文档。
标记化
分析通过标记化使全文搜索成为可能:将文本分解为更小的块,称为标记。在大多数情况下,这些标记是单个单词。
如果我们将短语“the quick brown fox jumps”作为单个字符串进行索引,并且用户搜索“quick fox”,则不会将其视为匹配项。但是,如果对短语进行标记并分别为每个单词编制索引,则可以单独查找查询字符串中的术语。这意味着可以通过搜索quick fox、fox brown或其他变体来匹配它们。
规范化
标记化允许在单个术语上进行匹配,但每个标记仍然是按字面匹配的。这意味着:
- 搜索Quick不会匹配quick,即使我们可能希望其中一个术语与另一个匹配
- 虽然fox和foxes共享同一个词根,但对foxes的搜索不会匹配fox,反之亦然。
- 对
jumps
的搜索与 leaps 不匹配。虽然它们没有共同的词根,但它们是同义词,具有相似的含义。
为了解决这些问题,文本分析(text analysis)可以将这些标记规范化为标准格式。这允许我们匹配与搜索词不完全相同但足够相似的标记,以保持相关性。例如:
- Quick可以小写:quick。
-
foxes
可以是词干,也可以简化为它的词根:fox。 -
jump和leap是同义词,可以作为一个单词索引:jump。
为了确保搜索词与这些词匹配,可以对查询字符串应用相同的标记化和规范化规则。例如,对Foxes leap的搜索可以规范化为对fox jump
的搜索。
自定义文本分析
文本分析由分析器执行,分析器是一组控制整个过程的规则。
Elasticsearch包括一个默认的分析器,称为标准分析器(standard analyzer),它可以很好地用于大多数开箱即用的用例。
如果我们想定制我们的搜索体验,我们可以选择不同的内置分析器,甚至可以配置自定义分析器。自定义分析器使我们可以控制分析过程的每个步骤,包括:
- 标记化之前对文本的更改
- 如何将文本转换为标记
- 索引或搜索之前对标记所做的规范化更改
文本分析的概念
内置分析器将这些构建块预打包为适合不同语言和文本类型的分析器。Elasticsearch还公开了各个构建块,以便可以将它们组合起来定义新的自定义分析器。
Character filters
字符过滤器将原始文本作为字符流接收,并可以通过添加、删除或更改字符来转换流。例如,字符过滤器可用于转换印度教阿拉伯数字(٠١٢٣٤٥٦٧٨٩) 将其转换为阿拉伯语和拉丁语的等价物(0123456789),或从流中删除HTML元素,如<b>。
分析器可以有零个或多个字符过滤器,这些过滤器按顺序应用。
分词器
分词器接收字符流,将其分解为单个标记(通常为单个单词),然后输出一个标记流。例如,whitespace分词器在看到任何空白时都会将文本分解为标记。它将转换文本“Quick brown fox!”进入术语[Quick, brown, fox!]。
分词器还负责记录每个术语的顺序或位置,以及该术语所代表的原始单词的起始和结束字符偏移量。
分析器(analyzer)必须只有一个分词器(tokenizer)。
令牌过滤器(Token filters)
令牌过滤器接收令牌流,并可以添加、移除或更改令牌。例如,小写令牌筛选器将所有令牌转换为小写,停止令牌筛选器从令牌流中删除常见单词(停止词),如,同义词令牌筛选器将同义词引入令牌流。
不允许令牌筛选器更改每个令牌的位置或字符偏移量。
分析器可以有零个或多个令牌过滤器,这些过滤器按顺序应用。
Configure text analysis
默认情况下,Elasticsearch使用标准分析器进行所有文本分析。标准分析器为大多数自然语言和用例提供现成的支持。如果选择按原样使用标准分析仪,则无需进一步配置。
如果标准分析器不符合我们的需要,请检查并测试Elasticsearch的其他内置分析器。内置分析器不需要配置,但有些支持选项可用于调整其行为。例如,我们可以使用要删除的自定义停止字列表配置标准分析器。
如果没有适合我们需要的内置分析器,我们可以测试并创建自定义分析器。自定义分析器涉及选择和组合不同的分析器组件,使我们能够更好地控制过程。
analyze API是查看分析器生成的术语的非常宝贵的工具。可以在请求中内联指定内置分析器:
POST _analyze
{
"analyzer": "whitespace",
"text": "The quick brown fox."
}
API返回以下响应:
{
"tokens": [
{
"token": "The",
"start_offset": 0,
"end_offset": 3,
"type": "word",
"position": 0
},
{
"token": "quick",
"start_offset": 4,
"end_offset": 9,
"type": "word",
"position": 1
},
{
"token": "brown",
"start_offset": 10,
"end_offset": 15,
"type": "word",
"position": 2
},
{
"token": "fox.",
"start_offset": 16,
"end_offset": 20,
"type": "word",
"position": 3
}
]
}
我们还可以测试以下各项的组合:
- 分词器
- 零个或多个令牌筛选器
- 零个或多个字符过滤器
POST _analyze
{
"tokenizer": "standard",
"filter": [ "lowercase", "asciifolding" ],
"text": "Is this déja vu?"
}
API返回以下响应:
{
"tokens": [
{
"token": "is",
"start_offset": 0,
"end_offset": 2,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "this",
"start_offset": 3,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "deja",
"start_offset": 8,
"end_offset": 12,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "vu",
"start_offset": 13,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 3
}
]
}
位置和字符偏移
从analyze API的输出可以看出,分析器不仅将单词转换为术语,还记录每个术语的顺序或相对位置(用于短语查询或单词接近查询),以及原始文本中每个术语的起始和结束字符偏移量(用于突出显示搜索片段)。
或者,在特定索引上运行analyze API时,可以参考自定义分析器:
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"std_folded": { (1)
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"asciifolding"
]
}
}
}
},
"mappings": {
"properties": {
"my_text": {
"type": "text",
"analyzer": "std_folded" (2)
}
}
}
}
GET my-index-000001/_analyze (3)
{
"analyzer": "std_folded", (4)
"text": "Is this déjà vu?"
}
GET my-index-000001/_analyze (3)
{
"field": "my_text", (5)
"text": "Is this déjà vu?"
}
(1)定义一个名为std_folded的自定义分析器。
(2)字段my_text使用std_folded分析器。
(3)要引用此分析器,分析API必须指定索引名称。
(4)按名称引用分析器。
(5)请参阅字段my_text使用的分析器。
配置内置分析器
内置分析器可直接使用,无需任何配置。但是,其中一些支持配置选项以改变其行为。例如,可以将标准分析器配置为支持停止字列表:
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"std_english": {
"type": "standard", (1)
"stopwords": "_english_"
}
}
}
},
"mappings": {
"properties": {
"my_text": {
"type": "text",
"analyzer": "standard", (2)
"fields": {
"english": {
"type": "text",
"analyzer": "std_english" (3)
}
}
}
}
}
}
POST my-index-000001/_analyze
{
"field": "my_text", (2)
"text": "The old brown cow"
}
POST my-index-000001/_analyze
{
"field": "my_text.english", (3)
"text": "The old brown cow"
}
- 我们将std_english分析器定义为基于标准分析器,但配置为删除预定义的英语停止词列表。
- my_text字段直接使用标准分析器,无需任何配置。此字段中不会删除任何停止字。由此产生的术语是:[The,old,brown,cow]
- my_text.english字段使用std_english分析器,因此英语停止词将被删除。由此产生的术语是:[old, brown, cow ]
创建一个自定义的分析器
当内置分析器不能满足我们的需要时,我们可以创建一个自定义分析器,该分析器使用以下各项的适当组合:
- 零个或多个字符过滤器
- 分词器
- 零个或多个令牌筛选器。
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom", (1)
"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>?"
}
(1)对于自定义分析器,请使用自定义类型或忽略类型参数。
上述示例产生以下术语:
[ is, this, deja, vu ]
上一个示例使用了分词器、标记过滤器和字符过滤器及其默认配置,但是可以创建每个过滤器的配置版本,并在自定义分析器中使用它们。
下面是一个更复杂的示例,它结合了以下内容:
字符过滤器
映射字符筛选器(Mapping Character Filter),配置为被替换 :( with _happy_ and :( with _sad_
分词器
Pattern Tokenizer,配置为按标点字符拆分
令牌过滤器
小写标记过滤器(Lowercase Token Filter)
停止标记过滤器(Stop Token Filter),配置为使用预定义的英语停止词列表
以下是一个例子:
PUT my-index-000001
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": { (1)
"char_filter": [
"emoticons"
],
"tokenizer": "punctuation",
"filter": [
"lowercase",
"english_stop"
]
}
},
"tokenizer": {
"punctuation": { (2)
"type": "pattern",
"pattern": "[ .,!?]"
}
},
"char_filter": {
"emoticons": { (3)
"type": "mapping",
"mappings": [
":) => _happy_",
":( => _sad_"
]
}
},
"filter": {
"english_stop": { (4)
"type": "stop",
"stopwords": "_english_"
}
}
}
}
}
POST my-index-000001/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "I'm a :) person, and you?"
}
(1)为索引指定默认的自定义分析器my_custom_analyzer。此分析器使用在请求后面定义的自定义分词器、字符筛选器和标记筛选器。此分析器还忽略类型参数。
(2)定义自定义 标点符号(punctuation) 分词器。
(3)定义自定义表情字符(emoticons)过滤器。
(4)定义自定义english_stop标记筛选器。
上述示例产生以下术语:
[ i'm, _happy_, person, you ]