Top hits aggregation

Top hits aggregation #

Keeps track of the most relevant documents being aggregated — useful inside a bucket aggregation (e.g. terms) to return the best-matching documents per bucket. Where most metric aggs reduce a bucket to one number, top_hits keeps the documents themselves.

How it works #

top_hits runs over a bucket’s document set and returns up to size documents in a hits envelope: hits.total — a plain count of the bucket’s documents — and hits.hits[], each entry carrying _id (the engine’s internal numeric document id) and _source.

  • Sort leniency. The sort body accepts every spelling — a bare string ("amount"), an array of strings or objects, or a single {"field": "order"} object — and normalizes them all to an object array; a bare field name means asc.
  • Flat buckets. The column-store fast path and the executor path both walk the bucket’s candidate documents and fetch each stored source. In the current engine the hits come back in index order: the normalized sort is not yet applied to flat-bucket ranking.
  • Nested buckets sort for real. Under a nested aggregation, top_hits is element-level: each hit is one stored element of the nested path, identified by _nested: {field, offset}, and the elements are sorted per the sort spec — numeric when both sides parse as numbers, lexicographic otherwise, missing values sorting last under asc.
  • _source. true / false / a list of fields all parse; the current implementation returns the full stored source either way.
  • Fan-out. Shard hit lists are concatenated and truncated back to size at the coordinator.
  • Request knobs such as from are not parsed and are ignored.

Examples #

Top 3 sales per merchant:

POST /sales/_search
{
  "aggs": {
    "top_merchants": {
      "terms": { "field": "merchant" },
      "aggs": {
        "top_sales": {
          "top_hits": {
            "size": 3,
            "sort": [{ "field": "amount", "order": "desc" }]
          }
        }
      }
    }
  }
}

Inside each terms bucket hits.total is the bucket’s document count (30 per merchant on the teaching dataset), and the entries are the bucket’s leading documents in index order.

Use cases #

  • Top-N rows per group — the classic shape: terms on merchant or category with a small top_hits inside, so one request returns the group counts and a few representative rows per group.
  • A representative product per category — terms(category) over the 30 products with top_hits: {size: 1} yields one exemplar row per category for a listing page.
  • Nested element extraction — under nested, surface stored child elements (a product’s variants, an order’s line items) ranked by an element field.
  • Bucket debugging — when a bucket’s count surprises you, attach top_hits and read the documents that actually fell into it.
  • Facet previews — “show me the documents behind this facet value” without issuing a second search per value.

Parameters for top_hits #

  • size
    (Optional, integer, default: 3) Number of top hits to return per bucket.
  • sort
    (Optional) Sort order for the top hits. Accepts "field", ["a", "b"] and [{ "field": "asc" }] forms — all normalize to an object array.
  • _source
    (Optional) true, false, or a list of fields to include. The full stored source is returned either way.
  • top_metrics — when one field value from the top document is enough and whole documents are too heavy.
  • terms — the bucket aggregation top_hits usually lives in.
  • nested — element-level top hits with real sorting.
Calendar October 6, 2026
Edit Edit this page