Today you finish BM25 by folding document length into the saturation denominator, so a term in a long document counts for less than the same term in a short one. This completes the ranking function your search will use.
Compute full BM25, idf * tf*(k1+1) / (tf + k1*(1 - b + b*dl/avgdl)), with k1 = 1.5 and b = 0.75.
Yesterday’s saturation ignored length; today you put it back. BM25 scales the k1
term in the denominator by 1 - b + b * (dl / avgdl), where dl is the document’s
length and avgdl the average. A document of exactly average length leaves the
factor at 1 and scores 1.4286 here; a document twice as long inflates the
denominator and drops to 1.0811. The parameter b (use 0.75) sets how hard
length is penalized - 0 disables it entirely.
Multiply by the term’s idf, sum over the query terms, and you have the full BM25
score for a document. Swap it in as your ranking function and run a query over a few
documents: this is the same formula behind Lucene, Elasticsearch, and most
production search today. Your engine now ranks like the real thing.
K1, B = 1.5, 0.75def bm25(idf, tf, dl, avgdl):denom = tf + K1 * (1 - B + B * dl / avgdl)return idf * (tf * (K1 + 1)) / denom
Robertson & Zaragoza, "The Probabilistic Relevance Framework: BM25 and Beyond" (2009).