Milvus BM25: How Sparse Keyword Retrieval Works Alongside Vector Search

How Milvus BM25 sparse retrieval works, when to combine it with dense vector search, and why exact-term matching protects labelled image dataset curation.

Milvus BM25: How Sparse Keyword Retrieval Works Alongside Vector Search
Written by TechnoLynx Published on 11 Jul 2026

A defect-code query returns the wrong images. The embeddings were fine; the retrieval strategy was blind to the one token that mattered. That is the gap Milvus BM25 closes, and why treating a vector database as a pure dense-embedding store quietly corrupts dataset curation.

Here is the failure most teams walk into. You stand up a vector store, embed every image and its caption with a dense model, and rely on cosine similarity for everything — search, deduplication, pulling training samples. It works beautifully for “images that look like a scratched surface.” Then someone searches for part number X48-221B or defect code DFC-07, and the results are a plausible-looking mess. The embedding put X48-221B near X48-220B and X49-221C in vector space, because to a semantic model those strings are almost identical. To your data pipeline, they are entirely different SKUs.

That is the divergence point, and it is not a corner case. When the tokens attached to your images carry meaning — part numbers, defect codes, filenames, annotation tags, camera IDs — semantic embeddings blur exactly the distinctions you need to preserve. Milvus BM25 exists to handle that half of the problem, and the expert move is not to choose between sparse and dense retrieval but to run both.

What’s worth understanding about Milvus BM25 first?

BM25 is a term-frequency ranking function that has been the backbone of full-text search for decades — it is what powers the relevance scoring in Elasticsearch and Lucene. Milvus brought it inside the vector store so you no longer need a separate search engine bolted alongside your embeddings.

Mechanically, Milvus tokenizes the text field, builds an inverted index, and represents each document as a sparse vector: a high-dimensional vector where most entries are zero and the non-zero entries correspond to the terms that actually appear, weighted by how informative they are. The BM25 score rewards a query term appearing in a document, dampens the reward as that term repeats (term-frequency saturation), and penalizes terms that are common across the whole corpus (inverse document frequency). A rare token like a specific defect code carries enormous weight; a filler word like “image” carries almost none.

The practical consequence: BM25 matches on the literal token. Search DFC-07 and you get documents containing DFC-07, ranked by how distinctive that token is — not documents that are semantically “in the neighbourhood.” Milvus computes this over a sparse vector field, which means the same query planner, the same collection, and the same top-k API you already use for dense search now also serve exact-term recall.

These are not two flavours of the same thing. They fail and succeed on opposite inputs, which is precisely why they compose well.

Dense vector search encodes meaning into a fixed-length embedding — typically a few hundred to a couple of thousand dimensions, every entry non-zero — and ranks by geometric proximity. It generalizes: a query for “corrosion on the flange” retrieves images captioned “rust near the joint” even with zero shared words. That generalization is the strength and the weakness. It also means X48-221B and X48-220B collapse toward each other, because the model was never trained to treat serial strings as discrete identities.

BM25 sparse retrieval does the reverse. It has no notion of meaning, only of tokens and their statistical rarity. It cannot connect “corrosion” to “rust,” but it will never confuse two part numbers that differ by one character, because they are different tokens in the inverted index.

Dimension BM25 sparse retrieval Dense vector search
What it matches Literal tokens, weighted by rarity Semantic proximity in embedding space
Vector shape Sparse (mostly zeros, term-indexed) Dense (fixed length, all non-zero)
Strong on Part numbers, defect codes, tags, filenames, rare jargon Paraphrase, synonyms, cross-lingual meaning, visual concepts
Blind to Synonyms, meaning, misspellings Exact-token distinctions between near-identical strings
Index type Inverted index ANN graph / IVF
Tuning knobs k1, b (see below) Embedding model, metric, ANN params

The decision rule is direct: use BM25 when the query hinges on an exact token that must not be confused with a near neighbour; use dense search when the query is about meaning and you want paraphrase and synonym coverage. Most real curation queries need both at once — “pull all DFC-07 samples that show surface damage” is a token constraint and a semantic one — which is what hybrid retrieval is for.

How do you set up hybrid retrieval in Milvus that combines BM25 with dense embeddings?

The shape of a hybrid setup in Milvus is consistent regardless of your embedding model. You define a collection with two vector fields on the same records: a dense field holding your image or caption embedding, and a sparse field that Milvus populates via a BM25 function over the text field. At query time you issue two searches — one per field — and fuse the results.

The steps, with the reasoning behind each:

  1. Schema with two vector fields. One dense field for embeddings, one sparse field wired to a BM25 function on the metadata/caption text. Milvus generates the sparse representation at insert time, so you are not maintaining a second index yourself.
  2. Choose the analyzer deliberately. The tokenizer decides what counts as a term. A default word tokenizer may split X48-221B on the hyphen, destroying the exact-match property you wanted. Configure tokenization so identifier-shaped tokens survive intact — this is the single most common misconfiguration we see when hybrid retrieval “doesn’t find the part number.”
  3. Run both searches and fuse. Milvus supports hybrid search that executes the dense and sparse legs and merges them with a reranker. Reciprocal Rank Fusion (RRF) combines the two ranked lists by rank position and needs no score calibration; a weighted fusion lets you bias toward exact-term recall when the query is identifier-heavy.
  4. Tune fusion by query class, not globally. An identifier lookup wants sparse weighted high; an open-ended “find similar defects” query wants dense weighted high. Routing by query shape beats a single fixed blend.

If you are already building a retrieval layer over visual data, this composes with the patterns we describe in Vision RAG for retail CV — that piece covers grounding generation in retrieved visual assets, whereas the concern here is making the retrieval itself trustworthy on exact-term queries. The embedding side, and why pooling choices change what your dense vector actually represents, is worth understanding alongside this; see how CLS pooling shapes sequence embeddings.

