suo
Concepts

The ranking cascade

The eight tiers that decide result order, each one only breaking ties the tier before it left.

suo ranks results with a cascade, the same shape Algolia and Meilisearch use: eight tiers, evaluated in a fixed order, where each tier only decides between hits the tiers above it could not separate. A hit's full ranking vector is available on every result — this is what the playground's "why this rank" panel shows.

The eight tiers

  1. Pinned — a query-time pin rule places this exact record at a fixed position, above everything else.
  2. Words matched — how many of the query's words were found at all, regardless of typos.
  3. Typos — fewer typo corrections beats more; an exact match beats a corrected one.
  4. Proximity — how close together the matched words appear in the text.
  5. Attribute — which searchable tier the match came from, and where in it (a title match outranks a body match).
  6. Exactness — an ICU whole-word match outranks a prefix match, which outranks a bigram-only match (CJK recall floor — see CJK).
  7. Custom ranking — your own tie-breaking sort, for example by recency or popularity.
  8. bm25 — a standard relevance score, as the final tiebreaker.

Why a cascade instead of one score

A single blended score is hard to reason about: why did result B beat result A? A cascade answers that directly — "B matched one more word than A" is the whole explanation, at tier 2, and nothing below tier 2 needed to run. This is also why custom ranking sits near the bottom: it should break ties among equally relevant results, not override relevance itself.

Reading the response

{
	"pinned": null,
	"words": 2,
	"typos": 0,
	"proximity": 1,
	"attribute": { "tier": 0, "position": 3, "name": "title" },
	"exactness": 2,
	"custom": [],
	"bm25": 8.41
}

Pass explain: true on a search to get this vector, plus decidedAgainstNextBy — which tier actually separated this hit from the next one — on every hit. See Search.

Typos are lazy

Typo expansion only runs when the exact and prefix candidates come up short — see Typo tolerance for the exact thresholds. This keeps a query that already has plenty of exact matches fast, and keeps a typo'd query from drowning out an exact one.

On this page