Chapter 08 · Aggregations: Metrics, Buckets, Pipeline Aggregations, Cardinality, and Analytical Search

Metric Aggregations: min / max / avg / sum / stats / percentiles / cardinality and Approximation Tradeoffs

Learn which metric aggregations are exact, which are approximate, how percentiles and cardinality trade memory for accuracy, and how to validate analytical answers against a hand-computable AtlasMart fixture.

Intermediate100–120 minutesDistributed aggregation labElasticsearch 9.5.3 · OpenSearch 3.8.0Last reviewed: September 2026

Learning outcomes

AtlasMart’s search page now needs revenue summaries, unique-customer counts and latency percentiles next to search results. The dangerous shortcut is to read every number in an aggregation response as equally exact. Metric aggregations have different mathematical contracts: min, max, sum, avg and stats reduce exact numeric values for the matched documents, while cardinality deliberately estimates distinct counts and percentile algorithms deliberately trade bounded memory for quantile accuracy.

01

Distinguish exact simple metric reductions from approximate cardinality and percentile computations.

02

Calculate a hand-computable AtlasMart baseline before trusting the distributed response.

03

Explain precision_threshold and percentile compression as resource/accuracy controls rather than “make exact” switches.

04

Identify how shards, missing values, field mappings and scripts change the population being measured.

05

Design acceptance tests that compare invariants and tolerated approximation instead of freezing arbitrary decimals.

Chapter baseline reviewed 11 September 2026

The reproducible examples target self-managed Elasticsearch 9.5.3 and OpenSearch 3.8.0. The mandatory aggregation exercises use free/local REST APIs and portable field types. Aggregation names often look similar across products, but exact algorithms, defaults, error metadata, scripting behavior, circuit-breaker accounting and managed-service limits must be verified per target version. Elasticsearch and OpenSearch both document cardinality as approximate and cap precision_threshold at 40,000. OpenSearch 3.8 also documents a hybrid cardinality collector whose runtime strategy can vary with memory conditions; this is not a portable tuning knob to copy into Elasticsearch.

Execution and safety note

The generation environment does not provide live Elasticsearch/OpenSearch clusters, so requests are specified as deterministic labs and expected invariants rather than represented as captured output. Run them against the pinned local course clusters, record actual response sizes/timings/error metadata, and remove only the dedicated AtlasMart Chapter 08 indices. Do not use production data, production scripts, or unbounded bucket trees for the failure exercises.

1. Start from the population, not the aggregation name

Every metric answers a question over the documents that survive the query and all enclosing bucket scopes. Before asking for an average, define the population: all orders, only paid orders, one tenant, one date range, or one nested item category. If the population is wrong, an exact sum is still analytically wrong.

