Already use Postgres — add vector search to the same database. Single source of truth for relational metadata and embeddings.
Adding a VECTOR to your existing filing cabinet. The cabinet knows everything else about the document (author, date, tags); now it also knows how similar it is to other documents.
CREATE EXTENSION vector;
CREATE TABLE chunks (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536),
metadata jsonb,
);
CREATE INDEX idx_chunks_vec ON chunks
USING hnsw (embedding vector_cosine_ops);
pgvector is the Postgres extension that adds a vector(N) type, distance operators (<-> L2, <=> cosine, <#> inner product), and ANN indexes (HNSW and IVFFlat). It is the right pick when you already run Postgres and want to keep embeddings and metadata in one place. Combine with jsonb for chunk metadata — a single SELECT can join semantic similarity with structured filters.
-- create extension and column
CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE chunks ADD COLUMN embedding vector(1536);
The <=> operator is cosine distance; pairs with vector_cosine_ops index.
-- nearest-neighbour retrieval
SELECT id, content
FROM chunks
ORDER BY embedding <=> :query_vec
LIMIT 5;
Filtered search pushes metadata into the same plan — no separate filtering service.
-- filtered ANN search
SELECT id, content
FROM chunks
WHERE tenant_id = :tenant
AND metadata @> '{"source_kind":"pdf"}'::jsonb
ORDER BY embedding <=> :query_vec
LIMIT 10;
jsonb metadata alongside vectors enables compound search: semantic + structured filters in one query.
Filtering AFTER ANN. Letting the index return 100 candidates and then filtering them down to 10 means most candidates are filtered out — low effective recall.
pgvector is the easy path when you already run Postgres. Use HNSW with vector_cosine_ops, pre-filter on metadata, store hot metadata as plain columns.