Notice that this sum is the inner product (dot product) of two sparse vectors over the vocabulary space V (dimension ∣V∣=30,522 for bert-base-uncased):
Document Sparse VectorvD: For each unique token t∈D, store the normalized term-frequency weight:
vD[t]=f(t,D)+k1⋅(1−b+b⋅avgdl∣D∣)f(t,D)⋅(k1+1)
Query Sparse VectorvQ: For each token qi∈Q, store its inverse document frequency weight IDF(qi).
When you compute the inner product vD⋅vQ using pgvector or pgvecto.rs’s <#> operator, PostgreSQL returns −BM25(D,Q) so that ORDER BY embedding <#> query_vec ASC returns the highest-scoring documents first!
Installation
Prerequisites
Install either pgvector (v0.7.0+ for sparsevec support) or pgvecto.rs (v0.3.0+ for svector support) first.
Install from Release Packages (Debian / Ubuntu)
Download the prebuilt .deb package matching your PostgreSQL major version from GitHub Releases:
# Example for PostgreSQL 16 on x86_64wget https://github.com/tensorchord/pg_bestmatch.rs/releases/download/v0.0.1/pg-bestmatch-pg16_0.0.1_amd64.deb
sudo dpkg -i pg-bestmatch-pg16_0.0.1_amd64.deb
Build from Source (cargo pgrx)
Ensure Rust and cargo-pgrx (0.12.0-alpha.1 or compatible) are installed:
git clone https://github.com/tensorchord/pg_bestmatch.rs.git
cd pg_bestmatch.rs
cargo pgrx install --release
Enable the Extension
Connect to PostgreSQL, create the extension, and add bm25_catalog to your search_path:
CREATEEXTENSIONIFNOTEXISTSpg_bestmatch;CREATEEXTENSIONIFNOTEXISTSvector;-- or vectors for pgvecto.rs
SETsearch_pathTO"$user",public,bm25_catalog;