Metric Contract Typical risk
min / max Exact extrema over collected numeric values Missing/null semantics or wrong query population.
sum / avg / stats Exact arithmetic reduction over collected values Floating-point representation, multi-valued fields, scripts, or unexpected coercion.
percentiles Estimated quantiles Algorithm/compression choice and small-sample interpretation.
cardinality Approximate distinct count Treating the estimate as exact billing/compliance truth.
value_count Count of values, not unique values Confusing “number of values” with “number of documents” or distinct entities.
Dev Tools · create the Chapter 08 analytical fixture
DELETE atlasmart-orders-agg-v1
PUT atlasmart-orders-agg-v1
{
  "settings": {"number_of_shards": 2, "number_of_replicas": 0},
  "mappings": {
    "properties": {
      "order_id":    {"type":"keyword"},
      "customer_id": {"type":"keyword"},
      "order_date":  {"type":"date"},
      "region":      {"type":"keyword"},
      "channel":     {"type":"keyword"},
      "status":      {"type":"keyword"},
      "total":       {"type":"double"},
      "latency_ms":  {"type":"long"},
      "store":       {"properties":{"location":{"type":"geo_point"}}},
      "items": {
        "type":"nested",
        "properties": {
          "sku":{"type":"keyword"},
          "category":{"type":"keyword"},
          "qty":{"type":"integer"},
          "line_total":{"type":"double"}
        }
      }
    }
  }
}
Dev Tools · six hand-computable AtlasMart orders
POST atlasmart-orders-agg-v1/_bulk?refresh=true
{ "index": { "_id": "o1" } }
{ "order_id":"o1","customer_id":"c1","order_date":"2026-09-01T10:00:00Z","region":"north","channel":"web","status":"paid","total":120.0,"latency_ms":80,"store":{"location":{"lat":35.72,"lon":51.41}},"items":[{"sku":"p1","category":"audio","qty":1,"line_total":100.0},{"sku":"p5","category":"office","qty":1,"line_total":20.0}] }
{ "index": { "_id": "o2" } }
{ "order_id":"o2","customer_id":"c2","order_date":"2026-09-01T12:00:00Z","region":"south","channel":"mobile","status":"paid","total":80.0,"latency_ms":160,"store":{"location":{"lat":29.61,"lon":52.53}},"items":[{"sku":"p3","category":"audio","qty":1,"line_total":80.0}] }
{ "index": { "_id": "o3" } }
{ "order_id":"o3","customer_id":"c1","order_date":"2026-09-02T08:30:00Z","region":"north","channel":"web","status":"refunded","total":60.0,"latency_ms":250,"store":{"location":{"lat":35.69,"lon":51.39}},"items":[{"sku":"p6","category":"sports","qty":1,"line_total":60.0}] }
{ "index": { "_id": "o4" } }
{ "order_id":"o4","customer_id":"c3","order_date":"2026-09-02T13:20:00Z","region":"west","channel":"store","status":"paid","total":200.0,"latency_ms":110,"store":{"location":{"lat":34.80,"lon":48.51}},"items":[{"sku":"p8","category":"audio","qty":1,"line_total":160.0},{"sku":"p7","category":"accessories","qty":2,"line_total":40.0}] }
{ "index": { "_id": "o5" } }
{ "order_id":"o5","customer_id":"c4","order_date":"2026-09-03T09:15:00Z","region":"north","channel":"mobile","status":"paid","total":150.0,"latency_ms":95,"store":{"location":{"lat":36.26,"lon":59.62}},"items":[{"sku":"p4","category":"audio","qty":1,"line_total":130.0},{"sku":"p5","category":"office","qty":1,"line_total":20.0}] }
{ "index": { "_id": "o6" } }
{ "order_id":"o6","customer_id":"c2","order_date":"2026-09-03T18:40:00Z","region":"south","channel":"web","status":"cancelled","total":40.0,"latency_ms":310,"store":{"location":{"lat":31.90,"lon":54.36}},"items":[{"sku":"p7","category":"accessories","qty":2,"line_total":40.0}] }

2. Prove the exact metrics first

Dev Tools · exact metrics plus two approximate metrics
GET atlasmart-orders-agg-v1/_search
{
  "size": 0,
  "aggs": {
    "min_total": {"min":{"field":"total"}},
    "max_total": {"max":{"field":"total"}},
    "avg_total": {"avg":{"field":"total"}},
    "sum_total": {"sum":{"field":"total"}},
    "total_stats": {"stats":{"field":"total"}},
    "latency_pct": {"percentiles":{"field":"latency_ms","percents":[50,95,99]}},
    "unique_customers": {"cardinality":{"field":"customer_id","precision_threshold":100}}
  }
}
scratchpad · expected exact answers
For the six-document fixture, calculate these BEFORE running the API:
- total values: 120, 80, 60, 200, 150, 40
- min(total) = 40
- max(total) = 200
- sum(total) = 650
- avg(total) = 650 / 6 = 108.333...
- stats.count = 6
- exact unique customers = 4 (c1,c2,c3,c4)

Then compare API output.
The simple metrics should match exactly for this fixture.
The cardinality result may also happen to equal 4, but the aggregation's contract is approximate.
Percentiles are estimates: validate ordering/range properties, not a hand-invented frozen decimal.

For the simple metrics, compare the response to the hand calculation. For cardinality, record both the exact fixture answer and the returned estimate. A matching value on six documents proves the fixture, not that the algorithm becomes exact on large production sets.

3. Cardinality is intentionally approximate

