Built by Evokoa

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.search

Hierarchical 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_sparse

SPLADE-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.query

Reciprocal 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.

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.

1 · QUERYvector + filtermust: tenantshould: topicmust_not: archived2 · FILTER-AWARE HNSWexcluded nodes still route the search3 · EXACT RECHECKMVCC · RLS · exact scoreRegistering a column or JSONB path makes it filterable immediately.An ordinary index is an optional speed-up, never a prerequisite.
filtered_search.sqlFilters
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.

quickstart.sqlSetup
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']);
index.sqlANN
-- 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.vectorDense

Up to 16,000 dimensions, pgvector-compatible text I/O, typmods, casts, and aggregates (sum, avg).

halfvecHalf precision

Half-precision storage with text I/O, dims, exact distances, operators, explicit rounding casts, and aggregates.

sparsevecLearned sparse

Canonical {index:value}/dim form, structured construction, dense real[] / vector casts, accessors, and aggregates.

bitvecBinary

Hamming 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.search

Dense vector search — exact baseline and ANN, with named-vector selection for multi-vector collections.

pgcontext.search_sparse

Sparse and learned retrieval over explicit arrays or registered sparse columns, with exact rerank.

pgcontext.query

Hybrid fusion — dense retrieval merged with full-text or sparse ranking by reciprocal rank.

pgcontext.rerank_late_interaction

ColBERT MaxSim scoring, with search, ANN, and explain variants over a companion token table.

pgcontext.recommend

Example-based retrieval from positive and negative point IDs or raw vectors.

pgcontext.discover / explore

Diversity-seeking retrieval anchored on a set of context points.

pgcontext.grouped_search

Per-group result caps keyed by a registered column or a JSONB path.

pgcontext.execute_query

Composite 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.

01

Exact scan

The correctness oracle, and the fallback every other path can drop to.

02

Page-native HNSW

Full-precision hierarchical descent with an ef_search frontier over durable index pages.

03

Masked traversal

A reusable candidate mask restricts what is returned while masked nodes still act as connectors.

04

Adaptive selection

A selectivity crossover picks exact scoring for tight masks, one masked traversal for broad ones.

05

ACORN-like second hop

Sparse masks expand through neighbours of neighbours.

06

Packed pre-filter

Sparse candidates as sorted IDs, dense ranges as packed bitmaps — no transient SQL arrays.

07

Iterative expansion

Bounded candidate batches with recheck, governed by hnsw_iterative_expansion_limit.

08

Visibility masking

Unfiltered traversal still masks to active, ACL/RLS-visible collection points.

09

Quantized candidates

Binary, scalar/SQ8, and product-quantized encoding, always followed by exact rerank.

10

Mapped generations

Read-only mmap with a generation pin, bound to database, index, relfilenode, epoch, and metapage LSN.

11

Shared registry

Cross-backend attach to packed generations instead of a per-backend rebuild.

12

Backend-local cache

Packed cache and unpacked directory reads when hnsw_pack_on_first_use is off.

13

Delta-segment merge

Exact scan over the bounded on-disk delta region, merged with base-graph results.

14

Densified sparse

Named sparse ANN traverses dense graph storage, then rescores exactly on sparse sources.

15

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.

pgContext