Too much of a good thing: Why 20 candidates beat 200 when reranking search results
We expected more candidates to help, but across 25 benchmarks in Elasticsearch jina-reranker-v3.5 peaked at 100 candidates for BM25 and just 20 for semantic search, where 200 scored worse than no reranker at all.
Get hands-on with Elasticsearch: Dive into our sample notebooks in the Elasticsearch Labs repo, start a free cloud trial, or try Elastic on your local machine now.
We recently tested jina-reranker-v3.5 across 25 retrieval benchmarks to find out what candidate pool size provides the highest accuracy vs token cost. Reranking the top 100 candidates from Elasticsearch BM25 cut error by about 21% on average, and halving that pool to 50 cost roughly 2% accuracy for half the tokens. Semantic search with jina-embeddings-v5-text-small peaked much earlier, at 20 candidates and an 8.5% error reduction. Feeding it 200 scored worse on average than running no reranker at all. More candidates is not better. The worse your initial retrieval, the more of them a reranker needs, and the more it has left to fix.
What is a reranker?
Reranker AI models take queries and candidate matches as input and independently produce new rankings, ordering the candidates from best to least matching.
In theory, you could use them by themselves for retrieval by giving them your entire database to rank. But using a reranker that way is prohibitively expensive, both in computing resources and in token costs via an API. It amounts to having an AI model read your database every time you query it. In practice, rerankers are almost always used in conjunction with some more efficient retrieval process, such as embeddings, that selects candidate matches for a reranker to then reevaluate.
Rerankers come in broadly three types: pointwise, pairwise, and listwise. The difference is how many candidates you can feed them at once, how many times you run the model, and whether you get a reliable ordering out.
Type | Input | Model runs for N candidates | Consistent ordering? | Candidate limit | Example |
|---|---|---|---|---|---|
Pointwise | Query and one candidate | N, one per candidate | Yes, candidates sort by score | Any number |
|
Pairwise | Query and two candidates | N-1, comparing two at a time | No, A can beat B, and B can beat C, while C beats A | Any number | PairReranker |
Listwise | Query and the full candidate pool | 1 | Yes, the model returns the ordering | Whatever fits the model’s input |
|
Table 1: A comparison of reranker types.
Figure 1: A pointwise reranker.
Pointwise rerankers are models that take as input a query and a single candidate match and produce a score. Multiple retrieval candidates are then sorted by their scores. They let you evaluate any number of candidates but require you to run the entire model for each candidate. This kind of reranker is best used when there is a large set of candidates to evaluate, like when there is no high-quality first-stage retrieval, or scenarios like federated search, where candidates come from multiple sources that don’t reveal their scores or ranks to you. When you can do a good job of restricting the candidate pool to a small number of high-probability best candidates, it’s a relatively expensive solution compared to the alternatives.
jina-colbert-v2 is an example of a pointwise reranker. It takes one query and document pair and returns a score, although it’s most efficiently used with batches of documents and returning scores for each so that the query doesn’t have to be re-encoded for every document independently.
Figure 2: A pairwise reranker.
Pairwise rerankers take a query and just two candidates, and they tell you which of the two is a better match. You can use them to rerank any number of candidates by comparing them two at a time, like an elimination tournament. But this means that if you have N candidate matches, you need to run the model N-1 times, almost as many as for a pointwise reranker. Furthermore, they don’t guarantee a consistent ordering. If Candidate A is better than Candidate B, and Candidate B is better than Candidate C, according to the reranker, it’s possible that Candidate C will be ranked better than Candidate A.
PairReranker is an example of a pairwise reranker designed to rank candidate outputs from text generation models.
Figure 3: A listwise reranker.
Listwise rerankers take a query and a collection of candidates and produce an ordering on them, from best match to worst. They only let you process as many candidates as you can fit into the model’s input, and they’re typically somewhat larger models than pointwise ones. However, you only need to invoke the model once to evaluate an entire candidate pool.
Listwise reranking generally outperforms pointwise and pairwise reranking in head-to-head comparisons, but it can only work with a small number of candidate matches. In cases with large numbers of candidates, or where the relatively large resource requirements of listwise rerankers pose a problem, pointwise and pairwise rerankers may be better solutions.
In this article, we use jina-reranker-v3.5, the latest listwise reranker from Elastic | Jina AI, to look at two variables:
How is listwise reranker output influenced by the quality of the initial retrieval step and the quality of the candidate pool?
How many candidate matches does
jina-reranker-v3.5need to return optimal matches?
It turns out that these two variables aren’t entirely independent of each other.
How reranking improves Elasticsearch BM25 search
BM25 is the classic text information retrieval algorithm. It doesn’t use AI models, but it’s very fast, has low computational demands, and is built into Elasticsearch. It remains in very widespread use, despite generally superior performance from recent AI-driven search, in part because it’s very cheap and fast, and in part because this over-30-year-old algorithm has trained many users to expect search systems to perform the way that BM25 does.
We used a collection of standard text retrieval benchmarks, drawn from four widely used benchmark suites, to evaluate the performance of jina-reranker-v3.5 in conjunction with retrieval candidates found using BM25:
Benchmarking-IR (BEIR) is the classic English text information retrieval benchmark, widely used and containing a variety of materials from different domains. We used a subset that, we admit, excluded the largest datasets because it would have taken a lot more time to run tests on them.
CQADupStack is a collection of question-and-answer datasets, primarily relating to information technologies, drawn from forums on the Stack Exchange website.
Code Information Retrieval Benchmark (CoIR) is a collection of code retrieval datasets. We only used three datasets from this collection, all of which are relatively small and involve natural language queries or documents.
BRIGHT is a dataset designed to be very difficult for conventional retrieval algorithms. Its authors created it to test the reasoning abilities of text retrieval systems, ensuring that lexical retrieval schemes, like BM25, and embeddings-based semantic retrieval would struggle to find matches. Most conventional retrieval systems, including semantic search, score poorly on BRIGHT. We selected these datasets specifically to see whether
jina-reranker-v3.5adds value under extremely difficult conditions.
The table below shows the retrieval scores, measured at nDCG@10, for each dataset, using Elasticsearch’s BM25 implementation by itself (Elasticsearch Serverless on Elastic Cloud, English analyzer, default BM25 settings) and with the reranker at different candidate sizes: 10, 20, 50, 100, and 200. Our BM25 baseline was configured to retrieve only 200 candidates, so we didn’t test with larger candidate lists.
Dataset | BM25 alone | RR @10 | RR @20 | RR @50 | RR @100 | RR @200 | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|
BEIR | |||||||||||
SciFact | 0.6836 | 0.7389 | 0.7583 | 0.7647 | 0.7607 | 0.7392 | |||||
TREC-COVID | 0.6191 | 0.6533 | 0.7320 | 0.7994 | 0.8280 | 0.8191 | |||||
Quora | 0.8066 | 0.8667 | 0.8857 | 0.8971 | 0.9007 | 0.8838 | |||||
ArguAna | 0.4782 | 0.6645 | 0.7477 | 0.7889 | 0.8028 | 0.7786 | |||||
Webis-Touche2020 | 0.3381 | 0.3394 | 0.3821 | 0.3407 | 0.3155 | 0.2534 | |||||
NFCorpus | 0.3232 | 0.3397 | 0.3504 | 0.3535 | 0.3521 | 0.3375 | |||||
FiQA | 0.2506 | 0.3240 | 0.3704 | 0.4208 | 0.4500 | 0.4313 | |||||
SCIDOCS | 0.1547 | 0.1776 | 0.2024 | 0.2081 | 0.2150 | 0.1951 | |||||
CQADupStack | |||||||||||
Gaming | 0.4876 | 0.5446 | 0.5767 | 0.6070 | 0.6274 | 0.6105 | |||||
Android | 0.3932 | 0.4435 | 0.4724 | 0.5140 | 0.5279 | 0.4986 | |||||
English | 0.3488 | 0.4021 | 0.4357 | 0.4683 | 0.4841 | 0.4697 | |||||
Physics | 0.3488 | 0.4053 | 0.4357 | 0.4641 | 0.4873 | 0.4748 | |||||
Webmasters | 0.3145 | 0.3546 | 0.3837 | 0.4103 | 0.4195 | 0.4049 | |||||
Programmers | 0.2997 | 0.3488 | 0.3744 | 0.4169 | 0.4300 | 0.4139 | |||||
GIS | 0.3055 | 0.3555 | 0.3923 | 0.4235 | 0.4412 | 0.4301 | |||||
Unix | 0.2965 | 0.3428 | 0.3715 | 0.4085 | 0.4310 | 0.4152 | |||||
Stats | 0.2926 | 0.3311 | 0.3541 | 0.3915 | 0.4016 | 0.3862 | |||||
WordPress | 0.2728 | 0.3230 | 0.3490 | 0.3729 | 0.3864 | 0.3543 | |||||
TeX | 0.2441 | 0.2715 | 0.2948 | 0.3205 | 0.3313 | 0.3112 | |||||
Mathematica | 0.2140 | 0.2576 | 0.2923 | 0.3176 | 0.3367 | 0.3266 | |||||
CoIR | |||||||||||
StackOverflow-QA | 0.6724 | 0.7431 | 0.7697 | 0.8100 | 0.8351 | 0.8560 | |||||
CoSQA | 0.1714 | 0.1937 | 0.2283 | 0.2609 | 0.2828 | 0.2935 | |||||
Apps | 0.0485 | 0.0701 | 0.0969 | 0.1354 | 0.1779 | 0.2399 | |||||
BRIGHT | |||||||||||
LeetCode | 0.1439 | 0.1439 | 0.1498 | 0.1268 | 0.1226 | 0.1341 | |||||
StackOverflow | 0.1405 | 0.1417 | 0.1492 | 0.1551 | 0.1674 | 0.1543 |
Table 2: BM25 search with jina-reranker-v3.5 and varying candidate pool sizes.
Reranking with jina-reranker-v3.5 can improve on the base BM25 score for every benchmark, but for listwise rerankers, it depends on the candidate pool size. If the candidate pool is too large, adding the reranker can sometimes make things worse than using no reranker at all. In Table 2, you can see this happen with the LeetCode and Webis-Touche2020 benchmarks.
If we had used pointwise or pairwise reranking, this wouldn’t be possible. They produce the same ordering no matter how many candidates they’re given. If Candidate A is better than Candidate B, adding Candidate C would never change that. But listwise rerankers look at the entire list of candidates at once, and,when given too much information, they suffer from attention dilution and noise accumulation. AI isn’t magic. Attention-based models have finite resources, and they perform more poorly when those resources are stretched thinner.
We see here that giving jina-reranker-v3.5 200 candidates produced worse outcomes than giving it just the top 100. Only the three CoIR benchmarks have higher scores at 200 than for smaller candidate pools. In some cases, using only 50 candidates scores better than 100. For two benchmarks, scores peaked at 20.
The variation in optimal sizes highlights a major consideration in deploying AI, in any scenario; that is, benchmarks are only very indirectly indicative of performance on your data, in your pipeline, and it’s important to track the performance of the AI models you choose for your use case and data.
However, if we look at how much jina-reranker-v3.5 reduced error across different benchmarks, we do get a rough picture of what kinds of results we can expect:
Number of reranking candidates | 10 | 20 | 50 | 100 | 200 |
Average reduction in error across all datasets | 0.0878 | 0.1433 | 0.1875 | 0.2097 | 0.1873 |
Table 3: Error reduction for BM25 across candidate pool sizes.
Figure 4: Average reduction in error across benchmarks versus candidate pool sizes for BM25 with reranking.
We can see that in this scenario, using Elasticsearch BM25, performance peaks around 100 candidates, but that using 50 candidates only slightly increases the error (circa 2% using nDCG@10). Halving the number of candidates sent to the reranker reduces the token count by half, cutting the cost by 50% and reducing latency. Only two of the 25 benchmarks had a more than 10% increase in error when we shrunk the candidate pool from 100 to 50: BEIR TREC-COVID and CoIR StackOverflow-QA. In both cases, this was because the error rate was already very low at 50 candidates.
These tests suggest that your best bargain, using jina-reranker-v3.5 with Elasticsearch’s BM25 implementation, is to use only 50 candidates.
How reranking performs with semantic vector search
But what if we have a better initial search scheme? Do these findings hold for semantic search with AI-generated embeddings?
TL;DR: No.
We ran the same tests using jina-embeddings-v5-text-small embeddings and vector search instead of BM25. First, semantic search using this model produces considerably better results than BM25, reducing error on average by 25.7% across the chosen benchmarks. On one benchmark, anomalously, Jina’s semantic search does more poorly, but not by a large margin:
Figure 5: BM25 compared to jina-embeddings-v5-text-small on various benchmarks.
However, using jina-reranker-v3.5 at 100 candidates raises BM25’s scores to become competitive with Jina’s best semantic retrieval:
Figure 6: BM25 with jina-reranker-v3.5 (with 100 candidates), compared to jina-embeddings-v5-text-small by itself on various benchmarks.
Overall, jina-embeddings-v5-text-small performs better on these retrieval benchmarks, but the margin is much smaller. On average, across these benchmarks, jina-embeddings-v5-text-small reduces error by 4.2%, compared to using BM25 in conjunction with jina-reranker-v3.5. On half of the BEIR benchmarks, BM25 with reranking actually beats semantic search.
But if a good listwise reranker can bring lower-quality retrieval close to matching frontier semantic embeddings-based retrieval, how much more can it add when we use the best retrieval first?
We repeated our tests using semantic search with jina-embeddings-v5-text-small, with the same candidate pool sizes: 10, 20, 50, 100, and 200. The results follow:
Dataset | v5-text Alone | RR @10 | RR @20 | RR @50 | RR @100 | RR @200 |
|---|---|---|---|---|---|---|
BEIR | ||||||
SciFact | 0.7655 | 0.7935 | 0.7895 | 0.7795 | 0.7768 | 0.7541 |
TREC-COVID | 0.7846 | 0.8223 | 0.8355 | 0.8583 | 0.8642 | 0.8053 |
Quora | 0.8909 | 0.9024 | 0.9067 | 0.9068 | 0.9063 | 0.9013 |
ArguAna | 0.6485 | 0.7819 | 0.8111 | 0.8173 | 0.8183 | 0.8007 |
Webis-Touche2020 | 0.3015 | 0.3151 | 0.3293 | 0.3234 | 0.3247 | 0.2903 |
NFCorpus | 0.3978 | 0.4033 | 0.4001 | 0.3976 | 0.3841 | 0.3364 |
FiQA | 0.4946 | 0.5145 | 0.5205 | 0.5182 | 0.5103 | 0.4496 |
SCIDOCS | 0.2310 | 0.2415 | 0.2517 | 0.2393 | 0.2267 | 0.1826 |
CQADupStack | ||||||
Gaming | 0.6209 | 0.6536 | 0.6676 | 0.6604 | 0.6536 | 0.6048 |
Android | 0.5412 | 0.5627 | 0.5782 | 0.5713 | 0.5596 | 0.5157 |
English | 0.5171 | 0.5418 | 0.5533 | 0.5534 | 0.5490 | 0.5072 |
Physics | 0.5344 | 0.5407 | 0.5404 | 0.5395 | 0.5293 | 0.4923 |
Webmasters | 0.4595 | 0.4660 | 0.4763 | 0.4675 | 0.4621 | 0.4124 |
Programmers | 0.4851 | 0.5017 | 0.5005 | 0.4911 | 0.4795 | 0.4260 |
GIS | 0.4480 | 0.4745 | 0.4846 | 0.4873 | 0.4850 | 0.4325 |
Unix | 0.4964 | 0.5055 | 0.5200 | 0.5052 | 0.4923 | 0.4455 |
Stats | 0.4266 | 0.4377 | 0.4492 | 0.4514 | 0.4432 | 0.4046 |
WordPress | 0.3647 | 0.3890 | 0.4112 | 0.4147 | 0.4169 | 0.3695 |
TeX | 0.3500 | 0.3707 | 0.3786 | 0.3767 | 0.3687 | 0.3242 |
Mathematica | 0.3636 | 0.3770 | 0.3891 | 0.3732 | 0.3659 | 0.3244 |
CoIR | ||||||
StackOverflow-QA | 0.9341 | 0.9486 | 0.9470 | 0.9437 | 0.9414 | 0.9322 |
CoSQA* | 0.3812 | 0.3687 | 0.3782 | 0.3725 | 0.3628 | 0.3403 |
Apps | 0.7332 | 0.7823 | 0.7897 | 0.7823 | 0.7695 | 0.7355 |
BRIGHT | ||||||
LeetCode | 0.1803 | 0.1689 | 0.1546 | 0.1232 | 0.1141 | 0.0878 |
StackOverflow | 0.1606 | 0.1631 | 0.1754 | 0.1665 | 0.1432 | 0.1327 |
* CoSQA scores vary by about 0.01 between runs.
Table 4: Semantic vector search using jina-embeddings-v5-text-small, combined with jina-reranker-v3.5 and varying candidate pool sizes.
Better first-stage retrieval changes the optimal candidate pool size. On average, it’s considerably smaller, and optimal candidate pool sizes are much more spread out over different benchmarks. Using 200 candidates actually made results worse on average than not using a reranker at all. For two benchmarks, BRIGHT LeetCode and CoIR CoSQA, reranking scored below the retriever alone at every pool size.
Number of reranking candidates | 10 | 20 | 50 | 100 | 200 |
Average reduction in error across all datasets | 0.0661 | 0.0848 | 0.0767 | 0.0629 | -0.0239 |
Table 5: Error reduction for semantic vector search across candidate pool sizes.
Figure 7: Average reduction in error across benchmarks versus candidate pool sizes for semantic vector search with reranking.
Reranking using 20 candidates maximized performance, when used with jina-embeddings-v5-text-small, in contrast to 100 with BM25. This indicates that using better strategies to get the initial candidate pool reduces the cost of using a reranker afterward. However, using a better initial search system also reduces the added value of the reranker. Reranking the best 100 candidates found by BM25 reduced error on average by over 20%. Reranking the best 20 candidates from jina-embeddings-v5-text-small (the best performing candidate pool size) only reduced error by approximately 8.5% on average.
If we compare BM25 with jina-reranker-v3.5 to jina-embeddings-v5-text-small with jina-reranker-v3.5, using semantic search and an intelligent reranker in combination scores higher on every benchmark.
Figure 8: Semantic vector search with jina-embeddings-v5-text-small and jina-reranker-v3.5 (with 20 candidates), compared to Elasticsearch BM25 and reranking with 100 candidates, on various benchmarks.
However, the difference is smaller than you might expect. Using jina-embeddings-v5-text-small with jina-reranker-v3.5 reduced error by 13.26% on average, compared to BM25 with jina-reranker-v3.5. This is roughly half the error reduction we get comparing jina-embeddings-v5-text-small to BM25 without using a reranker.
What matters when tuning a listwise reranker
In this article, we use one high-performance listwise reranker. Some of the calculations here might not apply equally to other rerankers. However, listwise rerankers in general will pose similar problems to the ones described here. We haven’t been scientifically rigorous here, but we can distill from these results some general principles for deploying listwise rerankers:
How many candidates should you send to a reranker?
There are significant differences in outcomes, depending on how many candidates you send to the reranker. We only tested strategies involving fixed numbers. Depending on what information you retrieve from the initial search, you might be able to discern how many candidates are worth sending to the reranker. BM25 and semantic embeddings-based search both return scores with their results. There may be some policy, like a score threshold, that would improve reranker performance. We didn’t test this possibility.
But we do find definitively that you can give too many candidates to the reranker. More is not always better. It’s often worse.
The better the initial search, the less a reranker adds
This result was one we expected. When a retrieval system makes fewer mistakes, there’s less room for reranking to improve it. But now, we have some empirical evidence to back that up. We also show that a good reranker can improve enough on a relatively poor initial search to make it competitive with high-performance semantic search. There are many practical use cases where this is an important result. BM25, in particular, is very cheap, efficient, fast, and deeply entrenched in information retrieval, and Elasticsearch contains an enhanced BM25 implementation that’s already a very strong retrieval system. It may be easier to add jina-reranker-v3.5 to a BM25 pipeline than to reengineer the entire retrieval process around semantic search.
The worse the initial search, the larger the candidate pool needs to be
Two data points aren't definitive, but the gap between optimal candidate pool sizes in our tests (100 for BM25 versus 20 for jina-embeddings-v5-text-small) is suggestive. It means that any savings you might make in compute, latency, or cost by using BM25 with a reranker over using semantic search may be partly offset by the increased cost of having to pass a much larger candidate pool to your reranker.
How much do reranking results vary between datasets?
The impact of reranking and the optimal candidate pool sizes can vary a lot for different benchmarks, which means it can vary at least as much for different real-world use cases. We encourage you to test with your own data and try to optimize your pipeline yourself, because no selection of benchmarks is identical to your pipeline. Both jina-reranker-v3.5 and jina-embeddings-v5-text-small are available for Elasticsearch through Elastic Inference Service (EIS). You can sign up for a free Elastic Cloud instance for 14 days to try it yourself.
How to add jina-reranker-v3.5 to your Elasticsearch pipeline
You can test all these findings on your own data without hosting any models yourself. Using Elastic Cloud means that Elastic hosts everything and manages the entire search pipeline and AI stack.
If you already use BM25 search in Elasticsearch, you can add jina-reranker-v3.5 without reindexing anything:
Create an Elastic Inference API endpoint for the reranker.
Add a
text_similarity_rerankerretriever to your existing queries.Set
rank_window_sizeto 100. This parameter controls how many candidates go to the reranker, which is the variable we tested here.Rerun with
rank_window_sizeset to 50, and compare the results. In our tests, the smaller pool cost very little accuracy for half the tokens.
If you use semantic vector search, the setup changes and so does the starting point:
Index your data with
jina-embeddings-v5-text-small, using thesemantic_textfield.Add a
text_similarity_rerankerretriever withjina-reranker-v3.5.Set
rank_window_sizeto 20, not 100. Better initial retrieval needs a much smaller candidate pool, and for some data, the best size will be smaller still.Don't assume that more candidates help. We found that reranking 200 semantic search candidates did worse on average than not reranking at all.
See the Elasticsearch documentation on text reranking for more information about using jina-reranker-v3.5 with your existing Elastic search pipeline.
Both jina-embeddings-v5-text-small and jina-reranker-v3.5 are also available through the Jina API, with free tokens to try them out, and as Jina on-prem containers that run on your own hardware. The weights for both are on Hugging Face for testing and research under a noncommercial license. For commercial use, contact Elastic sales.
The best candidate pool size for your pipeline depends on your data, so measure it rather than trusting any default, including ours.
How helpful was this content?




