第三阶段 24 · filter / filters 桶聚合

24 · filter / filters 桶聚合

阶段:第三阶段 / 聚合能力

ES:filter / filters agg | PostgreSQL:COUNT(*) FILTER (WHERE ...)


1. 概念

  • filter 聚合:只对满足某条件的文档 做子聚合,相当于 SQL 的 FILTER (WHERE ...)
  • filters 聚合(复数):一次定义多个命名条件桶 ,各自统计,
    相当于在一个查询里同时算多个 FILTER 分类。

2. PostgreSQL 对照

sql 复制代码
-- 一次查询里分别统计不同条件
SELECT
  COUNT(*) FILTER (WHERE geo_cd = 'AP')            AS ap_cnt,
  COUNT(*) FILTER (WHERE geo_cd = 'EMEA')          AS emea_cnt,
  SUM(net_amount) FILTER (WHERE net_amount > 1000) AS big_total
FROM salesdata;

3. ES DSL

单条件 filter

复制代码
GET salesdata_idx/_search
{
  "size": 0,
  "aggs": {
    "big_orders": {
      "filter": { "range": { "net_amount": { "gt": 1000 } } },
      "aggs": {
        "total": { "sum": { "field": "net_amount" } }
      }
    }
  }
}

多命名桶 filters

复制代码
GET salesdata_idx/_search
{
  "size": 0,
  "aggs": {
    "by_geo_group": {
      "filters": {
        "filters": {
          "ap":   { "term": { "geo_cd": "AP" } },
          "emea": { "term": { "geo_cd": "EMEA" } }
        }
      },
      "aggs": {
        "total": { "sum": { "field": "net_amount" } }
      }
    }
  }
}

返回 ap / emea 两个命名桶,各带 doc_count 和子聚合。


4. Spring Boot 实现

java 复制代码
@Component
public class Doc24FilterAgg {

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    /** 多命名条件桶:分别统计各 geo 的金额 */
    public Map<String, Double> totalByNamedGeo(String indexName) throws IOException {
        Map<String, Query> named = Map.of(
                "ap",   Query.of(q -> q.term(t -> t.field("geo_cd").value("AP"))),
                "emea", Query.of(q -> q.term(t -> t.field("geo_cd").value("EMEA"))));

        SearchResponse<Void> resp = elasticsearchClient.search(s -> s
                .index(indexName)
                .size(0)
                .aggregations("by_geo_group", a -> a
                        .filters(f -> f.filters(fl -> fl.keyed(named)))
                        .aggregations("total", sub -> sub.sum(su -> su.field("net_amount")))),
                Void.class);

        Map<String, Double> result = new LinkedHashMap<>();
        var buckets = resp.aggregations().get("by_geo_group").filters().buckets().keyed();
        buckets.forEach((name, bucket) ->
                result.put(name, bucket.aggregations().get("total").sum().value()));
        return result;
    }
}

import:co.elastic.clients.elasticsearch._types.query_dsl.Query

命名(keyed)桶用 .buckets().keyed() 取到 Map<String, Bucket>


5. 坑与最佳实践

  1. filter vs queryfilter 聚合作用在聚合层,query 作用在整个搜索层;
    只想影响某个子统计用 filter 聚合。
  2. filtersother_bucket :可开启 other_bucket 收集不匹配任何条件的文档。
  3. keyed 结果更好读:用命名桶(keyed)比匿名数组桶更方便取值。
  4. 条件多时性能:每个 filter 都要过一遍,条件极多时注意开销。
  5. 组合 bool :filter 里可以放 bool,实现复杂多条件分类。

阶段小结

第三阶段完成后,你应该能把常见 SQL 聚合翻译成 ES:

GROUP BYtermsdate_truncdate_histogram、多级分组→嵌套子聚合、

FILTER (WHERE ...)filter/filters

下一篇

27-pipeline-管道聚合.md(对聚合结果再算一层:累计/环比/移动平均)。


← 上一篇:23-嵌套与子聚合总览下一篇:25-nested-嵌套对象与查询 →

相关推荐
倒流时光三十年7 小时前
第二阶段 17 · multi_match 多字段匹配
es
倒流时光三十年11 小时前
第三阶段 23 · 嵌套与子聚合 / top_hits
es
倒流时光三十年11 小时前
第二阶段 19 · _source 字段裁剪
es
倒流时光三十年2 天前
第二阶段 15 · prefix / wildcard / fuzzy 模糊匹配
es
粗体鱼2 天前
RAG/Agent 记忆混合检索多路召回:RRF 算法与Chunk RRF、Document RRF如何决策TopK
postgresql·milvus·es·rag·rff·mermory
huisheng_qaq2 个月前
【项目篇-01】Vmware虚拟机和环境安装配置
redis·mysql·canal·rocketmq·es·vaware虚拟机
JAVA面经实录9172 个月前
Elasticsearch 完整版完整知识体系
java·elasticsearch·搜索引擎·es
代码讲故事2 个月前
在没有kibana的ES(elasticsearch)线上生产环境集群中,如何通过命令行修改或增加字段而不需要reindex?
大数据·elasticsearch·搜索引擎·命令行·es·索引·模版
yurenpai(27届找实习中)2 个月前
Elasticsearch 核心总结 + 面试题实战(黑马点评项目)
redis·es