停用词与短语查询
所有查询中 phrase-matching 大约占到5%,但是在慢查询里面它们又占大部分。短语查询性能相对较差,特别是当短语中包括常用词的时候,如 “To be, or not to be”
短语全部由停用词组成,这是一种极端情况。原因在于几乎需要匹配全量的数据。
在 停用词的两面 pros-cons-stopwords,中,我们提到移除停用词只能节省倒排索引中的一小部分空间。这句话只部分正确,一个典型的索引会可能包含部分或所有以下数据:
词项字典(Terms dictionary)
索引中所有文档内所有词项的有序列表,以及包含该词的文档数量。
倒排表(Postings list)
包含每个词项的文档(ID)列表。
词频(Term frequency)
每个词项在每个文档里出现的频率。
位置(Positions)
每个词项在每个文档里出现的位置,供短语查询或近似查询使用。
偏移(Offsets)
每个词项在每个文档里开始与结束字符的偏移,供词语高亮使用,默认是禁用的。
规范因子(Norms)
用来对字段长度进行规范化处理的因子,给较短字段予以更多权重。
将停用词从索引中移除会节省 词项字典 和 倒排表 里的少量空间,但 位置 和 偏移 是另一码事。位置和偏移数据很容易变成索引大小的两倍、三倍、甚至四倍。
@# 位置信息
analyzed
字符串字段的位置信息默认是开启的,所以短语查询能随时使用到它。词项出现的越频繁,用来存储它位置信息的空间就越多。在一个大的文档集合中,对于那些非常常见的词,它们的位置信息可能占用成百上千兆的空间。
运行一个针对高频词 the
的短语查询可能会导致从磁盘读取好几G的数据。这些数据会被存储到内核文件系统的缓存中,以提高后续访问的速度,这看似是件好事,但这可能会导致其他数据从缓存中被剔除,进一步使后续查询变慢。
这显然是我们需要解决的问题。
索引选项
我们首先应该问自己:是否真的需要使用短语查询或 近似查询?
答案通常是:不需要。在很多应用场景下,比如说日志,我们需要知道一个词 是否 在文档中(这个信息由倒排表提供)而不是关心词的位置在哪里。或许我们要对一两个字段使用短语查询,但是我们完全可以在其他 analyzed
字符串字段上禁用位置信息。
index_options
参数 允许我们控制索引里为每个字段存储的信息。可选值如下:
docs
只存储文档及其包含词项的信息。这对 `not_analyzed` 字符串字段是默认的。
freqs
存储 `docs` 信息,以及每个词在每个文档里出现的频次。词频是完成<<relevance-intro,TF/IDF>> 相关度计算的必要条件,但如果只想知道一个文档是否包含某个特定词项,则无需使用它。
positions
存储 `docs` 、 `freqs` 、 `analyzed` ,以及每个词项在每个文档里出现的位置。 这对 `analyzed` 字符串字段是默认的,但当不需使用短语或近似匹配时,可以将其禁用。
offsets
存储 `docs`,`freqs`,`positions`, 以及每个词在原始字符串中开始与结束字符的偏移信息( {ref}/search-request-highlighting.html#postings-highlighter[`postings` highlighter] )。这个信息被用以高亮搜索结果,但它默认是禁用的。
我们可以在索引创建的时候为字段设置 index_options
选项,或者在使用put-mapping
API新增字段映射的时候设置。我们无法修改已有字段的这个设置:
PUT /my_index
{
"mappings": {
"my_type": {
"properties": {
"title": { (1)
"type": "string"
},
"content": { (2)
"type": "string",
"index_options": "freqs"
}
}
}
}
<1> title
字段使用默认的 positions
设置,所以它适于短语或近似查询。
<2> content
字段的位置设置是禁用的,所以它无法用于短语或近似查询。
停用词
删除停用词是能显著降低位置信息所占空间的一种方式。 一个被删除停用词的索引仍然可以使用短语查询,因为剩下的词的原始位置仍然被保存着,这正如 maintaining-positions 中看到的那样。 尽管如此,将词项从索引中排除终究会降低搜索能力,这使我们难以区分 Man in the moon 与 Man on the moon 这两个短语。
幸运的是,鱼与熊掌是可以兼得的:请查看 common_grams
过滤器。