Trying pgContext on an Existing pgvector Database

pgContext can be installed alongside pgvector and can index existing vector columns directly — no data movement, no schema changes, no downtime. This page covers coexist mode: what works, what to expect, and how to migrate when you are ready.

Install order matters

Install pgvector first, pgContext second:

CREATE EXTENSION vector;     -- already present on a pgvector database
CREATE EXTENSION pgcontext;  -- detects pgvector and binds to its types

When pgvector is present, pgContext skips creating its own vector, halfvec, and sparsevec types and instead binds its operators, index operator classes, and functions to pgvector’s types. The two vector representations are byte-for-byte identical, so there is no conversion anywhere on the query path.

If pgContext was installed first, its own types occupy the public type names and CREATE EXTENSION vector will fail. Reaching coexist mode then requires reinstalling in the right order (DROP EXTENSION pgcontext, install pgvector, reinstall pgContext); pgcontext.enable_pgvector_binding() explains the same steps in-band.

Indexing an existing pgvector column

CREATE INDEX ON items USING pgcontext_hnsw (embedding pgcontext.vector_hnsw_cosine_ops);

The pgvector index (if any) and the pgContext index coexist on the same column; PostgreSQL’s planner picks one per query. Available operator classes: pgcontext.vector_hnsw_ops (L2), pgcontext.vector_hnsw_cosine_ops, pgcontext.vector_hnsw_ip_ops, pgcontext.vector_hnsw_l1_ops.

The registered-collection API works over pgvector columns the same way: pgcontext.create_collection / register_vector accept them directly.

The advisory notice

The first time a backend builds or scans a pgContext index over a pgvector-typed column, it emits one NOTICE recommending pgcontext.migration_report(). Results are always complete — the notice is guidance, never a gate. Disable it with:

SET pgcontext.pgvector_compat_warnings = off;

Migration tooling

  • pgcontext.migration_report() — read-only inventory: every pgvector-typed column, its dimensions, existing pgvector and pgContext indexes, and the exact suggested CREATE INDEX command per column.
  • pgcontext.adopt_pgvector(target => NULL, dry_run => true, drop_old => false) — migrates pgvector hnsw/ivfflat indexes to pgcontext_hnsw equivalents with the matching metric. The default is a dry run that only prints the commands; pass dry_run => false to execute, and drop_old => true to also drop each pgvector index after its replacement builds. Index creation inside the function uses plain CREATE INDEX (it cannot run CONCURRENTLY); on busy tables, prefer taking the dry-run commands and running them yourself with CREATE INDEX CONCURRENTLY.

Column types are never changed by these tools: in coexist mode your data stays in pgvector’s types, and dropping the pgvector extension is intentionally not possible. Full drop-in replacement (pgvector-spelled SQL with pgvector absent) is a separate roadmap stage.

Comparing indexes side by side

pgcontext.compare_indexes(table_name, column_name, queries => 20) measures every ANN index on a column — pgvector hnsw/ivfflat and pgcontext_hnsw alike — using sampled stored vectors as queries. For each reachable operator family it times the planner-chosen index scan and scores top-10 recall against an exact sequential-scan oracle with the same operator, returning one row per index: (index_name, access_method, operator, p50_ms, p95_ms, recall_at_10). Indexes the planner never chose (for example one shadowed by a cheaper index of the same operator family) report NULL measurements. The function is read-only; expect it to run 2 x queries statements per measured family.

Current limitations

  • vector and halfvec columns are fully supported: pgContext’s storage for both is byte-for-byte pgvector’s layout (halfvec elements are true IEEE 754 binary16, canonicalized with round-to-nearest-even on input exactly as pgvector does). sparsevec columns are not yet served in coexist mode: pgContext operations over them fail with an error rather than producing results (that representation has not yet been certified byte-compatible).
  • Restoring a dump of a coexist database requires pgvector to be created before pgContext, which matches the order pg_dump preserves.
  • DROP EXTENSION vector CASCADE in coexist mode also drops the pgContext objects bound to pgvector’s types. Run pgcontext.migration_report() first and migrate deliberately instead.