文本分析
当你使用全文搜索查找文档时,你希望获得所有相关的结果。如果你搜索“walk”,你可能会对包含该词的任何形式的结果感兴趣,例如“Walk”、“walked”或“walking”。为了方便全文搜索,OpenSearch 使用文本分析。
文本分析的目标是将源文档的非结构化自由文本内容拆分为一系列术语(term),然后将这些术语存储在倒排索引中。随后,当对用户查询应用类似的文本分析时,生成的术语序列有助于匹配相关的源文档。
从技术角度来看,文本分析过程包含几个步骤,其中一些是可选的
-
在将自由文本内容拆分为单个单词之前,在字符层面进行文本细化可能会有所帮助。此可选步骤的主要目的是帮助分词器(tokenizer)(分析过程的后续阶段)生成更好的词元(token)。这可以包括删除标记标签(如 HTML)或处理特定的字符模式(如将🙂表情符号替换为文本
:slightly_smiling_face:
)。 -
下一步是将自由文本拆分为单个单词——词元(token)。这由分词器(tokenizer)执行。例如,在分词后,句子
Actions speak louder than words
被拆分为词元Actions
、speak
、louder
、than
和words
。 -
最后一步是通过应用一系列词元过滤器(token filter)来处理单个词元。目的是将每个词元转换为可预测的形式,然后直接存储在索引中,例如,通过将它们转换为小写或执行词干提取(将单词还原为词根)。例如,词元
Actions
变为action
,louder
变为loud
,words
变为word
。
尽管**词元(token)**和**术语(term)**听起来相似,有时可以互换使用,但理解它们之间的区别会很有帮助。在 Apache Lucene 的上下文中,它们各自扮演着不同的角色。**词元(token)**是在文本分析过程中由分词器创建的,并且在通过词元过滤器链时通常会经历多次额外修改。每个词元都关联着元数据,这些元数据可以在文本分析过程中进一步使用。**术语(term)**是直接存储在倒排索引中的数据值,并且关联的元数据少得多。在搜索期间,匹配操作在术语层面进行。
分析器
在 OpenSearch 中,包含文本分析的抽象称为分析器(analyzer)。每个分析器都包含以下按顺序应用的组件
-
字符过滤器(Character filters):首先,字符过滤器接收原始文本作为字符流,并添加、删除或修改文本中的字符。例如,字符过滤器可以从字符串中剥离 HTML 字符,使文本
<p><b>Actions</b> speak louder than <em>words</em></p>
变为\nActions speak louder than words\n
。字符过滤器的输出是字符流。 -
分词器(Tokenizer):接下来,分词器接收经字符过滤器处理后的字符流,并将文本拆分为单个词元(token)(通常是单词)。例如,分词器可以根据空白字符拆分文本,使得前述文本变为[
Actions
,speak
,louder
,than
,words
]。分词器还维护词元的元数据,例如它们在文本中的起始和结束位置。分词器的输出是词元流。 -
词元过滤器(Token filters):最后,词元过滤器接收来自分词器的词元流,并添加、删除或修改词元。例如,词元过滤器可以将词元转换为小写,使
Actions
变为action
,删除停用词如than
,或者为单词speak
添加同义词如talk
。
一个分析器必须包含且仅包含一个分词器,并且可以包含零个或多个字符过滤器以及零个或多个词元过滤器。
还有一种特殊类型的分析器称为标准化器(normalizer)。标准化器类似于分析器,但它不包含分词器,并且只能包含特定类型的字符过滤器和词元过滤器。这些过滤器只能执行字符级操作,例如字符或模式替换,而不能对整个词元执行操作。这意味着不支持用同义词替换词元或进行词干提取。有关更多详细信息,请参阅标准化器。
支持的分析器
有关支持的分析器列表,请参阅分析器。
自定义分析器
如果需要,你可以组合分词器、词元过滤器和字符过滤器来创建自定义分析器。有关更多信息,请参阅创建自定义分析器。
索引时和查询时的文本分析
OpenSearch 在索引文档时和发送搜索请求时对文本字段执行文本分析。根据文本分析的时间,用于文本分析的分析器分类如下
-
索引分析器(index analyzer)在索引时执行分析:当你索引一个文本字段时,OpenSearch 会在索引它之前对其进行分析。有关指定索引分析器的更多信息,请参阅索引分析器。
-
搜索分析器(search analyzer)在查询时执行分析:当你对文本字段运行全文查询时,OpenSearch 会分析查询字符串。有关指定搜索分析器的更多信息,请参阅搜索分析器。
在大多数情况下,你应在索引时和搜索时使用相同的分析器,因为文本字段和查询字符串将以相同的方式进行分析,并且生成的词元将按预期匹配。
示例
当你索引一个文本字段包含文本Actions speak louder than words
的文档时,OpenSearch 会分析该文本并生成以下词元列表
文本字段词元 = [action
, speak
, loud
, than
, word
]
当你搜索与查询speaking loudly
匹配的文档时,OpenSearch 会分析查询字符串并生成以下词元列表
查询字符串词元 = [speak
, loud
]
然后,OpenSearch 将查询字符串中的每个词元与文本字段词元列表进行比较,发现两个列表中都包含词元speak
和loud
,因此 OpenSearch 会将此文档作为与查询匹配的搜索结果的一部分返回。
测试分析器
要测试内置分析器并查看文档索引时生成的令牌列表,您可以使用Analyze API。
在请求中指定分析器和要分析的文本
GET /_analyze
{
"analyzer" : "standard",
"text" : "Let’s contribute to OpenSearch!"
}
下图显示了查询字符串。
响应包含每个令牌及其起始和结束偏移量,这些偏移量对应于原始字符串中的起始索引(包含)和结束索引(不包含)
{
"tokens": [
{
"token": "let’s",
"start_offset": 0,
"end_offset": 5,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "contribute",
"start_offset": 6,
"end_offset": 16,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "to",
"start_offset": 17,
"end_offset": 19,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "opensearch",
"start_offset": 20,
"end_offset": 30,
"type": "<ALPHANUM>",
"position": 3
}
]
}
验证分析器设置
要验证哪个分析器与哪个字段关联,您可以使用 get mapping API 操作
GET /testindex/_mapping
响应提供了每个字段的分析器信息
{
"testindex": {
"mappings": {
"properties": {
"text_entry": {
"type": "text",
"analyzer": "simple",
"search_analyzer": "whitespace"
}
}
}
}
}
标准化器
分词将文本分割成单个词项,但它不处理令牌形式的变化。规范化通过将令牌转换为标准格式来解决这些问题。这确保了即使相似的词项不完全相同也能正确匹配。
规范化技术
以下规范化技术可以帮助解决令牌形式的变化
-
大小写规范化:将所有令牌转换为小写以确保不区分大小写的匹配。例如,“Hello”被规范化为“hello”。
-
词干提取:将单词简化为其词根形式。例如,“cars”的词干被提取为“car”,“running”被规范化为“run”。
-
同义词处理:将同义词视为等价。例如,“jogging”和“running”可以作为共同词项“run”进行索引。
规范化
搜索Hello
将匹配包含hello
的文档,因为进行了大小写规范化。
搜索cars
也将匹配包含car
的文档,因为进行了词干提取。
查询running
可以检索包含jogging
的文档,通过同义词处理。
规范化确保搜索不局限于精确的词项匹配,从而获得更相关的结果。例如,搜索Cars running
可以被规范化以匹配car run
。