文章三十一:ElasticSearch 管道聚合

为什么使用管道聚合:

Elasticsearch 基础聚合主要分为指标聚合和桶聚合两类,仅能实现对原始文档的分组、基础数值统计,满足常规的数据分析需求,但在复杂业务统计、时序数据复盘、指标二次计算等场景中存在明显短板。而**管道聚合(Pipeline Aggregation)**的出现,完美弥补了基础聚合的缺陷,也是我们业务开发中必须使用它的核心原因。

首先,基础聚合仅能直接扫描原始文档数据,完成单次计算,无法对聚合后的结果进行二次加工。例如我们可以通过桶聚合按天统计每日订单销售额,通过指标聚合算出单日总额,但无法直接在ES中完成累计销售额、日环比、整体均值、峰值筛选等衍生计算,传统方案只能将聚合结果返回客户端,通过代码二次处理,不仅增加接口网络传输开销、客户端计算压力,还会导致数据统计口径不统一、可视化联动性差的问题。

其次,管道聚合的核心特性是基于已有聚合结果计算,不重复扫描原始文档 ,所有衍生计算均在ES协调节点完成。它依托 buckets_path 关联上层聚合的桶数据和指标数据,实现一次查询、多层统计,大幅提升复杂数据分析的效率,减少系统资源消耗。

最后,针对时序数据统计、动态桶筛选、自定义业务指标计算等高频场景,管道聚合提供了原生能力支持。无论是时间维度的累计统计、趋势平滑、差值环比,还是基于阈值的桶过滤、多指标组合运算,都可以通过管道聚合原生实现,无需额外开发代码,极大简化了数据分析的开发成本。

简单总结:基础聚合负责「原始数据的分组和基础统计」,管道聚合负责「聚合结果的二次分析和高阶衍生计算」,二者搭配才能完成完整的业务数据分析。

常见的数值类型的管道聚合:

同级管道聚合与上层桶聚合平级,作用于所有桶的整体数据,用于统计全部分组指标的全局数值,输出全局汇总结果。

2.1.1 avg_bucket 桶均值聚合

遍历所有聚合桶的指定指标,计算全部桶数值的平均值。常用于统计日均销量、平均单量、平均访问量等全局均值指标。

2.1.2 sum_bucket 桶总和聚合

累加所有聚合桶的指定指标数值,得到全局总和。区别于基础sum聚合,它是对分组后的统计结果再次求和,可用于核对分段数据总汇总值。

2.1.3 max_bucket / min_bucket 桶极值聚合

遍历所有桶指标,筛选出数值最大/最小的桶及对应数值。可快速定位销量峰值、低谷、流量最高时段等极值场景。

2.1.4 stats_bucket / extended_stats_bucket 桶统计聚合

stats_bucket 一次性返回所有桶指标的 count、min、max、avg、sum 基础统计结果;extended_stats_bucket 在其基础上新增方差、标准差、变异系数等高级统计数据,适用于数据波动分析、异常数据研判场景。

2.2 父级数值管道聚合(单桶衍生计算)

父级管道聚合嵌套在桶聚合内部,针对每一个单独的桶做个性化数值衍生计算,为每个分组新增自定义指标,是时序数据分析的核心工具。

2.2.1 cumulative_sum 累计和聚合

最常用的数值管道聚合,按照桶的排序规则(默认时间正序),从第一个桶开始逐桶累加,生成当前桶的累计总值。核心用于统计年度累计销售额、累计用户数、累计交易量等时序累计指标,支持空桶补零、跳过空桶两种策略,保证数据连续性。

2.2.2 derivative 差值/环比聚合

计算相邻两个桶的指标差值,也就是一阶导数,可快速实现日环比、月环比、数据波动差值统计,精准捕捉数据增减变化幅度,适配业务数据异动监控场景。

2.2.3 moving_fn Moving Function 移动函数管道聚合

通过自定义滑动窗口大小,对连续多个桶的数值做平均计算,平滑时序数据的噪声、抹平短期数据波动,常用于流量、销量、监控指标的趋势分析,让数据走势更直观。

功能说明 Groovy 脚本写法
滑动求和 MovingFunctions.sum(values)
滑动平均值 MovingFunctions.mean(values)
滑动最大值 MovingFunctions.max(values)
滑动最小值 MovingFunctions.min(values)
滑动中位数 MovingFunctions.median(values)

使用案例展示:

java 复制代码
GET sales_data/_search
{
  "size": 0,
  "aggs": {
    "terms_order_date": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "day"
      },
      "aggs": {
        "scale_stats":{
          "stats": {
            "field": "sales"
          }
        },
        
        "pipeline_move":{
          "moving_fn": {
            "buckets_path": "scale_stats.sum",
            "window": 2,
            "script": "MovingFunctions.min(values)"
          }
        }
      }
    }
  }
}
2.2.4 bucket_script 桶自定义公式聚合

支持通过Painless脚本,对同一个桶内的多个指标做自定义四则运算,是灵活性最高的数值聚合。可实现各类复杂业务指标计算,例如毛利率、转化率、客单价、增长率等衍生指标,解决固定聚合无法满足的个性化统计需求。

