What is hybrid search?
Hybrid search runs a keyword retriever and a dense-vector retriever against the same query and combines their ranked lists into one. The keyword side, usually BM25 or a learned sparse encoder, matches exact tokens and rare terms. The dense side matches paraphrases and semantic neighbours. A fusion step, most often Reciprocal Rank Fusion or a weighted score sum controlled by an alpha, merges the two lists into a single top-k so exact-token queries and natural-language questions both surface without picking one indexing strategy over the other.
What is hybrid search and how do the two sides combine?
A retrieval pipeline runs two indexes over the same corpus and asks both at once. The keyword index scores documents with BM25 or a learned sparse encoder such as SPLADE, so it rewards documents that contain the query tokens themselves, including rare terms, product codes, and acronyms the embedding model has never seen. The dense index scores documents by the cosine similarity between a query embedding and a document embedding, so it rewards semantic overlap even when no words match. Each side returns its own top-k. Hybrid search is the fusion step that turns those two ranked lists into one.
Every hosted retriever exposes this as a single call. Weaviate's hybrid() query takes an alpha in [0, 1], where 1 is pure vector and 0 is pure keyword, and a fusion type of ranked or relative score. Elasticsearch exposes an rrf retriever that takes a list of child retrievers, a rank_window_size, and a rank_constant. Pinecone stores dense and sparse vectors on the same record and combines them server-side, or runs the two searches separately and fuses the results client-side with RRF. The shape is the same in every case: two rankers, one fused list.
Reciprocal Rank Fusion vs a weighted score sum
RRF ignores the raw scores and works on ranks. For each document that appears in either list, the fused score is the sum over lists of 1 / (k + rank), where rank starts at 1 and k is a smoothing constant. Cormack, Clarke, and Buettcher's 2009 SIGIR paper set k = 60, and Elasticsearch and most reference implementations kept that default. The appeal of RRF is that BM25 scores and cosine similarities live on incompatible scales, and a rank-only method sidesteps that problem without a normalisation pass over the corpus.
Weighted score sums do combine the raw scores, usually as alpha * dense_score + (1 - alpha) * sparse_score. That requires normalising both sides first, since BM25 is unbounded and cosine sits in [-1, 1]. Weaviate's relative score fusion is exactly this shape: it min-max normalises each side to [0, 1] and blends them with the alpha the caller passed. The benefit is that a strong keyword score dominates the fused list even when its rank sits close to the dense rank; the cost is one more tuning knob and a normalisation step that has to hold up as the corpus grows.
When is hybrid search worth two indexes?
Hybrid pays off when the query distribution mixes exact-token lookups and natural-language questions over the same corpus. A support portal that answers both ERR_CONNECTION_RESET and "why can't my laptop reach the wifi" is the canonical case: BM25 wins the first query, the dense retriever wins the second, and neither wins both on its own. Product catalogues with SKUs and free-text descriptions, code search over identifiers and comments, and legal or medical corpora full of statute numbers and paraphrase all show the same pattern.
The cost is that you now maintain two indexes, run two searches per query, and tune a fusion parameter on top. If your queries are all natural language over a corpus with no jargon, a dense retriever alone is usually within a point or two of the hybrid version and simpler to run. If your queries are all keyword lookups over structured records, BM25 alone stays cheaper and easier to explain. Measure recall at 50 and precision at 5 on a labelled query set from your own traffic before adding a second index.
Hybrid search is orthogonal to reranking. Fusion picks the best top-k across two first-stage retrievers; a reranker then rescores each candidate against the query with a cross-encoder to reorder that top-k. A common production pipeline runs BM25 and dense retrieval, fuses the lists with RRF, and reranks the fused top-k before the LLM sees it.
Elasticsearch RRF retriever fusing BM25 with a kNN vector query
POST /docs/_search
{
"retriever": {
"rrf": {
"retrievers": [
{ "standard": {
"query": { "match": { "text": "rotate signing key without downtime" } }
} },
{ "knn": {
"field": "text_vector",
"query_vector": [0.12, -0.03, 0.44, /* ... 764 more dims */],
"k": 50,
"num_candidates": 200
} }
],
"rank_window_size": 50,
"rank_constant": 60
}
},
"size": 5
}
The rrf retriever runs a BM25 match query and a kNN vector query in parallel, takes the top 50 from each list, and returns the five documents with the highest fused rank score. rank_constant is the k in 1 / (k + rank); Elasticsearch keeps the 60 that the 2009 RRF paper picked.
Common questions
Hybrid search: frequently asked
Do I need hybrid search, or is a dense retriever enough?
It depends on your queries. If your users ask natural-language questions over a corpus with little jargon and few rare tokens, a single dense retriever with a good embedding model is usually within a point or two of the hybrid version and much simpler to run. If the corpus contains SKUs, error codes, statute numbers, or terms the embedding model has never seen, BM25 recovers those on the keyword side while the dense retriever handles the rest. Measure recall at 50 on a labelled query set from your own traffic before deciding.
What value should I use for the RRF constant k?
The 2009 Cormack, Clarke, and Buettcher paper set k to 60, and Elasticsearch, LangChain, and most open implementations kept that number. Larger k flattens the contribution of top ranks and lets deeper matches influence the fused list; smaller k concentrates the weight on the first few positions of each list. In practice, tuning k rarely moves precision much once k is in the 20 to 100 range. Vary rank_window_size, the number of candidates each retriever contributes, before you tune k.
How is hybrid search different from a reranker?
Hybrid search is about first-stage recall. Two cheap retrievers with different strengths run against the whole corpus, and fusion picks the top-k across their ranked lists. A reranker is a second stage. It takes that top-k and rescores each candidate against the query with a cross-encoder so the order improves before the LLM sees it. The two are usually combined: BM25 and dense retrieval fuse with RRF, then a reranker orders the fused top-50 down to the top-5.
Can BM25 and dense retrieval share one index?
In Elasticsearch, OpenSearch, Weaviate, and Pinecone, yes: the same collection stores both an inverted index over the text and a vector field, and a single query hits both. Older stacks kept them separate, so the application ran a BM25 query against one service and a vector query against another and fused the results client-side. Both shapes are common in production. The single-index option cuts a network hop and keeps the two views consistent; the split option lets each side scale on its own hardware.
Sources
Where these facts come from
- Cormack, Clarke, Buettcher: Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods (SIGIR 2009): the original RRF paper, the 1 / (k + rank) formula, and the k = 60 default that hosted retrievers inherited
- Elasticsearch reference: Reciprocal rank fusion: the rrf retriever, its rank_window_size and rank_constant parameters, and the score += 1.0 / (k + rank) formula with k default 60
- Weaviate docs: Hybrid search: the hybrid() query, the alpha parameter (0 = keyword, 1 = vector), and the ranked vs relative score fusion options
- Pinecone docs: Hybrid search: dense plus sparse vectors per record, server-side combination, and client-side RRF fusion of separate keyword and dense searches