Elasticsearch Aggregations
Besides finding documents, Elasticsearch can summarize them. Aggregations compute analytics over the documents matching a query: counts per category, revenue per day, the 99th percentile latency per service, the number of unique users. They power faceted navigation in e-commerce search ("Brand: Acme (42)"), dashboards in Kibana, and log and metrics analytics, all computed in near real time over the same index you search.
Aggregations resemble SQL's GROUP BY (see SQL GROUP BY), but they're built from composable bucket and metric aggregations that can nest arbitrarily. Some of them, like cardinality, percentiles, and terms across shards, are approximate by design, trading exactness for speed at scale.
TL;DR
- Bucket aggregations group documents:
terms,date_histogram,histogram,range,filters,geo_*. - Metric aggregations compute values:
avg,sum,min,max,stats,cardinality,percentiles,top_hits. - Pipeline aggregations compute from other aggregations:
derivative,cumulative_sum,moving_fn,bucket_sort. - Aggregations run on doc values: use
keyword, numeric, and date fields, nottext. - For facets, combine aggregations with
post_filterso selected facets don't hide other options. - Use
compositeaggregations to page through all buckets, and setsize: 0when you only need aggregations.
Quick Example
A faceted product search, and an analytics query:
Core Concepts
Bucket Aggregations
Buckets can contain sub-aggregations, for example revenue per day per country.
Metric Aggregations
avg, sum, min, max, value_count, stats/extended_stats, percentiles, percentile_ranks, cardinality (distinct count), top_hits (sample documents per bucket), weighted_avg, geo_bounds, and more.
Pipeline Aggregations
These operate on other aggregations' outputs: derivative (change between buckets), cumulative_sum, moving_fn (moving averages), bucket_script (per-bucket formulas like conversion rate), bucket_selector (filter buckets, like SQL HAVING), and bucket_sort (sort or truncate buckets).
Doc Values and Field Types
Aggregations read doc values, a columnar on-disk structure built for keyword, numeric, date, boolean, IP, and geo fields. Aggregating on text fields requires fielddata (in-memory and expensive) and aggregates individual tokens, which is almost never what you want. Map aggregatable strings as keyword. See Elasticsearch mappings.
Faceted Search With post_filter
When a user selects "Brand: Acme", you want the result list filtered, but the brand facet should still show counts for other brands so the user can switch. post_filter applies after aggregations are computed:
- Aggregations see the query without the selected facet filter.
- Hits are filtered by it.
For multiple facet groups, use per-facet filter aggregations that apply all other selected facets, to get correct counts.
Approximation and Accuracy
termson sharded indices: each shard returns its topshard_sizeterms, so counts for long-tail terms can be slightly off. Responses reportdoc_count_error_upper_bound. Increaseshard_sizefor accuracy, or use a single shard for small indices.cardinalityuses HyperLogLog++: approximate, with configurableprecision_threshold(exact up to the threshold, then about 1% error).percentilesuse TDigest (or HDR histograms), so they're approximate, most accurate at the extremes.
Paging Through Buckets
terms returns the top N only. To iterate through all buckets (for example to export every customer's totals), use the composite aggregation with after_key pagination.
Best Practices
Set size: 0 for Pure Analytics
When you only need aggregation results, "size": 0 skips fetching and scoring hits. It's faster and lets aggregation results be cached (the shard request cache).
Filter Before Aggregating
Narrow the document set with filters (time range, tenant, status) in filter context. Aggregations over fewer documents are faster and more meaningful.
Pre-Aggregate High-Volume Data
For dashboards over billions of events, use transforms, rollups, or downsampling of time-series data streams to maintain summary indices, rather than aggregating raw events on every page load.
Watch Memory and Bucket Counts
High-cardinality terms aggregations with deep nesting can create enormous numbers of buckets. Elasticsearch caps it with search.max_buckets. Limit sizes, avoid nesting high-cardinality fields, and use composite for exhaustive iteration.
Common Mistakes
Aggregating a text Field
Using terms for Exact Unique Counts
The number of buckets from a terms aggregation isn't a distinct count, since it's truncated to size. Use cardinality for approximate distinct counts, or composite to enumerate them exactly.
Time Zone Mismatches in Date Histograms
Daily buckets default to UTC, so "sales per day" for a European shop splits days at the wrong hour. Set time_zone on date histograms and date ranges.
FAQ
How do Elasticsearch aggregations compare to SQL GROUP BY?
Bucket aggregations are like GROUP BY (and more flexible: ranges, histograms, filters), and metric aggregations are like aggregate functions (SUM, AVG, COUNT DISTINCT). They nest to arbitrary depth, and they can run in the same request as a full-text search. Elasticsearch also offers a SQL interface that translates into aggregations.
Why are my terms aggregation counts slightly wrong?
On multi-shard indices, each shard returns its own top terms, and the coordinating node merges them, so terms that were just below a shard's cutoff can be undercounted. Increase shard_size, check doc_count_error_upper_bound, or use fewer shards for small datasets.
How do I build facet counts that don't disappear when selected?
Run aggregations against the query without the facet's own selection, and apply selected facets through post_filter (for hits) plus filter aggregations for each facet's cross-filtering. That keeps counts for alternative options visible.
Are aggregations fast enough for real-time dashboards?
Generally yes for filtered time ranges and moderate cardinality: doc values and caching make them efficient. For very large datasets or high-cardinality breakdowns, use transforms or downsampled indices, or push heavy analytics to an OLAP store such as ClickHouse.
Related Topics
- Elasticsearch — The search engine overview
- Elasticsearch Query DSL — Queries that scope aggregations
- Elasticsearch Mappings — Aggregatable field types
- SQL GROUP BY & Aggregation — The relational equivalent
- Log Aggregation — Aggregations in log analytics
- ClickHouse — Columnar analytics at scale