0
点赞
收藏
分享

微信扫一扫

elasticsearch-java api之过滤

猎书客er 2022-06-17 阅读 51

一、过滤相关点:

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 标签 — 标签越多,相关性越高

一个评分查询计算每一个文档与此查询的 _相关程度_,同时将这个相关程度分配给表示相关性的字段 `_score`,并且按照相关性对匹配到的文档进行排序。这种相关性的概念是非常适合全文搜索的情况,因为全文搜索几乎没有完全 “正确” 的答案。


说明:

自 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.

filteredQuery(qb, qb);


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​​




举报

相关推荐

0 条评论