Date range aggregation

Date range aggregation #

A multi-bucket aggregation like range, but for date fields. Each bucket is a from/to period you name — “before the launch”, “launch week”, “after” — which is the shape reporting questions naturally take, rather than the uniform grid of date_histogram.

How it works #

Bounds are epoch milliseconds: from accepts a number (or a numeric string) and is inclusive, to is exclusive, and either may be omitted for an open-ended period. Date strings like "2024-06-04" are not parsed by the aggregation — convert them to epoch millis first (the teaching examples below show the constants).

Every declared range produces exactly one bucket, in declaration order, even when zero documents match. The bucket key is your label verbatim, or — when omitted — the built form "<from>-<to>" with * for unbounded sides and whole numbers rendered without a decimal tail ("*-1717459200000"). Documents missing the field are skipped unless missing supplies a default epoch-millis value, which drops them into whichever period covers it.

Example #

Split logs at a mid-June cutoff (epoch millis 1718534400000, i.e. 2024-06-16T10:40:00Z):

The teaching logs dataset ends on June 7, so that cutoff matches no document — the query scope is empty and both buckets return doc_count: 0, which is the point worth noting: declared ranges always materialize. A split inside the data window behaves as you would expect:

POST /logs/_search
{
  "size": 0,
  "aggs": {
    "by_period": {
      "date_range": {
        "field": "date",
        "ranges": [
          { "key": "early", "to": 1717459200000 },
          { "key": "late",  "from": 1717459200000 }
        ]
      }
    }
  }
}

1717459200000 is 2024-06-04T00:00:00Z; of the 150 teaching logs, 72 fall before it and 78 from it onward:

{
  ...
  "aggregations": {
    "by_period": {
      "buckets": [
        { "key": "early", "doc_count": 72 },
        { "key": "late",  "doc_count": 78 }
      ]
    }
  }
}

Use cases #

  • Before/after an event. Split at the launch timestamp, the incident time, or the migration cutover, and nest a sum or avg per period to compare behavior across it.
  • Named reporting periods. Fiscal quarters, holiday windows, retention cohorts — periods with business names, not equal widths.
  • Open-ended cohorts. "before", "between", "ever since" map directly onto omitted from/to sides.
  • Missing-value handling. missing folds documents without the date field into a chosen period instead of silently dropping them from every bucket.

Parameters for date_range #

  • field
    (Required, string) Date field you wish to aggregate.
  • ranges
    (Required, object array) Buckets with optional from / to (epoch millis numbers or numeric strings; from inclusive, to exclusive). key names a bucket.
  • missing
    (Optional, number) Epoch-millis value substituted for documents without the field.
  • format
    (Optional, string) Date format for rendering key_as_string. Accepted for compatibility; the engine builds keys from the bounds as described above.
  • aggs
    (Optional, object) Nested sub-aggregations, computed per bucket.
  • range — the numeric-field sibling.
  • date_histogram — equal calendar buckets when you want a timeline rather than named periods.
  • ip_range — bands and CIDR masks over IP fields.
  • Range query — filter documents by a date window instead of bucketing them.
Calendar October 6, 2026
Edit Edit this page