java 复制代码
GET sales_data/_search
{
  "size": 0,
  "aggs": {
    // 按天做时间分桶(桶聚合,必须有桶才能用管道聚合)
    "terms_order_date": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "day"
      },
      "aggs": {
        // 先统计每个桶内 sales 的 min、max、avg、sum 等基础指标
        "scale_stats": {
          "stats": {
            "field": "sales"
          }
        },

        // bucket_script:父级管道聚合,桶内自定义公式计算
        "pipeline_diff": {
          "bucket_script": {
            // 👉 buckets_path:专门【定义变量】
            // 格式:"自定义变量名" : "上层聚合名称.子指标"
            "buckets_path": {
              // 定义变量 number,接收 scale_stats 聚合里的 min 值
              "number": "scale_stats.min"
            },

            // 👉 script:用上面定义的变量写自定义计算公式
            // 通过 params.自定义变量名 取值运算
            "script": {
              "source": "params.number * 10"
            }
          }
        }
      }
    }
  }
}
bucket_selector设置删选条件
java 复制代码
GET sales_data/_search
{
  "size": 0,
  "aggs": {
    "terms_order_date": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "day"
      },
      "aggs": {
        "scale_stats": {
          "stats": {
            "field": "sales",
            "missing": 0
          }
        },
        "pipeline_diff": {
          "bucket_selector": {
            "buckets_path": {
              "number": "scale_stats.min"
            },
            "script": {
              "source": """
                params.number>10
              """
            }
          }
        }
      }
    }
  }
}
2.2.5 差值计算serial_diff

获取桶之间的差值serial_diff

java 复制代码
GET sales_data/_search
{
  "size": 0,
  "aggs": {
    "terms_order_date": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "day"
      },
      "aggs": {
        "scale_stats":{
          "stats": {
            "field": "sales"
          }
        },
        "pipeline_diff":{
          "serial_diff": {
            "buckets_path": "scale_stats.sum",
            "lag": 2  #指定是和第几个比较差值
          }
        }
      }
    }
  }
}
2.2.6percentiles_bucket百分位获取
java 复制代码
GET sales_data/_search
{
  "size": 0,
  "aggs": {
    "terms_order_date": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "day"
      },
      "aggs": {
        "scale_stats":{
          "stats": {
            "field": "sales",
            "missing": 0
          }
        },
        "pipeline_diff":{
          "moving_fn": {
            "script": "MovingFunctions.sum(values)",
            "buckets_path": "scale_stats.sum",
            "window": 2,
            "gap_policy": "insert_zeros"
          }
        }
      }
    },
    "pipeline_percent":{
      "percentiles_bucket": {
        "buckets_path": "terms_order_date>scale_stats.max"
      }
    }
  }
}
2.2.7 bucket_sort排序

必须写在子聚合中

java 复制代码
GET sales_data/_search
{
  "size": 0,
  "aggs": {
    "terms_order_date": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "day"
      },
      "aggs": {
        "scale_stats": {
          "stats": {
            "field": "sales",
            "missing": 0
          }
        },
        "sum_bucket_sort": {
          "bucket_sort": {
            "sort": [
              {
                "scale_stats.sum":{
                  "order":"desc"
                }
              }
              ]
          }
        }
      }
    }
  }
}
总体使用案例展示:

数据形式:

java 复制代码
 {
        "_index": "sales_data",
        "_id": "aXPvM54B9Gv8TwgHTtwV",
        "_score": 1,
        "_source": {
          "order_date": "2025-01-02",
          "product_type": "电脑",
          "sales": 1500,
          "cost": 800
        }
      }
java 复制代码
GET sales_data/_search
{
  "size": 0,
  "aggs": {
    "group_by_date": {
      "date_histogram": {
        //创建直方图
        "field": "order_date",
        "calendar_interval": "day"
      },
      "aggs": {
        //计算每天的钱
        "daily_sales": { 
          "sum": { 
            "field": "sales" 
          }
        },
        "daily_cost": { "sum": { "field": "cost" } },
        
        // 父级管道(每天内部计算)
        "cumulative_sales": {
          "cumulative_sum": { "buckets_path": "daily_sales" }
        },
        "sales_chain": {
          "derivative": { "buckets_path": "daily_sales" }
        }
      }
    },
    
    // 同级管道(全局汇总计算)
    "total_days_avg": {
      "avg_bucket": { "buckets_path": "group_by_date>daily_sales" }
    },
    "total_sales_all": {
      "sum_bucket": { "buckets_path": "group_by_date>daily_sales" }
    },
    "max_day_sales": {
      "max_bucket": { "buckets_path": "group_by_date>daily_sales" }
    },
    "min_day_sales": {
      "min_bucket": { "buckets_path": "group_by_date>daily_sales" }
    },
    "all_stats": {
      "stats_bucket": { "buckets_path": "group_by_date>daily_sales" }
    }
  }
}
相关推荐
Full Stack Developme1 小时前
Spring 发展历史
java·后端·spring
组合缺一1 小时前
Java 流程编排新范式 Solon Flow:一个引擎,七种节点,覆盖规则/任务/工作流/AI 编排全场景
java·spring·ai·solon·workflow·flow
largecode2 小时前
企业号码认证可以线上办理吗?支持线上申请,设置来电显示品牌名
java·python·智能手机·微信公众平台·facebook·paddle·新浪微博
humcomm2 小时前
2026年 Java 面试新特点
java·开发语言·面试
lili00122 小时前
CC GUI 插件架构剖析:如何为 JetBrains IDE 打造完整的 AI 编程工作台
java·ide·人工智能·python·架构·ai编程
Royzst2 小时前
学生信息管理案例
java
爱棋笑谦2 小时前
单元测试简述
java
音符犹如代码2 小时前
Docker 一键部署带有 TimescaleDB 插件的 PostgreSQL
java·运维·数据库·后端·docker·postgresql·容器
sleepcattt2 小时前
Java反射技术
java