Why exact-term retrieval matters when curating labelled image datasets

This is where the abstract retrieval argument becomes a data-quality problem with a cost attached. In a production computer vision pipeline, you are constantly pulling subsets of labelled data: samples for a retraining run, examples of a specific failure to investigate drift, every image tagged with a particular annotation to audit label quality. Those queries are token queries. “Give me everything labelled occlusion_heavy” is a request for an exact tag, not a semantic neighbourhood.

Run that with dense-only retrieval and the failure is silent. You do not get an error; you get a top-k list that looks right — visually similar images, plausibly related captions — but that quietly includes samples tagged occlusion_light and excludes some genuinely occlusion_heavy cases whose captions embedded oddly. Nobody notices at retrieval time. The mis-curated samples flow into the training set, the model absorbs the annotation confusion, and the next drift investigation inherits a corpus that no longer means what its tags say.

Hybrid BM25 plus dense retrieval typically lifts top-k recall on exact-term queries where pure dense search misses (an observed pattern across the data-curation work we do, not a benchmarked rate for any specific corpus). The mechanism is simple: the sparse leg guarantees the literal tag is present, and the dense leg fills in the semantic breadth. Fewer wrong samples re-enter training, which means less amplified annotation bias in the next model generation. That reliability under labelled-data retrieval is exactly what a data-quality audit depends on, and it is why we treat retrieval strategy as part of production CV readiness rather than an infrastructure detail settled after the model works.

Practical trade-offs and tuning parameters of BM25 in Milvus

BM25 has two classic knobs, and both matter more in production than the defaults suggest.

  • k1 (term-frequency saturation). Controls how quickly repeated occurrences of a term stop adding score. Higher k1 keeps rewarding repetition; lower k1 saturates fast. For short metadata and tag fields — where a token appears once or not at all — a lower k1 is usually the right instinct, because repetition carries little signal in that content.
  • b (length normalization). Controls how much a document’s length discounts its score. For fields of wildly varying length (a terse tag list versus a paragraph caption), tuning b prevents long fields from being unfairly penalized or short ones over-rewarded.

Beyond the knobs, three trade-offs are worth naming plainly. Tokenization is a correctness decision, not a performance one — get the analyzer wrong and exact-match silently breaks, as noted above. Sparse indexes add storage and insert-time cost, though far less than maintaining a separate search engine. And fusion weighting is genuinely query-dependent: there is no universally correct BM25-versus-dense blend, which is why routing by query class outperforms a single global setting. If your team already runs ML experiment tracking, treat fusion weights and analyzer config as tracked parameters — they change retrieval behaviour as much as a hyperparameter changes a model.

FAQ

How does Milvus BM25 work in practice?

Milvus tokenizes a text field, builds an inverted index, and represents each document as a sparse vector whose non-zero entries are the terms present, weighted by term-frequency saturation and inverse document frequency. In practice this means it matches on the literal token — search a defect code and you get documents containing that exact code, ranked by how distinctive it is — using the same collection and top-k API as dense search.

What is the difference between BM25 sparse retrieval and dense vector search in Milvus, and when should I use each?

BM25 matches literal tokens and is blind to meaning; dense vector search ranks by semantic proximity and is blind to exact-token distinctions between near-identical strings. Use BM25 when a query hinges on an exact token that must not be confused with a near neighbour (part numbers, defect codes, tags); use dense search when the query is about meaning and you want paraphrase and synonym coverage. Most real curation queries need both.

How do I set up hybrid retrieval in Milvus that combines BM25 with dense embeddings?

Define a collection with two vector fields on the same records — a dense embedding field and a sparse field populated by a BM25 function over the text — then run both searches at query time and fuse the results with a reranker such as Reciprocal Rank Fusion. The critical detail is analyzer configuration: tokenize so identifier-shaped tokens like part numbers survive intact, and tune fusion weights by query class rather than globally.

Why does exact-term retrieval matter when curating labelled image datasets for computer vision?

Curation queries are usually token queries — “give me everything labelled occlusion_heavy” is a request for an exact tag, not a semantic neighbourhood. Dense-only retrieval fails silently here: it returns a plausible-looking top-k that includes wrong-tag samples and misses correct ones, and those mis-curated samples flow into training where they amplify annotation confusion in the next model generation.

How can BM25 keyword matching over image metadata and annotation tags reduce mis-curated training samples?

The sparse leg of hybrid retrieval guarantees the literal tag or code is present in the results, while the dense leg supplies semantic breadth. Hybrid BM25 plus dense retrieval typically lifts top-k recall on exact-term queries where pure dense search misses (an observed pattern in our data-curation work, not a benchmarked rate), so fewer wrong samples re-enter training and less annotation bias is amplified downstream.

What are the practical trade-offs and tuning parameters of BM25 scoring in Milvus for production retrieval?

The two classic knobs are k1 (term-frequency saturation, usually set lower for short metadata fields) and b (length normalization, worth tuning when field lengths vary widely). The main trade-offs are that analyzer/tokenization choice is a correctness decision that can silently break exact match, sparse indexes add storage and insert cost, and fusion weighting is query-dependent — routing by query class beats one global blend.

The open question for any team standing this up is not whether to add BM25, but where in the pipeline exact-term recall silently fails today. Audit a handful of your real curation queries — the ones that pull training samples and drive drift investigations — and check whether a dense-only store is returning the tag you asked for or merely something that looks like it. That single check usually decides whether your labelled-data retrieval is trustworthy enough to feed back into training.

Back See Blogs
arrow icon