
4.4 分析器与规整器
在Elasticsearch中,文档编入索引时会从全文数据中提取词项,这个过程被称为文档分析(Analysis)。文档分析不仅存在于文档索引时,也存在于文档检索时。文档分析会从查询条件的全文数据中提取词项,然后再根据这些词项检索文档。文档分析器(Analyzer)是Elasticsearch中用于文档分析的组件,通常由字符过滤器(Character Filter)、分词器(Tokenizer)和分词过滤器(Token Filter)三部分组成。它们就像是连接在一起的管道,共同完成对全文数据的词项提取工作。如图4-1所示。

图4-1 文档分析器
字符过滤器读入最原始的全文数据,并对全文数据中的字符做预处理,比如从HTML文档中将类似<b>这样的标签删除。字符过滤器可以根据实际情况添加,可以没有也可以有多个。分词器接收由字符过滤器处理完的全文数据,然后将它们根据一定的规则拆分成词项。英文分词规则比较简单,直接使用空格分隔单词即可;但中文分词规则比较复杂,需要根据词意做分词且需要字典支持。对于一个分析器来说,分词器是必不可少的,而且只能有一个。在使用分析器时,可以根据文档内容更换分词器,比如对于中文来说需要将分析器的分词器更换为中文分词器。最后,分词过滤器的职责是接收由分词器提取出来的所有词项,然后对这些分词做规范化处理,比如将分词转换为小写、去除“的、地、得”等停止词(Stop Word)。分词过滤器与字符过滤器一样,可以根据需要添加和配置,可以没有也可以有多个。
除了分析器以外,Elasticsearch还提供了一种称为规整器(Normalizer)的文档标准化工具。规整器与分析器的最大区别在于规整器没有分词器,而只有字符过滤器和分词过滤器,所以它能保证分析后的结果只有一个分词。文档规整器只能应用于字段类型为keyword的字段,可通过字段的normalizer参数配置字段规整器。规整器的作用就是对keyword字段做标准化处理,比如将字段值转为小写字母等等。规整器与分析器共享相同的字符过滤器和词项过滤器,但规整器只能使用那些结果只有一个词项的过滤器。
4.4.1 设置分析器
由于文档分析通过分析器完成词项提取,所以想要影响文档索引和文档检索时的词项提取,就要修改它们使用的分析器。对于所有text类型的字段,都可以在创建索引时为它们指定分析器。对于非text类型字段来说,由于本身不存在全文本数据词项提取的问题,所以也就没有设置分析器的问题。例如,在示例4-20中,title字段在文档编入索引时使用standard分析器,而通过title字段检索文档时则使用simple分析器:

示例4-20 设置分析器
在创建索引时如果没有指定分析器,Elasticsearch会查找名为default的分析器,如果没有则使用standard分析器。文档检索时使用的分析器有一点复杂,它依次从如下参数中查找文档分析器,如果都没有设置则使用standard分析器:
1)检索请求的analyzer参数;
2)索引映射字段的search_analyzer参数;
3)索引映射字段的analyzer参数;
4)索引配置中的default_search参数。
以示例4-20中的articles索引为例,尽管设置的检索分析器为simple,但如果在检索文档时使用analyzer参数设置为english分析器,最终使用的分析器依然是english分析器。如第4.1节所述,analyzer参数可以作为_search接口的URI参数也可以出现的请求体中,例如:

示例4-21 analyzer参数
4.4.2 _analyze接口
Elasticsearch提供了一个_analyze接口,可以用于查看分析器处理结果,这在学习分析器时非常有帮助。由于_analyze接口仅用于查看分析器功能,所以在发送请求时不需要指定索引,虽然在指定索引时也不会报错。例如示例4-22就是用standard分析器处理文本:

示例4-22 _analyze接口
_analyze接口可以使用POST发送请求,也可以使用GET发送请求。返回的结果中,将包含所有提取出来的词项以及它们的位置、偏移量等信息:

示例4-23 _analyze接口返回结果
其中,token是提取出来的词项,start_offset与end_offset是词项在整个文本中的起始位置和终止位置(即偏移量),position是词项在所有词项中的次序,而type则是词项的类型(与词项本身和分析器相关)。如果要查看分词中更详细的内容,可以在请求体中将explain参数设置为true。
实际上除了analyzer参数以外,还可以使用tokenizer、filter、char_filter的组合来测试分词器、分词过滤器、字符过滤器的功能。
4.4.3 _termvectors接口
_termvectors接口提供了实时查看一段文本提取词项结果的功能,可以使用POST或GET方法请求该接口。该接口查看词项结果时所使用的文本内容可以来自索引中某一文档的字段内容,也可以是在调用接口时动态指定一段文本。所以该接口有两种请求方式,并且它们不能同时使用,只能任选其一。第一种方式是在请求路径中指定文档_id,并在路径或请求体中通过设置fields参数指定要查看的字段名称。例如:

示例4-24 _termvectors接口fields参数
在示例4-24中,请求路径中的索引名称和文档ID(_termvectors后面的值)都必须要指定。在示例中文档ID由于太长,所以做了一些节略。两个请求都是通过fields参数指定要查看词项的字段名称,可以指定一个也可以指定多个。第二种方式与此不同,它要求路径中一定不能包含文档_id,而是在请求体中通过doc参数指定文本内容。例如:

示例4-25 _termvectors接口doc参数
在示例4-25中,doc参数指定了两个字段message和agent,Elasticsearch会分别返回这两个字段词项的提取情况。doc参数指定的字段必须是索引中已经存在的字段,该接口会忽略索引中不存在的字段。通过这种方式也能看出来,这个接口是实时做词项分析。即使是使用第一种方式指定到具体的文档字段上,它也不是直接去读取已经提取的词项,而是使用索引或字段设置的分析器实时提取词项。_termvectors接口返回的结果中主要包含field_statistics和terms两个字段,它们分别给出提取词项的统计数据和详细信息,这些对于分析检索时出现的问题将会十分有用。
除此之外,Elasticsearch还提供了_mtermvectors接口,可以一次查询多个文档的词项信息。这些文档甚至可以分散在不同的索引和映射类型中,例如:


示例4-26 _mtermvectors接口
在示例4-26中,_mtermvectors接口的路径中并没有包含索引和文档ID,它们是通过在请求体中添加docs数组,并设置数组每个元素的_index和_id参数指定。如果要查询的词项信息在同一个索引中,则可以在_mtermvectors前面添加索引名称,而无须在docs数组中再设置对象的_index参数。