DEV Community

Daniel Kim
Daniel Kim

Posted on

pgContext Claims 5x Faster Vector Search Than pgvector — But the Benchmark Is Doing a Lot of the Work

pgContext banner

Polygres launched on Product Hunt on July 31, 2026 with a pitch that sounds, on first read, like every other "AI meets your database" launch of the last two years: point it at your existing tables and get retrieval-augmented generation without standing up a separate vector store. That pitch is cheap. Every vendor from Pinecone to Supabase has some version of it.

What's actually underneath the pitch is more interesting, and it's open source, which means you don't have to take Polygres's word for any of it. The core engine is pgContext, a native PostgreSQL 17/18 extension written in Rust that runs ten retrieval methods against your live tables at once — dense HNSW, sparse, full-text, filtered, quantized, and late-interaction search — and fuses the results into one ranked list without copying your data anywhere. Sitting next to it is pgGraph, which compiles the foreign keys you already have into a traversable graph, so multi-hop joins and neighborhood search work without you writing recursive CTEs. Polygres is the managed product wrapped around both; the extensions themselves are Apache-2.0 and permanently free to self-host.

The headline number driving most of the launch coverage is a benchmark claim: pgContext runs 3.8 to 5.3x faster than pgvector at matched recall on a standard ANN benchmark dataset. That's a real, specific, checkable claim, which already puts it ahead of most Product Hunt launches — and it's worth taking apart carefully, because the benchmark is doing more work than the launch page lets on.

What it actually does

Strip away the "AI context window" framing and Polygres is a search engine that lives inside Postgres instead of beside it. That distinction matters more than it sounds. The dominant pattern for the last three years has been: keep your source-of-truth data in Postgres, then ETL a copy of it (embeddings plus whatever metadata you need to filter on) into a purpose-built vector store like Pinecone or Qdrant. You now own two systems that need to agree with each other, a sync pipeline that can silently drift, and a second place for access control to leak from.

pgContext's bet is that this trade was never necessary — that the reason people reached for external vector databases wasn't that Postgres couldn't do approximate nearest-neighbor search, it's that pgvector (the extension most people used to try) left a lot of engineering on the table: filtering was mostly post-filter-after-ANN, HNSW graphs weren't tightly integrated with Postgres's own page and buffer management, and there was no first-class way to combine vector similarity with full-text or graph relevance in a single ranked query.

Concretely, when you install pgContext, you get:

  • Native vector types with L2, inner-product, cosine, and L1 distance metrics, plus experimental support for smaller non-dense representations (halfvec, sparsevec, bitvec) for teams trying to control index size.
  • Filter-aware ANN, using Qdrant-style must / should / must_not syntax over registered columns and JSONB paths, compiled to a typed AST with bound parameters — so filtering happens during the graph walk, not as a discard step after you've already retrieved candidates.
  • Hybrid retrieval that fuses dense vector search with Postgres's native full-text ranking via reciprocal-rank fusion, plus collections, scrolling, counting, facets, and grouping — the vocabulary of a dedicated vector database, implemented as SQL you can EXPLAIN.
  • Exact re-checking: every approximate candidate gets resolved back to its live source row and verified against PostgreSQL's MVCC visibility rules and any row-level security policies before it's returned. An ANN index can tell you a row was similar; it can't tell you whether that row still exists, was updated since the index last touched it, or is one your calling user is even allowed to see. pgContext checks all three at query time instead of trusting the index.

pgGraph is smaller in scope but solves a genuinely annoying problem: most "graph over relational data" tooling wants you to either maintain a second graph database (Neo4j, Memgraph) or hand-write recursive SQL for every traversal. pgGraph reads your existing foreign keys and exposes expand() and related() operations for multi-hop traversal, shortest-path, and neighborhood search directly against tables you didn't have to model any differently.

How it works

The interesting engineering decision is where the HNSW graph structures actually live. Most Postgres vector extensions build an index and store it as a Postgres index object, which works, but the index's internal graph layout is opaque to Postgres's own storage engine — it's a black box bolted onto the side. pgContext instead persists HNSW graph structures directly on Postgres index pages, using Postgres's own page-native storage APIs rather than a side-channel format. In practice this means the vector index participates in normal Postgres mechanisms — WAL, buffer cache, pg_dump — the same way a B-tree index does, instead of requiring its own backup and replication story.

