本篇概览
-
《Elasticsearch 权威指南中,post_filter出现在聚合章节,描述post_filter的作用为:只过滤搜索结果,不过滤聚合结果;
-
描述比较简单,还是用实例来加深理解吧。
环境信息
- 以下是本次实例的环境信息,请确保您的Elasticsearch可以正常运行:
- 操作系统:Ubuntu 18.04.2 LTS
- JDK:1.8.0_191
- Elasticsearch:6.7.1
- Kibana:6.7.1
实例数据
- 查询用到的数据是个名为cars的索引,里面保存了多条汽车销售记录,字段有品牌(make)、颜色(color)、价格(price)、售卖时间(sold)等,在elasticsearch-head查看数据如下图:
- 如果您想将上图中的数据导入到自己的es环境,请参考《Elasticsearch聚合学习之一:基本操作》,文中有详细的导入步骤;
假设一个查询聚合的需求
-
对上述索引,假设我们的查询需求是:品牌为ford的汽车有哪些颜色
-
这个需求是容易实现的:先限定查询范围(品牌为ford),再用类型为terms的桶对color字段做聚合;
-
具体的DSL怎么写呢?随着我们想要的数据的不同,有不同的写法:
第一种:用查询结果生成聚合结果,然后只返回聚合结果
- 只要设置size参数为0,就不会返回查询结果(返回JSON中,hits.hits的数组长度为0):
GET /cars/transactions/_search
{
"size": 0,
"query": { ---查询
"bool": { ---布尔查询
"filter": { ---用了过滤器
"term": { ---精确匹配
"make": "ford" ---匹配make字段为ford的文档
}
}
}
},
"aggs": {
"colors": {
"terms": { ---桶类型为terms
"field": "color" ---根据color字段聚合
}
}
}
}
- 返回内容中只有聚合结果:
{
"took" : 144,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 0.0,
"hits" : [ ] ---这里面是查询结果,现在长度为0
},
"aggregations" : { ---这里面是用前面的查询结果来做聚合的结果,所有品牌为ford的文档,根据颜色不同进入了两个桶
"colors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "blue", ---蓝色
"doc_count" : 1
},
{
"key" : "green", ---绿色
"doc_count" : 1
}
]
}
}
}
第二种:用查询结果生成聚合结果,然后返回聚合结果和查询结果
- 如果我们想在返回内容中包含查询结果,只要把**"size": 0,**这个参数去掉即可:
GET /cars/transactions/_search
{
"query": { ---查询
"bool": { ---布尔查询
"filter": { ---用了过滤器
"term": { ---精确匹配
"make": "ford" ---匹配make字段为ford的文档
}
}
}
},
"aggs": {
"colors": {
"terms": { ---桶类型为terms
"field": "color" ---根据color字段聚合
}
}
}
}
- 返回的内容如下所示,hits.hits的内容不再为空,而是包含了查询结果,至于聚合结果和前面是一样的:
{
"took" : 39,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 0.0,
"hits" : [ ---这个JSON数组的每个元素,都一个品牌字段为ford的文档
{
"_index" : "cars",
"_type" : "transactions",
"_id" : "hVOQeGoBgL6DDzH6-9ay",
"_score" : 0.0,
"_source" : {
"price" : 30000,
"color" : "green", ---颜色是green
"make" : "ford",
"sold" : "2014-05-18"
}
},
{
"_index" : "cars",
"_type" : "transactions",
"_id" : "ilOQeGoBgL6DDzH6-9ay",
"_score" : 0.0,
"_source" : {
"price" : 25000,
"color" : "blue", ---颜色是blue
"make" : "ford",
"sold" : "2014-02-12"
}
}
]
},
"aggregations" : {
"colors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "blue",
"doc_count" : 1
},
{
"key" : "green",
"doc_count" : 1
}
]
}
}
}
第三种:用查询结果生成聚合结果,然后返回聚合结果和被再次过滤后的查询结果
- 第三种返回的聚合结果与前面两种一模一样,都是品牌为ford的汽车的颜色;
- 聚合结果生成完毕后,查询结果可以根据post_filter参数的设置而被再次过滤,例如品牌为ford的汽车,可以把其他颜色过滤掉,只要蓝色的,然后再返回,此时返回的查询结果中就只有品牌为ford并且颜色是blue的文档,具体的DSL如下:
GET /cars/transactions/_search
{
"query": {
"bool": {
"filter": {
"term": {
"make": "ford"
}
}
}
},
"post_filter": { ---在生成了聚合结果后,对即将返回的查询结果做一次过滤
"term": {
"color": "blue" ---过滤条件是只保留color字段为blue的文档
}
},
"aggs": {
"colors": {
"terms": {
"field": "color"
}
}
}
}
- 返回数据如下,可见聚合结果和前面两种一模一样,只是查询结果中只有color字段为blue的文档:
{
"took" : 13,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 0.0,
"hits" : [
{
"_index" : "cars",
"_type" : "transactions",
"_id" : "ilOQeGoBgL6DDzH6-9ay",
"_score" : 0.0,
"_source" : {
"price" : 25000,
"color" : "blue",
"make" : "ford",
"sold" : "2014-02-12"
}
}
]
},
"aggregations" : {
"colors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "blue",
"doc_count" : 1
},
{
"key" : "green",
"doc_count" : 1
}
]
}
}
}
- 至此,post_filter的基本功能已介绍完毕,如果只做查询不做聚合,post_filter的作用和我们常用的filter是类似的,但由于post_filter是在查询之后才会执行,所以post_filter不具备filter对查询带来的好处(忽略评分、缓存等),因此,在普通的查询中不要用post_filter来替代filter;