Skip to content

feat(search-api-graphql): add a declarative tie-break for sorted queries - #867

Open
ddeboer wants to merge 2 commits into
mainfrom
feat/search-tie-break
Open

ddeboer wants to merge 2 commits into
mainfrom
feat/search-tie-break

Conversation

@ddeboer

@ddeboer ddeboer commented Sep 24, 2026

Copy link
Copy Markdown
Member

Sorted by date, all datasets in a catalog registered at once share a datePosted, and Typesense returns such ties in insertion order. A reindex inserts them in a different order, so a client paging through the block can see a dataset twice or miss one.

A search type can now declare a tie-break in its GraphQL options:

types: {
  Dataset: { tieBreak: [{ field: 'title', direction: 'asc' }] },
},
  • It is appended after the sort the request and queryDefaults settled on. A term on a field already sorted on is skipped.
  • A query without a sort keeps the engine鈥檚 default order (relevance, or the collection鈥檚 defaultSortingField). To break ties there too, set a default sort in queryDefaults.
  • Facet-only queries (perPage: 0) get no tie-break.
  • Schema build rejects a tie-break on a field that isn鈥檛 sortable (Typesense has no sort index for it) and one longer than two terms (Typesense sorts on three at most, and one is the primary sort).
  • The Typesense compiler rejects more than three sort terms with an error naming them, which covers a queryDefaults policy that sets two.

An integration test writes six documents with the same date, pages through them, rebuilds in reverse insertion order and gets the same order both times. Without the tie-break the rebuild does reshuffle them.

Not fully deterministic. Datasets with the same title still come back in storage order. A unique final key would need a sortable copy of the id in every collection, because Typesense can鈥檛 sort on id (tested on 30.2: a declared id field is dropped, and sort_by=id fails). We decided the extra field and reindex aren鈥檛 worth it for rare exact title ties.

Fix #866

- Add a per-type tieBreak, appended after the sort the request and queryDefaults
  settled on; terms on a field already sorted on are skipped
- Leave queries without a sort to the engine鈥檚 default order, and facet-only
  queries without a tie-break
- Reject a tie-break on a non-sortable field, or one longer than two terms,
  when the schema is built
- Reject more than three sort terms in the Typesense compiler, naming them
- Pin the order of a block of equal dates across pages and a rebuild
  against a real Typesense

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deterministic tie-break for sorted search queries

1 participant