Library / Documentation
Lucene BM25Similarity
The class implementing BM25 in Lucene, the library under Elasticsearch and Solr.
By Apache Lucene · Apache Software Foundation · Added 2026-10-12
All content belongs to Apache Lucene / Apache Software Foundation. We only link to it and give credit. Report a problem
Learn this doc
About 30 min left. Your work is saved as you go.
What you want from these docs
Apache Software Foundation documents a tool you may use. Decide what you want to do with it, then read only what answers that.
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:
- Saturation: the first few occurrences of a term matter a lot; the twentieth barely matters.
- 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 andavgdlthe 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.
100%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.
100%Figure SDA-SAG-02-2 · Retrieval then re-ranking · © forcsguy.com
Real-world reading
Related reading
- The Probabilistic Relevance Framework: BM25 and BeyondStephen Robertson and Hugo Zaragoza · City University of LondonPapers
- Practical BM25, part 2: the BM25 algorithm and its variablesElasticBlogs
- Lucene BM25SimilarityApache Lucene · Apache Software FoundationDocumentation
- Elasticsearch GuideElasticDocumentation
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
100%After: sharded, scatter and gather
100%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.
100%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.
100%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
Related reading
Search, Analytics and Geo · 10 minNearby search with Redis GEOHow Redis stores locations in a sorted set using geohash scores, how GEOSEARCH finds nearby members, how to expire stale entries, and when to move to a spatial database.Read here
The problem
"Show the nearest available drivers", "stores within 3 km", "friends nearby". The data is a large set of points that change often, and the question is always about a small neighbourhood. You want a fast, in-memory structure.
How Redis does it
Redis stores locations in an ordinary sorted set. Each member (a driver id) has a score that is a 52-bit geohash of its longitude and latitude. Geohash interleaves the bits of the two coordinates so that nearby places have nearby scores. A search turns a radius into a few score ranges, reads those ranges, then filters by exact distance.
100%Figure SDA-SAG-09-1 · A nearby query in Redis · © forcsguy.com
Typical commands:
GEOADD drivers:blr 77.5946 12.9716 driver:42 GEOSEARCH drivers:blr FROMLONLAT 77.60 12.97 BYRADIUS 2 km ASC COUNT 20 WITHDISTDesign details
- Partition the key. One sorted set for the whole planet is a hot spot and slow to search. Use a key per city, region or grid cell so each set is small and local, and shard keys across nodes.
- Updates are cheap. Moving a member is another GEOADD, about O(log n). 75,000 updates per second is manageable on a few nodes.
- Freshness. Sorted set members have no individual expiry. Keep a second sorted set
lastseenwith the update time as score, and run a job that removes members older than, say, 30 seconds from both sets. - Availability filter. Store only drivers who are available, or look up status after the search; keep the hot set small.
- Accuracy. Redis uses a spherical model of the Earth, so distances can be off by a fraction of a percent, and results near a cell boundary rely on the exact filter. That is fine for "within 2 km", not for surveying.
- Durability. Treat Redis as a rebuildable copy. The system of record is a database, and clients re-send their location every few seconds anyway.
100%Figure SDA-SAG-09-2 · Keeping the hot set fresh · © forcsguy.com
When Redis GEO is not enough
Move to a spatial database such as PostGIS, or a purpose-built index, when you need polygons and fences, joins with other tables, constraints (opening hours, vehicle type) together with distance, or durable queries over history. Many systems combine them: a database for rich queries and Redis for the live, hot position data.
Other grid systems are covered in the geofencing lesson and in the real-world reading: H3 (hexagons, good for aggregation and neighbours) and S2 (cells on a sphere).
Real-world reading
Go deeper on this
A path through videos, articles, other case studies and code on the same ideas as Lucene BM25Similarity.
Warm upGet the vocabulary first.
Books · Free from the author
Mining of Massive Datasets
Large-scale data processing: MapReduce, similarity search, recommendation and streams. The authors offer the whole book as a free download.
Stanford UniversityAdvanced
Books · Free from the author
Introduction to Information Retrieval: assessing relevance
How relevance judgments are collected and how agreement between judges is measured.
Stanford UniversityIntermediate
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)
ReadEngineers explain it in their own words.
Papers
The Probabilistic Relevance Framework: BM25 and Beyond
The survey that explains where BM25 comes from, its parameters and extensions such as BM25F.
City University of LondonAdvanced
Blogs
Algolia Blog
Read our latest blogs about engineering.
AlgoliaIntermediate
See it in another companyThe same ideas, a different system.
Case Studies · Read here
Aroma: Using ML for Code Recommendation at Facebook
Thousands of engineers write the code to create our apps, which serve billions of people worldwide. This is no trivial task—our services have grown so diverse and complex that the codebase contains…
FacebookIntermediate
Case Studies · Read here
Cross-Lingual End-to-End Product Search with Deep Learning at Zalando
How We Built the Next Generation Product Search from Scratch using a Deep Neural Network
ZalandoIntermediate
Keep learning
Loading community…