elasticsearch-java api之过滤

17 篇文章 3 订阅

一、过滤相关点:

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值