Category and static-quality boosts push the 'couch riser' accessory out of the top 10 for 'couch': it matches the word but sits in the wrong category and price band.
Level 7 · Attribute boosting
Use what I meant, not only the words I typed
First work out what the query points at. A bare 'marble' defeats the guess and nothing is boosted; add a product word, such as 'marble end table', and the category resolves. Then add points to listings in that category and at a fitting price, plus points that do not depend on the query at all: a recent post, a longer title, and a price that sits high in its category.
Where to look: match is passed through unchanged from chapter 6. score takes the chapter 6 score and adds the named boostsA hand-chosen number added to the score when a listing has a wanted property, for example the category the query points at. to it.
Guided example
Prerequisites
Same candidatesAll listings that matched the query at all, before they are ordered. Scoring can only reorder this set. It cannot add to it. as chapter 6. The order changes: the BM25The standard formula that scores how strongly a listing's text matches the query words. A word counts more when it is rare in the whole corpus and appears often in this listing. Very long listings are penalised a little. score gets boostsA hand-chosen number added to the score when a listing has a wanted property, for example the category the query points at. for query attributes and for static qualityA quality score that does not depend on the query, for example how new a listing is or how complete its title is. It can be computed once for every listing. of the listing.
import { level2Content } from "../../content/levels/level-2.ts";import { } from "../signals/build-signals.ts";import { } from "../signals/price-fit.ts";import { } from "../signals/static-quality.ts";import type { CorpusItem } from "../types/corpus.ts";import type { EvolutionLevel, LevelDeps, LevelRanker } from "../types/level.ts";import type { QueryUnderstanding } from "../types/query.ts";import { as } from "./level-1-canonical.ts";const CATEGORY_BOOST = 0.1;const PRICE_FIT_BOOST = 0.05;/** Recency and other static quality must not outweigh a real text match. */const STATIC_QUALITY_WEIGHT = 0.01;/** Duplication across levels is accepted so each evolution level stays independently readable. */export function createRanker(deps: LevelDeps): LevelRanker { const previous = (deps); const quality = (deps.index.availableItems, deps.clock); return { match: previous.match, score(understanding, candidate) { const base = previous.score(understanding, candidate); const categoryMatch = (understanding, candidate); const priceFit = (understanding, candidate); const staticQuality = quality.get(candidate.id); const recency = staticQuality?.recency ?? 0; const titleQuality = staticQuality?.titleQuality ?? 0; const pricePercentile = staticQuality?.pricePercentile ?? 0; const added = * categoryMatch + * priceFit + * (recency + titleQuality + pricePercentile); return { item: candidate, score: base.score + added, signals: (understanding, candidate, [ // Preserve each carried weight: the spelling flag has none, so the printed // total still matches the score for corrected queries. ...base.signals.map((signal) => ({ name: signal.name, value: signal.value, weight: signal.weight, })), { name: "categoryMatch", value: categoryMatch, weight: }, { name: "priceFit", value: priceFit, weight: }, { name: "recency", value: recency, weight: }, { name: "titleQuality", value: titleQuality, weight: }, { name: "pricePercentile", value: pricePercentile, weight: }, ]), }; }, };}function categoryMatchScore(understanding: QueryUnderstanding, candidate: CorpusItem): number { if (understanding.predictedCategoryId === null) { return 0; } if (understanding.predictedCategoryId === candidate.categoryId) { return 1; } return 0;}