Term-Based Retrieval with BM25
Term extraction is faster than generating an embedding, and looking up which documents contain a term can be cheaper than a nearest-neighbor search.
Why Scanning Every Vector Gets Expensive
Imagine a collection of embeddings large enough that comparing a query against every stored vector is slow. Each comparison is cheap on its own, but multiplied across millions of stored items, the cost of answering one query grows quickly. Vector search algorithms exist to avoid that full comparison. Instead of checking everything, they organize the stored vectors ahead of time so that a query only needs to look at a promising subset.
The trade-off is that the answer becomes approximate. The search may skip some vectors that a full scan would have found. Two of the best-known strategies for this are IVF and Annoy. IVF groups similar vectors into clusters and searches only the clusters that look promising. Annoy builds several random tree structures and gathers candidates from the places a query lands in them. Both accept a small loss of exactness in exchange for a large gain in speed.
Grouping Vectors Into Clusters
An IVF index is built by grouping. Each stored vector is placed into one cluster, so that vectors near one another in the embedding space tend to share a cluster. The grouping is what lets a query skip most of the database. The index is organized by cluster rather than by individual vector, which means the search can reason about whole groups at once.
The diagram above uses invented vector labels. The highlighted cluster is the one whose centroid sits closest to the query. The other clusters stay unsearched, which is where the speed comes from.
Tracing an IVF Query Step by Step
Searching a three-cluster IVF index
An index holds eight vectors, split into three clusters with centroids C1, C2, and C3. A new query is closest to C2. Which vectors does the search compare against the query?
- 1Compare the query with the centroids
The search measures how close the query is to C1, C2, and C3. These three comparisons are cheap compared with checking all eight stored vectors.
- 2Select the closest centroid
The query is closest to C2, so cluster 2 is the one the search will examine.
- 3Gather the members of that cluster
Cluster 2 holds v2 and v5 in this invented example. Those two vectors are compared with the query.
- 4Skip the other clusters
The members of cluster 1 (v1, v4, v7) and cluster 3 (v3, v6, v8) are not compared. If one of them was a true close match, the approximate search can miss it.
Only v2 and v5 are compared directly. Six of the eight stored vectors are skipped, which is the source of IVF's speed and of its approximate character.
What do you think happens?
Given the query is closest to C2, which vectors are compared against the query?
Reveal answer
Only the vectors in cluster 2
IVF searches the clusters whose centroids are closest to the query. The vectors in the other clusters are not compared with the query.
Collecting Candidates with Annoy Trees
Annoy, which stands for Approximate Nearest Neighbors Oh Yeah, takes a different route from IVF. It is a tree-based approach introduced by Bernhardsson in 2013. Rather than computing centroids, Annoy builds a number of binary trees. Each tree divides the vectors using random criteria. One simple way to picture a split is to draw a random line through the space, send the vectors on one side into one branch, and send the vectors on the other side into the other branch. Repeating this splitting produces a tree whose leaves hold groups of vectors.
The diagram shows the idea with invented labels. Annoy does not rely on one tree. It builds several trees, each with its own random splits. During a search, the algorithm traverses these trees and gathers candidate neighbors from the leaves the query reaches. Because each tree splits the space differently, a neighbor missed by one tree may be caught by another. In the diagram, v4 appears in both leaves, while v1 and v2 each come from only one tree. Combining several trees improves the chance of collecting the true close matches.
Annoy is also a practical example of industrial use. Spotify has open sourced its implementation of the approach. The same broad goal is shared by other libraries the source names, namely SPTAG, which stands for Space Partition Tree And Graph and comes from Microsoft, and FLANN, which stands for Fast Library for Approximate Nearest Neighbors. You do not need to memorize their internals to see the pattern: each one trades a small amount of exactness for a large gain in speed.
Term-Based and Embedding-Based Retrieval Compared
Long before embeddings, retrieval relied on matching terms, and that history has produced many mature solutions. This is why both term-based and embedding-based retrieval are relatively easy to start with. Each approach has strengths, and the trade-offs the source describes can be laid out side by side.
| Dimension | Term-based retrieval | Embedding-based retrieval |
|---|---|---|
| Speed | Generally faster, since term extraction is quicker than generating an embedding and term lookup can be cheaper than a nearest-neighbor search | Generally slower because each item needs an embedding and a nearest-neighbor search |
| Out-of-the-box use | Works well out of the box | Usable from the start, but its strengths appear after adaptation |
| Tuning | The source does not describe a tuning path for term-based retrieval | Can be improved over time, and the source notes it can be tuned to outperform term-based retrieval |
| Keyword handling | Matches exact terms directly | Can blur exact strings, so specific values such as EADDRNOTAVAIL (99) may be harder to find |
Trade-offs between term-based and embedding-based retrieval, as described in the source
Keyword Handling and the EADDRNOTAVAIL Case
The speed gap has a clear cause. Term extraction is faster than generating an embedding, and looking up which documents contain a term can be cheaper than a nearest-neighbor search. The strength of embedding-based retrieval is that it can be improved over time. Its weakness is that embeddings can blur exact strings. A specific error code such as EADDRNOTAVAIL (99) can therefore become harder to find later on.
Measuring Retrieval Quality
A retriever is judged by the quality of the data it returns. Two metrics are often used by RAG evaluation frameworks: context precision and context recall, often called precision and recall for short. The source notes that context precision is also called context relevance. Precision asks how much of what was retrieved is actually relevant. Recall asks how much of the relevant material was retrieved at all.
These two measures are the natural way to judge an approximate method such as IVF or Annoy, because those methods can trade a missed neighbor for speed. A search that skips clusters or trees may return fewer irrelevant items, which helps precision, yet it may also miss relevant items, which hurts recall.
Scoring one retrieval result
A retriever returns four documents for a query. Three of those four are relevant. In total, six documents in the collection are relevant to the query. What are the precision and recall of this result?
- 1Count the relevant items retrieved
Three of the four returned documents are relevant.
- 2Compute precision
Precision looks at what was retrieved. Three out of four retrieved documents are relevant, which is a precision of 75 percent.
- 3Compute recall
Recall looks at what should have been found. Three out of the six relevant documents were retrieved, which is a recall of 50 percent.
- 4Interpret the pair
The results are fairly clean, but half of the relevant material was missed. An approximate search that skipped a cluster could produce exactly this pattern.
Context precision is 75 percent and context recall is 50 percent for this invented example.
Common Mistakes When Reading Approximate Search
Common mistake Assuming an IVF search compares the query with every stored vector
What it looks likeTreating a three-cluster index as if all eight vectors are checked for every query
Why it failsIVF searches only the clusters whose centroids are closest to the query, so the other clusters are never compared directly.
Do this insteadCompare the query with centroids first, then search the members of the chosen clusters.
Common mistake Believing one Annoy tree is enough to find every close neighbor
What it looks likeUsing a single random tree and expecting the leaf it reaches to hold all true neighbors
Why it failsEach tree splits the space differently, and a neighbor missed by one tree may be caught by another.
Do this insteadBuild multiple trees and gather candidates from the leaves the query reaches in each one.
Common mistake Using embedding-based retrieval alone for exact error codes
What it looks likeSearching for EADDRNOTAVAIL (99) and expecting a meaning-level match to return it reliably
Why it failsEmbeddings can blur exact strings, so the specific code can become harder to find.
Do this insteadCombine embedding-based retrieval with term-based retrieval, as the source suggests.
Common mistake Judging a retriever by precision alone
What it looks likeReturning two very relevant documents and calling the retriever excellent without checking how many relevant documents exist
Why it failsPrecision says nothing about relevant documents that were never retrieved, which is what recall measures.
Do this insteadReport context precision and context recall together.
Practice Tracing Candidates
A three-cluster IVF index has clusters with members {v1, v4}, {v2, v5, v6}, and {v3, v7, v8}. A query is closest to the centroid of the second cluster. List which vectors are compared with the query, and say how many comparisons were skipped out of the eight stored vectors.
Hints
- First identify which centroid the query is closest to, then list only that cluster's members.
- Compare the number of members you listed with the total of eight stored vectors.
A retriever returns five documents for a query. Four are relevant. Eight documents in the collection are relevant overall. Compute context precision and context recall, then explain which one is lower and what that suggests about the search.
Hints
- Precision uses the five retrieved documents as the denominator.
- Recall uses the eight relevant documents as the denominator.
Key Takeaways
- IVF groups stored vectors into clusters with K-means, then searches only the clusters whose centroids are closest to the query, so most vectors are never compared directly.
- Annoy builds several random binary trees, and a query gathers candidate neighbors from the leaves it reaches in each tree, so a neighbor missed by one tree may be caught by another.
- Term-based retrieval is generally faster and works well out of the box, while embedding-based retrieval can be tuned to improve but may blur exact keywords, so combining both is a common remedy.
- Context precision measures how much of what was retrieved is relevant, while context recall measures how much of the relevant material was retrieved, and together they judge approximate methods.