induwara.lk
induwara.lkAI · Search & ranking

BM25 Score Calculator (Okapi BM25)

Score a query against a corpus and see exactly why each document ranks where it does. The tool shows every term's IDF, term frequency and contribution — the same Okapi BM25 formula Lucene, Elasticsearch and OpenSearch use. Runs in your browser, no signup.

By Induwara AshinsanaUpdated Jul 10, 2026
Score & rank a corpusOkapi BM25
Lucene-verified
Try

3 documents · up to 50. Blank lines are ignored. No stemming or stop-word removal — “cat” ≠ “cats”.

Range 0–3. Default 1.2. Higher = more reward for repeated terms.

Range 0–1. Default 0.75. 0 = ignore length, 1 = full normalization.

IDF variant
Tokenization
Documents (N)
3
Avg length (avgdl)
5.6667
tokens
Query terms scored
2
cat · sat
Top BM25 score
1.4167

Ranked results

#Document|D|BM25
1the cat sat on the mat61.4167
2the dog sat on the log60.4590
3cats and dogs are friends50.0000

Per-term breakdown

Rank 1: the cat sat on the mat

Termf(qᵢ,D)n(qᵢ)IDFContribution
cat110.98080.9578
sat120.47000.4590
Total BM25 score1.4167

Runs fully in your browser — nothing is uploaded.

How it works

BM25 (“Best Matching 25”), also called Okapi BM25, is the ranking function that decides which documents a keyword search returns first. For a query Q and a document D drawn from a corpus of N documents, the score is the sum of a per-term contribution over each distinct query term qᵢ(Robertson & Zaragoza, 2009):

BM25(D,Q) = Σᵢ  IDF(qᵢ) · f(qᵢ,D)·(k1+1)
                    ───────────────────────────────
                    f(qᵢ,D) + k1·(1 − b + b·|D|/avgdl)

Here f(qᵢ,D) is how many times the term appears in the document (term frequency), |D|is the document's length in tokens, and avgdl is the average document length across the corpus. Two parameters tune the behaviour:

  • k1 (default 1.2) sets term-frequency saturation. A term appearing twice does not score twice as high as once — the denominator grows with each occurrence, so extra repeats add less and less. This is BM25's signature difference from plain TF-IDF.
  • b (default 0.75) sets length normalization. At b=0 length is ignored; at b=1 a document twice the average length is penalised in full, stopping long documents from winning just by containing more words.

The inverse document frequency IDF(qᵢ) rewards rare terms. With n(qᵢ) the number of documents containing the term, this tool offers two forms. The default Lucene variant, ln(1 + (N − n + 0.5)/(n + 0.5)), is always non-negative and is what Elasticsearch and OpenSearch ship. The classic Robertson–Spärck-Jones variant, ln((N − n + 0.5)/(n + 0.5)), goes slightly negative for terms in more than half the corpus — which is why a BM25 score can be negative.

The tool tokenizes the query and every document identically: lowercase (unless case sensitivity is on) then split on runs of non-alphanumeric characters. There is no stemming or stop-word removal, so results stay reproducible and “cat” is a different term from “cats”. It then counts term frequencies, computes each term's IDF, sums the contributions per document, and sorts descending. Every number in the breakdown is the exact value that feeds the total.

Worked examples

Query “cat sat” · D1 “the cat sat on the mat” · Lucene, k1=1.2, b=0.75

  1. Corpus: N = 3, lengths 6/6/5, avgdl = 17/3 = 5.6667
  2. Length norm for D1: 1.2·(0.25 + 0.75·6/5.6667) = 1.2529
  3. cat: n=1, IDF = ln(1 + 2.5/1.5) = 0.9808, tf=1
  4. contribution = 0.9808·2.2/(1 + 1.2529) = 0.9578
  5. sat: n=2, IDF = ln(1.6) = 0.4700, tf=1
  6. contribution = 0.4700·2.2/(1 + 1.2529) = 0.4590
  7. BM25(D1) = 0.9578 + 0.4590 = 1.4167
  8. D2 has only sat → 0.4590, so D1 outranks D2.

Term-frequency saturation · Query “the” · D1, Lucene

  1. the: n=2, IDF = ln(1.6) = 0.4700, and in D1 tf=2
  2. contribution = 0.4700·(2·2.2)/(2 + 1.2529) = 0.6357
  3. A term seen once at the same IDF and length scored 0.4590.
  4. Doubling tf 1→2 raised the score only 1.386× — not 2×.
  5. That saturation is the k1 effect BM25 is known for.

Negative score · Query “the” · D1, Classic RSJ variant

  1. “the” is in 2 of 3 documents — more than half the corpus.
  2. Classic IDF = ln((3 − 2 + 0.5)/(2 + 0.5)) = ln(0.6) = −0.5108
  3. contribution = −0.5108·(2·2.2)/(2 + 1.2529) = −0.6910
  4. BM25(D1) = −0.6910 — negative, because the term is too common.
  5. Switch back to Lucene IDF and the same term scores +0.6357.

Frequently asked questions

Sources & references

The formula, IDF variants and parameter defaults on this page were last cross-checked against these sources on 2026-07-10. The worked examples are reconciled by hand to four decimal places.

Related tools

Rate this tool
Be the first to rate

Comments & feedback

Spotted a bug or want an improvement? Tell us — our team reviews every comment, and good ideas get built. Comments are public and anonymous.

Found a bug, edge case, or want to suggest an improvement?

Email me at [email protected] — most fixes ship within 24 hours.