AI Search Over
Your Database
Introducing pgContext
A full AI search engine, built into Postgres. Hybrid dense + full-text retrieval, filter-aware ANN, and exact, MVCC-visible re-scoring — a dedicated vector engine's feature set, shipped as a PostgreSQL 17 and 18 extension.
Two branches. One ranked answer.
Semantic similarity finds what a query means. Lexical matching finds what it literally says. pgContext runs both and fuses the ranked lists, so the documents that surface on both sides rise to the top.
Dense ANN
pgcontext.searchHierarchical descent from the upper layers to the base with an ef_search frontier, running over durable PostgreSQL index pages. Named-vector selection covers multi-vector collections, and exact scan stays available as the correctness oracle.
Sparse & lexical
pgcontext.search_sparseSPLADE-style learned sparse retrieval over explicit arrays or registered sparse columns, alongside a PostgreSQL to_tsvector / plainto_tsquery full-text branch. Named sparse ANN traverses dense storage, then rescores exactly.
Fusion
pgcontext.queryReciprocal rank fusion at k = 60 with deterministic ties, plus weighted branches, scoring formulas, score thresholds, and prefetch-then-rerank.
Same recall. Up to 5.3× faster.
pgContext vs pgvector on the standard GloVe-100-angular benchmark — 1,183,514 vectors, cosine, both engines in one PostgreSQL 17 container with the same parallel build budget, scored against the dataset's own ground-truth neighbors.
Apple M4 Pro (NEON kernels) · dataset ground-truth neighbors · Full report →
Faster at the same recall
pgContext matches pgvector’s recall at every search setting while answering each query 3.8–5.3× faster — and the advantage widens as you raise the recall target.
Higher recall for the same latency
Read the other way, that speed is quality: given roughly 2.5 ms per query, pgContext reaches 0.91 recall@10 where pgvector reaches 0.75.
Every answer re-checked exactly
ANN candidates are resolved back to the live row and re-scored exactly, under PostgreSQL MVCC visibility, ACL/RLS, and SQL predicates.
Filters that are part of the search.
Filtering is where vector search usually breaks: bolt a WHERE clause onto an approximate search and recall quietly collapses as the filter gets selective. pgContext treats filtering as part of the search, not an afterthought.
SELECT source_key, score
FROM pgcontext.search(
'docs', '[ ... ]'::pgcontext.vector,
'{
"must": [{"key": "tenant_id", "match": "acme"},
{"key": "price", "range": {"gte": 10, "lt": 20}}],
"should": [{"key": "metadata.topic", "match": {"value": "billing"}}],
"must_not": [{"key": "archived", "match": true}]
}'::jsonb,
10
);No filter index to build
Registering a column or JSONB path makes it filterable immediately. Add an ordinary index later only to speed a specific filter up — it’s an optimization, never a prerequisite.
Filter-aware ANN, not post-filtering
Below a selectivity crossover pgContext scores exactly; above it, a single reusable mask rides the persisted HNSW graph while excluded nodes still act as connectors — so recall holds up even when the filter is highly selective.
One grammar for search, count, and facets
The same must / should / must_not filter drives filtered search, counts, and facet aggregation: a cohesive retrieval API, not hand-assembled SQL per query.
Safe and governed by PostgreSQL
Filters compile to a typed AST with bound parameters over registered fields only, and every result is re-checked against MVCC visibility and RLS/ACL before it is returned.
Plain SQL. No sidecar service.
Vectors are columns, filters are JSON over registered fields, and results come back as rows you can join against everything else in the database.
CREATE EXTENSION pgcontext;
CREATE TABLE docs (
id text PRIMARY KEY,
embedding pgcontext.vector(3) NOT NULL,
category text NOT NULL,
metadata jsonb NOT NULL
);
SELECT * FROM pgcontext.create_collection('docs', 'public.docs');
SELECT pgcontext.register_vector(
'docs', 'embedding', 'embedding', 3, 'cosine');
SELECT pgcontext.register_filter_column(
'docs', 'category', 'category');
SELECT pgcontext.upsert_points(
'docs', ARRAY['postgres', 'rust', 'vectors']);-- exact search is the correctness oracle;
-- the HNSW index accelerates it
CREATE INDEX docs_embedding_hnsw
ON docs USING pgcontext_hnsw (
embedding pgcontext.vector_hnsw_cosine_ops
);
SELECT id,
embedding OPERATOR(pgcontext.<=>)
'[1,0,0]'::pgcontext.vector AS distance
FROM docs
ORDER BY embedding OPERATOR(pgcontext.<=>)
'[1,0,0]'::pgcontext.vector
LIMIT 3;Four representations, one type system.
L2, inner product, cosine, and L1 across dense, half, and sparse; Hamming and Jaccard across bit. Every type carries comparison operators and a default btree opclass, with a typed lossless and lossy conversion policy between them.
pgcontext.vectorDenseUp to 16,000 dimensions, pgvector-compatible text I/O, typmods, casts, and aggregates (sum, avg).
halfvecHalf precisionHalf-precision storage with text I/O, dims, exact distances, operators, explicit rounding casts, and aggregates.
sparsevecLearned sparseCanonical {index:value}/dim form, structured construction, dense real[] / vector casts, accessors, and aggregates.
bitvecBinaryHamming and Jaccard distance, boolean[] / bit / bit varying casts, typmods, and bitwise OR/AND aggregates.
More than one way to ask.
Every method accepts the same filter grammar, so switching retrieval strategy never means rewriting your predicates.
pgcontext.searchDense vector search — exact baseline and ANN, with named-vector selection for multi-vector collections.
pgcontext.search_sparseSparse and learned retrieval over explicit arrays or registered sparse columns, with exact rerank.
pgcontext.queryHybrid fusion — dense retrieval merged with full-text or sparse ranking by reciprocal rank.
pgcontext.rerank_late_interactionColBERT MaxSim scoring, with search, ANN, and explain variants over a companion token table.
pgcontext.recommendExample-based retrieval from positive and negative point IDs or raw vectors.
pgcontext.discover / exploreDiversity-seeking retrieval anchored on a set of context points.
pgcontext.grouped_searchPer-group result caps keyed by a registered column or a JSONB path.
pgcontext.execute_queryComposite query IR — nearest, full_text, sparse_nearest, prefetch, rerank, weight, formula, score_threshold, recommend, discover, lookup.
Fifteen ways to walk the graph.
Graph kernels are metric-bound and ascending-distance, validated against the metapage's stored metric — never silently substituted with L2. The planner picks the serving strategy; correctness never depends on which one it chose.
Exact scan
The correctness oracle, and the fallback every other path can drop to.
Page-native HNSW
Full-precision hierarchical descent with an ef_search frontier over durable index pages.
Masked traversal
A reusable candidate mask restricts what is returned while masked nodes still act as connectors.
Adaptive selection
A selectivity crossover picks exact scoring for tight masks, one masked traversal for broad ones.
ACORN-like second hop
Sparse masks expand through neighbours of neighbours.
Packed pre-filter
Sparse candidates as sorted IDs, dense ranges as packed bitmaps — no transient SQL arrays.
Iterative expansion
Bounded candidate batches with recheck, governed by hnsw_iterative_expansion_limit.
Visibility masking
Unfiltered traversal still masks to active, ACL/RLS-visible collection points.
Quantized candidates
Binary, scalar/SQ8, and product-quantized encoding, always followed by exact rerank.
Mapped generations
Read-only mmap with a generation pin, bound to database, index, relfilenode, epoch, and metapage LSN.
Shared registry
Cross-backend attach to packed generations instead of a per-backend rebuild.
Backend-local cache
Packed cache and unpacked directory reads when hnsw_pack_on_first_use is off.
Delta-segment merge
Exact scan over the bounded on-disk delta region, merged with base-graph results.
Densified sparse
Named sparse ANN traverses dense graph storage, then rescores exactly on sparse sources.
Late-interaction ANN
A companion token-table HNSW collects source keys, then exact MaxSim on the authoritative table.
Search meaning where the data already is.
Apache-2.0, for PostgreSQL 17 and 18 — one CREATE EXTENSION away via Docker, Homebrew, PGXN, or source. Or skip the ops entirely with managed hosting.
Start Building with pgContext
Dense, sparse, and hybrid retrieval with Postgres transactions, visibility rules, and joins intact.