本文主要对Elasticsearch中查询相关的知识做一个简单的总结,内容主要包括查询的评分机制,查询改写,过滤器,以及对常见的查询做一个简单的分类
在Lucense中默认使用TF/IDF算法对文档进行评分,该算法已经在前文做了简单的介绍,这里主要总结下什么样的因素可能得到高分
1. 越罕见的词被匹配上,得分越高。
2. 文档字段越短(包含更少的词项),文档的得分越高
3. 权重越高(可以是索引期或是查询期赋予的权重),得分越高
Elasticsearch 提供的评分算法比较丰富(默认为BM25),可以通过similarity进行配置,可配置的项目包括:
1. BM25
2. Classic(基于TF/IDF)
3. DFR/DFI
4. IB
5. ….
关于每个算法的介绍以及可配置参数可以参见:https://www.elastic.co/guide/en/elasticsearch/reference/5.2/index-modules-similarity.html
另外需要注意的是除了使用similarity进行评分的控制之外,还可以通过function_score,constant_score,rescore 等多种机制对评分进行更加精确的控制。
在进行==前缀查询==或是==通配符查询==时,实际上会转化为对多个关键词的查询,处于性能优化的考虑,Lucene会把这种查询转化为一组开销较小的查询方式,采取的主要手段便是“查询改写(rewrite)”
查询改写使用的主要策略为通过使用常量得分查询(constant score query)来代替常规查询来减少查询时性能的开销,对于可能匹配term较多的查询时,性能提升尤为明显。
在查询改写的过程中可以根据需要自定义改写的方式,可支持的配置如下
对于如何选择rewrite配置的问题,可以遵循如下原则
- 如果您能接受较低的精度和相关性(但是追求更高的性能),那么可以采用top-N查询改写方法。
- 如果您需要更高的查询精度和更好的相关性(同时可以接受较低的性能),那么应该采用布尔方法。
Elsaticsearch在提供了丰富查询功能的同时,还提供了过滤器的功能,过滤器的作用顾名思义,主要用来对查询结果过滤,但过滤的过程中不会涉及到store的计算性能损耗(是不会对已有查询的store造成影响),同时频繁使用的过滤器还会被Elasticsearch自动的缓存,总而提高查询的性能。
过滤器持有的关于文档的唯一重要信息是该文档是否匹配这个过滤器(仅仅一个标记而已)
过滤器通过返回一个被称为DocIdSet(org.apache.lucene.search.DocIdSet)的数据结构来提供匹配信息。这个数据结构的用途是为索引段提供经过滤器过滤后的数据。它可以使用Bits接口(org.apache.lucene.util.Bits)的有关实现。Bits接口可以随机访问过滤器中的文档信息(主要是检查索引段中的某个文档是否和该过滤器匹配)。
Bits的数据结构非常高效,因为CPU可以使用位运算来完成过滤
下面的表格展示了Bits接口的工作方式
doc | bits.get(doc) | Result |
---|---|---|
1 | False | |
2 | False | |
3 | True | 3 |
4 | True | 4 |
Elasticsearch和Lucene可以根据不同的使用场景选择不同的DocIdSet实现,不是所有的过滤器都使用Bits结构,比如数值区间过滤器、脚本过滤器、以及基于地理位置的一组过滤器。这些特殊的过滤器选择把数据记录在字段缓存里,然后再遍历所需处理的文档集合,逐个进行过滤操作。这意味着过滤器链条中的下一个过滤器只能获取到匹配前一个过滤器的文档集合。因此,可以针对这些过滤器进行优化,比如把最重的(匹配文档最多的,或者性能最差的)过滤器放到过滤器链的最后去执行。
本章节主要总结Elasticsearch支持的常见查询类型并进行分类,由于Elasticsearch的查询功能较多,本部分只会选择性的进行说明,具体使用方法与参数可以参见官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/5.2/query-dsl.html
在不使用API的情况下,可以使用两种方式对数据进行查询
curl -XGET 'localhost:9200/twitter/tweet/_search?q=user:kimchy&pretty'
curl -XGET 'localhost:9200/twitter/tweet/_search?pretty' -H 'Content-Type: application/json' -d'
{
"query" : {
"term" : { "user" : "kimchy" }
}
}
'
这类查询通常作为其他复杂查询的一部分,或是单独传递给Elasticsearch进行查询
该种类型的查询可以嵌入其他查询以及过滤器
- bool查询:比较常用的组合查询,可以把多个查询用布尔逻辑组织在一起。
- dis_max查询:该种查询的结果文档得分和最高权重的子查询打分高度相关,如果希望最高得分的子查询能够在打分过程中起决定性的作用,可以使用该类查询
- 其他:bool,boosting,dis_max,has_child,nested,span-XXX等
该种类型的输入不会被分析,而是直接原样传递给lucene
相关查询主要包括:term,common,ids,prefix,wildcard等等
当需要构建类似于Google这样的查询时,可以使用该类查询,该类查询会根据输入与配置进行查询结果的打分操作。
相关查询主要包括:match,multi_match,query_string,simple_query_string等
顾名思义,该类查询主要是用来支持通配符查询的。需要注意的是该类查询的资源损耗一般都比较大
相关查询主要包括:prefix,regrex,wildcard
该类查询典型的应用场景为近似词查询,或是提供类似于Google的”你想找的是不是XXX”的功能
相关查询主要包括:fuzzy_like_this,fuzzy_like_this_field,more_like_this等
该类查询可以通过自定义权重的方式来改善查询精度或是查询得分。
相关查询主要包括:boosting,constant_score,function_score,indices
可以使用索引中存储的词项位置作为查询条件的查询,例如 “需要查询masting以及elasticsearch两个词项,且这连个单词相互临近,顺序必须是masting在前面,前后距离还不超过3个位置”
该类查询的开销也很大,需要消耗大量的CPU资源
相关查询主要包括:match_phrase,span_XXX
该种类型的查询可以提供类似于SQL中Join的关系查询
- nested查询:文档中可以包含嵌套类型的字段,这些字段可以用来索引一些数组对象,每个对象都可以单独的查询,详细细节可以参考官方文档的nested类型(https://www.elastic.co/guide/en/elasticsearch/reference/5.2/nested.html),以及nested查询(https://www.elastic.co/guide/en/elasticsearch/reference/5.2/query-dsl-nested-query.html)
该类查询主要针对geo_point的查询以及geo_shape
相关查询主要包括:geo_distance,geo_bounding_box,geo_polygon,geo_shape
该类查询主要通过对查询结果使用默认标签()或是自定义标签的方式来实现查询结果高亮的应用场景
相关查询主要包括:highlighter,fast_vector_highlighter
Elasticsearch中默认的查询结果是按照评分排序的,可以使用sort进行排序