Choosing an index mode
Every mode ranks by cosine similarity and reports true cosine scores in [-1, 1]. They differ in how they search and how much they store.
| Mode | Search | Stored per vector | Training |
|---|---|---|---|
flat (default) |
Exact scan | 4 bytes × dimension | None |
hnsw |
Approximate graph | Full vectors + graph | None |
ivf |
Partitions, then graph | Full vectors + index | train_ivf() |
sq8 |
Scan of 8-bit codes | 1 byte × dimension | First batch |
pq |
Scan of PQ codes | dimension ÷ 8 bytes (÷ 4 if not divisible by 8) | First batch, ≥ 256 vectors |
auto |
Flat, then HNSW | Full vectors | None |
How to choose
Section titled “How to choose”- Start with
flat. Results are exact. It is fast enough for many local collections. - Search too slow? Try
hnsw, then raiseset_ef_search()until recall is good enough. - Storage too large? Try
sq8(4× smaller), thenpq(up to 32×). - Always measure recall against
flaton a sample of your own data. Approximate modes vary with the data; PQ in particular can lose accuracy on tightly clustered embeddings.
def recall_at_k(exact_db, approx_db, queries, k=10): hits = 0 for q in queries: truth = {i for i, _ in exact_db.search(q, k)} hits += len(truth & {i for i, _ in approx_db.search(q, k)}) return hits / (k * len(queries))db = srvdb.Database("./db", dimension=768, mode="flat")A layered graph searched by walking from neighbour to neighbour. The graph is
saved by persist() and rebuilt from the vectors if missing or damaged.
db = srvdb.Database.new_hnsw("./db", dimension=768, m=16, ef_construction=200, ef_search=50)db.set_ef_search(120) # higher: better recall, slower searchGroups vectors into nlist partitions (spherical k-means) and searches only
the nprobe closest ones, each with its own HNSW graph.
db = srvdb.Database("./db", dimension=768, mode="ivf")db.add(ids, embeddings, metadatas)db.configure_ivf(nlist=100, nprobe=10)db.train_ivf()db.persist() # the trained index is saved and reloaded on openEach value stored as one byte, scaled per dimension from the first batch.
db = srvdb.Database("./db", dimension=768, mode="sq8")Each group of 8 (or 4) dimensions stored as one byte: the index of its nearest learned centroid. Trained on the first batch, which needs at least 256 vectors.
db = srvdb.Database("./db", dimension=768, mode="pq")From Rust, SrvDB::new_with_hnsw_quantized builds an HNSW graph over PQ
codes.
Starts flat. At each persist(), once the database holds at least 50,000
vectors and at least 2 GB of RAM is available, it builds an HNSW graph.
It never compresses: compression is always your choice.
db = srvdb.Database("./db", dimension=768, mode="auto")