HNSW
Hierarchical Navigable Small World: a multi-layer proximity graph for fast approximate nearest-neighbor (ANN) search over vectors. Every inserted point is randomly assigned to a set of "highway" layers using the same coin-flip trick as a Skip List, which is what makes this a genuinely randomized structure rather than just an approximation heuristic.
How it works
Think of it as a Skip List built out of a proximity graph instead of a sorted linked list. Each point is added to layer 0 (the "ground floor", containing every point) and, with probability p per extra layer (a repeated coin flip, exactly like a Skip List's express lanes), also to layers 1, 2, 3, .... Most points only exist at layer 0; a lucky few climb several layers and become long-range shortcuts.
insert() descends greedily from the single global entry point (always the node at the current top layer) down to the new point's own top layer, keeping only the single closest node found per layer. From there down to layer 0, it does a wider search (candidate list size efConstruction), connects the new point to its M closest neighbors at each layer, and (since every node has a degree cap) prunes whichever existing neighbor now has too many edges back down to its own M closest.
search() does the same greedy single-best-neighbor descent from the top layer down to layer 1, then widens to keep ef candidates at layer 0 and returns the k closest among them. Skipping straight to a good starting point via the upper layers is what gives HNSW roughly logarithmic search time instead of having to crawl the full graph from an arbitrary point.
Interactive demo
API surface
| Operation | Description | Complexity |
|---|---|---|
| new HNSW({ M?, efConstruction?, efSearch?, levelProbability? }) | Empty index. M caps edges/layer, efConstruction/efSearch trade recall for speed. | O(1) |
| insert(id, vector): level, events | Randomly assigns a top layer (coin flip), greedily descends to it, then connects to M nearest neighbors per layer down to 0. | O(log n) expected |
| search(query, k, ef?): results, path | Greedily descends to layer 1, then widens to ef candidates at layer 0 and returns the k closest. | O(log n) expected |
| getSnapshot(): layers[] | Every layer's nodes and deduplicated edges, top layer first, for visualization. | O(n) |
| bruteForceKnn(points, query, k) | Exhaustive exact k-NN, used to measure HNSW's recall against ground truth. | O(n log n) |
References
Original papers
- Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World GraphsYu. A. Malkov, D. A. Yashunin · 2018
Library implementations
Real-world use cases
- pgvector, Qdrant, Weaviate, and Milvus all use HNSW (or a close variant) as their primary vector index for embedding/similarity search
- Elasticsearch dense_vector fields and OpenSearch knn_vector fields both use HNSW (via Lucene or other engines) for approximate k-NN queries
- Retrieval-augmented generation (RAG) pipelines indexing document/chunk embeddings for fast semantic search over millions of vectors