Range aggregation

Range aggregation #

A multi-bucket aggregation over numeric fields where each bucket is a user-defined range. It answers “how many documents fall in each band?” — the bands are yours to name and place, unlike the regular grid of histogram.

How it works #

Each range is a half-open window: from is inclusive, to is exclusive, and either side may be omitted for unbounded. A document whose value v satisfies from <= v < to counts in that range — and because the test is per range, overlapping definitions put one document in several buckets at once.

Buckets come back in the order the ranges were declared, one per definition even at doc_count: 0. Each bucket reports key and doc_count: the key is your key verbatim when given, otherwise the engine builds it as "<from>-<to>" with * for unbounded sides ("*-50", "50-100", "100-*"). The engine does not echo separate from/to fields on each bucket — the bounds live in the composite key.

Examples #

POST /sales/_search
{
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 50 },
          { "from": 50, "to": 100 },
          { "from": 100 }
        ]
      }
    }
  }
}

Response (the teaching prices are 15/30/45/60 across 120 sales, so the top band is empty but still returned):

{
  ...
  "aggregations": {
    "price_ranges": {
      "buckets": [
        { "key": "*-50",   "doc_count": 110 },
        { "key": "50-100", "doc_count": 10 },
        { "key": "100-*",  "doc_count": 0 }
      ]
    }
  }
}

Use cases #

  • Price-band reports. low/mid/high labels read better on an executive dashboard than numeric edges — name the ranges with key and let the engine count. Over the teaching sales, under-50 holds 110 of 120 documents.
  • Rating or tier thresholds. Business rules are rarely grid-aligned (“under 3 stars”, “3 to 4.5”, “top”): express the rule once as ranges instead of deriving intervals.
  • Overlapping cohort windows. Ranges may overlap — “under 30”, “under 60”, “all” gives cumulative counts in a single response, something a histogram grid cannot do.
  • Bucketed filters for drill-down. Wrap a range in the query of a filter aggregation (or add sub-aggs per band) to power click-into-band dashboards.

Parameters for range #

  • field
    (Required, string) Field you wish to aggregate (must be numeric).
  • ranges
    (Required, array) Range definitions; each entry takes:
    • key — (Optional, string) Range label; generated from the bounds when omitted.
    • from — (Optional, number) Lower bound, inclusive. Omit for unbounded below.
    • to — (Optional, number) Upper bound, exclusive. Omit for unbounded above.
  • aggs
    (Optional, object) Nested sub-aggregations, computed per bucket.
  • date_range — the same band idea over date fields.
  • ip_range — bands and CIDR masks over IP fields.
  • geo_distance — distance rings around a point, the geo sibling of this family.
  • histogram — a regular grid when the bands do not need names.
  • Range query — filter documents by bounds instead of bucketing them.
Calendar October 6, 2026
Edit Edit this page