BM25 and Inverted Indexes
An inverted index maps words to the documents that contain them, and BM25 scores those matches by balancing term frequency against how rare the word is overall.
Why Does This Exist?
Before the era of dense vector embeddings and neural networks, search engines relied entirely on lexical (keyword-based) search. If you typed "red shoes" into a search bar, the engine looked for documents that contained the exact words "red" and "shoes".
Even today, in the age of generative AI and semantic search, lexical search remains a foundational technology. It is exceptionally fast, highly explainable, and excels at exact-match queries where semantic search often fails (e.g., searching for a specific product ID, a rare technical term, or a person's name).
The industry standard for lexical search is built on two core components: the Inverted Index (the data structure that makes searching fast) and BM25 (the mathematical formula that scores how relevant a document is to a query). Modern retrieval systems, particularly Retrieval-Augmented Generation (RAG) pipelines, almost always use a hybrid approach that combines BM25 for exact keyword matching with dense embeddings for semantic understanding.
Think of It Like This
The index at the back of a textbook
Imagine you are reading a massive 1,000-page history textbook and you want to find information about "Abraham Lincoln".
If you didn't have an index, you would have to start at page 1 and read every single word until you found his name. This is a "forward index," and it's how data is naturally stored.
Instead, you flip to the back of the book to the Index. There, you find the word "Lincoln, Abraham" followed by a list of pages: 42, 105, 212, 450-455. The book's index is literally an inverted index. It maps the content (the word) back to its location (the page).
But which page is the most relevant? Page 42 might just mention him in passing (low Term Frequency). Pages 450-455 dedicate an entire chapter to him (high Term Frequency). Furthermore, the word "President" appears on almost every page (low Inverse Document Frequency), so finding "President" isn't as helpful as finding "Lincoln". BM25 is the mathematical formula that calculates which of those pages is the most useful based on these factors.
How It Actually Works
The Inverted Index
An inverted index is a hash map (or dictionary) where the keys are every unique term (word or token) in your entire corpus, and the values are lists of document IDs that contain that term.
- Tokenization: Every document in the database is split into tokens. Punctuation is removed, words are lowercased, and often reduced to their root forms (stemming). Stop words (like "the", "is", "and") are frequently discarded.
- Indexing: For each token, the document's ID is appended to the token's list in the index.
- Retrieval: When a query arrives (e.g., "fast car"), the engine splits it into "fast" and "car". It looks up "fast" in the inverted index and gets a list of document IDs (e.g.,
[Doc1, Doc4, Doc9]). It looks up "car" and gets[Doc4, Doc7]. The intersection or union of these lists determines the candidate documents.
Because hash map lookups are , an inverted index allows a search engine to instantly find all documents containing a keyword, even if the database contains billions of records.
The BM25 Algorithm
Once you have the candidate documents, you need to rank them. Best Matching 25 (BM25) is a robust variation of TF-IDF (Term Frequency-Inverse Document Frequency) that fixes some of its flaws. It scores a document for a query (containing terms ) based on three main factors:
- Term Frequency (TF): How many times does the query term appear in the document? More appearances = higher score.
- Inverse Document Frequency (IDF): How rare is the query term across the entire database? If a word appears in every document (like "the"), it is useless for distinguishing relevance, so it gets a low IDF. If a word is rare (like "chupacabra"), finding a document that contains it is a strong signal, so it gets a high IDF.
- Document Length Normalization: This is where BM25 shines compared to raw TF-IDF. A 10,000-word document is more likely to contain the word "apple" just by chance than a 50-word document. BM25 penalizes long documents, leveling the playing field so short, highly-focused documents can rank well.
The BM25 Formula
The score of document for query is the sum of the scores for each term :
- is the term frequency of in .
- is the length of the document.
- is the average document length in the corpus.
- is a tuning parameter (usually 1.2 to 2.0) that caps the effect of term frequency. (Seeing a word 10 times is better than 1 time, but seeing it 100 times isn't much better than 10 times).
- is a tuning parameter (usually 0.75) that controls how much document length penalizes the score.
Show Me the Code
You don't usually implement an inverted index from scratch (you'd use Elasticsearch or OpenSearch). However, the rank_bm25 Python package allows us to easily compute BM25 scores to see how it works in memory.
from rank_bm25 import BM25Okapi
# 1. Our corpus of documentscorpus = [ "The quick brown fox jumps over the lazy dog", "A fast brown fox", "The dog is lazy", "Artificial intelligence is fascinating", "The fast fox is not lazy"]
# 2. Tokenize the corpus (simple whitespace split for demonstration)tokenized_corpus = [doc.lower().split(" ") for doc in corpus]
# 3. Initialize the BM25 indexbm25 = BM25Okapi(tokenized_corpus)
# 4. Our queryquery = "fast fox"tokenized_query = query.lower().split(" ")
# 5. Get the BM25 scores for each documentscores = bm25.get_scores(tokenized_query)
print("BM25 Scores:")for i, score in enumerate(scores): print(f"Doc {i}: {score:.4f} - '{corpus[i]}'")
# -> BM25 Scores:# -> Doc 0: 0.1705 - 'The quick brown fox jumps over the lazy dog'# -> Doc 1: 1.5833 - 'A fast brown fox'# -> Doc 2: 0.0000 - 'The dog is lazy'# -> Doc 3: 0.0000 - 'Artificial intelligence is fascinating'# -> Doc 4: 1.1396 - 'The fast fox is not lazy'
# Doc 1 wins because it contains both terms ("fast" and "fox") # and is shorter than Doc 4, meaning those words represent a # higher density of the document's total content.Watch Out For
Failing on synonyms and typos
Because BM25 relies on exact lexical matches via the inverted index, it is incredibly brittle to vocabulary mismatch. If the document says "automobile" and the user searches for "car", BM25 will return a score of zero. If the user misspells "receive" as "recieve", BM25 fails. This is exactly why dense vector embeddings (semantic search) were invented, and why modern systems combine both approaches.
The Quick Version
- Lexical search relies on exact keyword matching, which is fast and explainable.
- An Inverted Index is a hash map connecting every unique word to a list of documents containing that word, enabling lookup times.
- BM25 is the industry-standard algorithm used to score the relevance of the documents found in the inverted index.
- BM25 improves upon TF-IDF by capping the reward for repeating the same word, and by penalizing long documents that contain the word simply by chance.
What to Read Next
- Read Learned Sparse Retrieval (like SPLADE) to see how neural networks can inject synonyms and contextual weights directly into an inverted index.
- Read Hybrid Search to understand how BM25 scores are mathematically combined with dense embedding scores (via Reciprocal Rank Fusion) in modern RAG pipelines.
- Read Embedding Models to understand the semantic alternative to lexical search.