OriginChainDB
Elasticsearch clients

Aggregations

An aggregation summarizes the documents that match a query. It can count groups, add values or arrange results by date. It summarizes the full match set, not only the page of returned hits. Use size: 0 when you need only the summary.

The examples use the Elasticsearch-compatible API. Check the supported aggregation types before reusing an existing request.

Turn matching documents into a summaryAggregations use the matching document set, not just the visible page of hits.
  1. Select the documentsApply the query or filters for the report.
  2. Choose grouping fieldsUse supported keyword, numeric, date or boolean fields.
  3. Calculate the summaryCount buckets or compute the requested measurements.
  4. Check a known totalCompare a small sample and confirm time-bucket boundaries.

Group by a field

A terms aggregation needs a field it can group on: keyword, numeric, date or boolean. On a text field use the .keyword sub-field — see indexing.

await es.search({
    index: 'shop.products',
    body: {
        size: 0,
        aggs: { by_brand: { terms: { field: 'brand' } } }
    }
})

Metrics

avg, sum, min, max, value_count, stats, extended_stats and percentiles are supported on their own or inside a bucket.

aggs: {
  avg_price: { avg: { field: 'price' } },
  spread:    { percentiles: { field: 'price' } },
  by_brand:  { terms: { field: 'brand' },
    aggs: { avg_price: { avg: { field: 'price' } } } }
}

missing is refused, not filled in

missing on a metric aggregation asks us to substitute a value for documents that do not have the field. We refuse it rather than fold in a number you did not choose, because the result would look like data and be an assumption.

Bucket by time

A date_histogram groups documents into calendar buckets. The field can hold an ISO-8601 string or epoch milliseconds — both bucket the same way. Buckets are UTC and calendar-aligned, so pass calendar_interval.

aggs: { per_day: { date_histogram: {
  field: 'placed_at',
  calendar_interval: 'day',     // minute hour day week month quarter year
  time_zone: 'UTC'
} } }

fixed_interval is refused: an arbitrary fixed width has no calendar-aligned equivalent here, and approximating it would put your documents in the wrong buckets.

Named filter buckets

One bucket per named query, counting the documents that match both your search and that filter, with sub-aggregations over exactly those documents. other_bucket_key adds a bucket for everything that matched none of them.

aggs: { by_price: {
  filters: {
    other_bucket_key: 'mid_range',
    filters: {
      clearance: { range: { price: { lt: 50 } } },
      premium:   { range: { price: { gte: 200 } } }
    }
  },
  aggs: { avg_price: { avg: { field: 'price' } } }
} }

send filters in its own request

A filters aggregation is answered with one search per filter, which is a different shape of work from the single pass every other aggregation takes. Combining it with other top-level aggregations in one request is refused, so that a request that looks like one search cannot quietly become several.

The documents behind a bucket

top_hits returns the best matching documents with their _source. It works at the top level, or nested so each bucket carries its own examples.

// three best matches overall
aggs: { best: { top_hits: { size: 3 } } }

// one example per brand, most expensive first
aggs: { by_brand: { terms: { field: 'brand' },
  aggs: { top: { top_hits: { size: 1, sort: [{ price: 'desc' }] } } } } }

Pipelines

cumulative_sum and derivative post-process the buckets of the aggregation they sit in — a running total or a rate of change over a date histogram.

aggs: { per_day: { date_histogram: { field: 'placed_at', calendar_interval: 'day' },
  aggs: {
    revenue: { sum: { field: 'total' } },
    running: { cumulative_sum: { buckets_path: 'revenue' } }
  } } }

What is not answered

  • composite and nested have no executor yet and are refused rather than partially answered.

  • An aggregation that names no field is refused — except a bare top_hits, which legitimately needs none.

  • A field that was never indexed comes back as an empty aggregation with your hits intact, not as a failed search.

  • Correct the query first — Did-you-mean over the words actually indexed.

  • See it applied — Catalog facets, log volume over time, transaction summaries.

Elasticsearch is a trademark of Elasticsearch B.V., registered in the U.S. and in other countries. OriginChainDB is not affiliated with, endorsed by, or sponsored by Elasticsearch B.V. OriginChainDB implements a compatible HTTP API so that existing Elasticsearch clients can talk to it; it does not distribute Elasticsearch software.

© 2026 Silicoyn Technologies Pvt Ltd

On this page