一、过滤相关点:
1、查询与过滤:
Elasticsearch 使用的查询语言(DSL) 拥有一套查询组件,这些组件可以以无限组合的方式进行搭配。这套组件可以在以下两种情况下使用:过滤情况(filtering context)和查询情况(query context)。
当使用于 过滤情况 时,查询被设置成一个“不评分”或者“过滤”查询。即,这个查询只是简单的问一个问题:“这篇文档是否匹配?”。回答也是非常的简单,yes 或者 no ,二者必居其一。
- created 时间是否在 2013 与 2014 这个区间?
- status 字段是否包含 published 这个单词?
- lat_lon 字段表示的位置是否在指定点的 10km 范围内?
- 查找与 full text search 这个词语最佳匹配的文档
- 包含 run 这个词,也能匹配 runs 、 running 、 jog 或者 sprint
- 包含 quick 、 brown 和 fox 这几个词 — 词之间离的越近,文档相关性越高
- 标有 lucene 、 search 或者 java 标签 — 标签越多,相关性越高
说明:
自 Elasticsearch 问世以来,查询与过滤(queries and filters)就独自成为 Elasticsearch 的组件。但从 Elasticsearch 2.0 开始,过滤(filters)已经从技术上被排除了,同时所有的查询(queries)拥有变成不评分查询的能力。
然而,为了明确和简单,我们用 "filter" 这个词表示不评分、只过滤情况下的查询。你可以把 "filter" 、 "filtering query" 和 "non-scoring query" 这几个词视为相同的。相似的,如果单独地不加任何修饰词地使用 "query" 这个词,我们指的是 "scoring query" 。
2、性能差异:
过滤查询(Filtering queries)只是简单的检查包含或者排除,这就使得计算起来非常快。考虑到至少有一个过滤查询(filtering query)的结果是 “稀少的”(很少匹配的文档),并且经常使用不评分查询(non-scoring queries),结果会被缓存到内存中以便快速读取,所以有各种各样的手段来优化查询结果。相反,评分查询(scoring queries)不仅仅要找出 匹配的文档,还要计算每个匹配文档的相关性,计算相关性使得它们比不评分查询费力的多。同时,查询结果并不缓存。
多亏倒排索引(inverted index),一个简单的评分查询在匹配少量文档时可能与一个涵盖百万文档的filter表现的一样好,甚至会更好。但是在一般情况下,一个filter 会比一个评分的query性能更优异,并且每次都表现的很稳定。过滤(filtering)的目标是减少那些需要通过评分查询(scoring queries)进行检查的文档。
参考:https://www.elastic.co/guide/cn/elasticsearch/guide/current/_queries_and_filters.html
二、过滤实例:
在之前es版本(2.0之前)的官网中,介绍了两种过滤:一个是Filtered Query,一个是Post Filter。前者是先使用filter,过滤后的结果再query;后者是先query,query的结果再过滤。而在es中,filter的操作使用了cache,速度比query快。
在最新2.0版本的官网上中,提到了:
现在是时候忘掉你所知道的关于查询和过滤器的一切了:Elasticsearch 2.0将自己做出更好的决定,而不是依靠用户来制定一个优化的查询。这一变化在API层面几乎是看不见的,但是让我们来深入探讨使之成为可能的内部变化。本篇文章中提到的大部分更改都是在Lucene 5.0,5.1和5.2中完成的,并将集成到Elasticsearch 2.0中。
在Elasticsearch 2.0中,查询和过滤器是相同的内部对象,可以配置这些对象来评分文档或跳过评分。然后,查询DSL确保正确传播信息:例如,如果bool查询需要产生分数,则bool查询的must子句将需要产生分数,而must_not子句永远不需要产生分数,因为它可能只是用于过滤文件。为了使查询的DSL更加符合这种变化,我们已经废弃了过滤的查询,赞成在bool查询中使用一个新的过滤器子句:过滤器子句就像must子句,除了它们没有贡献分数。意味着:
{
“filtered” : {
“query”: { query definition },
“filter”: { filter definition }
}
}
应该被替代成:
{
“bool” : {
“must”: { query definition },
“filter”: { filter definition }
}
}
注:查询DSL仍然是向后兼容的,尽管有这种改变:如果你尝试运行一个过滤的查询,它将在内部解析为一个bool查询。但是,我们鼓励您迁移到新的语法,因为在未来的发行版中,已过滤的查询将被删除。
实例1:postfilter
public static void filterQuery1(String indexName,String indexType) {
QueryBuilder qb = QueryBuilders.boolQuery()
.must(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("title", "java")).boost(1f))
.should(QueryBuilders.constantScoreQuery(QueryBuilders.termQuery("title", "elasticsearch")).boost(5f))
.should(QueryBuilders.termQuery("title", "hadoop"));
QueryBuilder fb = QueryBuilders.rangeQuery("age").from(10).to(20);
SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType)
.setQuery(qb)
.setPostFilter(fb)
.get();
SearchHits hits = searchResponse.getHits();
//long totalHits = hits.getTotalHits();
SearchHit[] hits2 = hits.getHits();
for (SearchHit sh:hits2) {
Map<String, Object> source = sh.getSource();
String id = sh.getId();
float score = sh.getScore();
System.out.println("source:"+source+",id:"+id+",score:"+score);
}
}
注:使用QueryBuilder创建过滤(代替了之前版本的FilterBuilder)
实例2:filter
public static void filterQuery2(String indexName,String indexType) {
QueryBuilder qb = QueryBuilders.boolQuery()
.must(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("title", "java")).boost(1f))
.should(QueryBuilders.constantScoreQuery(QueryBuilders.termQuery("title", "elasticsearch")).boost(5f))
.should(QueryBuilders.termQuery("title", "hadoop"))
.filter(QueryBuilders.rangeQuery("age").from(10).to(20));
System.out.println(qb.toString());
SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType)
.setQuery(qb)
.get();
SearchHits hits = searchResponse.getHits();
//long totalHits = hits.getTotalHits();
SearchHit[] hits2 = hits.getHits();
for (SearchHit sh:hits2) {
Map<String, Object> source = sh.getSource();
String id = sh.getId();
float score = sh.getScore();
System.out.println("source:"+source+",id:"+id+",score:"+score);
}
}
A、之前这种写法已经被废弃:QueryBuilders.
B、对于Filtered Query已经被bool查询+filter替代,我们可以手工在bool查询中指定filter,同时es内部将bool查询也会自动优化成filter。例如:
QueryBuilder qb = QueryBuilders.boolQuery()
.must(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("title", "java")).boost(1f))
.should(QueryBuilders.constantScoreQuery(QueryBuilders.termQuery("title", "elasticsearch")).boost(5f))
.should(QueryBuilders.termQuery("title", "hadoop"));
System.out.println(qb.toString());
这样的boolean查询,会被优化成如下的dsl:
{
"bool" : {
"must" : {
"constant_score" : {
"filter" : {
"match" : {
"title" : {
"query" : "java",
"type" : "boolean"
}
}
},
"boost" : 1.0
}
},
"should" : [ {
"constant_score" : {
"filter" : {
"term" : {
"title" : "elasticsearch"
}
},
"boost" : 5.0
}
}, {
"term" : {
"title" : "hadoop"
}
} ]
}
}
参考:
https://www.elastic.co/blog/better-query-execution-coming-elasticsearch-2-0
https://www.elastic.co/guide/en/elasticsearch/reference/2.0/breaking_20_java_api_changes.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_filter_bucket.html