Both products use a HyperLogLog++-style cardinality estimator. The point is bounded memory as distinct values grow, not exact set materialization. precision_threshold raises the range over which counts are expected to remain close to accurate, but it does not convert the algorithm into an exact DISTINCT operation. The documented maximum is 40,000; larger values do not keep buying precision.

Wrong mental model

“The fixture returned 4, so cardinality is exact.” That is a property of this tiny test instance, not the API contract.

For invoices, quotas, legal counts, uniqueness enforcement or money movement, use a system/process with the required exactness semantics. For faceting, dashboards and exploratory analytics, an approximate distinct count is often a better resource trade.

4. Percentiles summarize a distribution; they do not reveal it

p50, p95 and p99 are quantile estimates. They answer “roughly what value bounds this percentage of observations?” They do not tell you the number of samples, multimodality, outliers above p99, or service-level error budget. Always retain count and, for operational latency work, consider histograms or raw telemetry systems when stronger quantile semantics are required.

Dev Tools · compare two percentile configurations
GET atlasmart-orders-agg-v1/_search
{
  "size": 0,
  "aggs": {
    "default_pct": {"percentiles":{"field":"latency_ms","percents":[50,95,99]}},
    "higher_compression": {
      "percentiles": {
        "field":"latency_ms",
        "percents":[50,95,99],
        "tdigest":{"compression":200}
      }
    }
  }
}

Do not assert that the second request is “more correct” for every workload. It spends a different resource budget. Evaluate on representative data and record the algorithm/settings with the result.

5. Missing, multi-valued and scripted inputs change the measurement

An aggregation reads field values, not your business vocabulary. A missing total, a multi-valued numeric field, or a runtime/scripted value can change count and cost. Verify mappings with _field_caps/_mapping, define missing-value policy explicitly, and avoid turning expensive per-document scripts into the default analytics path merely because they make a prototype convenient.

Dev Tools · inspect field capabilities
GET atlasmart-orders-agg-v1/_field_caps?fields=total,latency_ms,customer_id,order_date

GET atlasmart-orders-agg-v1/_mapping

6. Acceptance tests: exact where exact, bounded where approximate

test plan
Exact fixtures:
  assert min_total == 40
  assert max_total == 200
  assert sum_total == 650
  assert abs(avg_total - 108.3333333333) < numeric_tolerance
  assert stats.count == 6

Approximate fixtures:
  exact_unique_customers = 4
  observed_cardinality = response.aggregations.unique_customers.value
  assert abs(observed_cardinality - exact_unique_customers) <= approved_small_fixture_tolerance

Percentiles:
  assert p50 <= p95 <= p99
  assert min_latency <= p50 <= p99 <= max_latency
  record algorithm/settings/version

All analytics:
  assert response._shards.failed == 0
  record took, response bytes, shard count, dataset size

Check your understanding

  1. Why can cardinality return the exact-looking answer on a small fixture and still be approximate?
  2. What must be defined before computing avg(total)?
  3. Does raising precision_threshold make cardinality exact?
  4. Why is p99 alone insufficient for an SLO investigation?
  5. What should exact metric tests use as an oracle?
Review the answers

1. The estimator can coincide with the true count on a given dataset; the API contract still uses an approximate algorithm designed for bounded memory.

2. The document population/query/bucket scope and the field/missing-value semantics.

3. No. It changes the memory/accuracy tradeoff within documented limits; it is still an estimator.

4. It hides sample count, tail values beyond p99, distribution shape and failure context.

5. A small deterministic fixture with independently hand-computed values.

Production judgment

Use metric aggregations when their mathematical contract matches the decision. Keep exact contractual accounting in systems designed for exact accounting; use search-cluster metrics for interactive analysis when freshness and approximate summaries are acceptable. Measure query latency, heap/circuit-breaker pressure and response size with realistic cardinality rather than extrapolating from six documents.

Summary and next step

You can now separate exact reductions from approximate estimators and validate both deliberately. The next lesson groups documents into buckets, where distributed candidate selection, size/shard_size and response-tree growth create a second class of analytical accuracy and resource tradeoffs.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.