Vector search
This page is part of the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at README.md, and llms.txt lists every page.Approximate nearest-neighbour search with Orleans.Lattice.Vector: sub-linear
query cost, honest per-query reporting, first-class deletes, and accuracy that is
measured rather than asserted.
What this sample shows
- Building an index over a clustered corpus of 10,000 vectors - the shape a real embedding model produces.
- The exhaustive-to-approximate transition. Before
Train()there is no partitioning, so the index answers exactly by scanning everything. It reportsVectorSearchMode.Exhaustiveand says so. AfterTrain()it reportsApproximateand scores only the partitions a query probes. - Measured recall against an exact oracle. The sample computes brute-force top-k over the same corpus and reports recall@10, so you can see the published floor being met rather than take it on trust.
- First-class deletes. A retired vector never reappears, with no tombstone left to compact away.
Running it
dotnet run --project samples/VectorSearch
No silo, no cluster, no storage. The index core is a pure algorithm with no Orleans dependency, which is precisely what makes it testable and benchmarkable in isolation.
What to look for in the output
- The mode changes from
ExhaustivetoApproximateacrossTrain(), and the exhaustive answer is exact. A warming index is not degraded - it is correct and slower. - The probe fraction. The sample prints what proportion of partitions each
query scores. That fraction falls as a corpus grows, which is the property
that breaks the "query cost is proportional to corpus size" law. It is why the
default probe count is derived from
sqrt(partitionCount)and must never be set to a fixed fraction of the partitions. - Recall at or above the published floor of 0.95 for a clustered corpus.
Going further
This sample uses the in-memory core. For a real deployment you also want the
durable layer, DurableVectorIndex, which persists the index on a Lattice tree in
bounded chunks and maintains it incrementally, so a restart reloads the index
rather than rebuilding it - about 23x cheaper at 250,000 vectors, and the gap
widens with the corpus.
See:
- Package overview
- Architecture - the index structure and the durable layout
- Recall and accuracy - the measured target, and distribution drift
- Configuration - every option, and the one setting that can silently undo the whole thing