pub fn rrf(
ranks: Vec<RankExpr>,
k: Option<u32>,
weights: Option<Vec<f32>>,
normalize: bool,
) -> Result<RankExpr, QueryConversionError>Expand description
Reciprocal Rank Fusion (RRF) - combines multiple ranking strategies.
RRF is ideal for hybrid search where you want to merge results from different ranking methods (e.g., dense and sparse embeddings) with different score scales. It uses rank positions instead of raw scores, making it scale-agnostic.
§Formula
score = -Σ(weight_i / (k + rank_i))Where:
weight_i= weight for ranking i (default: 1.0)rank_i= rank position from ranking i (0, 1, 2…)k= smoothing parameter (default: 60)
Score is negative because Chroma uses ascending order (lower = better).
§Arguments
ranks- List of ranking expressions (must havereturn_rank=true)k- Smoothing parameter (None = 60). Higher values reduce emphasis on top ranks.weights- Weight for each ranking (None = all 1.0)normalize- If true, normalize weights to sum to 1.0
§Returns
A combined RankExpr or an error if:
ranksis emptyweightslength doesn’t matchrankslengthweightssum to zero when normalizing
§Examples
§Basic RRF with default parameters
use chroma_types::operator::{RankExpr, QueryVector, Key, rrf};
let dense = RankExpr::Knn {
query: QueryVector::Dense(vec![0.1, 0.2, 0.3]),
key: Key::Embedding,
limit: 200,
default: None,
return_rank: true, // Required for RRF
};
let sparse = RankExpr::Knn {
query: QueryVector::Dense(vec![0.1, 0.2, 0.3]),
key: Key::field("sparse_embedding"),
limit: 200,
default: None,
return_rank: true, // Required for RRF
};
// Equal weights, k=60 (defaults)
let combined = rrf(vec![dense, sparse], None, None, false).unwrap();§RRF with custom weights
use chroma_types::operator::{RankExpr, QueryVector, Key, rrf};
// 70% dense, 30% sparse
let combined = rrf(
vec![dense, sparse],
Some(60),
Some(vec![0.7, 0.3]),
false,
).unwrap();§RRF with normalized weights
use chroma_types::operator::{RankExpr, QueryVector, Key, rrf};
// Weights [75, 25] normalized to [0.75, 0.25]
let combined = rrf(
vec![dense, sparse],
Some(60),
Some(vec![75.0, 25.0]),
true, // normalize
).unwrap();§Adjusting the k parameter
use chroma_types::operator::{RankExpr, QueryVector, Key, rrf};
// Small k (10) = heavy emphasis on top ranks
let top_heavy = rrf(vec![dense.clone(), sparse.clone()], Some(10), None, false).unwrap();
// Default k (60) = balanced
let balanced = rrf(vec![dense.clone(), sparse.clone()], Some(60), None, false).unwrap();
// Large k (200) = more uniform weighting
let uniform = rrf(vec![dense, sparse], Some(200), None, false).unwrap();