bm25-rerank 0.1.0

BM25 reranker for RAG: in-memory term-frequency reranking against a small candidate set. Stateless, zero deps.
Documentation
use bm25_rerank::{rerank, score, Bm25Opts};

#[test]
fn keyword_match_wins() {
    let docs = ["the quick brown fox", "a brown dog", "lazy fox jumps"];
    let order = rerank("fox", &docs, Bm25Opts::default());
    // Doc 0 and 2 contain "fox"; doc 1 does not. Doc 1 should be last.
    assert_eq!(order[2], 1);
}

#[test]
fn shorter_doc_with_match_outranks_longer() {
    let docs = [
        "fox",
        "the quick brown fox jumps over the lazy dog repeatedly",
    ];
    let order = rerank("fox", &docs, Bm25Opts::default());
    assert_eq!(order[0], 0);
}

#[test]
fn empty_query_returns_zero_scores() {
    let docs = ["one two", "three four"];
    let s = score("", &docs, Bm25Opts::default());
    assert_eq!(s, vec![0.0, 0.0]);
}

#[test]
fn no_match_returns_zero_for_all() {
    let docs = ["one two", "three four"];
    let s = score("zzz", &docs, Bm25Opts::default());
    assert_eq!(s, vec![0.0, 0.0]);
}

#[test]
fn case_insensitive() {
    let docs = ["Fox jumps", "dog walks"];
    let order = rerank("FOX", &docs, Bm25Opts::default());
    assert_eq!(order[0], 0);
}

#[test]
fn multi_term_query() {
    let docs = ["quick brown fox", "lazy dog jumps"];
    let order = rerank("brown fox", &docs, Bm25Opts::default());
    assert_eq!(order[0], 0);
}

#[test]
fn empty_doc_list_safe() {
    let docs: [&str; 0] = [];
    let s = score("anything", &docs, Bm25Opts::default());
    assert!(s.is_empty());
}