Lesson 4 — Aggregations #
From counts to histograms to composite paging — over sales and logs,
with known answers you can verify by hand.
The use case #
Dashboards, reports, and faceted navigation — wherever the deliverable is not a page of hits but an answer. The questions this lesson answers:
- “What is the average / min / max price?” — metric aggregations.
- “How is revenue split by status?” — terms buckets.
- “Volume per week / per price band” — histograms.
- “Every (merchant, day) pair, complete — not just the top N” —
composite aggregations with
aftercursors. - “Just the number, skip the hits” —
size: 0.
Metric aggregations #
One number out:
sum,
avg, min, max,
stats (all of them at once),
cardinality (distinct
values):
Four distinct product types; stats gives you min/max/sum/avg/count in one
round trip.
The same question as SQL — global aggregates are the M1 subset’s scalar lane (see the SQL reference):
120 sales, prices 15–60, four distinct types — one row, no size: 0 to
remember.
Bucket aggregations #
terms builds a bucket per value —
the dataset’s known answer is t-shirt 60, hat 30, mug 20, sticker 10:
As SQL that is one GROUP BY — order by the count to get the terms
ordering:
Same answer, column names instead of a bucket tree: t-shirt 60, hat 30, mug 20, sticker 10.
One bucket per value — every document falls into exactly one, and the count is the answer:
graph LR subgraph sales["the 120 sales"] d1("t-shirt") d2("t-shirt") d3("hat") d4("mug") d5("sticker") end d1 -->|"type"| b1["t-shirt — 60"] d2 -->|"type"| b1 d3 -->|"type"| b2["hat — 30"] d4 -->|"type"| b3["mug — 20"] d5 -->|"type"| b4["sticker — 10"]
histogram slices a number into
fixed intervals;
date_histogram
slices time — 92 sales in 2024, 28 in 2025:
The SQL spelling is DATE_TRUNC as the grouping key:
92 rows land in the 2024 bucket, 28 in 2025 — the same split, as rows.
Sub-aggregations #
Every bucket runs its own aggs — revenue per type as sum of price
inside a terms bucket:
A sub-aggregation is just another column in SQL — the metric aggregates over each group:
Filtered aggregations #
filter scopes an aggregation to a
query;
filters builds named
buckets — the logs level split (error 25 / warn 25 / info 100):
One GROUP BY answers the same split in SQL (a WHERE scopes it first if
you need only part of the data):
error 25, info 100, warn 25 — sorted by the key, so the rows read like an index.
Paging through buckets #
composite pages through
multi-dimensional buckets with an after cursor — for dashboards, not top-10
lists:
SQL pages the same walk with a keyset cursor — LIMIT bounds the page,
the envelope’s cursor continues it (the full playground’s
SQL mode carries the Next-page button):
What you learned #
- Metric aggs summarize; bucket aggs group; sub-
aggsnest. size: 0skips hit assembly — the aggregations ARE the result.composite+aftercursors for complete bucket walks.- Every aggregation on this page has a SQL spelling: global metrics are
scalar aggregates, buckets are
GROUP BY, sub-aggsare more columns, composite walks are cursor pages.
Next: Lesson 5 — graph traversal.