Bucket script aggregation

Bucket script aggregation #

A parent pipeline aggregation computing arithmetic on a bucket’s own metrics — the script references sibling metrics by name through params and its result becomes a new per-bucket value. It is the SQL SELECT a / b AS c of the aggregation world: one derived column, evaluated once per bucket over values the bucket already holds.

"avg_order": {
  "bucket_script": {
    "buckets_path": { "r": "revenue", "n": "orders" },
    "script": "params.r / params.n"
  }
}

How it works #

bucket_script is declared inside the multi-bucket aggregation it decorates, next to the metrics it consumes. After the buckets are built, the engine resolves each named path against every bucket, binds the numbers into the script’s scope, evaluates the script once, and inserts its result under the pipeline’s own name — the response shape buckets[i].avg_order.value.

Details verified against the engine:

  • buckets_path is a map of script variable names to sibling metric paths: { "r": "revenue", "n": "orders" }. A .value suffix and "_count" (the bucket’s document count) also resolve. A plain string instead of a map binds a single variable named _value.
  • Each variable is reachable two ways — params.r and bare r. Values arrive as floats.
  • A gap (a named metric missing in that bucket) yields a null value; the bucket itself stays in the response.
  • A runtime failure or a non-numeric result also yields null for that bucket — never a dropped bucket, never a failed request.
  • The script’s last expression is its result; integer division truncates (5 / 2 is 2), so keep a .0 on literals — see pipeline scripts.
  • A second, engine-specific placement also works: declaring bucket_script next to the bucket aggregation, with full parent>metric paths ({ "s": "by_cat>sum_p" }), returns a standalone buckets array carrying one value per bucket; under the default skip policy, buckets with gaps are dropped from that output.

Examples #

The teaching dataset sells exactly once per day, so every day’s avg_order equals that day’s single amount — the more interesting shape appears over terms buckets: per product type the four revenues 1800, 1800, 1350, 1800 over 60, 30, 20, 10 orders give average order values of 30, 60, 67.5, 180.

Use cases #

  • Average order value — revenue / orders per bucket, the classic derived commerce metric, without post-processing.
  • Ratios and rates — conversion (converted / total), error rate (errors / requests), fill rate — any numerator/denominator pair the bucket already computed.
  • Unit economics — revenue / quantity for price-per-unit, margin expressions when cost lives in another sub-agg.
  • Threshold flags — if params.r > 1000 { 1.0 } else { 0.0 } marks buckets for downstream counting or alerting.
  • Scripted quality scores — weighted combinations (0.4 * params.a + 0.6 * params.b) that rank buckets by design.

Parameters for bucket_script #

  • buckets_path
    (Required, object) Map of script variable names to sibling metric paths ({ "r": "revenue" }); a plain string binds _value.
  • script
    (Required, string) Expression over params.<name> (or the bare <name>) — see pipeline scripts.
  • gap_policy
    (Optional, string) skip (default) or insert_zeros — effective in the sibling placement; nested, a gap always yields a null value and keeps the bucket.
  • bucket_selector — the same machinery as a keep/drop predicate instead of a metric
  • moving_fn — scripts over a window of buckets rather than one bucket’s siblings
  • pipeline scripts — the script language reference
Calendar October 6, 2026
Edit Edit this page