Question presented to candidate: "We store OpenAI-style embeddings for a few million support articles in Postgres with pgvector and run cosine-similarity search. Which index would you put on the embedding column, HNSW or IVFFlat, and how would you tune it?"
What a strong answer should cover:
- Cosine distance, the
<=>operator and the matchingvector_cosine_opsoperator class; why normalized vectors make cosine, inner product and L2 rank alike - Both indexes are approximate (ANN): they trade recall for speed, so recall@k has to be measured against exact search
- IVFFlat: k-means lists built from existing data,
listsat build time andivfflat.probesat query time; cheaper to build, must be built after the data is loaded and rebuilt when the data drifts - HNSW: a layered neighbour graph,
mandef_constructionat build time andhnsw.ef_searchat query time; better speed-recall trade-off, slower to build and more memory, no training step (pgvector README) - Version facts: HNSW arrived in pgvector 0.5.0 (2023), iterative index scans in 0.8.0 (2024); indexes on
vectorare capped at 2,000 dimensions,halfvecallows 4,000 - Operations: memory for the build (
maintenance_work_mem), the index only helpsORDER BY embedding <=> $1 LIMIT k, and filtered queries need extra care
Clarifying questions expected:
- How many vectors, how many dimensions, and how fast does the table grow or change?
- What recall is acceptable (for example 95% of the true top 10), and what latency budget per query?
- Do queries also filter on other columns (tenant, category, language)?
Code / implementation expected: Optional — the index DDL and tuning settings, plus a way to measure recall against an exact query.
Schema used by the examples: