Search Rules: Pins, Redirects, Banners¶
Text relevance, stock and sales decide the order of a listing (Relevance) until a merchandiser has a view: the new Kestrel drill should open the Power Tools page, a search for "screws" shouldn't start with the discontinued pack, and the own-brand range should sit ahead of the rest. Rankings say that, per category and per search term, by product SKU.
Some searches aren't for products at all ("returns", "delivery"), or are better answered by a page than a list ("kestrel" by the Kestrel brand page), and some deserve a word above the results ("Trade accounts get 10% off fixings"). A search rule handles a search term in up to three ways, any combination of:
| Part | Effect |
|---|---|
| Products | Pins, boosts and buries for the term's results (below) |
| Send to (redirect) | A fresh search for the term goes straight to a page on the site |
| Banner | A message, with an optional link, above the term's results |
A rule needs at least one of the three.
What a ranking does¶
A ranking has three lists of SKUs:
| List | Effect |
|---|---|
| Pinned | Shown first, in the order given |
| Boosted | Shown after the pinned products, ahead of everything else |
| Buried | Shown last |
Everything not named keeps its normal order between the boosted and the buried products, and each list keeps the normal order within it (best match, then best sellers, as the Featured sort does).
-
Stock. Pinned products keep their place even when sold out: the merchandiser chose them. Boosted, unranked and buried products each go in stock first, so a sold-out boosted product comes after the boosted ones in stock but still before every unranked product.
-
It only reorders. A pinned product that isn't in the category, or doesn't match the search, isn't added. Filters still apply: a pinned product filtered out is simply not shown.
- Only under Featured. A shopper who picks Price, low to high or Newest gets exactly that.
- A variant's SKU means its product. The listing holds products, so
DRILL-18V-KITis saved as the drill's own SKU. - A SKU is in one list. Pinned wins over boosted, boosted over buried.
- At most 100 SKUs in each list. Every SKU must be a product (or variant) in the site; saving names any that aren't.
- By SKU, not id, so rankings survive site transfer, where product ids are regenerated.
Where rankings apply¶
| Listing | Ranking used |
|---|---|
A category page (/c/…), nothing searched |
The category's ranking |
/search?q=… |
The rule for that search term |
Searching within a category (/c/…?q=…) |
The rule for that search term (not the category's) |
| A manual-list category | None: the list is already in the order wanted |
A search term's rule applies when what the shopper typed matches the rule's term as a whole,
ignoring case and extra spaces: a rule for drill applies to "Drill" and " drill ", not to
"cordless drill". A category's ranking is about browsing it; once the shopper searches, the order
follows the search.
The public REST endpoints /v1/search/products and /v1/merchandising/products use the same
query layer, so they follow the same rankings.
Redirects¶
A rule's redirect sends a search for its term to a page of the site instead of the results:
returns to /p/returns, kestrel to /c/tools/brand/kestrel.
- Only a fresh search on
/search: page 1, no filters, no sort chosen. Paging, filtering or sorting the term's results stays on the results, so a shopper who wants them can still get them. Searching within a category (/c/…?q=…) never redirects. - Site paths only. A redirect starts with a single
/and has no spaces, backslashes or control characters, so it can't send shoppers to another site (no open redirect). It can't be/search, which would loop. - Recorded in search analytics as a search with a result count of 1: it went somewhere, so it doesn't show up among the searches that found nothing.
- The REST search (
/v1/search/products) doesn't redirect; it returns the rule'sredirectwith the results, and a headless storefront should send the shopper there on a fresh search.
Banners¶
A rule's banner is shown above the results on /search and when searching within a category.
- Plain text, at most 300 characters; themes escape it, so HTML shows as typed.
- An optional link: a site path (
/p/trade-accounts) or anhttps://address, with link text that defaults to "Find out more". A link that's neither is refused. /v1/search/productsreturns it asbanner(text,linkUrl,linkText).
Managing rules¶
- Search terms: Search > Rules and ranking (
/admin/search/ranking) lists the rules with their products, redirect and banner, with a link to see each term's results, and adds, edits and deletes them. The editor's products are optional; Send this search to takes a site path and Banner takes text, a link URL and link text.?term=drillopens the editor for that term. - Categories: Catalogue > Merchandising trees, open a tree, Ranking on a node. The Ranking column shows each node's counts. Manual-list and group nodes don't offer it. Preview shows the ranked order.
- REST (spec-first, see the API reference):
/v1/admin/search/ranking-rules(list, create, get, update, delete; each rule haspinned,boosted,buried,redirectandbanner, and an update replaces the whole rule) and/v1/admin/search/category-ranking/{nodeId}(get, put; three empty lists clear it). - MCP:
productRankingandmanageProductRanking(see MCP), so an agent can act on plain requests such as "pin the new Kestrel 18V drill at the top of Power Tools", "bury the discontinued screws when people search screws" or "what's pinned on drills?". - Names, not just SKUs. A product can be given as its SKU, a variant SKU, its URL name
(seoName) or its exact name ignoring case; a name two products share is refused with each
candidate's name and SKU. A category (
category) can be its node id, its path (/c/tools/power-toolsortools/power-tools) or its name in the primary tree ignoring case; a name two categories share is refused with their paths. - One product at a time.
pin(atposition, 1-based, default after the existing pins),boost,buryandunrank(back to the normal order) takeproductsand acategoryor a search term (query), and change only those products: a product moves out of any other list, the rest of the ranking, and a rule's redirect and banner, are kept. Pinning, boosting or burying for a term with no rule creates one; unranking a rule's last product deletes it unless it has a redirect or banner. - Whole lists.
setRule(SKU or name lists,redirect,bannerText,bannerLinkUrl,bannerLinkText) andsetCategoryreplace everything, so anything left out is cleared;deleteRuleandclearCategoryremove it. - Readable results.
rulesandcategorylist each product as SKU and name.preview(acategory, aquery, or both for a search within a category) runs the real query, under Featured, and returns the first results (limit, default 12, at most 48) as shoppers see them, each markedpinned,boosted,buriedornormal, plus the pinned products the listing doesn't hold (pinnedNotListed).
A site may have up to 500 search-term rules. Changes apply from the next page load.
How it works¶
Rankings are applied at query time, so nothing is reindexed when one changes.
- A category's ranking is
MerchandisingNode.ranking(ProductRanking), set throughMerchandisingTreeService.setNodeRanking. Search-term rules areSearchRankingRuledocuments (searchRankingRulescollection, unique on catalogue and term), throughSearchRankingRuleService. Both check SKUs withProductRankingResolver, which swaps variant SKUs for their product's. For MCP,ProductReferenceResolverturns names into SKUs (oneProductService.findByReferencesquery for every reference) andCategoryReferenceResolvernames into nodes (findNode, thenfindNodeByPath, thenMerchandisingTreeService.findNodesByName); the one-list edits areProductRanking.pin,boost,buryandwithout. MerchandisingQueryService.searchpicks the ranking (above) andRankingSortturns it into the Typesensesort_by:
_eval([(skuId:=`A`):7,(skuId:=`B`):6,(skuId:=[`C`] && inStock:true):5,(skuId:=[`C`] && inStock:false):4,
(skuId:!=[`A`,`B`,`C`,`D`] && inStock:true):3,(skuId:!=[`A`,`B`,`C`,`D`] && inStock:false):2,
(skuId:=[`D`] && inStock:true):1]):desc,_text_match:desc,popularity:desc
Pins score from 5 + the number of pins down to 6, boosted 5 in stock and 4 sold out, unranked 3
and 2, buried 1 in stock and 0 sold out. The clauses don't overlap, so each product scores one
tier. The tail is Featured's own order (best match, then best sellers). Typesense allows three
sort fields, which this uses. A product with no SKU counts as unranked.
- Facet counts are unaffected: only the main query, which returns the products, is sorted.
- Looking up a search term's rule is one indexed Mongo read per search
(SearchRankingRuleService.findRuleForQuery, which returns the whole rule: ranking, redirect and
banner). If it fails, the search runs without a ranking, redirect or banner.
- The storefront's SearchController redirects a fresh search and puts the banner on the model as
searchBanner; MerchandisingPageController does the banner for a search within a category.
Redirects and banner links are validated on save (SearchRankingRuleServiceImpl.sitePath and
link) and again on import.
Tests¶
RankingSortTest: the sort expression, tiers, de-duplication across lists, quoting.MerchandisingQueryServiceTest: a category's ranking under Featured, a chosen sort ignoring it, a search term's rule replacing the category's, a term with no rule, manual lists.MerchandisingQueryServiceTypesenseRuntimeTest(needsTYPESENSE_RUNTIME_URL): a rule's order from a real Typesense, and a chosen sort winning.ProductRankingResolverTest,SearchRankingRuleServiceImplTest: variant SKUs, unknown SKUs, limits, term normalisation, one rule per term, import.ProductRankingAdminRenderTest,SearchRankingAdminControllerTestandSearchControllerRulesTest(REST: admin rules, and the redirect and banner on/v1/search/products),ProductRankingToolsTest(MCP).ProductReferenceResolverTest,CategoryReferenceResolverTest,ProductRankingTest: names, variant SKUs, ambiguity, unknown products and categories, pin positions, unranking.
Related¶
- Search and Discovery
- Search Synonyms: make a search find the products; a ranking orders them.