Postgres
pg_textsearch
BM25 / Keyword
pgvectorscale
Vector / Semantic
Zero Complexity
Forget provisioning three separate systems. It's just one database.
Zero Latency
Queries run where your data lives. No ETL pipelines or sync lag.
Total Consistency
Single source of truth. Writes and searches are transactionally safe.
pg_textsearch
BM25 / Keyword
Modern ranked text search as a native Postgres extension. Industry-standard BM25 scoring with the <@> operator, Block-Max WAND optimization, and parallel index builds.
CREATE INDEX ON documents USING bm25(content)
WITH (text_config='english');
SELECT * FROM documents
ORDER BY content <@> 'database system'
LIMIT 10;Supports 29 languages
Configurable parameters
pgvectorscale
Vector / Semantic
High-performance embedding search built on top of pgvector.
The StreamingDiskANN index delivers 28x lower p95
latency and 16x higher throughput than Pinecone's storage-optimized index.
CREATE INDEX ON documents
USING diskann (embedding vector_cosine_ops);
SELECT * FROM documents
ORDER BY embedding <=> $query_vector
LIMIT 10;75% less cost on AWS
Statistical Binary Quantization
The Killer Feature
The real power is using both together. Combine the lexical precision of BM25 with the semantic understanding of vector embeddings—without leaving your database, without an external reranker, and without syncing data.
How RRF works
Run BM25 keyword search and vector similarity search in parallel, then fuse results using Reciprocal Rank Fusion. One database, one query layer, state-of-the-art retrieval quality.
-- BM25 keyword results
WITH keyword_results AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY content <@> 'search query') AS rank_kw
FROM documents
ORDER BY content <@> 'search query'
LIMIT 20
),
-- Vector similarity results
semantic_results AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $query_vec) AS rank_vec
FROM documents
ORDER BY embedding <=> $query_vec
LIMIT 20
)
-- Reciprocal Rank Fusion
SELECT COALESCE(k.id, s.id) AS id,
COALESCE(1.0/(60 + k.rank_kw), 0) + COALESCE(1.0/(60 + s.rank_vec), 0) AS rrf_score
FROM keyword_results k
FULL OUTER JOIN semantic_results s ON k.id = s.id
ORDER BY rrf_score DESC
LIMIT 10;
How RRF works
Run BM25 keyword search and vector similarity search in parallel, then fuse results using Reciprocal Rank Fusion. One database, one query layer, state-of-the-art retrieval quality.
One system to operate
No ETL pipelines. No sync jobs. No eventual consistency. Insert a row and it's searchable.
ACID guarantees for search
Your search index participates in transactions. Rollback a write and the index rolls back with it.
Security you already have
Row-level security, roles, and permissions apply to search queries automatically.
Backups include everything
pg_dump, streaming replication, point-in-time recovery—your search indexes are just part of your database.
SQL you already know
No new query language. No client library. ORDER BY, LIMIT, WHERE, JOIN—search composes with everything else.
# Install pg_textsearch
cd /tmp && git clone https://github.com/timescale/pg_textsearch
cd pg_textsearch && make && make install
# Install pgvectorscale (requires Rust)
cd /tmp && git clone https://github.com/timescale/pgvectorscale
cd pgvectorscale/pgvectorscale
cargo pgrx install --release
-- Enable both extensions
CREATE EXTENSION pg_textsearch;
CREATE EXTENSION vectorscale CASCADE; -- also installs pgvector
-- You now have BM25 + vector search in one database