Skip to the solution
HardSystem Design14 min read

pgvector cosine similarity index in 2025-26 — HNSW vs IVFFlat for semantic search at scale?

465 views

The problem

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 matching vector_cosine_ops operator 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, lists at build time and ivfflat.probes at query time; cheaper to build, must be built after the data is loaded and rebuilt when the data drifts
  • HNSW: a layered neighbour graph, m and ef_construction at build time and hnsw.ef_search at 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 vector are capped at 2,000 dimensions, halfvec allows 4,000
  • Operations: memory for the build (maintenance_work_mem), the index only helps ORDER 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:

  • pgvector
  • hnsw
  • cosine-search

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

14 min read · 13 sections

Run the code

Edit the query and run it against the tables for this question.

SQLExact cosine search: rank every row by cosine distance to the query (the ground truth)
SQLSimulate IVFFlat: scan only the nearest lists, and measure recall@3 for probes = 1, 2, 3
SQLSimulate an HNSW layer-0 walk: hop to the closest neighbour until none is closer
PostgreSQL without a vector index: ORDER BY distance LIMIT 3 is a Seq Scan plus a top-N sort
CREATE INDEX docs_e1_e2_idx ON docs (e1, e2);
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT id
FROM docs
ORDER BY 1 - (e1 * 0.643 + e2 * 0.766) / sqrt(e1 * e1 + e2 * e2)
LIMIT 3;

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 77 of 82 in SQL

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All SQL questions
Up nextHard

Recursive CTE with cycle detection — how do you handle circular manager references and produce a search order?

Continue