query查询与filter过滤
DSL查询语言中存在两种:查询DSL(query DSL)和过滤DSL(filter DSL)。
Elasticsearch 2.0之前查询和过滤器是单独组件。从 Elasticsearch 2.0 之后,过滤器在技术上被消除,并且所有查询都获得了成为非评分的能力。
Elasticsearch 使用的 DSL 具有一组称为查询的组件,它们可以混合并以无穷组合进行匹配。这一组组件可以在两个上下文中使用:过滤上下文和查询上下文。
query DSL在查询上下文中,查询会回答这个问题——“这个文档匹不匹配这个查询,它的相关度高么?”
如何验证匹配很好理解,如何计算相关度呢?ES中索引的数据都会存储一个_score分值,分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂的,因此也需要一定的时间。
filter DSL在过滤器上下文中,查询会回答这个问题——“这个文档匹不匹配?”
答案很简单,是或者不是。它不会去计算任何分值,也不会关心返回的排序问题,因此效率会高一点。
一条过滤语句会询问每个文档的字段值是否包含着特定值,查询语句会询问每个文档的字段值与特定值的匹配程度如何。这一点较好区分,一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 _score,并且 按照相关性对匹配到的文档进行排序。 这种评分方式非常适用于一个没有完全配置结果的全文本搜索。
性能上的差异
使用过滤语句得到的结果集 -- 一个简单的文档列表,快速匹配运算并存入内存是十分方便的, 每个文档仅需要1个字节。这些缓存的过滤结果集与后续请求的结合使用是非常高效的。
查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比 过滤语句更耗时,并且查询结果也不可缓存。
幸亏有了倒排索引,一个只匹配少量文档的简单查询语句在百万级文档中的查询效率会与一条经过缓存 的过滤语句旗鼓相当,甚至略占上风。 但是一般情况下,一条经过缓存的过滤查询要远胜一条查询语句的执行效率。
过滤语句的目的就是缩小匹配的文档结果集,所以需要仔细检查过滤条件。
适用场景
原则上来说,使用查询语句做全文本搜索或其他需要进行相关性评分的时候,剩下的全部用过滤语句.。
filter过滤语句:
- term
- terms
- range
- exists
- 已上面语句跟bool[ must ,must_not, should]的组合
过滤上下文大多数用于过滤结构化数据,如范围查询(给定日期范围)、状态检查等。elasticsearch会自动缓存频繁使用过滤上下文,从而提升查询性能。
无论何时将过滤参数传给查询子句,过滤上下文都有效,如在bool查询中的filter 或者 must_not 参数,constant_score 查询中的过滤参数,或filter聚集。
GET /stock_news/_search
{
"query": {
"bool": {
"filter": {
"range": { "at_time": { "gte": now-1d }}
}
}
}
}
query查询语句:
- match
- match_all
- multi_match
- match_phrase
- phrase_prefix
- regexp
- 已上面语句跟bool[ must ,must_not, should]的组合。
查询结果列出所有相关文档并按照相关性评分进行排序。相关性评分有查询上下文中的查询子句计算出来,用_score表示,即相对于其他文档的匹配程度。
无论何时将查询参数传给查询子句,查询上下文都有效,如搜索API中的查询参数。下面示例带有查询上下文的查询,返回所有描述包括science单词的课程。
GET /stock_news/_search
{
"query": {
"match": {
"title": "行情"
}
}
}
query DSL和 filter DSL可以组合使用
GET /stock_news/_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "行情" }},
],
"filter": [
{ "range": { "at_time": { "gte": now-1d }}}
]
}
}
}
总结
filter: 不需要计算相关度分数,不需要按照相关度分数进行排序,同时还有内置的自动缓存cache,会缓存住最常使用的filter数据
query: 需要计算相关度分数,并按照相关度分数进行排序,无法缓存结果
区分清楚以上DSL查询方式,对入门ES是非常必要的,习惯了SQL,接触这块往往对查询这块没啥概念,困惑了很久!