Sort and pagination

Sort and pagination #

Control which documents come back and in what order — sort, from/size, search_after/search_before, and total-hit accounting.

POST /my-collection/_search

How it works #

sort is an array of sort clauses evaluated in order; the first clause that produces a difference decides the order. Sorting on an indexed field lets the collector skip documents that cannot make the cut (early termination), so field sorts — and _doc above all — are cheaper than score sorts, not just differently ordered.

Three details the engine guarantees:

  • Ties break by internal doc order. Documents whose sort keys are equal (including two missing values) keep a stable, repeatable order across pages — safe for cursor pagination.
  • Sort keys echo per-field precision. Sorting a date field returns millisecond keys; a date_nanos field returns full nanosecond keys (.123456789 survives), with the key rendered according to each field’s mapping — and the sort works through wildcard index patterns (conformance case 240_date_nanos).
  • Geo proximity is a score, not a sort key. There is no _geo_distance sort clause; order by distance with the distance_feature query, which scores every hit by boost × pivot / (pivot + distance) from an origin — nearest first simply falls out of relevance order.

Sort #

Parameters #

  • field
    (Required, string) The field to sort by. Two pseudo-fields are always available: _score (relevance, descending by default) and _doc (index order — the cheapest possible sort).
  • order
    (Optional, asc | desc) Sort direction. When omitted in a canonical clause the engine sorts descending — for _score and regular fields alike. The Elasticsearch shorthand forms ("sort": "date", bare "date" in the array) are the exception: the gateway normalizes them to asc, so the direction you see depends on the form you send. When in doubt, spell order out.
  • missing
    (Optional, first | last; the gateway also accepts the Elasticsearch spellings _first / _last) Where documents without a value for the field land, independent of the sort direction. Default: last. Documents whose keys tie (including two missing values) break the tie by internal doc order, so pages are repeatable.
  • script
    (Optional, string | object) With field: "_script" only: the sort key is computed per hit of the returned page by this script (doc and params in scope) instead of an indexed field. At most one _script clause per request — see scripted queries.

Index-time sorted fields (see field parameters) join _doc among the fastest keys.

Pagination #

  • from
    (Optional, integer, default: 0) Offset of the first hit to return.
  • size
    (Optional, integer, default: 10) Number of hits to return. 0 with aggregations skips hit assembly entirely.
  • track_total_hits
    (Optional, boolean | integer) Whether to compute the exact hit count. false skips counting when you only need the page; a number caps the counting work. The gateway accepts the Elasticsearch form, default cap 10000.
  • collect_size
    (Optional, integer) Upper bound on candidate documents collected before ranking — a guard rail for very broad queries.

Deep from paging re-ranks everything before the offset. Prefer cursors:

  • search_after
    (Optional, array) Return the rows that follow the given sort-key tuple. Take the tuple from the last hit of the previous page (sort values of that hit) and pass it back.
  • search_before
    (Optional, array) The mirror image — the rows that precede the tuple. Together with search_after this enables bidirectional cursored paging.

For a stable view across pages, run the search against a point-in-time snapshot (pit — see API conventions).

Examples #

Cheapest first page of everything, newest first:

Cursor to the next page with the last hit’s sort tuple — the engine answers the rows strictly AFTER the tuple:

The first page’s newest document is S120 (epoch 1745276400000); the cursor page starts at the second-newest. A multi-key sort array gives the cursor a unique tuple per document (see the wasm note below about float-typed single keys).

Notes #

  • Sorting by a field that is only in the document source (not indexed, not a column) forces full materialization — index the field or accept the cost.
  • The gateway also accepts the shorthand forms ("sort": ["date"], "sort": [{"date":"asc"}]) and normalizes them to the canonical clause above.

Use cases #

  • Newest-first feeds. sort: [{field: date, order: desc}] over the 120-sale history (or the 150-log stream) with track_total_hits for the total and cursors for endless scroll.
  • Deterministic exports. _doc order — index order, the cheapest key the engine has — for dump jobs that must replay identically.
  • Composite leaderboards. Multi-clause sorts (price desc, date asc) with explicit missing placement so documents without the field land where the product spec says, not wherever ties dump them.
  • Geo-ordered results. places nearest-first: no _geo_distance sort — sort by _score under a distance_feature query centered on the user (see How it works).
  • Deep pagination at scale. search_after/search_before cursor walks instead of from offsets, pinned to a pit snapshot when the index keeps moving.
  • Field collapsing and rescore — the other two result-shaping stages; collapse pages over groups of the sorted list, rescore re-sorts the top window by blended score.
  • distance_feature query — proximity ordering as scoring, for dates and geo points.
  • Scripted queries — the _script sort key and script_score, the query-time scripting surface.
  • API conventions — pit snapshots and the cursor contract in the wider API.
Calendar October 7, 2026
Edit Edit this page