barrel_vectordb_bm25 (barrel_vectordb v2.1.2)

View Source

BM25 sparse embeddings (pure Erlang, in-memory)

Implements BM25 (Best Matching 25) algorithm for sparse text retrieval. No external dependencies - runs entirely in Erlang.

Important: In-Memory Index

This module maintains an **in-memory** index. The index is not persisted and will be lost when the process terminates. This design is intentional:

- Simple and fast for small/medium datasets - Suitable for hybrid search (combine with dense vectors) - Can be rebuilt from documents stored in barrel_vectordb on startup - No additional storage overhead

For large datasets requiring persistence, consider rebuilding the index from your document store on application startup.

Use Cases

- Hybrid search: combine BM25 keyword scores with dense vector similarity - Re-ranking: use BM25 to re-rank dense search results - Keyword filtering: pre-filter candidates before expensive dense search - Small datasets: where the index comfortably fits in memory

Usage

   %% Create a new BM25 index
   Index = barrel_vectordb_bm25:new().
  
   %% Add documents
   Index1 = barrel_vectordb_bm25:add(Index, <<"doc1">>, <<"Hello world">>).
   Index2 = barrel_vectordb_bm25:add(Index1, <<"doc2">>, <<"Hello there">>).
  
   %% Search
   Results = barrel_vectordb_bm25:search(Index2, <<"hello">>, 10).
   %% => [{<<"doc1">>, 0.85}, {<<"doc2">>, 0.82}]
  
   %% Get sparse vector for a document
   {ok, SparseVec} = barrel_vectordb_bm25:get_vector(Index2, <<"doc1">>).
   %% => #{<<"hello">> => 0.5, <<"world">> => 0.8}
  
   %% Hybrid search example (combine with dense vectors)
   DenseResults = barrel_vectordb:search(Store, Query, #{k => 100}),
   BM25Results = barrel_vectordb_bm25:search(Index, Query, 100),
   HybridResults = combine_scores(DenseResults, BM25Results, 0.5).

Algorithm

BM25 score = sum(IDF(t) * TF(t,d) * (k1 + 1) / (TF(t,d) + k1 * (1 - b + b * |d| / avgdl)))

Where: - IDF(t) = log((N - n(t) + 0.5) / (n(t) + 0.5) + 1) - TF(t,d) = term frequency of t in document d - N = total number of documents - n(t) = number of documents containing term t - |d| = length of document d (in tokens) - avgdl = average document length - k1 = 1.2 (term frequency saturation parameter) - b = 0.75 (length normalization parameter)

Summary

Functions

Add a document to the index.

Encode text into a sparse vector without adding to index. Uses current index statistics for IDF calculation.

Get the sparse vector representation of a document. Returns term weights based on BM25 scoring.

Create a new empty BM25 index with default parameters.

Create a new empty BM25 index with custom parameters.

Remove a document from the index.

Search the index with a query. Returns list of {DocId, Score} sorted by score descending.

Get index statistics.

Types

bm25_config/0

-type bm25_config() ::
          #{k1 := float(), b := float(), min_term_length := pos_integer(), lowercase := boolean()}.

bm25_index/0

-type bm25_index() ::
          #{docs := #{binary() => doc_info()},
            vocab := #{binary() => vocab_info()},
            total_docs := non_neg_integer(),
            total_tokens := non_neg_integer(),
            config := bm25_config()}.

doc_info/0

-type doc_info() :: #{terms := #{binary() => pos_integer()}, length := non_neg_integer()}.

sparse_vector/0

-type sparse_vector() :: #{binary() => float()}.

vocab_info/0

-type vocab_info() :: #{doc_freq := non_neg_integer()}.

Functions

add(Index, DocId, Text)

-spec add(bm25_index(), binary(), binary()) -> bm25_index().

Add a document to the index.

encode(Index, Text)

-spec encode(bm25_index(), binary()) -> sparse_vector().

Encode text into a sparse vector without adding to index. Uses current index statistics for IDF calculation.

get_vector(Index, DocId)

-spec get_vector(bm25_index(), binary()) -> {ok, sparse_vector()} | {error, not_found}.

Get the sparse vector representation of a document. Returns term weights based on BM25 scoring.

new()

-spec new() -> bm25_index().

Create a new empty BM25 index with default parameters.

new(Config)

-spec new(map()) -> bm25_index().

Create a new empty BM25 index with custom parameters.

remove(Index, DocId)

-spec remove(bm25_index(), binary()) -> bm25_index().

Remove a document from the index.

search(Index, Query, K)

-spec search(bm25_index(), binary(), pos_integer()) -> [{binary(), float()}].

Search the index with a query. Returns list of {DocId, Score} sorted by score descending.

stats(Index)

-spec stats(bm25_index()) -> map().

Get index statistics.