Library / Papers

The Probabilistic Relevance Framework: BM25 and Beyond

The survey that explains where BM25 comes from, its parameters and extensions such as BM25F.

AdvancedSearch

By Stephen Robertson and Hugo Zaragoza · City University of London · Added 2026-10-12

Read the paper

All content belongs to Stephen Robertson and Hugo Zaragoza / City University of London. We only link to it and give credit. Report a problem

Learn this paper

About 37 min left. Your work is saved as you go.

What you are about to study

City University of London wrote this paper. Read the abstract and headings first. It assumes you are comfortable with distributed systems.

Ideas you will meet

Search
Inverted indexes, relevance, autocomplete and search engines.

What we already know about this

Our own lessons on these ideas. Read them here before you start, so it makes sense.

  • Search, Analytics and Geo · 11 minBM25: the ranking function behind most search enginesHow BM25 adds saturation and length normalisation to TF-IDF, what k1 and b control, and how to compute a score by hand.Read here

    Two fixes to TF-IDF

    BM25 ("best matching 25") keeps the idea of weighting rare terms, and adds two corrections:

    1. Saturation: the first few occurrences of a term matter a lot; the twentieth barely matters.
    2. Length normalisation: a term appearing three times in a tweet means more than three times in a book.

    The formula

    For a query with terms q, the score of document D is the sum over terms of:

    IDF(q) x f(q,D) x (k1 + 1) / ( f(q,D) + k1 x (1 - b + b x |D| / avgdl) )

    • f(q,D) is how many times the term occurs in D.
    • |D| is the length of D and avgdl the average document length in the index.
    • k1 (about 1.2) sets how fast the term saturates.
    • b (about 0.75) sets how strongly length matters.
    • A common IDF is ln(1 + (N - n + 0.5) / (n + 0.5)), which never goes negative.

    Figure SDA-SAG-02-1 · What BM25 adds on top of IDF · © forcsguy.com

    A worked example

    With k1 = 1.2, b = 0.75 and a term appearing 3 times:

    • Average-length document: 3 x 2.2 / (3 + 1.2) = 1.57.
    • Twice the average length: 3 x 2.2 / (3 + 1.2 x 1.75) = 6.6 / 5.1 = 1.29.
    • Same document but the term appears 30 times at average length: 30 x 2.2 / (30 + 1.2) = 2.12, close to the ceiling of k1 + 1 = 2.2.

    So ten times as many occurrences is worth only 35 percent more. That is saturation.

    // Minimal BM25 term score, ready to use in a toy engine
    function bm25(f: number, len: number, avgLen: number, N: number, n: number, k1 = 1.2, b = 0.75) {
      const idf = Math.log(1 + (N - n + 0.5) / (n + 0.5));
      return idf * (f * (k1 + 1)) / (f + k1 * (1 - b + (b * len) / avgLen));
    }
    

    Tuning in practice

    • Start with the defaults; they work for general text.
    • Short fields such as titles often want a lower b (length matters less), while long bodies keep the default.
    • Fields have different value: a match in the title usually counts for more than in the body, so engines let you weight fields (BM25F style) or run separate fields and combine the scores.
    • BM25 is a first-stage ranker. Many systems retrieve a few hundred candidates with BM25 and re-rank them with a machine-learned model that also uses freshness, clicks and personalisation.
    • Measure changes with the metrics in the evaluation lesson; never tune by looking at three queries.

    Figure SDA-SAG-02-2 · Retrieval then re-ranking · © forcsguy.com

    Real-world reading

    Open this lesson with its quiz and exercise

  • Search, Analytics and Geo · 11 minMeasuring search: precision, recall, MRR and NDCGHow to know whether a ranking change helped: relevance judgments, the standard metrics, a worked NDCG example, and why offline numbers need online confirmation.Read here

    Why measure at all

    Search quality feels subjective, and tuning by trying a few queries is how regressions ship. A small evaluation set with agreed metrics turns "this feels better" into a number you can track.

    Relevance judgments

    You need, for a sample of queries, a label for each returned document: relevant or not, or a grade such as 0 (bad) to 3 (perfect). Sources:

    • Human raters following written guidelines (best quality, costly).
    • Click and engagement logs (cheap and plentiful, but biased towards what was shown first).
    • Synthetic labels from a strong model, checked against a human sample.

    The core metrics

    • Precision@k: of the top k results, the fraction that are relevant. Good when users only look at the first page.
    • Recall@k: of all relevant documents, the fraction found in the top k. Matters when missing something is costly, such as legal or support search.
    • MRR (mean reciprocal rank): average of 1 / rank of the first relevant result. Good for "find the one right answer" queries.
    • DCG and NDCG: use graded relevance and discount lower positions. DCG = sum of (2^grade - 1) / log2(position + 1). NDCG divides by the DCG of the ideal order, giving a value from 0 to 1.

    Figure SDA-SAG-04-1 · Computing NDCG · © forcsguy.com

    A worked example

    Results graded 3, 0, 2 (in the order returned):

    PositionGradeGain 2^g - 1Discount log2(p+1)Contribution
    1371.007.00
    2001.580.00
    3232.001.50

    DCG = 8.5. The ideal order (3, 2, 0) gives 7 + 3 / 1.58 = 8.89. NDCG = 0.96. Swapping the second and third results would have scored a perfect 1.0.

    Offline versus online

    Offline metrics are fast and repeatable, so use them to reject bad ideas and compare candidates. They are still a proxy. Confirm winners with an online experiment: split live traffic, compare click-through, successful sessions, reformulation rate and latency, and watch for segments that got worse (new users, rare queries).

    Figure SDA-SAG-04-2 · Offline first, then online · © forcsguy.com

    Pitfalls

    • A tiny or unrepresentative query set overfits quickly.
    • Averages hide failures: also look at the worst queries and at queries with no results.
    • Click data rewards what was already ranked high; use it alongside, not instead of, judged sets.

    Real-world reading

    Open this lesson with its quiz and exercise

  • Search, Analytics and Geo · 11 minSharding a search index: scatter, gather and mergeHow a search index is split across machines, why queries fan out to every shard, how top-k results are merged, and what goes wrong with deep paging and uneven shards.Read here

    Why shard

    One machine cannot hold or search a very large index within the latency budget. Sharding splits the index into pieces, each on its own machine, so storage and work are spread out. Replicas copy each shard for availability and extra read capacity.

    Before: one big index

    After: sharded, scatter and gather

    What changed

    • Storage and work are spread over machines
    • Each shard returns its own top results to be merged
    • The slowest shard decides how fast the query is

    Two ways to partition

    • Document-partitioned (local index): each shard indexes a subset of the documents and holds a complete inverted index for them. Writes touch one shard. Every query must visit every shard. This is what Elasticsearch and Solr do.
    • Term-partitioned (global index): each shard holds the postings for a subset of terms. A query touches only the shards for its terms, but a write touches many shards and long posting lists must be combined across machines. It is rare in practice.

    Figure SDA-SAG-05-1 · Scatter, gather and merge · © forcsguy.com

    Decisions that matter

    • Shard size and count. Too many tiny shards waste memory and add fan-out; too few huge shards recover slowly and cannot spread load. Many teams aim for tens of gigabytes per shard. The primary shard count is hard to change later, so plan for growth or use time-based indexes.
    • Routing. By default a document goes to a shard by hashing its id, which spreads data evenly. Routing by tenant or user keeps one customer's data together so queries can hit a single shard, at the risk of uneven shards.
    • Tail latency. A query is as slow as its slowest shard. Use replicas, timeouts, and hedged requests (send to a second replica if the first is slow).
    • Deep pagination. Page N needs the first N x pageSize results from every shard. Use cursors (search-after) instead of large offsets.
    • Scoring across shards. Each shard computes statistics such as IDF from its own documents. Even, large shards make the difference small; otherwise gather global statistics first.
    • Rebalancing and reindexing. Plan how to add capacity without downtime, usually by indexing into a new index and switching an alias.

    Figure SDA-SAG-05-2 · Time-based indexes · © forcsguy.com

    Keeping the index fresh

    Documents usually live in a primary database and are copied to the search index. Use a change stream or an outbox so updates are not lost, accept that search is eventually consistent, and keep a way to rebuild the whole index from the source of truth.

    Real-world reading

    Open this lesson with its quiz and exercise

Go deeper on this

A path through videos, articles, other case studies and code on the same ideas as The Probabilistic Relevance Framework: BM25 and Beyond.

  1. Warm upGet the vocabulary first.

  2. WatchHow others solve the same problem, on video.

    Videos belong to their creators and play from YouTube. More on YouTube (opens in a new tab)

  3. ReadEngineers explain it in their own words.

  4. See it in another companyThe same ideas, a different system.

Keep learning

Loading community…