Learned Boosts¶
Ranking by what sells orders each product by how it does across the shop. A
product can do much better in one listing than overall: a drill bit that sells well to people who
search "drill", a coat that sells in Outerwear but not in Sale. Each night, on a shop using ranking
by what sells, such products are suggested for a boost in that listing, and the shop's mode
decides what happens to the suggestion. It is Phase 3 of the plan in
docs/implementation-plan/commerce-search-optimisation.md.
Modes¶
search-learned-boosts (Search settings, Learned Boosts):
| Mode | What happens to a suggestion |
|---|---|
review (default) |
It waits on Search > Learned boosts for a merchandiser to accept or reject |
ai-checked |
The shop's own AI provider accepts or rejects it, with a reason; a merchandiser can undo it, or decide while it waits (if AI isn't set up, it waits and says why) |
autopilot |
It is applied at once |
off |
Nothing is suggested, and applied boosts are taken out |
Guardrails, in every mode¶
- The merchandiser's call wins. A product pinned, boosted or buried in the listing is never
suggested there, and if a merchandiser later ranks it, their list takes it out of the learned
boosts (
ProductRanking.normalised). A learned boost never moves a pinned product. - A tier of their own. Applied boosts go in the listing's ranking as learned boosts
(
ProductRanking.learnedBoosted): after the merchandiser's boosts and ahead of everything else, in stock first, best first. A merchandiser's edits leave them alone. - A few per listing: at most
search-learned-boosts-max(default 5) applied in one listing, and at most that many suggested a night. - They lapse. An applied boost lasts
search-learned-boost-days(default 14) and each night that it still qualifies renews it; otherwise it lapses and comes out. A suggestion that stops qualifying before anyone decides is withdrawn. - A rejection sticks for 30 days: the product isn't suggested in that listing again before then.
- Evidence first. A listing needs 500 (decayed) impressions before its products are judged in it; a product needs 100 impressions and 3 orders there, a score of at least 1,300 (30% better than the listing's average for its goal), and must not already be mostly shown in the top four.
- Only shops that qualify for ranking by what sells get them; when a shop stops qualifying, its boosts are taken out.
How a product is judged in a listing¶
LearnedRankScorer.Scoring.scoreListing: the product's counts in that listing (its category, or the
results of that search term, inside a category or not), corrected for position by the site's click
curve and shrunk towards how the product does everywhere, against the listing's own average
for the listing's goal (RankingGoalService). So a product with little evidence in the listing
scores about what it does elsewhere, and only real, listing-specific performance stands out.
How it works¶
- Nightly. After scoring a catalogue,
LearnedRankingJob(worker) callsLearnedBoostService.refreshwith the same computation: it suggests (SUGGESTED, orAI_REVIEWwhen AI-checked), applies (autopilot), renews, withdraws and expires boosts, then puts each changed listing's applied boosts in its ranking: a category's throughMerchandisingTreeService.setNodeLearnedBoosts, a search term's throughSearchRankingRuleService.setLearnedBoosts(which creates a rule holding only learned boosts for a term without one, and deletes it when they go; if the site has its 500 rules, the boost waits). Boosts areLearnedBoostdocuments (learnedBoosts), one per catalogue, listing and product. - AI check.
LearnedBoostService.reviewPendingruns every five minutes (badger.search.learned-boosts.review-interval-ms) on the nodes that have the AI module (the web app; the worker has none). It claims up to 20 boosts atomically (findAndModifytoAI_REVIEWING, so two nodes never review the same one; a claim older than 30 minutes is taken again) and asksAiLearnedBoostReviewer(ai module, the site's own provider throughTenantAiService) for a verdict on each. The prompt carries the listing, the product's name and SKU, scores against the listing's average, rounded counts, where it is usually shown and its margin as a band ("higher", "similar", "lower" than the shop's): never a cost price or a margin figure. A boost without a verdict waits for the next pass. - Site transfer leaves learned boosts behind: they come from the source's traffic. Imported category rankings and search rules keep only the merchandiser's lists.
Managing them¶
- Admin: Search > Learned boosts (
/admin/search/boosts, see the Search section): waiting (accept or reject, with an optional reason), applied (with who decided, why and until when; remove), and recent rejections, withdrawals and lapses. The margin band is shown to admin and finance roles only. - REST:
GET /v1/admin/search/learned-boosts(statusto filter; default waiting and applied),POST /v1/admin/search/learned-boosts/{boostId}/acceptand/reject(optionalreason). - MCP:
productRankingactionboosts;manageProductRankingactionsacceptBoostandrejectBoost(id). See MCP.
Tests¶
LearnedBoostServiceImplTest: what is and isn't suggested, each mode, the AI deciding and waiting, renewal and lapsing, the rejection cooldown, the per-listing limit, search terms, off.LearnedBoostRepositoryMongoRuntimeTest(needsMONGO_RUNTIME_URL): the atomic claim.AiLearnedBoostReviewerTest: the prompt carries bands, never costs; lenient reply parsing.RankingSortTest,ProductRankingTest,SearchRankingRuleServiceImplTest: the learned tier, the merchandiser's lists winning, edits keeping learned boosts, import dropping them.LearnedBoostsRenderTest,LearnedBoostsAdminControllerTest(REST),ProductRankingToolsTest(MCP),LearnedRankingJobTest.