Search vs. recommendation — the query changes everything
This is the first lesson of the Search track. One structural fact — the presence of an explicit query — reshapes the candidate set, the primary signal, the latency budget, and the failure modes. We establish the engineering frame the whole track hangs on, then map it onto the cascade you already know.
- Isolate the one structural difference between search and recsys — the query as a stated intent and a relevance contract — and trace what it changes downstream.
- Separate relevance from engagement as objectives, and show why optimising pure clicks degrades a search engine in a way it doesn't degrade a feed.
- Lay out the query taxonomy (navigational / informational / transactional, head / torso / tail) and show how intent type changes retrieval and ranking.
- Work the Zipfian query distribution and derive the cache-hit economics and the tail-quality / zero-result problem it forces.
- Map the search funnel onto the shared cascade in 01 · the funnel, naming every search-specific divergence, with a worked funnel-size and latency walk-through.
- Preview the metrics (graded NDCG, abandonment, reformulation) and contrast them with recsys CTR / watch-time. Forward-link to 48 for depth.
The one structural difference
A recommender system runs with no query. You open YouTube Home or a feed and the system must guess what you want from your identity and context alone. Formally it predicts engagement from a (user × item × context) interaction — "given who you are and what you've done, what will you engage with?" There is no stated intent; the system infers intent from behaviour, and it is free to be wrong on any single impression because the next one is a fresh draw.
Search inverts the setup. The user types a string — noise cancelling headphones under 200, how to deglaze a pan, united flight status UA 482 — and that string is two things at once:
- A stated intent: the user has told you, in words, what they want. You no longer have to infer it from a click history; you have to understand the words.
- A relevance contract: by issuing the query the user obligates the system to return things that are about that query. Returning ten beautifully personalised but off-topic results is not a mediocre outcome — it is a broken contract. The user will notice instantly, because they know what they asked for.
That single fact — an explicit query carrying a stated intent and an obligation — is the root from which every downstream difference grows. Four of them matter enough to organise the whole track:
| Dimension | Recommendation | Search |
|---|---|---|
| Candidate set | Query-free, user-personalised. A fixed pool retrieved by user×item affinity (two-tower over the whole catalogue). The same user gets a similar pool from one visit to the next. | Query-conditioned. The eligible set is recomputed per query — only documents that match the query (lexically and/or semantically) are candidates. Two queries from the same user retrieve disjoint pools. |
| Primary signal | Engagement (clicks, watch-time, likes). It is the objective — there is no ground truth for "what this user should have seen." | Query–document relevance. Engagement exists but is secondary and heavily biased: a click on result 1 mostly tells you it was result 1, not that it was relevant. Relevance has an external ground truth (human judgments / qrels). |
| Session shape | Long, exploratory, low-precision-per-item. A feed of 50 items where 8 are good is a good session. | Short, goal-directed, high-precision. The user wants the answer in the first few results and leaves. Precision@1–3 dominates the experience. |
| Failure visibility | A mediocre feed is invisible — the user can't see the better feed they didn't get. There is no hard floor. | A hard recall floor. Returning nothing for a query that has answers, or returning obviously wrong things, is a visible, reportable failure. "No results for iphone charger" is a bug, not a tuning opportunity. |
python list comprehension, the relevance contract says: show me documents about that. If your system instead surfaces the JavaScript content I usually read because it "knows me," it has violated the stated intent to chase an inferred one. Personalisation in search lives inside the relevance-satisfying set as a tie-breaker (and matters most for genuinely ambiguous queries like java or delta), not as a way to override it. We make this precise in 47.
The relevance contract: relevance is not engagement
The deepest consequence of the query is that search has two objectives that can disagree: relevance (is this document about the query?) and engagement (will the user click / dwell / convert?). In a feed these collapse into one — engagement is the target. In search, optimising the wrong one of these silently degrades the product.
Consider what pure click-optimisation does to a search engine:
- Clickbait wins. A title-tuned, ad-laden, low-substance page can out-click the authoritative answer. In a feed that's tolerable — entertainment is a legitimate goal. For
chest pain when breathingit is dangerous, and erodes trust the user will remember. - Vocabulary mismatch is punished the wrong way. The relevant document may use different words than the query (
heart attackvsmyocardial infarction). A click-maximiser learns to favour lexically showy results that look like the query; a relevance-maximiser must bridge the vocabulary gap. Search tolerates far less topical drift than a feed but must tolerate far more surface-form variation — the opposite balance from recsys. - Position and presentation bias dominate the click logs. Result 1 gets clicked because it's first. If you train ranking on raw clicks you learn "be where you already are." Search eval therefore leans on graded human relevance judgments as the anchor, with clicks as a debiased secondary signal (we treat position bias / IPS in 07).
The engineering posture this forces: relevance is the constraint, engagement is the refinement. First guarantee that what you show is genuinely about the query (the contract); only then use engagement to order within the relevant set. Trust — measured over many sessions as continued use and low abandonment — is the real long-term objective, and trust is destroyed faster by one irrelevant top result than by a hundred merely-okay ones.
Query taxonomy: intent type changes the algorithm
Not all queries want the same shape of answer. The canonical taxonomy is Broder's (2002), which classifies a query by the user's goal:
| Intent class | The user wants | Examples | What the system owes |
|---|---|---|---|
| Navigational | To reach one specific known destination. | facebook login, united airlines, irs.gov |
One exact answer at rank 1. Diversity is a bug here — showing ten "Facebook-ish" sites when the user wants the Facebook is a failure. Precision@1 is almost the whole game. |
| Informational | To learn about a topic — possibly from several sources. | how do tariffs work, best hiking trails near seattle, symptoms of flu |
A diverse, high-coverage set. No single page is "the" answer; the user may read three. Diversity and coverage are features, not bugs. Tolerates more results, rewards breadth. |
| Transactional | To do something — buy, download, book, sign up. | buy airpods pro, cheap flights to tokyo, download vlc |
Actionable results with the right attributes — price, availability, ratings, the buy/download action. Freshness, stock, and structured facets matter more than prose relevance. This is the e-commerce / ads regime (47). |
Why this is not academic: the intent class changes both retrieval and ranking. A navigational query collapses the problem to near-exact lookup — you want the inverted index's exact-match path to dominate and you want a single confident top result; semantic broadening is risk, not value. An informational query is where dense / semantic retrieval (43) earns its keep — bridging vocabulary, returning a diverse set — and where you may deliberately de-duplicate near-identical pages to maximise coverage. A transactional query routes into structured retrieval over a product catalogue with facet filters and inventory joins. Classifying intent is itself a step of query understanding, the subject of the next lesson (41).
A second, orthogonal axis is query popularity and length:
- Head queries: the few thousand strings issued enormously often (
weather,youtube,amazon). Usually short, often navigational. You can afford to hand-tune, cache aggressively, and measure them precisely because the volume is there. - Torso queries: moderately frequent, often 2–4 words, a mix of intents. The workhorse middle.
- Tail queries: long, rare, often unique (
why does my 2014 subaru forester make a clicking noise when turning left in cold weather). Each is seen a handful of times or once ever. No per-query tuning is possible; no cache helps; you cannot even measure them individually. Tail quality is decided entirely by how well your general retrieval and ranking generalise.
Short queries are under-specified — jaguar could be the animal, the car, or the NFL team, so the system must handle ambiguity (and this is where modest personalisation helps). Long queries are over-specified — they contain many constraints, often with no document matching all of them, so the system must decide which terms are droppable. The track spends real time here: query understanding (41) handles short/ambiguous, and term-dropping / weak-AND retrieval (42) handles long/over-specified.
The query distribution is Zipfian — and that dictates the architecture
Query frequency follows a Zipf law: rank the distinct queries by volume, and the r-th most frequent query gets a share proportional to 1 / rˢ with exponent s ≈ 1. Two facts fall out, and both are load-bearing for the system you build.
Fact 1 — a tiny head carries a huge fraction of volume, so caching is enormously effective. With s = 1 the share of total volume captured by the top-K queries out of D distinct queries is the ratio of partial harmonic sums:
share(top K) = HK / HD, where Hn = Σr=1..n 1/r ≈ ln n + 0.577
Worked number. Take a service with D = 10⁸ distinct queries over a window. HD ≈ ln(10⁸) + 0.577 = 18.42 + 0.58 ≈ 19.0. The top 100 queries: H100 ≈ ln(100) + 0.577 = 4.605 + 0.577 ≈ 5.18, so their share is 5.18 / 19.0 ≈ 27%. The top 10,000: H10⁴ ≈ 9.21 + 0.58 = 9.79, share 9.79 / 19.0 ≈ 52%.
So a cache holding the top 100 query→SERP results absorbs ~27% of all traffic, and the top 10k absorbs over half. Cache-hit economics: if a full SERP costs ~150 ms of backend work and a cache lookup costs ~1 ms, caching the top 100 cuts mean backend load by ~27% for the cost of storing 100 result lists — a trivially good trade. (A search result page is called a SERP — Search Engine Results Page — the ranked list plus any structured blocks; we'll use the term throughout.) The catch is invalidation: SERPs go stale as the corpus changes, so head caches carry short TTLs and per-query freshness rules.
Fact 2 — the tail is fat, so zero-result and tail-quality handling is a first-class problem. Because so many queries are seen once, a large fraction of distinct queries are tail queries, and tail queries are where retrieval most often returns nothing (over-specified, misspelled, or using out-of-vocabulary words). A zero-result rate that looks small per-impression can be a large fraction of distinct queries. The architectural responses — spelling correction and query relaxation (46), term-dropping in retrieval (42), and semantic fallback when lexical match fails (43) — exist primarily to serve the tail. A recsys has no analogue: it never "returns nothing," because there is no query to fail to match.
The search funnel — same cascade, query-conditioned
Search reuses the retrieve → rank → re-rank cascade you learned in 01 · the funnel. The arithmetic that forces a cascade is identical: you cannot run an expensive scorer over 10⁸ documents per request inside a sub-second budget. But the query adds a stage before retrieval, changes what retrieval keys on, and adds a relevance label that recsys lacks. Here is the search-specific funnel:
Worked funnel-size and latency walk-through
Numbers that make the cascade concrete, sized for a mid-large vertical search engine with a 10⁸-document corpus and a sub-300 ms p99 contract:
| Stage | In → out | Per-unit cost | Stage latency |
|---|---|---|---|
| Query understanding | 1 query → 1 structured query | tokenize + spell + intent model | ~10 ms |
| Retrieval (lexical + dense, fused) | 10⁸ docs → 10⁴ candidates | posting-list walk + ANN probe; sub-µs/doc effective | ~40 ms |
| Ranking (LTR) | 10⁴ → 10³ | ~12 µs/doc (GBDT/LTR over ~100 features): 10⁴ × 12 µs = 120 ms | ~120 ms |
| Rerank / blend | 10³ → 10² → 10 shown | ~0.6 ms/doc cross-encoder on top 100: 100 × 0.6 ms = 60 ms | ~60 ms |
| Total (ML path) | 10 shown | ≈ 230 ms p99 |
The arithmetic that makes this fit: you never run the 0.6 ms cross-encoder over 10⁴ documents (that would be 10⁴ × 0.6 ms = 6 seconds) — you run it only on the top 100 the LTR model promoted. And you never run the LTR model over 10⁸ documents (10⁸ × 12 µs = 20 minutes) — you run it only on the 10⁴ retrieval surfaced. Each stage is ~10× cheaper-per-unit and ~10× more units than the next; the product stays roughly constant, which is exactly the cascade's design goal. (The widget below lets you push these knobs and watch the budget break.)
What is search-specific — the four divergences from 01
| Aspect | Recsys funnel (01) | Search funnel |
|---|---|---|
| Candidate generation | Query-free: retrieve a user-personalised pool by user×item affinity over a fixed catalogue. | Query-conditioned: retrieve only documents that match this query. The pool is recomputed per request and is disjoint across queries. Lexical (inverted index) and dense (ANN) retrievers run and their lists are fused. |
| Primary training label | Engagement (clicks, watch-time). No external ground truth. | Relevance labels (graded human judgments / qrels) anchor the model; engagement is a debiased secondary signal because clicks are dominated by position. |
| Latency posture | Tight, but a feed can pre-compute and the user tolerates loading. | Tighter and synchronous: the user is waiting on a typed query, expecting an instant answer. Sub-300 ms p99 is typical; head queries served from cache in single-digit ms. |
| Hard floor | None — a worse feed is invisible. | A recall floor: returning nothing, or obviously-wrong results, for an answerable query is a visible failure. This is why retrieval optimises recall hard and why zero-result handling is a dedicated subsystem. |
The extra query-understanding stage at the front is the structural addition that has no recsys counterpart: before you can retrieve, you must turn the raw string into something the index can be queried with (tokens, corrected spelling, an intent label, extracted facets like price < 200). That stage is lesson 41.
Interactive · search funnel explorer
Set the corpus size and the keep-counts and per-document costs at each stage. The widget computes how many candidates survive each cut, the total estimated latency, and a verdict that flags either a recall risk (you cut so hard that good documents are likely lost before ranking) or a latency-budget overrun (you kept so many that the per-stage scorers blow the p99 budget).
Metrics preview — relevance, not just clicks
Because search has the relevance contract, its metrics differ in kind from recsys CTR / watch-time. A preview (full treatment in 48, building on the NDCG / interleaving foundations in 08):
| Metric | What it measures | Why search needs it |
|---|---|---|
| NDCG with graded judgments | Ranking quality against human relevance grades (e.g. Perfect / Excellent / Good / Fair / Bad → gains 4/3/2/1/0), discounted by position. | The anchor metric. Uses an external relevance ground truth, not engagement — directly tied to the contract. Graded (not binary) because "the right page" and "an okay page" are different outcomes. |
| Success rate / abandonment | Fraction of sessions where the user got what they wanted (a satisfied click / long dwell / task completion) vs. abandoned with no useful click. | Captures the short, goal-directed session shape. A high CTR with high abandonment can mean clickbait — the click was a mistake the user backed out of. |
| Reformulation rate | How often the user re-types / refines the query within a session. | A reformulation is a signal the first query failed — either the system misunderstood intent or the results missed. A recsys has no analogue; there's no query to reformulate. |
| Queries-per-session / ambiguity signals | Whether multiple queries reflect a multi-step task (good) or repeated failure (bad), and click-entropy as an ambiguity proxy. | Distinguishes a productive research session from a frustrated one, and flags ambiguous queries (java) that need diversification or disambiguation. |
The contrast with recsys is sharp: a feed's north-star is long-term engagement (watch-time, retention), and CTR is a within-list proxy. Search's north-star is task success / sustained trust, with graded relevance as the offline proxy and abandonment / reformulation as the online proxies. Optimising search to a feed's CTR objective is precisely the failure mode the relevance contract warns against.
Interview prompts you should be ready for
- "What actually changes when you move from a recommender to a search engine?" (The senior answer names the query as both a stated intent and a relevance contract, then traces the four downstream changes — query-conditioned candidates, relevance as the primary label, shorter higher-precision sessions, and a hard recall floor — rather than just saying "search has a query box.")
- "You're told to maximise click-through on a search engine. What goes wrong?" (Pure CTR rewards clickbait and lexical-lookalike results, ignores the relevance contract, and is corrupted by position bias. The senior reframes: relevance is the constraint, engagement the refinement; the real objective is task success and long-term trust, anchored offline on graded relevance judgments.)
- "How does query intent type change your retrieval and ranking?" (Navigational → one exact answer, suppress diversity, exact-match path dominates; informational → diverse high-coverage set, dense retrieval shines, dedup near-duplicates; transactional → structured catalogue retrieval with facets and inventory. The junior treats every query the same.)
- "Your query distribution is Zipfian. What are the two systems consequences?" (One: a tiny head carries a large share of volume — cache the top-N SERPs for a big load cut, with short TTLs for freshness. Two: the tail is fat, so zero-result handling, spelling, relaxation and semantic fallback are first-class subsystems. Bonus: don't sample eval queries by volume or you only measure the easy head.)
- "Map the search funnel onto the recsys cascade and name what's different." (Same retrieve→rank→rerank arithmetic, plus a query-understanding front stage; candidates are query-conditioned not user-personalised, the label is relevance not engagement, latency is tighter and synchronous, and there's a hard recall floor. Walk the funnel-size numbers to show the per-stage budget closes under 300 ms.)
- "Why can't you evaluate search with the same metrics as a feed?" (A feed optimises long-term engagement with CTR as a proxy and has no external ground truth. Search has a relevance contract, so it anchors on graded NDCG against human judgments and watches abandonment / reformulation — signals that have no recsys analogue because there's no query to satisfy or reformulate.)