How Ranking Works
This guide explains how Catalog Search orders results, so you can predict why one product ranks above another and choose the right setting to change it.
How Results Are Ordered
Filters decide which products can appear. These include search profile filters, exclusions, search rule filters, and the search request's filter_by. Hidden products are removed. Filters never change the order, only what appears in the results.
The remaining products are sorted by these criteria, in order. Each criterion only breaks ties on the criteria before it:
- Request sort. The
sort_byfield on the search request, for example a price sort chosen by a shopper or a popularity field based sort set by your storefront. See Sorting Search Results. - Boost score. Merchandising boosts, when they apply.
- Relevance score. How well the product matches the query. Browse requests (
q=*) have no relevance score.
Only the first three criteria are used, and each request sort_by field counts as one. For example, a keyword search on a profile with boosts:
Request sort_by | Results are ordered by |
|---|---|
| None | Boost score. Products with the same boost score are ordered by relevance score |
price.amount:asc | Price. Products with the same price are ordered by boost score, then relevance score |
price.amount:asc,created_at:desc | Price, then creation date. Remaining ties are ordered by boost score. Relevance score is not used |
| Three fields | The three sort_by fields only. Boosts and relevance score are not used |
Without boosts, relevance moves up to take the boost score's place. Without request sort_by and boosts, relevance score drives ranking.
Finally, pinned products are inserted at their positions.
Boost Scores
Profile boosts and the boosts of matching search rules produce one boost score. Each product gets the highest weight among all the boost conditions it matches, from either source, or 0 if it matches none. Weights do not add up.
{
"boosts": [
{ "condition": "extensions.products(Details).is_featured:=true", "weight": 100 },
{ "condition": "extensions.products(Details).on_sale:=true", "weight": 50 },
{ "condition": "extensions.products(Details).stock_status:=out_of_stock", "weight": -100 }
]
}
With these boosts, five example products score as follows:
| Product | Conditions it matches (weight) | Boost score |
|---|---|---|
| Featured, on sale | is_featured (100), on_sale (50) | 100 |
| On sale | on_sale (50) | 50 |
| On sale, out of stock | on_sale (50), stock_status (-100) | 50 |
| In stock, not featured, not on sale | None | 0 |
| Out of stock | stock_status (-100) | -100 |
A product matching a boost and a bury is boosted, because the higher weight wins. To bury out-of-stock sale items, exclude them from the boost condition: on_sale:=true && stock_status:!=out_of_stock.
The same applies across sources. A search rule bury does not demote a product that matches a higher profile boost, and a profile weight of 100 outranks a rule weight of 50. When several search rules match, their boosts are merged before this step.
Relevance Score
The relevance score is built from signals in a fixed priority order. A lower signal only breaks ties on the signals above it, however large its difference. For example, with max_score or max_weight, a product matching both words of cordless drill always ranks above one matching only drill, even if that one is an exact match in a heavier field. Under max_weight, the exception is a match only in weight 0 fields (see below).
With the default text_match_type of max_score, the order is:
| Priority | Signal | Ranks higher when | Setting |
|---|---|---|---|
| 1 | Query words matched | More query words are found across all query fields, including typo-corrected and partially typed words | — |
| 2 | Words in the field | More query words, including typo-corrected and partially typed words, appear within 10 words of each other. Ties go to the field containing more query words overall | — |
| 3 | Typos and partial words | Fewer corrections were needed. A typo costs more than completing a partially typed word | typo_tolerance, prefix |
| 4 | Proximity | Fewer other words sit between the matched words | — |
| 5 | Exact match | The field contains only the query words | prioritize_exact_match |
| 6 | Word position | The matched word is nearer the start of the field. A word that appears more than once is judged by its last occurrence | prioritize_token_position |
| 7 | Synonym match | The product matched directly, not through a synonym | demote_synonym_match |
| 8 | Field weight | The match is in a heavier field | fields[].weight |
| 9 | Fields matched | The query matches in more fields | — |
Signals 2–7 form a field score, computed for each query field. Different query words can match in different fields. Word order is not a signal: shoe red and red shoe score the same for either query.
Typo correction, compound words, and word dropping also decide which products are found at all. By default each runs only when a stricter search finds nothing. So if one product matches shoez as typed, products named shoes are not returned. See Handling Typos.
Text Match Types
text_match_type on a search profile changes where field weight sits. The default suits most catalogs.
text_match_type | Effect |
|---|---|
max_score (default) | The best field score is used. Weight only breaks ties between equally good matches. Suits general product search |
max_weight | The highest-weighted matched field is used, and weight moves above signal 2. A match in a heavier field wins even when a lighter field matches better. Use when field importance matters more than match quality |
sum_score | Signals 1–8 are replaced by the sum of each matched field's score multiplied by its weight, rewarding matches in several fields. Query words matched is not ranked, so a product matching one query word in several fields can rank above a product matching every query word |
Example. A profile queries name (weight 10) and description (weight 5). A search for drill finds product A named Drill Bit Set and product B whose description is exactly drill. B has the better field score because it is an exact match.
max_score: B ranks first.max_weight: A ranks first.
Under max_weight and sum_score, a product matching only in weight 0 fields gets the lowest relevance score and ranks below every product that matches a weighted field. Once any field has a weight, fields without one count as weight 0. Give every field that should affect ranking a weight above 0.
Ranking Settings
ranking_settings on a search profile controls signals 5 and 6:
{
"ranking_settings": {
"prioritize_exact_match": true,
"prioritize_token_position": true
}
}
Both are optional. On update, the object is replaced as a whole. Send "ranking_settings": null to restore the defaults. Ranking settings do not apply to autocomplete.
prioritize_exact_match
Default true. A field is an exact match when it contains only the query words, in any order, ignoring case. For cordless drill, Cordless Drill ranks above Cordless Drill Kit. With false, the two tie on this signal.
A single-word query is exact only when matched as typed, without typo correction or partial word completion. Turn this off only when an exact field match carries no more intent than a partial one.
prioritize_token_position
Default false. When true, a match nearer the start of a field ranks higher. For bosch, Bosch Cordless Drill ranks above Drill Bit Set for Bosch.
Turn it on when field values lead with the most significant words, such as names that start with the brand. Leave it off for descriptive text.
Inspecting Relevance
Each keyword-search hit includes text_match_info with the signals behind its score, such as tokens_matched, best_field_weight, and typo_prefix_score. Pinned products, and hits with no relevance score, do not include it. Use it to debug ranking. Under sum_score, only score and fields_matched are meaningful. The score is only comparable within one search and is not a match percentage, so do not show it to shoppers or filter on it.
Pinned and Hidden Products
Search rules can pin and hide products outside the sort stack:
- Pinned products are placed at one-based positions across the whole result set. They do not need to match the query, but must match the active filters.
- Hidden products are removed. A product that is both pinned and hidden is hidden.