rrf

Function rrf 

Source
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 have return_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:

  • ranks is empty
  • weights length doesn’t match ranks length
  • weights sum 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();