Skip to content

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) calls LearnedBoostService.refresh with the same computation: it suggests (SUGGESTED, or AI_REVIEW when AI-checked), applies (autopilot), renews, withdraws and expires boosts, then puts each changed listing's applied boosts in its ranking: a category's through MerchandisingTreeService.setNodeLearnedBoosts, a search term's through SearchRankingRuleService.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 are LearnedBoost documents (learnedBoosts), one per catalogue, listing and product.
  • AI check. LearnedBoostService.reviewPending runs 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 (findAndModify to AI_REVIEWING, so two nodes never review the same one; a claim older than 30 minutes is taken again) and asks AiLearnedBoostReviewer (ai module, the site's own provider through TenantAiService) 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 (status to filter; default waiting and applied), POST /v1/admin/search/learned-boosts/{boostId}/accept and /reject (optional reason).
  • MCP: productRanking action boosts; manageProductRanking actions acceptBoost and rejectBoost (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 (needs MONGO_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.