Reciprocal Rank Fusion (RRF) in Elasticsearch
A complete, friendly guide to the simple voting trick that merges keyword search, vector search, and other ranked lists into one better list, why it needs no score tuning, how its two settings behave, and how to design, measure, secure, and troubleshoot it in production.
Imagine two movie critics who are asked to rank the same ten films. The first critic gives each film a score out of one hundred. The second critic gives stars from one to five. You want one combined ranking, so you try to add up their numbers. Very quickly you see the problem: a film that gets 87 points from the first critic and 3 stars from the second is dominated completely by the first critic, because 87 is a much bigger number than 3. The numbers were never on the same scale, so adding them makes no sense.
Now try a cleverer idea. Forget the points and the stars. Look only at the positions. Each critic puts the films in a line, from best to worst. A film that stands near the front of both lines is probably a good film, no matter what numbers each critic used. You give each film some credit for every line it stands in, with bigger credit for standing closer to the front, and you add up the credits. That is reciprocal rank fusion, or RRF.
Elasticsearch uses this idea to combine different kinds of search, such as classic keyword search and modern meaning-based vector search, into one result list. In this guide you will learn what RRF is, why it exists, how its formula and its two settings work, how Elasticsearch runs it, how it compares with other ways of combining results, and how to design, measure, and operate it well. You do not need any background in search or machine learning. Every new word is explained the first time it appears, and each big idea comes with an everyday comparison.
1What Is Reciprocal Rank Fusion?
The shortest possible answer: RRF is a method that merges several ranked result lists into one, using only the position of each document in each list and never its score.
Let us build the definition slowly. Elasticsearch is a search and analytics engine. Companies use it to search products, help articles, documents, and logs. When you send a search request, it returns a list of matching documents, called hits, ordered from the most relevant to the least relevant. Each hit has a rank, which is simply its position in that list: rank one is the top result, rank two is the second, and so on.
A modern search request can ask several questions at once. One retriever, which is a part of the request that finds and ranks documents, may look for matching words. Another may look for documents with similar meaning. Each produces its own ranked list. Fusion means merging those lists into a single final list, and reciprocal rank fusion is a particular recipe for doing it.
The recipe is short. For every document and every list in which it appears, take the document’s rank, add a constant number called the rank constant, and compute one divided by that sum. That is the document’s credit from that list. Add up the credits from all lists. Sort the documents by the total. The word reciprocal refers to the “one divided by” step, because the reciprocal of a number is one divided by that number.
Think of a talent show with several judges, and each judge hands in a list of contestants in order of preference. The organizers do not care whether a judge thinks in points, stars, or thumbs. They give each contestant a “podium credit” for every list: big credit for first place, a bit less for second, less again for third, with the credit shrinking gently as the position gets worse. The contestant with the most total credit wins. Someone who is second on every list beats someone who is first on one list and absent from the others.
The formula in one sentence
The RRF score of a document is the sum, over all result lists that contain it, of one divided by the sum of the rank constant and the document’s rank in that list. In Elasticsearch, the rank constant defaults to sixty.
People say “RRF,” “rank fusion,” “hybrid ranking,” and “hybrid search with RRF.” Hybrid search is the whole idea of combining several kinds of retrieval. RRF is one specific way of merging their results, and it is the one that Elasticsearch offers as a ready-made option.
What RRF is not
RRF does not search by itself. It does not understand text, it does not create vectors, and it does not decide which documents match. It only takes lists that other retrievers have already produced and blends them. It also does not produce a score that means “probability of relevance.” The final number is only good for sorting.
Where you will meet it
You will meet RRF when a help-center search must find both exact error codes and questions phrased in everyday language, when an online shop wants exact product names and loosely worded descriptions to both work, when a document search combines keyword matches with semantic similarity, and when a team wants better relevance without spending weeks tuning score weights. In each case, RRF offers a simple and sturdy way to merge the answers.
2Why It Exists: The Problem of Mixing Rankings
No single search method is best at everything. Combining methods helps, but combining their scores is surprisingly hard. RRF removes the hard part.
For decades, search meant matching words. If you searched for “laptop overheating,” the engine looked for documents that contained those words and ranked them by how well the words fit. This works wonderfully for exact terms such as product codes, names, and error numbers. But it fails when the words differ from the way the document is written. A help article titled “Fix your computer running too hot” contains none of the searched words, so a purely word-based search may miss it.
A newer approach turns text into lists of numbers called vectors that capture meaning, so that texts about the same idea end up close together in number space. A search can then find documents whose meaning is near the meaning of the question, even when the words differ. This works wonderfully for natural questions and for synonyms. But it can stumble on exact tokens, rare names, and codes, because the numbers blur exact spelling into general meaning.
Two strengths, two weaknesses
The two methods fail in different places, which is exactly why combining them is attractive. Where one is weak, the other is strong. The combined system is called hybrid search. The natural question is how to turn two ranked lists into one.
Imagine hiring for a job with two interviewers. One is excellent at checking exact technical facts. The other is excellent at judging whether the candidate understands the big picture. Neither is perfect alone. You want a way to combine their opinions that does not depend on how many points each interviewer happens to use, because one gives scores up to 10 and the other up to 100. RRF is the fair way to merge their rankings.
The old answer: add up scaled scores
The first idea most teams try is to add the scores from the two searches, perhaps with weights, such as “sixty percent keyword score plus forty percent vector score.” This can work, but it demands careful tuning. The scores live on different scales, they change with the data, and they behave differently from one query to the next. Chapter 5 explains why this is so fragile.
The RRF answer: use positions
Positions are much steadier than scores. The best document of a list is always rank one, whatever score it has. By using only positions, RRF avoids the scale problem entirely. It needs no weights to start with, no normalization, and almost no tuning, and yet it tends to produce results that are better than either list alone.
An illustrative story
HelpDeskHero, an imaginary customer support portal
HelpDeskHero has forty thousand help articles. Customers type things like “printer offline after update” and “error 0x80070057.” The keyword search handles the error code perfectly but misses articles about the printer, because the articles say “device shows as disconnected.” A meaning-based vector search finds the printer articles but ranks the error code article below several vaguely related pages. The team fuses the two lists with RRF. Now the error code article stays at the top, thanks to the keyword list, and the printer articles reach the top ten, thanks to the vector list. This story is invented, but the pattern is very common.
A short history
The idea of merging ranked lists is old, and RRF itself comes from academic research published in 2009, which showed that this very simple recipe often beat more complicated ways of combining rankings. Elasticsearch added RRF for hybrid search in the 8.x line, first as a preview feature in the search request and later as one of several building blocks called retrievers. The names and details have evolved between versions, and some ranking features depend on the subscription level, so check the documentation for the release and license that you use.
RRF cannot rescue a list that is bad. If one of the retrievers returns poor results, fusing them with a good list may still help a little, but you will not get quality from nothing. Fusion works best when each retriever brings a real strength that the other lacks.
3The Building Blocks You Need to Know First
To follow the rest of this guide you need eight simple ideas: rank, score, lexical search, vector search, sparse vectors, retriever, window, and fusion.
Position in a list
Rank one is the top hit, rank two is the next, and so on. RRF uses only this number.
A number that measures fit
Every retriever gives its hits scores. Scores are useful inside one list but are not comparable between lists.
Matching words
The classic full-text search, usually ranked by a formula named BM25, which rewards rare matching words and sensible text lengths.
Matching meaning
Text is turned into a vector, and the engine finds documents whose vectors are close to the vector of the question. The common method is called kNN, short for k nearest neighbors.
Meaning as weighted terms
A different kind of vector search in which text becomes a list of weighted words, including related words that were not typed. It sits between lexical and dense vector search.
A part of a search that returns a ranked list
Retrievers describe how to get a list of hits. Some retrievers combine or reorder the lists of others, and RRF is one of them.
How many hits each list contributes
The rank window size limits how deep into each list the fusion looks. Hits beyond the window are ignored.
Merging lists into one
Fusion takes several ranked lists and produces one final list. RRF is a fusion method based on ranks.
A closer look at BM25
BM25 is the standard formula for ranking text matches. It has three simple instincts. First, a document that mentions the searched words more often is probably more relevant, but with diminishing returns: mentioning a word twenty times is not twenty times better than once. Second, a rare word is more informative than a common one, so matching “kubernetes” counts for more than matching “the.” Third, a short document that contains the words is more focused than a very long one, so length is taken into account. BM25 scores are positive numbers with no fixed upper limit, and they depend on the collection.
A closer look at dense vectors
A dense vector is a list of numbers, often hundreds of them, produced by a machine learning model called an embedding model. The model reads a piece of text and outputs numbers such that similar meanings give similar numbers. Elasticsearch stores these vectors in a field and finds the nearest ones using a graph-based index. The similarity scores of such a search live on a small, fixed scale, often between zero and one, which is a completely different scale from BM25.
Picture a huge library with two kinds of librarians. The first is a word-matcher who checks the exact words on the covers and in the indexes of the books. The second is a topic expert who has read every book and knows what each is about. Ask the first for “overheating laptop” and you get books with those words. Ask the second and you get books about cooling problems, even if they never use those words. Hybrid search asks both and then combines their recommendations.
The path of a hybrid search
The diagram shows how a hybrid request travels. The client usually reaches the cluster through a load balancer or an API gateway, which passes the request to a coordinating node. Every relevant shard runs both retrievers and returns candidates. The coordinating node collects the candidates for each retriever, applies RRF to fuse them, and returns the final page.
flowchart TD
A["Client application"] --> B["Load balancer or API gateway"]
B --> C["Coordinating node"]
C --> D["Shards run the lexical retriever"]
C --> E["Shards run the vector retriever"]
D --> F["Ranked list one: keyword hits"]
E --> G["Ranked list two: meaning hits"]
F --> H["RRF fusion on the coordinating node"]
G --> H
H --> I["One final ranked list returned to the client"]
Notice in Figure 1 that the fusion step happens after the retrievers finish. It works only with ranked lists. This is the reason why RRF is so easy to plug into any combination of retrievers: it does not care how the lists were made.
Elasticsearch describes searches with retrievers in its newer syntax, and with a separate rank option in older releases. Chapter 11 explains both. The idea is the same in each: several ranked lists go in, and one fused list comes out.
4Lexical and Semantic Search: Two Kinds of Relevance
To see why fusion pays off, look at where each kind of search shines and where it stumbles.
Relevance is not one single thing. Sometimes a user wants the exact words they typed. Sometimes they want the idea behind the words. Lexical and semantic retrievers each capture one side of this.
| Situation | Lexical search (words) | Vector search (meaning) |
|---|---|---|
| Exact codes, ids, error numbers | Excellent | Often weak, since numbers and codes blur together |
| Rare names and brand terms | Excellent | Variable, depends on the embedding model |
| Natural language questions | Weak when the wording differs from the documents | Excellent |
| Synonyms and paraphrases | Weak unless synonyms are configured | Strong |
| Typing mistakes | Needs fuzzy matching options | Often tolerant |
| Explaining why a hit matched | Easy, the matching words can be highlighted | Hard, the match is a distance in number space |
| Cost of setup | Low, works out of the box | Needs an embedding model and vectors stored per document |
| Behavior on very short queries | Good | Can be vague |
| Behavior on other languages | Needs analyzers per language | Good with multilingual models |
Think of two ways to find a person in a large crowd. The first is to look for someone wearing a red hat, an exact detail, which works if the person really is wearing one. The second is to ask, “Who looks like my friend’s cousin?” which works even if the hat is missing but may bring several look-alikes. Using both clues at once gives the most reliable result.
Examples of queries and their needs
- “error 0x80070057” needs exact token matching. A meaning-based search may return general articles about errors.
- “how do I stop my laptop from getting so hot” needs meaning. The right article may say “thermal throttling” or “cooling,” which a keyword search may not match.
- “iPhone 14 Pro Max case” needs both exact product terms and a sense of the intent to buy an accessory.
- “lawsuit about broken promise to sell house” needs meaning to find “breach of contract for real estate,” and exact terms to find named cases.
Why not just pick the better one?
You could measure both on your data and choose the winner. Often, though, the winner changes by query type. A single global winner leaves the losing cases unresolved. Fusion lets each method contribute where it is strong, without requiring you to know in advance which queries belong to which method.
The reverse problem: agreement as a signal
A helpful side effect of fusion is that agreement between independent methods is a strong sign of relevance. A document that both the word matcher and the meaning matcher rank highly is very likely a good result. RRF turns this agreement into a higher score in a natural way, because a document that appears in both lists collects credit twice.
Before adding a retriever to a fusion, ask what it can find that the others cannot. A retriever that returns almost the same documents as another adds cost without adding value, as Chapter 14 explains.
5The Score Scale Problem: Why Adding Scores Fails
The obvious way to combine two searches is to add their scores. This chapter shows why that idea is fragile and why using ranks is safer.
Every retriever gives each hit a score. Inside one list, scores are perfectly good for ordering. Between lists, they are not comparable, because each method invents its own scale.
| Property | Lexical BM25 score | Vector similarity score |
|---|---|---|
| Typical range | From zero up to twenty, thirty, or more | Usually between zero and one |
| Upper limit | None | Fixed by the similarity measure |
| Depends on the collection? | Yes, on word frequencies and lengths | Mostly on the embedding model |
| Changes between queries? | Strongly: a query of rare words scores higher than one of common words | Moderately |
| Changes when data is added? | Yes, because word statistics shift | Rarely |
A small example with numbers
Suppose a lexical search gives two documents the scores 14.2 and 9.8. A vector search gives the same two documents the scores 0.71 and 0.83. If you simply add the scores, the first document gets 14.91 and the second gets 10.63. The vector scores barely changed anything: the sum is decided almost entirely by the lexical scores, because they are about twenty times bigger. The vector search, which preferred the second document, has almost no vote.
flowchart LR
A["Lexical scores: 14.2 and 9.8"] --> C["Add the scores"]
B["Vector scores: 0.71 and 0.83"] --> C
C --> D["Sums: 14.91 and 10.63"]
D --> E["Lexical scores dominate: the vector opinion is drowned out"]
Imagine measuring a person’s fitness by adding their weight in kilograms to their height in meters. The weight number is dozens of times larger, so the sum is almost the same as the weight alone. The height, which may be just as important, is invisible in the total. To combine such measures sensibly, you must first bring them to a common scale, and that step is where the trouble begins.
The fix and its cost: normalization
Teams that combine scores usually normalize them first: they squeeze each list into a common range such as zero to one, often using the minimum and maximum of that list. Then they add the normalized scores with weights. This works, but it has weaknesses.
- Outliers distort the scale. One extremely high score in a list compresses all the others toward zero.
- The scale changes per query. The maximum of one query’s list differs from another’s, so the same normalized value means different things.
- Weights need tuning. The weight balance that works for one kind of query may fail for another.
- Data drift breaks it. When the collection or the embedding model changes, the tuned numbers stop being right.
Why ranks are steadier
A rank is always an integer starting from one, no matter how the scores were calculated. Rank one is always the best hit of its list. Using ranks means there is nothing to normalize and no scale to tune. RRF exploits this by converting each rank into a gentle credit and adding credits across lists. The result is less sensitive to the personality of any single retriever.
What ranks throw away
Ranks lose information. If the top document has a score far above the second, and the second is barely above the third, the ranks still say one, two, three. RRF cannot see that the first hit was a runaway winner. This is the main price of the method, and Chapter 18 discusses when it matters.
Sometimes people combine an RRF result with a score threshold from one retriever, or add a boost from another. Because the final RRF score is built from ranks, such mixtures are hard to reason about and easy to get wrong. Keep the fusion clean: ranks in, one fused ranking out.
6The RRF Formula in Plain Words
The whole method is three small steps: turn each rank into a credit, add the credits from all lists, and sort by the total.
Take each list separately
Every retriever gives a list of hits in order. Forget the scores. Keep only the position of each document in its list.
Convert each position into a credit
Add the rank constant to the rank, then divide one by the result. Rank one earns the largest credit, rank two a little less, and so on.
Add the credits of each document
A document that appears in several lists gets several credits. A document that appears in only one list gets one credit. A document missing from a list gets nothing from it.
Sort by the total
The document with the highest total is first in the final list. The final list is then cut to the requested page.
flowchart LR
A["List one: ranks of documents"] --> C["Credit = 1 divided by k plus rank"]
B["List two: ranks of documents"] --> C
C --> D["Add the credits of each document across lists"]
D --> E["Sort by the total, highest first"]
E --> F["Final fused list"]
Picture a school sports day where several referees each publish a ranking of the athletes. The organizers give “podium credit”: a large credit for first place, a smaller credit for second, and so on, with the credits shrinking slowly. Each athlete’s credits from all referees are added together. An athlete who is placed well by all referees wins, even if no referee ranked them first. That is exactly how RRF treats documents.
The size of the credits with the default constant
With the default rank constant of sixty, the credit for a given rank is one divided by sixty plus the rank. The table shows a few values. Notice how gently the credit declines: the difference between rank one and rank ten is small, and even rank one hundred still earns more than a third of the credit of rank one.
| Rank in the list | Credit with k = 60 | Credit as a share of rank one |
|---|---|---|
| 1 | 0.016393 | 100 percent |
| 2 | 0.016129 | 98 percent |
| 3 | 0.015873 | 97 percent |
| 5 | 0.015385 | 94 percent |
| 10 | 0.014286 | 87 percent |
| 20 | 0.012500 | 76 percent |
| 50 | 0.009091 | 55 percent |
| 100 | 0.006250 | 38 percent |
Why one divided by something?
The reciprocal shape has a helpful property: it gives a lot of weight to the very top positions and then flattens out. In relevance terms, the difference between first and second place matters more than the difference between fiftieth and fifty-first. The rank constant controls how strongly the curve favors the top, which Chapter 8 explains in depth.
Why add across lists?
Adding turns agreement into a bonus. A document that two independent retrievers both consider good collects two credits. A document that only one retriever likes collects one. This is a natural way of saying “trust what several methods agree on.”
The largest possible score
With two lists and a rank constant of sixty, the best possible total is two divided by sixty-one, about 0.0328, which happens when a document is first in both lists. With three lists, the limit is three divided by sixty-one. In general, the maximum equals the number of lists divided by the rank constant plus one. These totals look tiny compared with typical scores, and that is normal: the numbers are only for sorting.
Missing means zero, not a penalty
When a document is absent from a list, it simply earns no credit from that list. It is not punished. This matters because a retriever may not return a document for perfectly good reasons: the exact words may be missing, or the document may be outside the window. The absence carries little information, so RRF does not treat it as a negative vote.
Do not try to interpret the absolute value of an RRF score. Compare scores only within the same query, to see the order. A score of 0.03 in one query says nothing about a score of 0.02 in another.
7A Worked Example: Fusing Two Lists
Numbers make the method concrete. Here is a complete calculation with two short lists and the default rank constant.
Imagine a search where the lexical retriever and the vector retriever each return their top five documents. The documents are named d1 to d7. The two lists are shown below.
| Rank | List A: lexical retriever | List B: vector retriever |
|---|---|---|
| 1 | d1 | d3 |
| 2 | d2 | d6 |
| 3 | d4 | d1 |
| 4 | d3 | d7 |
| 5 | d5 | d2 |
Now compute the credits with a rank constant of sixty. Each document’s credit from a list is one divided by sixty plus its rank in that list. The total is the sum over both lists.
| Document | Rank in A | Credit from A | Rank in B | Credit from B | RRF total |
|---|---|---|---|---|---|
| d1 | 1 | 0.016393 | 3 | 0.015873 | 0.032266 |
| d3 | 4 | 0.015625 | 1 | 0.016393 | 0.032018 |
| d2 | 2 | 0.016129 | 5 | 0.015385 | 0.031514 |
| d6 | Absent | 0 | 2 | 0.016129 | 0.016129 |
| d4 | 3 | 0.015873 | Absent | 0 | 0.015873 |
| d7 | Absent | 0 | 4 | 0.015625 | 0.015625 |
| d5 | 5 | 0.015385 | Absent | 0 | 0.015385 |
Sorting by the total gives the final order: d1, d3, d2, d6, d4, d7, d5.
Two friends each list their favorite restaurants. The restaurants that appear on both lists rise to the top of the group’s plan, even if neither friend put them first. A restaurant that only one friend mentions comes after all the shared ones. Nobody needed to compare the friends’ rating systems.
What the example teaches
- Agreement wins. The three documents that appear in both lists, d1, d3, and d2, take the top three places. Each has a total above 0.031, while every document from only one list has a total below 0.017.
- Top of one list is not enough. Document d3 was fourth in list A but first in list B, and it still beats d2, which was second in A and fifth in B. Being high in both matters more than being highest in one.
- Single-list documents follow in their own order. Among documents that appear in only one list, the order simply reflects their rank in that list: d6 (rank two in B) comes before d4 (rank three in A), which comes before d7 and d5.
- The lists are treated equally. Neither retriever has more influence by default. This is a fair starting point that you can change in newer versions with weights.
How different the raw scores might have been
In a real search, list A might have scores like 14.2, 12.7, 10.1, 9.8, and 8.3, while list B might have similarities like 0.91, 0.88, 0.86, 0.84, and 0.80. Adding these directly would let list A dominate. RRF ignored them completely and still produced a sensible blend. That is the entire point.
A test of consensus against champions
Consider a document that is rank five in both lists. Its total is two divided by sixty-five, which is about 0.0308. A document that is rank one in only one list scores about 0.0164. So with the default constant, a consistent, moderately good document beats a champion of a single list. With a much smaller rank constant, this can flip, as Chapter 8 shows. The balance between consensus and champions is exactly what the rank constant tunes.
Take a real query from your own data, write down the top five hits of each retriever, and compute the fusion by hand. Seeing the winners emerge from simple sums builds trust in the method and makes later tuning far easier to understand.
8The Rank Constant: How Top-Heavy Should the Credit Be?
The rank constant, called k, controls how quickly the credit falls as rank gets worse. It sets the balance between trusting the top of each list and trusting agreement across lists.
In Elasticsearch, the setting is named rank_constant, and its default is sixty. It must be at least one. A small constant makes the credit fall steeply, so the very top ranks dominate. A large constant makes the credit fall gently, so all ranks in the window receive nearly equal credit and the deciding factor becomes how many lists a document appears in.
| Rank constant | Credit at rank 1 | Credit at rank 5 | Credit at rank 20 | Rank 1 compared with rank 20 |
|---|---|---|---|---|
| 1 | 0.5000 | 0.1667 | 0.0476 | About 10.5 times larger |
| 10 | 0.0909 | 0.0667 | 0.0333 | About 2.7 times larger |
| 60 (default) | 0.0164 | 0.0154 | 0.0125 | About 1.3 times larger |
| 200 | 0.0050 | 0.0049 | 0.0045 | About 1.1 times larger |
Think of two prize systems. In the first, the winner gets ten thousand dollars, second place a thousand, and third place a hundred: only the top matters. In the second, the winner gets ten thousand and second place nine thousand nine hundred: almost everyone near the top is rewarded equally. A small rank constant is the first system. A large rank constant is the second.
A case where the choice flips the winner
Compare two documents. Document X is rank one in a single list and absent from the other. Document Y is rank five in both lists. With a rank constant of one, X scores 0.5 and Y scores two divided by six, about 0.333, so X wins: the champion of one list beats a consistent performer. With the default of sixty, X scores about 0.0164 and Y scores about 0.0308, so Y wins: consensus beats the champion. The same data gives opposite answers depending on k.
flowchart TD
A["Choose the rank constant k"] --> B{"How much do you trust the very top of each list?"}
B -->|Very much| C["Use a smaller k: top ranks dominate, champions of one list can win"]
B -->|Balanced| D["Keep the default of 60"]
B -->|Less, and you value agreement| E["Use a larger k: credits flatten, documents in several lists win"]
C --> F["Measure on your own queries"]
D --> F
E --> F
When to leave it at sixty
The default is a well-tested compromise, and for most hybrid searches it performs well. If you have no evaluation data, keep it. Many teams never change it and still get excellent results. The value of sixty is the one popularized by the original research, which found that results were not very sensitive to the exact choice in a broad range around it.
When to consider lowering it
- One of your retrievers is extremely precise at the top, such as exact identifier matching, and you want its top hit to win even if the other list disagrees.
- Your lists are short, and small differences at the top matter a lot.
- Your evaluation shows that users are hurt when a strong single-list match is pushed down by weaker consensus documents.
When to consider raising it
- Your retrievers are noisy, and their exact ordering is not very reliable, but the set of documents they return is good.
- You want documents that appear in several lists to be strongly preferred.
- Your lists are long, and you want the depth to matter more.
The interaction with the window
The rank constant and the rank window size work together. With a window of one hundred and a constant of sixty, the credits range from 0.0164 down to 0.0063, a ratio of about 2.6. With a window of ten, the range is smaller. With a small constant, deeper ranks earn almost nothing, so a large window adds little. Consider both settings together when you tune.
Changing the rank constant without measurement is guessing. Build a small set of test queries with judged answers first, as Chapter 19 describes, and compare a few values before choosing.
9The Rank Window Size: How Deep Each List Goes
Fusion cannot use what it never sees. The rank window size decides how many hits from each retriever enter the fusion.
The setting rank_window_size tells each retriever how many top hits to contribute. If the window is fifty, then each list contributes its top fifty documents, and everything beyond the fiftieth is invisible to the fusion. A document ranked fifty-first in one list and thirtieth in another earns credit only from the second.
Imagine each judge in a contest is asked to submit only their top twenty contestants, and the organizers never see the rest. A contestant who is twenty-first for one judge and third for another receives credit only from the second. If the organizers had asked each judge for the top fifty, that contestant would have collected two credits. The window is the length of the list that each judge hands in.
Why the window matters
- Recall. A larger window lets documents that are moderately ranked in several lists collect credits and rise. A tiny window throws away this evidence.
- Cost. Each shard must find and return the window’s worth of candidates for every retriever, and the coordinating node must merge them. A larger window costs more time and memory.
- Depth of pagination. The final list can never be deeper than what the window supplies. To show the third page of ten results, the window must be at least thirty.
Defaults and how to think about them
The default depends on the version and on the syntax you use. In the newer retriever syntax, the default is small, on the order of ten, while older syntax tied it to the result size. Small defaults are safe for cost but poor for quality. In practice, teams choose the window on purpose, often somewhere between fifty and two hundred, and confirm the effect with measurements. The window must be at least as large as the number of results you plan to return, including any skipped by an offset.
| Setting | What it controls | Relationship to the window |
|---|---|---|
| Size | How many fused hits are returned to the client | The window must be at least as large |
| From | How many fused hits to skip | The window must be at least from plus size |
| Rank window size | How many hits each retriever contributes | The subject of this chapter |
| Vector search k | How many nearest neighbors the vector retriever returns | Should be at least the window, or the vector list is shorter than intended |
| Vector search candidates | How many candidates the vector index examines per shard | Should be at least k, and larger values improve accuracy at a cost |
A frequent mistake with the vector retriever
The vector retriever has its own parameters for how many neighbors to find. If you set the window to one hundred but the vector retriever returns only ten neighbors, the vector list contains ten documents, not one hundred, and the fusion sees a shallow list. Always check that the vector settings deliver at least as many hits as the window expects.
What each shard does with the window
To assemble the top window of a list, every shard finds its own best candidates for that retriever, and the coordinating node merges them by score to get the global top window. This means the work grows with the number of shards, the number of retrievers, and the size of the window. A cluster with many shards and a large window multiplies the cost. Keep the window as small as gives good quality.
Windows and pagination
Because the fused list is at most as deep as the window, users cannot page beyond it. For search result pages that show ten results per page, a window of one hundred supports up to ten pages, and hybrid search rarely needs more. For deep reading of everything, such as exports, RRF is not the right tool, and a plain sorted search with a bookmark is better.
Start with a window several times larger than your page size, for example fifty or one hundred for a page of ten, and measure quality and latency. Increase it only while quality improves noticeably.
10How Elasticsearch Runs an RRF Search Inside
Follow one hybrid request from the client to the final page, and you will see where each retriever runs and where the fusion happens.
Elasticsearch normally answers a search in two phases, called query then fetch. In the query phase, each shard finds its best matching documents and returns small records. In the fetch phase, the coordinating node collects the full contents of the winning documents. RRF fits between the two.
The request arrives
A coordinating node receives a request that lists two or more retrievers and an RRF combination with its constant and window.
Each shard runs every retriever
The coordinating node forwards the request to one copy of every shard. Each shard runs the lexical query and the vector query and finds the top candidates for each, as many as the window.
Shards return candidates per retriever
Each shard returns small records: document references with the score for each retriever that found them.
The coordinating node builds global lists
For each retriever, it merges the shards’ candidates by score and keeps the global top window. This gives one ranked list per retriever, with ranks starting at one.
RRF computes the fused score
For every document in any list, it adds the credits from the lists that contain it, using the rank constant. The result becomes the document’s final score.
The final page is cut
The fused list is sorted, the requested offset is skipped, and the requested size is kept.
The fetch phase collects the documents
Only the documents on the final page are fetched with their sources and other requested fields, and the response is returned.
sequenceDiagram
participant C as Client
participant N as Coordinating node
participant S1 as Shard 1
participant S2 as Shard 2
C->>N: Hybrid search with RRF, constant, and window
N->>S1: Run lexical and vector retrievers
N->>S2: Run lexical and vector retrievers
S1-->>N: Top candidates per retriever
S2-->>N: Top candidates per retriever
N->>N: Build one global ranked list per retriever
N->>N: Compute RRF scores and sort
N->>S1: Fetch the documents on the final page
N->>S2: Fetch the documents on the final page
S1-->>N: Sources
S2-->>N: Sources
N-->>C: Final page of hits with fused scores
Consider a talent contest held in several rooms at once. In each room, two judges write down their top names. A central office gathers the top names from all rooms, builds one master list per judge, then applies the podium credits and adds them up. Only after the winners are known does the office ask the rooms for the winners’ full profiles. The rooms are the shards, the judges are the retrievers, and the office is the coordinating node.
Approximate lists
Vector search in Elasticsearch is usually approximate. It uses a graph structure that finds very close neighbors quickly but does not guarantee to find the exact closest ones every time. Because of this, the vector list can vary slightly between runs, between shard copies, or after new data is added. RRF inherits this small variation. In practice, it is rarely noticeable, but it explains why perfect repeatability of hybrid results is not guaranteed.
Lexical statistics and shards
Lexical scores depend on word statistics, such as how rare a word is. By default, each shard computes these statistics from its own data. For small or unevenly distributed indices, this can make lexical ranks differ slightly from what a single global computation would give. A search option that gathers global statistics first exists, at some extra cost. For well-sized shards, the difference is usually small.
Where the time goes
The total time is about the time of the slowest retriever on each shard, plus the merging and the fetch phase. Since the retrievers run in the same query phase, a slow vector search directly delays the whole request. This is why the cost of each retriever matters and why Chapter 17 discusses performance separately.
The fused score replaces the individual scores in the response. If you want to see the contribution of each retriever, ask Elasticsearch to explain the search, which Chapter 12 describes.
11Retrievers: The Modern Way to Describe RRF
Retrievers are building blocks that return ranked lists. Some find documents, and some combine or reorder the lists of others. RRF is one of the combiners.
In the newer search syntax, a request has a retriever section. Inside it, you describe a tree of retrievers. The leaves of the tree find documents. The nodes above them combine or refine what the leaves return. This design makes it possible to build pipelines such as “fuse keyword search and vector search, then rerank the top results” in a single request.
flowchart TD
R["RRF retriever: rank constant and rank window size"] --> L["Standard retriever: a text query for keyword matching"]
R --> V["Vector retriever: nearest neighbors of the query embedding"]
R --> S["Optional: sparse vector retriever for weighted terms"]
L --> LH["Ranked list one"]
V --> VH["Ranked list two"]
S --> SH["Ranked list three"]
| Retriever kind | Job | Typical use |
|---|---|---|
| Standard | Runs an ordinary query, with filters, and returns its ranked hits | Keyword search, and sparse vector queries |
| Vector (kNN) | Finds the nearest neighbors of a query vector in a vector field | Semantic search with dense embeddings |
| RRF | Fuses the ranked lists of its child retrievers using reciprocal rank fusion | Hybrid search without score tuning |
| Linear, in newer versions | Combines child scores after normalization, with weights | Hybrid search where you want to tune weights |
| Text similarity reranker | Reorders the top hits of a child using a reranking model | A precise second stage after a broad first stage |
| Rule based retrievers | Applies business rules such as pinned results on top of a child | Merchandising and curated answers |
Think of a kitchen line. Some cooks chop, some grill, and one head chef combines the plates and sends them out. Retrievers work the same way: leaf retrievers do the raw work, and combiner retrievers such as RRF assemble the final result. Because each part has one clear job, the kitchen, or the search pipeline, is easy to rearrange.
The older syntax
Before the retriever syntax, RRF was requested with a rank section in the search body, alongside separate sub-searches and vector searches. It produced the same fusion but had fewer options for combining with other stages. New projects should prefer retrievers, while older code may still use the rank section. The details and the maturity of each syntax differ between versions, so check the documentation for the release you run.
Retrievers are composable
Because a retriever can contain other retrievers, you can nest them. An RRF retriever can be the child of a reranker. A standard retriever can have its own filters. This means you can express pipelines declaratively, and Elasticsearch runs them as one request.
License and availability
Some retrievers and ranking features are part of paid subscription levels, and their status, from preview to general availability, changed across versions. Before you build on a feature, confirm that your version and license include it, and note whether it is still marked as a preview.
Names, defaults, and the list of retrievers can change between Elasticsearch releases. The ideas in this guide stay the same, but confirm parameter names and behavior against the documentation of your version before you deploy.
12Anatomy of an RRF Request and Response
An RRF search has only a few moving parts. Knowing each one, and how to ask Elasticsearch to explain its result, makes tuning and debugging far easier.
| Part of the request | Role | Notes |
|---|---|---|
| Child retrievers | Produce the ranked lists to be fused | At least two are needed for fusion to make sense. Each can have its own query and filters. |
| Rank constant | Sets how steeply credit falls with rank | Default sixty, must be at least one |
| Rank window size | How many hits each child contributes | Must be at least the requested size, and at least the offset plus the size when paging |
| Size | How many fused hits to return | The final list is cut to this number |
| From | How many fused hits to skip | Limited by the window |
| Source filtering | Chooses which fields to return | Recommended to keep responses light |
| Explain | Asks for the reasoning behind each hit’s score | Useful for debugging, but adds overhead |
Think of an order form for a panel of judges. It says which judges to invite, how many names each judge may submit, how steeply the podium credits should shrink, and how many winners to announce. If you forget to invite a judge, or let a judge submit only three names, the final list will reflect that. Each field on the form has a clear effect.
What the response looks like
The response looks like an ordinary search response. Each hit contains its index, its identifier, and its source. The score of each hit is the fused RRF score, not the score of any single retriever. Hits are sorted by that score, highest first. You do not get separate scores per retriever in a normal response, because RRF deliberately ignores them.
Asking Elasticsearch to explain
When you ask for an explanation, the response adds a description for each hit. For a fused hit, the explanation states the RRF score, the ranks that the document had in each child list, and the rank constant used. It also includes the explanation of each child’s own score, so you can see, for example, that a document was rank one in the lexical list with a certain BM25 score, and rank three in the vector list with a certain similarity.
| Piece of the explanation | What it tells you | How to use it |
|---|---|---|
| The fused score | The final RRF total for the hit | Compare with neighbors to see how close the ordering was |
| The list of initial ranks | Where the document stood in each child list | Find out which retriever pulled a document up |
| The rank constant | The value used in the calculation | Confirm that your setting was applied |
| Child explanations | The raw scores inside each list | Check whether a retriever’s own ranking looks sensible |
| A missing rank for a child | The document was not in that child’s window | Consider a larger window or a better child query |
Reading an explanation like a detective
Suppose a user complains that the right article is not in the top ten. Search for it with explain switched on for a wider page. You may find that the article was rank two in the vector list but absent from the lexical list, so it earned only one credit and was outranked by documents that appeared in both lists. That tells you which retriever to improve, or whether the window was too small.
Profiling
Elasticsearch can also profile a request to show how long each part took on each shard. Use this to find out which retriever is slow. Explain answers “why this order,” and profile answers “why this slow.”
Save a few dozen queries that represent your users, together with what you expect to see at the top. Whenever you change a setting, run them with explain and check that the important ones still behave.
13Filters, Pagination, and Other Options
Real searches have filters, pages, facets, and timeouts. Some of these work naturally with RRF, and some need care.
Filters belong inside every child
Suppose a shop wants to show only in-stock products in a certain category. If the filter is applied only after fusion, the windows may have been filled with documents that are later removed, leaving fewer results than expected. Worse, a document could be pulled up by a list in which it should never have appeared. The safe approach is to apply the same filter inside every child retriever, so that each list contains only allowed documents before the fusion begins.
Some versions let you put a shared filter on the fusing retriever so that it applies to all children. Where that is available, it reduces the risk of forgetting a filter in one child. Filters for security, such as which tenant or which access group may see a document, must be applied to every list without exception.
Imagine two scouts sent to find good restaurants, and you only want places that are open now. If you check opening hours only after the scouts return, one scout may have used all their time on closed places, leaving too few options. Telling both scouts the rule before they leave means both come back with useful lists. Filters inside each child are the rule given before departure.
Pagination
The from and size options work with RRF, but the window limits the depth. To show the third page of ten results, the window must cover thirty. Because fusion of deep lists costs more, hybrid search is best for the first few pages, where users actually look. It is not designed for reading thousands of results in order.
Other options
| Option | Behavior with RRF | Advice |
|---|---|---|
| Source filtering | Works as usual | Use it to keep responses small |
| Explain | Supported | Use for debugging, not on every request |
| Custom sort | Not compatible in the usual way, since the order comes from the fusion | Use RRF for relevance-ordered lists, and other searches for sorted lists |
| Scroll and bookmark paging | Limited or not supported, since the result is a fused window | Do not use RRF for full exports |
| Aggregations | Possible in some versions, but results depend on how the children match | Read the documentation for your version, and test facets carefully |
| Highlighting | Depends on the version and on the child queries | Test with your queries |
| Minimum score | Technically applies to the fused score | Avoid: fused scores are tiny and not comparable across queries |
| Timeouts | A timeout may cut a child’s list short | Set generous timeouts, and monitor partial results |
Because support can change between versions, treat this table as a guide to what to check, not as a final answer. Confirm the behavior in the documentation and in a test.
Timeouts and partial results
If a search times out on some shards, the lists from those shards are incomplete, and the fusion works with what it has. This can quietly change the ranking. Track the timed-out flag and the count of failed shards in responses, and treat frequent partial results as an incident.
Empty children
If a child returns nothing, for example because the lexical query matches no words, the fusion continues with the other lists. The final list is then based on fewer retrievers. This is a good graceful behavior: one list saves the day when another fails. But it also means that the quality of a query can shift depending on which child had results.
Highlighting and snippets
Users often like to see why a result matched. Lexical matches can be highlighted easily, but vector matches cannot be highlighted in the same way, since there are no matching words. Design the interface so that a result that came from the vector list alone still shows a sensible snippet, such as the beginning of the document or a summary.
Access control filters must never depend on a post-processing step that happens after the lists are built. Put them inside every child retriever, and rely on Elasticsearch’s own access controls for document-level restrictions.
14Fusing More Than Two Lists
RRF is not limited to two lists. Adding more independent views can help, but each list should earn its place.
The formula adds credits from any number of lists, so you can fuse three, four, or more. There are several natural ways to have more than two lists.
Title list and body list
Run one lexical query against the title and another against the body, and fuse them. Documents that match in both rise, without hand-tuned field boosts.
Two kinds of semantic search
Fuse a dense vector list, a sparse vector list, and a lexical list. Each captures a different notion of similarity.
Several phrasings of the question
Rewrite the user’s query in different ways, perhaps with a language model, run each, and fuse. This helps when a question can be worded in many ways.
Different embeddings
Use vectors from different models or from different parts of a document, such as a title vector and a body vector.
flowchart TD
Q["User query"] --> L1["Lexical query on the title"]
Q --> L2["Lexical query on the body"]
Q --> V1["Dense vector query"]
Q --> V2["Sparse vector query"]
L1 --> F["RRF fusion of four ranked lists"]
L2 --> F
V1 --> F
V2 --> F
F --> R["Final ranked list"]
A hiring panel with more interviewers gives a more reliable opinion, as long as each interviewer brings a different view. But if three of the five interviewers went to the same school and always think alike, their shared opinion counts three times and drowns out the others. The same happens in fusion when several lists are nearly copies of each other.
Correlated lists overweight one opinion
Because each list has an equal vote, adding two lists that return nearly the same documents doubles the influence of that opinion. Two lexical queries on nearly identical fields, for example, may behave like one list counted twice. Check the overlap between lists on real queries. Lists that overlap almost completely add cost without adding information.
Weights in newer versions
Newer versions of Elasticsearch allow you to give each child retriever a weight, so that its credits are multiplied by a number before adding. This lets you say “the lexical list counts twice as much as the vector list” while keeping the rank-based robustness. Use weights with care and only with measurements, since they bring back some of the tuning burden that RRF aims to avoid. Confirm availability in your version.
How many lists are enough?
Most systems use two to four lists. Each additional list costs more time, memory, and complexity, and yields diminishing benefits. Add a list only when an evaluation shows a clear improvement for real queries.
The maximum score grows with the number of lists
As the formula sums over lists, the largest possible total is the number of lists divided by the rank constant plus one. Comparing totals between searches that use a different number of lists is meaningless. Keep the number of lists constant within a feature, so that scores stay comparable across queries if you need any monitoring based on them.
For a sample of queries, compute how many of the top ten hits two lists share. Very high overlap suggests redundancy. Very low overlap suggests that the lists bring different strengths, which is what fusion loves, provided that both are of good quality.
15RRF Compared with Other Ways of Combining Results
RRF is one tool among several. Knowing the alternatives helps you choose the right one for your situation.
| Method | How it combines | Needs tuning? | Uses scores? | Strength | Weakness |
|---|---|---|---|---|---|
| Reciprocal rank fusion | Adds credits based on ranks | Very little | No | Robust, simple, no scale problems | Ignores how big the gaps between scores are |
| Weighted sum of normalized scores | Normalizes each list, then adds with weights | Yes, weights and normalization | Yes | Keeps score gaps, allows fine control | Sensitive to outliers and drift |
| Weighted sum inside one query | Adds boosted scores from a query and a vector search in the same request | Yes, boosts | Yes | Simple to write | Scales differ, so boosts are fragile |
| Cross-encoder reranking | A model reads the query and each candidate together and reorders them | Model choice and window | Model score | Very precise at the top | Slower and costlier per query |
| Learning to rank | A trained model combines many features | Needs training data and upkeep | Feature values | Can learn your business goals | Requires labels, clicks, and maintenance |
flowchart TD
A["Need to combine several retrievers"] --> B{"Do you have judged queries to tune with?"}
B -->|No| C["Start with RRF and its defaults"]
B -->|Yes| D{"Do score gaps carry real meaning for you?"}
D -->|Yes| E["Consider normalized scores with weights, and compare with RRF"]
D -->|No| C
C --> F{"Is top-of-list precision still not good enough?"}
E --> F
F -->|Yes| G["Add a reranking stage on the top results"]
F -->|No| H["Keep it simple"]
Choosing a way to combine opinions is like choosing how to pick a restaurant with friends. Voting by ranked lists is quick and fair, and it works without anybody having to agree on a rating scale. Asking everybody for a score out of ten and averaging requires trusting that everyone uses the scale the same way. Hiring a food critic to taste the top three finalists gives a very precise final choice but costs time and money. Each approach has its place.
When RRF is especially attractive
- You have no labeled data yet, and you want a strong baseline quickly.
- Your retrievers use different score scales, and you do not want to normalize them.
- Your data or your models change often, and tuned weights would keep going stale.
- You want a method that is easy to explain to colleagues.
When another method may be better
- You have plenty of judged queries and want to squeeze out every bit of relevance, so you can afford tuning.
- Score gaps are meaningful, for example an exact identifier match with a very high score that should stay far above everything else.
- You need to combine relevance with business signals such as price or popularity, where a learned model or a dedicated function score fits better.
A middle path
Many production systems begin with RRF, measure quality, and add a reranking stage for the top results. Others compare RRF with a weighted linear combination on their own data and keep the winner. It is a good habit to make the fusion method a configurable part of the pipeline, so that it can be compared and switched without rewriting the application.
General claims about which method wins are averages over many datasets. Your data may differ. A small, honest evaluation on your own queries is worth more than any general advice.
16RRF and Reranking Together
A popular design uses RRF to gather a good candidate set cheaply, and then a stronger but slower model to reorder the best few. Each stage does what it does best.
Retrieval must be fast over millions of documents, so retrievers use compact tricks. The result is a good but imperfect ordering. Reranking takes the top few dozen candidates and examines each together with the query, using a model that reads both, and reorders them more carefully. It is too expensive to run on millions of documents, but affordable on fifty.
flowchart LR
A["Query"] --> B["Lexical retriever"]
A --> C["Vector retriever"]
B --> D["RRF fusion: recall oriented, cheap"]
C --> D
D --> E["Top candidates, for example 50 to 100"]
E --> F["Reranker model: precision oriented, costly"]
F --> G["Final top results shown to the user"]
A company receiving a thousand job applications first uses a quick screening that combines several simple signals to produce a shortlist of fifty. Then senior interviewers spend an hour each on the shortlisted candidates. The screening is cheap and rough, and the interviews are expensive and precise. Putting them in that order is efficient, since expensive interviews for all thousand would be impossible.
How the stages divide the work
| Aspect | Stage one: RRF | Stage two: reranker |
|---|---|---|
| Goal | Recall: make sure good documents are in the candidate set | Precision: put the best ones at the very top |
| Input | The whole index | Only the top candidates |
| Cost per query | Low to moderate | Higher, grows with the number of candidates |
| Uses | Ranks from several retrievers | Query and document text read together |
| Main setting | Rank constant and window | Number of candidates to rerank, and the model |
Choosing how many candidates to rerank
More candidates give the reranker more chances to find a hidden gem, but they cost more time and money. Common choices are between twenty and one hundred. Measure the improvement in quality as the number grows, and stop when the gain flattens.
Expressing it as one request
Because retrievers can be nested, a reranking retriever can wrap the RRF retriever, and Elasticsearch runs both stages in one request. The reranker calls a model through an inference service, either one hosted with Elasticsearch or an external provider. Confirm which retrievers and services your version and license support.
Watch the latency budget
The reranker adds a call to a model, and the time of that call is often larger than the retrieval time itself. Keep the number of candidates modest, set timeouts, and have a plan for when the model service is slow or unavailable, such as returning the fused list without reranking.
Not always needed
Many applications are well served by RRF alone. Add a reranker only if an evaluation shows that top-of-list precision is the bottleneck, and if your latency and cost limits allow it.
A reranking model receives the text of the query and the candidate documents. If it runs at an external provider, that text leaves your environment. Confirm that this is acceptable for your data and your rules.
17Performance and Scalability
The fusion arithmetic is almost free. The cost of hybrid search is the cost of running several retrievers, and a few habits keep it under control.
Where the time goes
| Component | Cost driver | Ways to reduce it |
|---|---|---|
| Lexical retriever | Number of matching documents, query complexity, shard count | Use filters, simple queries, and well-sized shards |
| Vector retriever | Number of candidates examined, vector size, memory access | Choose candidates carefully, keep vectors in fast memory, and consider compressed vector formats |
| Query embedding | Calling an embedding model to turn the query into a vector | Use a fast model, cache repeated queries, and keep the model close to the cluster |
| Fusion on the coordinating node | Shards times retrievers times window | Use a moderate window and fewer shards |
| Fetch phase | Size of the final page and of the documents | Filter the source, and keep pages small |
| Optional reranker | Candidates times model latency | Rerank fewer candidates, and set timeouts |
Running a hybrid search is like sending two couriers to two different warehouses and waiting for both before you can assemble the order. Adding up the packing lists at the end takes a few seconds, but the trips take most of the time. If one courier is slow, the whole order is late. The total time is set by the slowest courier, not by the packing.
Latency is set by the slowest retriever
All retrievers must finish before fusion can start. A vector search that is twice as slow as the lexical search means the request is about twice as slow as the lexical alone. Measure each retriever separately with profiling, and improve the slowest first.
Query embedding is often the hidden cost
To search by meaning, the query text must first be turned into a vector. If this happens at search time by calling a model, that call adds latency and may be the largest part of the total. Options include computing it in the application with a fast local model, using an inference service near the cluster, and caching the vectors of frequent queries. Always set a timeout so that a slow model service does not block searching entirely.
Memory for vectors
Vector search is fastest when the vectors and the graph index can be read from memory. Large collections of large vectors need a lot of memory, either in the operating system’s file cache or elsewhere, and searches slow down sharply when the data must be read from disk. Compressed or quantized vector formats reduce memory needs at some cost in precision. Plan capacity for the vectors you will have, not the vectors you have today.
Scaling with shards and replicas
More shards let a large index be searched in parallel, but each retriever runs on every shard, so the work and the merge cost also grow. Replicas add capacity for many simultaneous searches. Right-size shards, avoid too many small ones, and test hybrid queries at your real concurrency, not just one at a time.
Measure percentiles
Average latency hides the slow requests that users notice. Track the median, the ninety-fifth, and the ninety-ninth percentiles for the whole request and for each retriever. A steady median with a rising ninety-ninth percentile often points to occasional slow embedding calls, memory pressure, or garbage collection.
Decide how long a search may take, for example three hundred milliseconds, and divide the budget among the stages: embedding, retrieval, fusion, and fetch. When a stage exceeds its share, you know exactly where to look.
18Advantages, Disadvantages, and Trade-offs
No method is perfect. Here is an honest scorecard so that you can decide where RRF fits.
Advantages
- Needs no score normalization, because it uses ranks only.
- Works with retrievers that have completely different score scales.
- Has only two settings, with good defaults, so it is easy to start.
- Rewards documents that several retrievers agree on.
- Stays useful when data or embedding models change, because it does not rely on tuned weights.
- Extends naturally to more than two lists.
- Is easy to explain, debug, and reason about by hand.
- Degrades gracefully when one retriever returns nothing.
Disadvantages
- Throws away the size of score gaps, so a runaway winner looks like any other first place.
- Gives equal influence to every list unless weights are used.
- Depends on the window: documents outside it earn no credit.
- Produces scores that are only good for sorting, not for thresholds.
- Costs the sum of all retrievers, and latency follows the slowest one.
- Does not support every search option, such as custom sorting and deep pagination.
- Can be diluted when lists are poor or redundant.
- May depend on version and license level for availability.
The central trade-off: robustness against precision
Using ranks makes the fusion robust, but it discards the information hidden in scores. A method that keeps scores can, in principle, exploit that information, but only if it is tuned well and kept up to date. RRF chooses robustness and simplicity. If the last few percent of relevance matter enough to justify tuning, other methods are worth a comparison.
The second trade-off: consensus against champions
The rank constant balances trust in the top of each list against trust in agreement between lists. The default favors agreement. Lowering it favors champions. There is no universally correct point, so it is chosen by measuring.
The third trade-off: window size against cost
A larger window brings more evidence and better recall, but costs time and memory on every shard and on the coordinating node. Choose the smallest window that gives good quality.
The fourth trade-off: more lists against more cost
Each added retriever can bring a new strength, but it adds latency, memory use, and complexity, and it may duplicate the opinion of another list. Add lists deliberately.
When RRF is the wrong tool
If you have only one retriever, there is nothing to fuse. If you must sort by a field, such as price or date, RRF’s relevance order does not apply. If you need to read all results in order, use a sorted search with a bookmark. And if you must combine relevance with strong business rules, consider dedicated functions or a trained model.
19Measuring and Tuning Relevance
RRF has few settings, but the two it has still deserve a measurement. A small, honest evaluation turns opinions into evidence.
Tuning without measuring is guessing. To know whether the fusion helps and which settings are best, you need a set of test queries and a way to judge the answers.
Building a judged query set
Collect a few dozen to a few hundred queries that resemble real user searches, including exact codes, natural questions, short keywords, and awkward phrasings. For each query, write down which documents are good answers, and if possible how good they are. The judgments can come from experts, from support staff, or from click and purchase behavior, though clicks are noisy and biased toward what was already shown.
Metrics in plain words
| Metric | What it measures | Good for |
|---|---|---|
| Recall at k | The share of all good documents that appear in the top k results | Checking that the candidate set contains what it should |
| Precision at k | The share of the top k results that are good | Checking how clean the visible page is |
| Mean reciprocal rank, MRR | The average of one divided by the rank of the first good result | Tasks where users want one right answer quickly |
| Normalized discounted cumulative gain, nDCG | A score that rewards putting the best documents highest, with credit for degrees of goodness | General ranking quality with graded judgments |
The names are similar, and both involve reciprocals of ranks, but they are unrelated. RRF is a method for merging lists. MRR is a metric for evaluating a ranking. You can use MRR to evaluate the results of RRF.
Think of testing a new recipe. You do not trust a single taste by the cook. You have several people taste it, ask them to score it, and compare it with the old recipe using the same tasters. A judged query set is the panel of tasters, and the metrics are the score sheets.
A tuning loop
flowchart TD
A["Collect judged queries"] --> B["Measure baselines: lexical alone, vector alone"]
B --> C["Measure RRF with the defaults"]
C --> D["Change one setting: rank constant or window"]
D --> E["Measure again on the same queries"]
E --> F{"Clearly better and not worse on any query group?"}
F -->|Yes| G["Keep the change and record why"]
F -->|No| H["Revert and try another idea"]
G --> D
H --> D
Steps of a small evaluation
Define success
Decide which metric matters for your product: one right answer fast, a clean first page, or coverage of many good documents.
Group the queries
Label queries as exact-term, natural-language, short, or long, so that you can see where each method helps.
Measure each retriever alone
This shows their individual strengths and gives the baselines that the fusion must beat.
Measure the fusion with defaults
Use the default constant and a sensible window. In most cases, this alone is a major improvement.
Try a few values
Test a couple of rank constants and window sizes, one change at a time, and watch both the averages and the worst groups.
Confirm online
Release the best candidate to a small share of real traffic and compare user behavior, such as clicks on the first results and how often users rephrase their query.
Watch out for overfitting
With a small set of queries, it is easy to find settings that look great on those queries but fail on new ones. Keep a separate set of queries that you use only for a final check, and prefer changes that help broadly over changes that help a few queries a lot.
The tools of the platform
Elasticsearch has a ranking evaluation operation that runs a set of queries with judged documents and reports metrics. Support for the newer retriever-based requests may vary by version, so if it does not fit your request type, a small script that runs your queries and computes the metrics gives the same information.
A new embedding model, a large change in the data, or a version upgrade can shift the balance between retrievers. Re-run your evaluation after any of these, since settings that were best before may no longer be.
20Best Practices and Design Patterns
Reliable hybrid search is mostly a matter of a few good habits, chosen at design time. These eight cover almost every case.
Fuse complementary retrievers
Pair a lexical retriever with a meaning-based one, so that each covers the weaknesses of the other.
Put the same filters in every child
Every list must respect the same tenant, permission, stock, and category rules before the fusion.
Choose the window on purpose
Use a window several times the page size, and check that the vector retriever returns at least as many hits.
Measure before tuning
Keep a judged query set and compare each retriever alone, the fusion, and any change to k or window.
Keep an explain library
Save a set of important queries and their expected top results, and check them with explain after every change.
Degrade gracefully
If the embedding service or the vector search fails, fall back to the lexical list alone, and record the event.
Add reranking only when needed
Use a second stage on the top candidates when precision at the top is the bottleneck, and mind its latency and privacy.
Cache and pin what you can
Cache vectors of frequent queries, and pin the versions of models and settings so that results change only when you decide.
Pattern 2 in action
An online shop applies its “in stock” and “region” filters inside both the lexical and the vector retriever. Early on, the team had applied the region filter only to the lexical child. Shoppers occasionally saw vector-only hits from other regions at the top, which confused them and caused failed orders. After moving the filter into both children, the problem vanished. The lesson is that a filter must be present in every list, since the fusion trusts what it receives.
Pattern 6 in action
A documentation portal calls an embedding service to turn queries into vectors. On a busy morning, the service slows down and starts to time out. Because the search code catches the timeout and falls back to a lexical-only request, users still get results, only slightly less smart. An alert tells the team about the fallback rate, and they fix the embedding service before most users notice anything.
Pattern 4 in action
A team believes that a rank constant of ten will help because their lexical retriever is very precise at the top. They test with two hundred judged queries. The average improvement is tiny, but the group of natural-language questions gets noticeably worse, because champions of the lexical list crowd out good consensus documents. They keep the default. The experiment took an afternoon and prevented a regression.
A pattern for documentation
Write a one-page design note for each hybrid search feature: which retrievers are fused, their queries and filters, the rank constant and window with the reason, the embedding model and its version, the evaluation results, the fallback behavior, and the owner. When someone changes a model or a mapping later, this note shows what depends on it.
The best hybrid search configuration is usually simple: two good retrievers, the default constant, a sensible window, and the same filters everywhere. Complexity should have to prove its value in numbers.
21Anti-Patterns and Common Mistakes
These are the traps behind most disappointing hybrid searches. Each is common and easy to avoid once you know it.
What people do
They add the raw scores of the lexical and vector searches with hand-picked boosts and call it hybrid search.
Why it hurts
The scales differ, so the larger scale dominates, and the balance changes with every query and every data update. The result looks fine in a few tests and fails in production.
Better approach
Fuse ranks with RRF, or normalize scores carefully with a measured comparison. Never add raw scores from different scales.
What people do
They leave the rank window at a tiny default, such as ten, and expect great fusion.
Why it hurts
Documents that are moderately ranked in several lists never collect credits from all of them, so the benefit of agreement is lost.
Better approach
Choose a window several times the page size, and measure the gain against the cost.
What people do
They apply filters to only one of the child retrievers.
Why it hurts
The unfiltered child brings forbidden or irrelevant documents into the fusion. In the worst case, this leaks documents that a user should not see.
Better approach
Put every filter, especially security filters, inside every child, or use a shared filter where the version supports it.
What people do
They set a minimum score threshold on the fused results to hide weak matches.
Why it hurts
Fused scores are tiny, depend on the number of lists and on the rank constant, and are not comparable across queries. A threshold that works for one query cuts everything for another.
Better approach
Limit the count of results, or apply thresholds on a retriever’s own meaningful measure, such as a vector similarity cutoff inside that child.
What people do
They fuse several retrievers that return almost the same documents.
Why it hurts
The shared opinion is counted several times, other views are drowned out, and the cost rises without any new information.
Better approach
Measure overlap, keep lists that bring different strengths, and drop redundant ones.
What people do
They change the rank constant or window based on a hunch after seeing a few queries.
Why it hurts
Improvements for some queries often hurt others, and without measurement nobody notices the regression until users complain.
Better approach
Tune with a judged query set and confirm with a small online experiment.
What people do
They use hybrid search with RRF for full exports, deep pagination, or sorted listings.
Why it hurts
The fused list is limited by the window and ordered by relevance, so deep reading and sorting do not fit, and the cost of every page is high.
Better approach
Use RRF for relevance-ordered first pages, and use a sorted search with a bookmark for exports and listings.
Other frequent mistakes
- Setting the vector retriever to return fewer neighbors than the window.
- Comparing fused scores across different queries or different numbers of lists.
- Forgetting that documents without vectors can appear only in the lexical list.
- Changing the embedding model for queries without reindexing the document vectors.
- Ignoring shard failures and timeouts, which quietly shorten the lists.
- Assuming that a feature marked as a preview will behave the same in a later version.
22Security, Privacy, and Cost Considerations
Hybrid search adds new places where your data and your money can leak: the model calls, the extra queries, and the explanation output. A little design closes them.
The fusion itself is only arithmetic on ranks, so it adds no new access to data. The risks come from the parts around it: the retrievers, the embedding and reranking models, and the logs.
| Risk | What could happen | Protection |
|---|---|---|
| A child retriever without the security filter | Documents that a user must not see enter the fused list | Apply the same access rules in every child, and rely on document level security for role-based restrictions |
| Query text sent to an external model | Sensitive questions leave your environment | Choose models that run inside your environment, or confirm the provider’s terms and data handling |
| Document text sent to a model for embedding or reranking | Private content is exposed to a third party | Use in-house or contractually approved services, and minimize what is sent |
| Secrets for model services | Leaked keys allow misuse and cost | Store keys in the secure keystore or a secret manager, and rotate them |
| Explain output | Reveals internal ranks, scores, and query structure | Restrict who may request explanations, and do not expose them to end users |
| Poisoned documents | Content built to rank high in the lexical or vector list, for example by keyword stuffing | Validate and moderate content that enters the index, and monitor sudden ranking changes |
| Abuse through expensive queries | Attackers or bugs run many heavy hybrid searches | Rate limits at the gateway, timeouts, and caps on window and page size |
| Sensitive queries in logs | Query text and slow logs hold personal data | Limit log content and access, and set short retention |
| Traffic captured on the network | Queries and results are exposed | Encrypt connections between clients, services, model endpoints, and the cluster |
Think of a newsroom that sends questions to outside experts. The editor must decide which questions may leave the building, who may see the experts’ answers, and how to pay the experts. Nothing is dangerous about combining the experts’ answers, but every phone call to an outsider is a place where a secret can slip out. Model calls in hybrid search are those phone calls.
Access control still applies
Elasticsearch checks the privileges of the caller on every search, and restrictions on documents and fields are enforced for each retriever. Use dedicated service accounts with only the privileges the feature needs. Do not give the search feature broader rights than the indices it reads.
Cost is a security concern too
Every hybrid search costs more than a simple one, and every model call may cost money. Without limits, a bug or an attack can produce a large bill or exhaust the cluster. Track cost per query, set budgets and quotas, and alert on unusual volume.
Data residency and rules
If your data may not leave a country or a network, check where your embedding and reranking models run, and where their logs are stored. Some organizations require that all model processing stays inside their own infrastructure. Include these rules in the design from the start.
Model and data integrity
The vectors in your index depend on the model that produced them. Restrict who may change models and mappings, and record which model version created which vectors. An unnoticed change can break search quality or open a way to manipulate rankings.
Queries and documents that pass through models, caches, and logs may contain personal data. Confirm with your security and legal teams where these copies live, who can read them, and how long they are kept.
23Monitoring, Logging, and Metrics
A healthy hybrid search is quiet. A handful of measurements shows when a retriever slows down, a model fails, or relevance quietly drifts.
| Question | Where to look | What healthy looks like |
|---|---|---|
| How long does each stage take? | Application timers for embedding, and profiling or search timing for the retrievers, fusion, and fetch | Stable percentiles within the latency budget |
| Are searches timing out or partial? | The timed-out flag and shard failure counts in responses | Zero or very rare |
| Is the model service healthy? | Error rates and latency of embedding and reranking calls | Low errors and steady latency |
| How often do searches fall back to one retriever? | A counter in the application for each fallback | Near zero, and alerts on spikes |
| How much do the lists overlap? | A sampled comparison of the top hits of each child | A stable, moderate overlap |
| Which list contributes the top results? | The share of the top ten that came from each list, from explain samples | A stable mix that matches your expectation |
| Do users find what they need? | Click position, rephrase rate, and abandonment | Improving or steady |
| Do queries return nothing? | Zero-result rate | Low, and lower than lexical alone |
| Are documents missing vectors? | A count of documents without a vector in the vector field | Zero, or a known small number |
| Are slow hybrid queries visible? | The search slow log, which records the query body | Few entries, and none from routine traffic |
A restaurant with two kitchens tracks how long each kitchen takes, how often a dish comes back, how many customers leave without ordering, and whether a key ingredient has run out. When the numbers are steady, nobody worries. When one drifts, the manager investigates. The same simple accounting keeps a hybrid search healthy.
Canary queries
Choose a small set of important queries and run them regularly against production, checking that the expected documents appear near the top. This catches silent relevance problems, such as a broken embedding model or a bad deployment, much earlier than complaints do.
Metrics worth charting
- Latency by stage and percentile. Shows where time goes and where the tail comes from.
- Fallback rate. The clearest sign of trouble in the vector or model path.
- Zero-result rate and partial-result rate. Show coverage and reliability.
- List contribution mix. Shows drift in the balance between retrievers.
- Click position and rephrase rate. Show whether users are satisfied.
- Cost per query. Tracks model and cluster spending.
- Vector coverage. The share of documents that have vectors.
Alerts that help
Create a short list of alerts. First, an alert when the fallback rate rises above a small limit. Second, an alert when the ninety-fifth percentile latency of any stage exceeds its budget. Third, an alert when partial results or timeouts rise. Fourth, an alert when a canary query loses its expected top results. Fifth, an alert when the share of documents without vectors grows. Each alert should link to a short runbook.
Store the model version, the rank constant, the window, and the deployment version next to your metrics. When a chart changes, you can see at once which change was responsible.
24Failure Scenarios and Recovery
Hybrid search depends on several moving parts, and each can fail on its own. A robust design expects this and degrades gracefully.
flowchart TD
A["A hybrid search runs"] --> B{"Did the query embedding call succeed in time?"}
B -->|No| C["Fall back to the lexical retriever alone and count the fallback"]
B -->|Yes| D{"Did all shards respond in time?"}
D -->|No| E["Retry once, then return results marked as partial and raise an alert"]
D -->|Yes| F["Fuse the lists with RRF"]
F --> G["Return the final page"]
C --> G
E --> G
Scenario: the embedding service is slow or down
The vector retriever needs a query vector. If the service that makes it fails, the vector list cannot be built. The best recovery is a fallback to the lexical list alone, with a counter and an alert. Users see slightly less smart results instead of an error. Set a short timeout on the embedding call so that the fallback happens quickly.
Scenario: the model changed but the document vectors did not
Vectors from different models live in different number spaces. If you switch the query model without reindexing the document vectors, the vector list becomes almost random, and the fusion is polluted. Symptoms are sudden drops in relevance for natural-language queries, and a suspicious loss of overlap between lists. The recovery is to restore the old query model, or to reindex all documents with the new model, and to test the pairing before every release.
Scenario: some documents have no vectors
If embedding of new documents lags or fails, those documents have no vector and can appear only in the lexical list. They are not lost, but they lose the chance to earn a second credit. Monitor vector coverage, and repair the embedding pipeline. A periodic job that finds documents without vectors and fills them in is a cheap safety net.
Scenario: a dimension mismatch
The query vector must have the same number of dimensions as the field. A mismatch produces an error, often after a model change. Confirm dimensions in the mapping and in the model output before switching.
Scenario: shards fail or time out
When a shard does not answer, its candidates are missing from every list, and the fused ranking is built from an incomplete set. Check the failure counts in every response, retry once, and treat frequent partial results as an incident. Do not cache partial results.
Scenario: relevance drifts after an upgrade or a data change
A version upgrade may change defaults, such as the window, or the behavior of a retriever. A big data import can change word statistics and shift the lexical list. Symptoms are gradual complaints or a change in the list contribution mix. Compare the settings before and after, re-run your judged queries, and adjust.
A restaurant with two kitchens must keep serving when one kitchen shuts down. The menu becomes shorter, but guests still eat. If the second kitchen suddenly starts cooking with the wrong recipes, the guests get strange dishes, and the manager must notice quickly. A hybrid search needs both the fallback plan and the taste tests.
Scenario: the cluster is overloaded
Hybrid searches are heavier than simple ones. Under load, searches may be rejected or slowed. Reduce the window, reduce the vector candidates, add capacity, or apply rate limits at the gateway. Because searches are read-only, retrying with growing delays is safe.
Habits that make recovery easier
- A tested fallback to a single retriever.
- Version records for models, mappings, and settings.
- Canary queries that run continuously.
- A repair job for missing vectors.
- A runbook for model changes that includes reindexing and testing.
Changing the embedding model is a data migration, not a setting. Plan it like one: build the new vectors alongside the old ones, test, switch, and keep a way back.
25Troubleshooting Common Problems
When something looks wrong, match the symptom to its likely meaning. This table covers what people meet most often.
| What you see | Most likely meaning | First things to check |
|---|---|---|
| Results look the same as plain keyword search | The vector child returns nothing or very little | Vector field name, missing vectors, vector k and candidates, and the query vector itself |
| Results look the same as plain vector search | The lexical child matches nothing, or its analyzer does not fit the query | The lexical query, field mappings, analyzers, and language settings |
| Fewer results than the requested size | The window is smaller than the size, or filters are too strict | Rank window size, filters in each child, and the number of matches |
| An error says the window must be at least the size | The window is smaller than the requested size or the offset plus size | Increase the window, or reduce size and from |
| Page three is empty while page two has results | The window does not reach deep enough | Window must be at least offset plus size |
| An error about the rank constant | The value is below one or of the wrong type | Use a whole number of at least one |
| An error about vector dimensions | The query vector has a different length from the field | Model output size against the mapping |
| Forbidden or out-of-scope documents appear | A filter is missing from one child | Add the same filters to every child |
| Scores look tiny or strange | This is normal for RRF, which sums small reciprocals | Compare only within one query, and do not threshold |
| Identical requests give slightly different results | Approximate vector search, differences between shard copies, or ties | Vector settings, replicas, and tie handling. Small differences are expected. |
| An exact code query does not put the exact match first | Consensus documents outrank the champion of the lexical list | Check ranks with explain, consider a smaller constant, an exact-match child, or weights where supported |
| Natural-language queries give poor results | Embedding model mismatch, missing vectors, or a weak model | Model versions for queries and documents, vector coverage, and evaluation |
| A custom sort is ignored or rejected | RRF orders by fused relevance | Use a different search for sorted listings |
| A feature is reported as unavailable or requires a license | The version or subscription level does not include it | Version, license level, and the documentation for your release |
| Searches got slow after adding hybrid | The slowest retriever or the embedding call dominates | Profile each stage, and check window, candidates, and model latency |
| Some results have no highlights | They came only from the vector list | Design the interface to show a fallback snippet |
A calm way to debug
Run each retriever alone
Check each list separately. Most fusion problems are actually problems in one of the lists.
Look at the overlap
Compare the top hits of the lists. No overlap, or complete overlap, both point to something worth investigating.
Turn on explain for one bad query
See the rank of the unwanted and wanted documents in each list, and how the credits added up.
Check the settings together
Confirm the window, the constant, the vector parameters, and the filters in every child.
Check the environment
Look at shard failures, timeouts, model service errors, and recent deployments or model changes.
Change one thing, then measure
Adjust a single setting, and re-run your judged queries. Record the finding in your runbook.
Pick one query where you know the right answer. Look at its rank in the lexical list alone, in the vector list alone, and in the fused list. If it is high in at least one list and low in the fusion, your window, constant, or filters deserve a closer look.
26Real-World Scenarios
Here are four common situations, with rough numbers, showing RRF at work. The companies are imaginary, and all figures are invented to show the shape of the effect.
Scenario 1: The help center that could not find both kinds of questions
HelpDeskHero, the imaginary support portal from Chapter 2, measures its keyword search and its vector search on two hundred judged queries. Exact error code queries do very well with keywords and badly with vectors, while natural questions show the opposite. Fusing the two lists with RRF and the default settings gives a result that is close to the best of each on their strong groups, and better than either overall. The team ships it without any score tuning.
Scenario 2: The shop with product codes and vague wishes
The imaginary retailer ShopSprout receives searches such as “SKU 48213-B” and “comfortable shoes for standing all day.” The first needs exact matching, the second needs meaning. They fuse a lexical child, with filters for stock and region, and a vector child with the same filters. After a mistake where the region filter was missing from the vector child, they add a shared checklist for filters in every child. Conversion on long descriptive queries improves, and exact SKU queries stay perfect.
Scenario 3: The legal research tool with three lists and a reranker
The imaginary firm CaseCompass fuses three lists: a lexical list on case text, a lexical list on case titles and citations, and a dense vector list. A reranking model then reorders the top forty. The team measures that the reranker adds about two hundred milliseconds, which fits their budget for a research product. They keep a fallback to the fused list if the reranker fails, and they run the model inside their own network for confidentiality.
Scenario 4: The code search that fused identifiers and intent
The imaginary company CodeCove offers search over source code. Developers type exact function names, and also questions like “where do we retry failed uploads.” A lexical child handles identifiers, and a vector child handles intent. When exact identifiers were sometimes outranked by consensus results, the team tested a smaller rank constant, found no overall gain, and instead added a third child that matches identifiers exactly. The exact matches stayed on top, and natural questions kept working.
A worked example with numbers
Set the scene
A team has two hundred judged queries. Measured by mean reciprocal rank, the lexical search alone scores 0.52, and the vector search alone scores 0.55. These figures are invented for illustration.
Fuse with the defaults
RRF with a constant of sixty and a window of one hundred scores 0.68, better than both lists alone, without any weights.
Look at the groups
For exact-code queries, lexical scores 0.91, vector 0.40, and the fusion 0.88. For natural questions, lexical scores 0.38, vector 0.62, and the fusion 0.66. The fusion is near the best on each group.
Check the latency
Embedding the query takes about 60 milliseconds. The lexical search takes 40 and the vector search 55 on the shards, running in the same phase, so the slower one, 55, sets the retrieval time. Fusion takes about 3, and fetching the page takes about 15. The total is roughly 60 plus 55 plus 3 plus 15, about 133 milliseconds.
Try a change
A window of fifty instead of one hundred reduces the merge work but lowers the score to 0.66. A constant of ten lowers natural-question performance. The team keeps the defaults with the larger window.
The result
The numbers are invented, but the pattern is realistic: the fusion is better than either list, the cost is the sum of the retrievers, and small measured changes settle the remaining choices.
27Frequently Asked Questions
28Summary and Key Takeaways
Reciprocal rank fusion is a simple idea with a large effect: merge ranked lists by position, not by score. Here is everything to remember.
Key Takeaways
- What it is: a method that merges several ranked lists into one by giving each document a credit of one divided by the rank constant plus its rank from each list, then adding the credits.
- Why it exists: keyword and vector searches have different strengths and incompatible score scales, so adding scores is fragile, while ranks can be combined without normalization or tuning.
- How its settings behave: the rank constant, sixty by default, balances top-heavy champions against agreement between lists, and the rank window size sets how deep each list goes and how far paging can reach.
- How it runs: every shard runs each retriever, the coordinating node builds one global list per retriever, applies RRF, cuts the final page, and fetches its documents.
- How to use it well: fuse complementary retrievers, put the same filters in every child, choose the window on purpose, and avoid raw-score mixing, thresholds, and near-duplicate lists.
- How to prove it works: keep a judged query set, compare each retriever alone against the fusion, tune one setting at a time, and confirm with a small online test.
- How to run it safely: plan a fallback to a single retriever, watch latency per stage, protect model calls and logs, and treat model changes as data migrations.
Keep in mind that syntax, defaults, supported options, and license requirements can change between Elasticsearch versions, so confirm exact details against the documentation of the release you run. With that habit, and the ideas in this guide, you can combine the best of keyword and meaning-based search into one dependable ranking, without turning your relevance work into a permanent tuning project.