The retrieval pipeline runs in three layers:

  1. Exact search as the ground-truth baseline (used for correctness testing and for small tables where approximation isn't worth the complexity).
  2. HNSW-based approximate nearest-neighbor retrieval for the fast path at scale.
  3. A re-scoring pass that takes ANN candidates, resolves each one back to its live row, and re-verifies it against MVCC visibility, any registered filters, and RLS/ACL policies — so an approximate index can never leak a row a user shouldn't see or return a row that was deleted a millisecond ago.

For access, you either talk to pgContext directly in SQL if you're self-hosting, or you go through the Polygres Python SDK (pip install polygres-sdk, Python 3.10+), which is an HTTP client against a Polygres project's Runtime API — it authenticates with an API key rather than a database password, so you're not handing your application a live Postgres connection string just to run a semantic search.

The SDK is worth a closer look because it exposes the hybrid design decisions as actual API surface instead of hiding them. On top of plain vector search, similar-row lookup, full-text search, and fuzzy matching, it gives you graph traversal through expand() and related(), and — the part that's actually novel — three distinct ways to combine graph and vector relevance in one query: graph-first (traverse, then rank by similarity within the neighborhood), vector-first (find similar rows, then filter by graph relationship), and joint ranking (score both signals together in one pass). Most teams doing "vector search plus a join" today hand-roll one of these three patterns in application code without naming it; pgContext just gives the pattern a name and a query-time implementation.

Self-hosting is a docker run away if you want multi-arch images, or brew install evokoa/tap/pgcontext if you're on macOS or Linux and prefer package managers; there's also a PGXN listing for teams that manage extensions through Postgres's own extension network. None of that requires touching Rust. You only hit the Rust toolchain (1.96.0) and cargo-pgrx 0.19.1 if you're building from source — to patch the extension, run it on an architecture without prebuilt binaries, or audit exactly what it's doing to your index pages before you trust it with production data.

What changed vs. before

The comparison that matters isn't "Polygres vs. nothing," it's "pgContext vs. pgvector" and "Postgres-native search vs. external vector databases," and those are two different arguments.

Against pgvector, the differentiators are architectural: page-native HNSW storage instead of a bolted-on index format, filter-aware ANN instead of post-filtering, and hybrid dense+full-text+graph retrieval fused server-side instead of stitched together in application code. pgvector has been the default "just use Postgres" answer for two years precisely because it's simple and battle-tested; pgContext is explicitly trying to out-engineer it on the axes pgvector left alone — at the cost of being a much younger, much less battle-tested codebase.

Against external vector databases (Pinecone, Qdrant, Weaviate), the argument is the one every "vector search inside your OLTP database" product makes: one system to operate, one place access control lives, no sync lag between your source of truth and your search index, one fewer network hop per query. Supabase has been making a version of this argument for years by bundling pgvector into its managed Postgres offering; Polygres's pitch is narrower and sharper — it's not "Postgres, but also has vectors," it's "a search engine, implemented as a Postgres extension, that happens to also give you your relational data back."

Why developers should care

Cost. Self-hosted pgContext and pgGraph are free — Apache-2.0, no seat limits, no vector-count ceiling baked into the license. The managed Polygres tier starts at $5/month for individuals and $99/month for production teams with higher limits and guaranteed uptime; during the current beta the managed cloud is free with $50 in credits. Compare that to a dedicated vector database bill that scales with both storage and query volume on top of whatever you're already paying for Postgres, and the "just use the database you already have" argument gets financially concrete, not just architecturally tidy.

Latency. Every hop between your application and a separate vector store is a hop pgContext removes. For a RAG pipeline doing filtered vector search plus a join back to relational metadata, that's the difference between one query and two round trips glued together in application code.

DX. You get vector search, full-text search, and graph traversal behind a query surface you already know: SQL, plus a typed Python SDK if you'd rather not hand-write it. No second query language, no second connection pool, no second observability stack to wire up.

Lock-in. This is the one worth being skeptical about, and it's also where Polygres does something unusual: the license is Apache-2.0 and self-hosting is a first-class, permanently free path, not a crippled trial tier pointing you at the paid product. If the managed offering disappoints you, you keep the extension and the data, full stop. That's a materially better position than most "database plus AI" startups, where the OSS core is a marketing funnel for the hosted product.

Security. Because retrieval happens inside Postgres, RLS and ACL enforcement apply to vector and graph queries the same way they apply to a plain SELECT. That's a real advantage over "copy your data into a vector store and hope your sync job also copied the access rules," which is a common and under-discussed failure mode in RAG systems handling multi-tenant data.

Maintainability. One database to back up, monitor, and reason about failure modes for, instead of two systems whose consistency guarantees you have to invent yourself.

There's a sixth factor that doesn't fit neatly into that list but matters just as much: correctness under concurrency. Any RAG system that syncs embeddings to an external store has to answer an uncomfortable question — what happens when a row is updated or deleted between the moment your sync job read it and the moment a user's query hits the stale copy in the vector store? Most teams answer this with "eventual consistency, best effort," which is fine until it isn't. Because pgContext's re-scoring pass resolves every ANN candidate back to the live row and checks MVCC visibility at query time, a deleted row simply can't come back as a result, and an updated row's stale embedding can't outrank its current content indefinitely. That's not a performance feature, it's a correctness one, and it's the kind of thing that's invisible in a demo and expensive in an incident review.

Practical use cases

  • Internal knowledge-base RAG where the source documents already live in Postgres (tickets, wiki pages, contracts) — no ETL step to keep a separate index in sync.
  • Multi-tenant SaaS search, where RLS-scoped hybrid search matters more than raw ANN speed, because a fast index that leaks another tenant's rows is worse than a slow one that doesn't.
  • Recommendation systems that benefit from combining graph relationships (via pgGraph's foreign-key traversal) with vector similarity — "items similar to this one, restricted to categories this user has purchased from before" is a graph-plus-vector query, not just a vector one.
  • Coding-agent and AI-agent memory, where an agent needs to pull relevant context from structured operational data (not just documents) — exactly the framing Polygres's own "context window for your database" pitch is going for, and one of the few places where that framing is doing real technical work rather than just marketing.
  • E-commerce and content search that wants full-text keyword precision and semantic fuzziness in the same ranked result set, via the built-in reciprocal-rank fusion, instead of running two separate searches and merging them by hand.

Limitations the launch page doesn't emphasize

  • PostgreSQL 17/18 only. If you're running an older Postgres (which a lot of production systems still are), pgContext isn't an option without an upgrade first.
  • Rust toolchain to build from source. Installing from Docker or Homebrew is straightforward; building from source needs Rust 1.96.0 and cargo-pgrx 0.19.1, which is a meaningfully higher bar than CREATE EXTENSION vector and puts self-hosters who need to patch or audit the extension into genuinely unfamiliar territory unless they already run Rust in production.
  • Non-dense vector types are explicitly experimental (halfvec, sparsevec, bitvec) — don't build a production index type decision around them yet.
  • The 3.8–5.3x benchmark is one dataset. GloVe-100-angular (1.18M vectors, cosine metric) is a standard, legitimate ANN benchmark — but it's a single dataset at a single dimensionality, and it's a benchmark pgContext's own team ran and published, not an independently reproduced one. Real workloads vary enormously by vector dimension, filter selectivity, and update rate, and HNSW-in-Postgres-pages is new enough that there's no independent, adversarial benchmark yet — the kind ANN-Benchmarks or a neutral third party would run across multiple datasets and index configurations. Recall@10 of 0.91 at 2.5ms vs. pgvector's 0.75 at the same latency is a genuinely large gap if it holds up broadly; treat it as "worth testing on your own data" rather than "proven."
  • No public numbers yet on write-heavy or high-churn workloads. Storing HNSW graph structures on Postgres's own pages is elegant for read/backup/replication, but HNSW graphs are notoriously expensive to maintain under heavy inserts and deletes — how that interacts with Postgres's own vacuum and page-management behavior at scale isn't something the launch materials address, and it's exactly the kind of thing that only shows up after months in production.
  • Young project, young company. pgContext and pgGraph are new; Evokoa (the company behind Polygres) doesn't have the multi-year production track record that pgvector or the established vector databases do. That's not disqualifying for a side project or an early-stage product, but it's a real factor for anyone considering it for a system where an index bug means a data-correctness incident, not just a slow query.
  • Beta pricing is a snapshot, not a commitment. The $50-credit free managed tier is explicitly a beta offer. The launch page states current list prices ($5/individual, $99/team) but says nothing about what happens to existing beta users' bills once the beta ends, or whether those numbers hold as usage scales. That's normal for a month-old hosted product, but it means the "cheaper than a vector database" argument is currently strongest for the self-hosted path, where the economics don't depend on a pricing page that could change.

Competitive comparison

pgContext / Polygres pgvector Pinecone / Qdrant / Weaviate Supabase (pgvector-based)
Where it runs Inside Postgres, page-native Inside Postgres, index-object Separate managed/self-hosted service Inside Postgres (managed)
Filtering Filter-aware ANN (in-graph) Largely post-filter Filter-aware ANN Post-filter (pgvector-based)
Hybrid search Native RRF, vector+text+graph Manual, app-side Native in most (Qdrant, Weaviate) Manual, app-side
Graph traversal pgGraph, from existing FKs None None None
Self-host license Apache-2.0, free forever PostgreSQL license, free forever Mostly source-available/proprietary tiers N/A (managed only)
Maturity Weeks old ~3 years, widely deployed Years, widely deployed at scale Years, widely deployed

The honest read of this table: pgContext is the only row that combines native graph traversal, filter-aware ANN, and hybrid fusion in one open-source Postgres extension. It's also the only row without years of production mileage behind it. Those two facts are directly in tension, and which one matters more depends entirely on what you're building.

Independent read

The engineering is legitimate. Persisting HNSW structures on Postgres's own pages instead of bolting on a side-channel index format, doing filtering during the ANN walk instead of after, and re-verifying every approximate candidate against MVCC and RLS before returning it — these are the specific, unglamorous fixes that "Postgres-native vector search" needed and mostly didn't get from pgvector, which optimized for simplicity and adoption over completeness. If the benchmark numbers hold up across more datasets and dimensions than the one published, pgContext is a genuine step forward for the "keep it all in Postgres" camp, not just a rebrand of it.

That said, the launch framing — "turn your database into a context window for AI" — is doing more marketing work than technical work. What pgContext actually built is a better hybrid search engine for Postgres, full stop; it would be exactly as useful for a product search bar or a support-ticket dedup pipeline as it is for a RAG chatbot. The AI framing is the growth lever, not the technical contribution, and it's worth reading the launch page with that separation in mind.

The single-benchmark-dataset issue is the thing I'd want resolved before betting a production system on the performance claims specifically. A 5x speedup is the kind of number that gets people to switch; it's also the kind of number that benchmark selection can produce without anyone doing anything dishonest — GloVe-100 has a particular dimensionality and clustering structure, and ANN performance is famously sensitive to both. None of that means the claim is wrong. It means "test it on your own data before you migrate off pgvector" isn't optional due diligence, it's the whole exercise.

Who should try it, wait, or skip it

Try it now if you're starting a new project on Postgres 17/18, your data model already has the foreign keys pgGraph wants to traverse, and you're comfortable running a young extension in a non-critical path (internal tools, a new feature behind a flag, a side project). The self-host path is genuinely free and Apache-2.0, so the cost of trying it is your time, not a contract.

Wait if you're running production RAG on pgvector today and it's working. Nothing about pgContext's current state — weeks-old, single-benchmark, no independent reproduction — justifies a migration off a system that's meeting your needs. Watch it for two or three more months; if the benchmark claims survive contact with other people's workloads and datasets, that's the signal to revisit.

Skip it if you're pinned to Postgres <17, need write-heavy vector workloads at real scale today, or don't have appetite for running an extension whose failure modes under production churn aren't publicly documented yet. That's not a knock on the project — it's just not the trade every team should be making with an eighteen-week-old index engine, however good the architecture looks on paper.


If you've actually run HNSW-on-Postgres-pages (pgvector, pgvectorscale, or now pgContext) against a write-heavy, high-churn table in production — what did vacuum and index bloat behavior actually look like, and did it change your index-rebuild cadence?

Sources:

Top comments (0)