Skip to content
ferroseekdocumentation homemain, packages at version v0.1.0
Ferroseek on GitHub

Sorting and pagination

sort takes one key or an array of up to 8. Each key is _score, _doc or a field of type keyword, numeric, boolean or date:

{
"sort": [
{ "price": "asc" },
{ "rating": { "order": "desc", "missing": "_last" } },
{ "sku": "asc" }
]
}
Form Meaning
"price" Ascending (_score defaults to descending)
{"price": "desc"} Explicit order
{"price": {"order": "desc", "missing": "_first", "mode": "max"}} Long form

missing takes _last (default) or _first; custom missing values are rejected. mode takes min or max, for fields with several values. Rejected: unmapped_type, numeric_type, format, nested, script sorts. Sorting on a text field is an error, with Elasticsearch’s message.

Each hit carries a sort array with its sort values. When _score is not among the sort keys, hits have "_score": null, as in Elasticsearch, unless you set "track_scores": true.

Two products with the same price can come back in either order unless something breaks the tie. End every sort with a unique field; the sample catalog has sku for this. The example shop does it on every sort:

examples/shop-worker/src/search-request.ts
const SORT_CLAUSES: Readonly<Record<Sort, estypes.Sort>> = {
relevance: [{ _score: { order: "desc" } }, { sku: { order: "asc" } }],
price_asc: [{ price: { order: "asc" } }, { sku: { order: "asc" } }],
price_desc: [{ price: { order: "desc" } }, { sku: { order: "asc" } }],
rating: [{ rating: { order: "desc" } }, { sku: { order: "asc" } }],
newest: [{ created_at: { order: "desc" } }, { sku: { order: "asc" } }],
};

size is the number of hits to return (default 10) and from the number to skip (default 0). from + size may not exceed 10,000 (Elasticsearch’s index.max_result_window):

{
"error": {
"root_cause": [{ "type": "illegal_argument_exception", "reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10010]. …" }],
"type": "search_phase_execution_exception",
"reason": "all shards failed",
"phase": "query",
"grouped": true,
"failed_shards": [ … ]
},
"status": 400
}

A negative from is an error; a negative size means the default, as in Elasticsearch 7.17. size: 0 returns no hits and is the usual way to ask for aggregations only.

For deep pagination, pass the sort values of the last hit of the previous page:

{
"size": 24,
"sort": [{ "price": "asc" }, { "sku": "asc" }],
"search_after": [44.99, "SKU-000001"]
}

search_after needs an explicit sort, one value per sort key, and from must be 0.

By default the total is counted exactly up to 10,000; above that, hits.total reports {"value": 10000, "relation": "gte"}. true counts exactly, false (or -1) leaves the total out, and a number sets the threshold.

Controls which fields of each document are returned:

Value Returns
true (default) The whole document
false No _source
"name" or ["sku", "name", "price"] Only these fields (patterns with * allowed)
{"includes": [...], "excludes": [...]} Included minus excluded

Returning fewer fields makes responses smaller, which matters for the per-response size limit of 8 MiB.