写点什么

理解 elasticsearch 的 post_filter

作者:程序员欣宸
  • 2022 年 9 月 16 日
    广东
  • 本文字数:2985 字

    阅读完需:约 10 分钟

理解elasticsearch的post_filter

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 《Elasticsearch 权威指南中,post_filter 出现在聚合章节,描述 post_filter 的作用为:只过滤搜索结果,不过滤聚合结果;

  • 描述比较简单,还是用实例来加深理解吧。

环境信息

  • 以下是本次实例的环境信息,请确保您的 Elasticsearch 可以正常运行:


  1. 操作系统:Ubuntu 18.04.2 LTS

  2. JDK:1.8.0_191

  3. Elasticsearch:6.7.1

  4. Kibana:6.7.1

实例数据

  • 查询用到的数据是个名为 cars 的索引,里面保存了多条汽车销售记录,字段有品牌(make)、颜色(color)、价格(price)、售卖时间(sold)等,在 elasticsearch-head 查看数据如下图:



假设一个查询聚合的需求

  • 对上述索引,假设我们的查询需求是:品牌为 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;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...


发布于: 刚刚阅读数: 3
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
理解elasticsearch的post_filter_elasticsearch_程序员欣宸_InfoQ写作社区