Collections & indexes¶
A collection is created with vector.NewCollection(name, cfg) (library) or
POST /v1/collections (server). The Config struct controls everything: index
type, graph parameters, quantization, quotas, durability, and filtering
behavior.
Core parameters¶
| Field | Default | Meaning |
|---|---|---|
Dim |
— (required) | vector dimensionality |
Metric |
— | Cosine, L2, or DotProduct |
M |
16 | HNSW graph degree |
EfConstruction |
200 | build-time beam width |
EfSearch |
64 | query-time beam width (recall/latency dial) |
MaxEfSearch |
1024 | cap on the automatic ef widening filtered search uses; unfiltered search ignores it |
Seed |
0 | RNG seed for deterministic builds |
Higher M/EfConstruction buys recall at build cost; EfSearch is the runtime
knob you tune first.
EfSearch below k has no effect
The effective beam width is max(EfSearch, k) — search silently raises ef
to k. So ef_search=10 with k=100 queries runs at ef=100, and a
benchmark that sweeps ef downward while holding k fixed will measure the
same number at every ef ≤ k and conclude the knob does nothing. To explore
low-ef behaviour, lower k as well. Filtered search additionally floors ef
at 2·k and keeps doubling it (up to MaxEfSearch) until it collects k
matches; quantized indexes floor it at RescoreFactor·k to feed the
rescore stage.
Index types¶
Set Config.IndexType; the search API is the same for all of them.
HNSW (default)¶
The default graph index: allocation-free search path, AVX2 distance kernels
(scalar fallback), incremental inserts, tombstoned deletes. Tuning extras:
ExtendCandidates / ExtendCandidatesMax (wider neighbor selection),
Level0FullDegree (denser base layer), QuantizedBuild (navigate on quantized
codes during bulk build to cut memory bandwidth; the exact rescore stage
recovers ranking).
The two build-time graph-quality knobs have been measured — QPS at matched
recall against the baseline curve, on SIFT (200k × 128d, L2, k=10, M 16,
EfConstruction 200), across two independent runs
(vector/recall_levers_test.go):
Level0FullDegree: +8.5% and +12.2%, every measured point positive in both runs, for ~1.6× build time. Level 0 already reserves2·Medge slots per node, so it costs no extra memory. This is the lever that pays.ExtendCandidates: +0.6% then −4.2% — the sign flips between runs, i.e. no measurable effect — for ~2.5× build time. Not worth its build cost on this data.- Both together measured worse than
Level0FullDegreealone.
So when tuning for recall, reach for Level0FullDegree first.
These are SIFT numbers
128-dimensional L2 at 200k points; graph-quality effects are
geometry-dependent, so don't change a production default without checking
your own corpus. The harness is committed — rerun it against your data
layout with ROSTAM_RECALL_LEVERS=1 go test ./vector -run TestRecallLevers
-v -timeout 30m (needs the SIFT corpus on disk; the test header explains
setup and how to read the output).
IVF¶
Inverted-file index: k-means coarse quantizer + inverted lists, trained when the
collection reaches IVFTrainThreshold (or at bulk build).
| Field | Default | Meaning |
|---|---|---|
IVFNlist |
≈ 4·√N | number of cells |
IVFNprobe |
8 | cells probed per query (recall/latency dial) |
IVFPQ, IVFPQM |
off | product-quantize residuals inside cells |
IVFRerank |
off | keep full vectors for an exact rescore pass |
IVFDriftFactor, IVFDriftGrowthFactor, IVFDriftRetrain |
— | online re-training as the data distribution drifts |
Vamana (DiskANN-style)¶
Single-layer graph with RobustPrune construction:
| Field | Default |
|---|---|
VamanaR (out-degree) |
64 |
VamanaL (build/search beam) |
100 |
VamanaAlpha (prune α) |
1.2 |
GPU (CUDA exact KNN)¶
IndexType: IndexGPU with a -tags cuda build gives exact brute-force KNN on
the GPU for Search/SearchFiltered (k ≤ 256; larger k or heavy tombstone
counts fall back to CPU brute force). Advanced queries (MMR, groups, hybrid,
recommend, discover) fall back to HNSW. Without the CUDA build the config fails
with ErrGPUNotCompiled.
Bulk loading¶
For large initial loads, stage vectors and build the graph on all cores instead of inserting one by one:
col.StageBulk(ids, vecs) // stage raw data
err := col.BuildStaged(runtime.NumCPU()) // multi-core graph construction
Use StageBulkPayloads(ids, vecs, metas) instead when the points carry metadata,
so a filtered workload gets the same multi-core build rather than falling back to
one indexed insert per point. Content and sparse vectors have no bulk form.
Over HTTP: POST .../points/bulk to stage, then POST .../points/bulk/build
with {"workers": 0} (0 = all cores).
Writes, reads, and versions¶
// Create-only — ErrDuplicateID if the id is live:
err := col.Insert(id, vec, ttl, meta, sparse)
// Insert-or-replace, with stored content for RAG:
err = col.Upsert(id, vec, "document text", ttl, meta, sparse)
// Atomic create-if-missing:
inserted, err := col.InsertIfAbsent(id, vec, ttl, meta, sparse)
// Optimistic concurrency — fails with ErrVersionConflict on version mismatch:
version, err := col.InsertCAS(id, vec, ttl, meta, sparse, vector.CASCond{Expected: v, Has: true})
// Reads (zero-copy; GetInto reuses a caller buffer):
vec, meta, ttl, sparse, version, ok := col.Get(id)
deleted := col.Delete(id)
count, err := col.DeleteByFilter(filter)
Per-point TTL is set at write time; per-key payload TTLs go through
InsertKeyTTL/UpsertKeyTTL (key_ttl_ms over HTTP).
Quotas & rate limits¶
MaxVectors, MaxBytes (→ ErrCollectionFull) and MaxInsertsPerSecond
(token bucket → ErrCollectionRateLimited) are per-collection guards. See
Collections, tenants & aliases.
Stats¶
col.Stats() returns live counters: Size, Tombstoned, SearchOps,
InsertOps, AvgSearchDepth, Expired, QuotaRejects, FilterRejects,
SparseVectors, and search/insert latency histograms. The server exports these
per collection at /metrics (Monitoring).
Errors¶
Config validation returns typed errors (ErrInvalidDim, ErrInvalidMetric,
ErrInvalidQuant, …). The runtime errors you should handle:
| Error | When |
|---|---|
ErrDuplicateID |
Insert on a live id |
ErrDimMismatch |
query/vector length ≠ Dim |
ErrCollectionFull |
MaxVectors/MaxBytes quota hit |
ErrCollectionRateLimited |
insert token bucket empty |
ErrVersionConflict |
CAS expected_version mismatch |
ErrFullTextDisabled |
SearchText without FullText enabled |