Histogram aggregation

Histogram aggregation #

A multi-bucket aggregation over numeric fields that applies a fixed interval to build the buckets. It answers “how are the values distributed?” — every document lands in exactly one bucket, and the bucket keys form a regular grid over the value axis.

How it works #

Each document’s value v is placed by index arithmetic — bucket index floor((v - offset) / interval) — so a bucket covers the half-open window [key, key + interval). Buckets accumulate in a sorted map, and the response lists them by ascending key.

With the default min_doc_count: 0, every bucket between the data minimum and maximum is materialized — gaps inside the data extent come back with doc_count: 0 (the fill is capped at 65,536 buckets). extended_bounds may only widen that fill range, never shrink it; hard_bounds clips it, drops buckets that fall entirely outside [min, max], and force-materializes the full in-window grid even at zero documents. offset slides the whole grid along the value axis (boundaries become k · interval + offset).

Each bucket reports key (the window’s lower bound, a number) and doc_count; the columnar path additionally renders key_as_string by reading the key as epoch milliseconds in strict ISO form — meaningful when the numeric field actually stores dates.

Examples #

Prices are 15/30/45/60, so an interval of 25 makes three buckets — HISTOGRAM(price, 25) on the SQL side:

POST /sales/_search
{
  "aggs": {
    "prices": {
      "histogram": {
        "field": "price",
        "interval": 50
      }
    }
  }
}

Response:

{
  ...
  "aggregations": {
    "prices": {
      "buckets": [
        { "key": 0.0,  "doc_count": 110 },
        { "key": 50.0, "doc_count": 10 }
      ]
    }
  }
}

Parameters for histogram #

  • field
    (Required, string) Field you wish to aggregate (must be numeric).
  • interval
    (Required, number) Bucket width.
  • offset
    (Optional, number, default: 0.0) Shift the bucket boundaries.
  • min_doc_count
    (Optional, integer, default: 0) Only return buckets with at least this many documents.
  • hard_bounds
    (Optional, object) Restrict bucket generation to [min, max) — buckets fully outside are dropped even with min_doc_count: 0.
  • extended_bounds
    (Optional, object) Widen the bucket range beyond the data extent (empty buckets are materialized up to these bounds when min_doc_count is 0). Can only extend, never shrink below the data extent.
  • aggs
    (Optional, object) Nested sub-aggregations, computed per bucket.

Range fields #

Histograms (and date histograms) accept _range field types (integer_range, long_range, double_range, date_range, …): a document’s stored range counts in EVERY bucket it overlaps — the bucket window [from, to) and the stored range intersect — rather than in the single bucket holding its midpoint. Open-ended sides ({"lte": 10}, {"gte": 15}) extend to the histogram’s hard_bounds window, which also force-materializes every in-window bucket even at zero documents.

POST /my-collection/_search
{
  "size": 0,
  "aggs": {
    "histo": {
      "histogram": {
        "field": "range",
        "interval": 1,
        "hard_bounds": { "min": 0, "max": 20 }
      }
    }
  }
}

Use cases #

  • Price-band analysis. Fixed-width bands over a price-style field are the backbone of merchandising reports. Over the teaching dataset’s 120 sales an interval of 50 yields two buckets — 110 sales under 50, 10 at 50 and above.
  • Distribution sketches. Where do values cluster, and how thick is the tail? A histogram with a small interval is the cheapest density estimate the engine offers; follow up with percentiles for exact quantiles.
  • Time-as-number timelines. Fields stored as epoch milliseconds can be bucketed directly (the fast path renders each key_as_string as an ISO timestamp); for calendar-aware windows prefer the date histogram.
  • Zero-filled chart axes. min_doc_count: 0 plus extended_bounds produces the contiguous, gap-free bucket series a charting front-end needs, without padding client-side.
  • date_histogram — the same fixed-grid idea over date fields, with calendar-aware intervals.
  • auto_date_histogram — you pick the bucket count, the engine picks the interval.
  • variable_width_histogram — bucket edges adapt to the data instead of staying fixed.
  • range — a handful of hand-named, possibly overlapping bands instead of a regular grid.
Calendar October 6, 2026
Edit Edit this page