Search Evolution Explorer

Level 6 · Canonical search policy

src/domain/levels/level-1-canonical.ts · 97 lines
Back to search

Keep the rug, remove the rug pad

Give every listing one canonicalOne shared product language for queries and listings: product type, brand, model and relations such as compatible-with. Chapter 6 maps both sides into it from hand-written tables, so a case and a phone stop looking the same. product family from its listing text, and read the same family from the query with a confidence. The policyThe rule that turns a query annotation into FILTER, BOOST or FEATURE. High confidence plus trusted listings filters; medium confidence only boosts; unclear listings are never filtered. filters only when the query is confident: high confidence removes known accessories and wrong sizes, medium confidence only boostsA hand-chosen number added to the score when a listing has a wanted property, for example the category the query points at., and listings the mapping cannot read are never removed.

Where to look: match annotates the corrected query words and removes known mismatches on family, brand and size only under FILTER. score adds productTypeMatch plus attributeMatch.

Guided example

area rugvs chapter 5

The policy filters the 'non slip area rug pad' out of the top 10 for 'area rug'. The pad writes 'area rug' too, so words and attributes cannot separate it; the canonical product family can.

Prerequisites

Reuses the ranking fromChapter 5
Foundation · Inverted indexbuilt at start
Declared artifactsready
src/domain/levels/level-1-canonical.ts97 linessame module the pipeline ran

Annotate the corrected query with one canonicalOne shared product language for queries and listings: product type, brand, model and relations such as compatible-with. Chapter 6 maps both sides into it from hand-written tables, so a case and a phone stop looking the same. product family plus brand and size, then filter or boostA hand-chosen number added to the score when a listing has a wanted property, for example the category the query points at. by policyThe rule that turns a query annotation into FILTER, BOOST or FEATURE. High confidence plus trusted listings filters; medium confidence only boosts; unclear listings are never filtered.. High confidence plus trusted ads filters; medium confidence boosts; UNKNOWN ads never filter.

import { levelCanonicalContent } from "../../content/levels/level-canonical.ts";
import { , type QueryAnnotation } from "../canonical/annotate.ts";
import { , } from "../canonical/match.ts";
import { , type PolicyDecision } from "../canonical/policy.ts";
import { } from "../query-understanding/entities.ts";
import { } from "../signals/build-signals.ts";
import type { EvolutionLevel, LevelDeps, LevelRanker } from "../types/level.ts";
import type { QueryUnderstanding } from "../types/query.ts";
import { as } from "./level-1-entities.ts";
const FILTER_BOOST = 0.1;
const ATTRIBUTE_BOOST = 0.05;
/**
* Assumed ad-side quality until it is measured per property. FILTER runs only
* above these gates; lower them and fewer queries filter. The lesson names
* the trade-off: assumed gates filter aggressively, measured gates filter safely.
*/
const AD_PRECISION = 0.98;
const AD_COVERAGE = 0.9;
/** Duplication across levels is accepted so each evolution level stays independently readable. */
export function createRanker(deps: LevelDeps): LevelRanker {
const previous = (deps);
return {
match: (understanding) => {
const base = previous.match(understanding);
const annotation = (understanding, previous);
if (annotation.productType === null || (annotation) !== "FILTER") {
return base;
}
return { items: base.items.filter((entry) => (annotation, entry)) };
},
score: (understanding, candidate) => {
const base = previous.score(understanding, candidate);
const annotation = (understanding, previous);
const matched = (annotation, candidate);
const active = annotation.productType !== null && (annotation) !== "FEATURE";
const boost = active
? * matched.productTypeMatch + * matched.attributeMatch
: 0;
return {
item: candidate,
score: base.score + boost,
signals: (understanding, candidate, [
...base.signals.map((signal) => ({
name: signal.name,
value: signal.value,
weight: signal.weight,
})),
{ name: "productTypeMatch", value: matched.productTypeMatch, weight: },
{ name: "", value: matched.attributeMatch, weight: },
{ name: "policyFilter", value: (annotation) === "FILTER" ? 1 : 0 },
]),
};
},
correctionsFor: (understanding) => previous.correctionsFor?.(understanding) ?? [],
conceptsFor: (understanding) => previous.conceptsFor?.(understanding) ?? [],
};
}
/** Annotate corrected words, so "iphne 13" tags what "iphone 13" tags. */
function readAnnotation(understanding: QueryUnderstanding, previous: LevelRanker): QueryAnnotation {
const fixes = previous.correctionsFor?.(understanding) ?? [];
return ((understanding.tokens, fixes).join(" "));
}
function readDecision(annotation: QueryAnnotation): PolicyDecision {
return ({
confidence: annotation.confidence,
adPrecision: ,
coverage: ,
});
}
a numbered note, explained on the righta function you can open heredefined further down in this filewhere level 5 joins