Vorcyc.Quiver
3.2.1
dotnet add package Vorcyc.Quiver --version 3.2.1
NuGet\Install-Package Vorcyc.Quiver -Version 3.2.1
<PackageReference Include="Vorcyc.Quiver" Version="3.2.1" />
<PackageVersion Include="Vorcyc.Quiver" Version="3.2.1" />
<PackageReference Include="Vorcyc.Quiver" />
paket add Vorcyc.Quiver --version 3.2.1
#r "nuget: Vorcyc.Quiver, 3.2.1"
#:package Vorcyc.Quiver@3.2.1
#addin nuget:?package=Vorcyc.Quiver&version=3.2.1
#tool nuget:?package=Vorcyc.Quiver&version=3.2.1
Vorcyc Quiver 3.2.1
A pure .NET embedded vector database โ zero native dependencies, runs in-process, no standalone database server deployment required.
๐ Github Repo (full documention)
Quiver draws on EF Core's DbContext design pattern, allowing developers to define entities and indexing strategies through declarative attributes such as [QuiverKey], [QuiverVector], and [QuiverIndex], with the framework automatically completing model discovery, index construction, and persistence management at runtime.
๐ What's Fixed in 3.2.1
v3.2.1 is fully backward-compatible with all previous data files.
| Fix | Description |
|---|---|
EntityPageCache thread-safety |
Fixed a data race in LazyPaging mode: concurrent reads via Parallel.ForEach (e.g., multiple threads calling Find / Search simultaneously) could corrupt the internal LRU structures (_loadedPages, _lru, _lruNodes). All LRU-mutating paths are now protected by an internal Lock (_pageLock). FullMemory mode is unaffected. |
๐ What's New in 3.2.0
v3.2.0 is fully backward-compatible with all previous data files.
| Feature | Description |
|---|---|
CompactMemory() / CompactMemoryAsync() |
Call on any QuiverSet<T> to flush dirty pages and evict all in-memory pages on demand. |
CompactAllMemoryAsync() |
Call on QuiverDbContext to compact every collection at once. |
Both methods are no-op in FullMemory mode. Vector index structures always remain in memory.
๐ What's New in 3.1.0
v3.1.0 is fully backward-compatible with v1.x, v2.x, and v3.0.0 data files โ no migration needed.
Breaking Changes
| Change | Before (v3.0.0) | After (v3.1.0) |
|---|---|---|
VectorStorageMode removed |
VectorStorage = VectorStorageMode.MemoryMapped โ optional mmap vector arena via MmapVectorStore |
Removed entirely. Vectors are always stored on the GC heap (HeapVectorStore). The LazyPaging entity cache already bounds memory; a separate mmap layer is redundant. |
QuiverSet constructor simplified |
Accepted DistanceMetric defaultMetric parameter |
The defaultMetric parameter is removed. Each vector field declares its metric independently via [QuiverVector(dim, metric)]. |
Migrating from v3.0.0: Remove VectorStorage = VectorStorageMode.MemoryMapped from your QuiverDbOptions if present. No other changes required.
What's New in 3.0.0
v3.0.0 is fully backward-compatible with v1.x and v2.x data files.
| Category | Change |
|---|---|
| Lazy-loading page cache | EntityCache = EntityCacheMode.LazyPaging โ entity objects loaded on demand in fixed-size pages, evicted by LRU when memory ceiling is reached |
| Bounded memory | MaxCachedPages ร PageSize ร entity size caps working-set size regardless of total dataset size |
| Vector indexes stay resident | HNSW / IVF / KDTree topology structures always in memory; search performance is unaffected |
IsLazyLoading property |
QuiverSet<T>.IsLazyLoading reflects the current caching mode |
What's New in 2.0.0
v2.0.0 is fully backward-compatible with v1.x data files โ no migration needed.
| Category | Change |
|---|---|
| Architecture | SimilarityFunc delegate โ ISimilarity<T> static abstract interface (JIT-inlined, zero dispatch overhead) |
| Binary-first storage | Primary storage is always binary. JSON/XML are export/import-only side channels (ExportAsync / ImportAsync) |
| New Metrics | 6 new: Manhattan, Chebyshev, Pearson, Hamming, Jaccard, Canberra โ plus the original 3 (Cosine / Euclidean / DotProduct), totaling 9 built-in |
| Custom Similarity | [QuiverVector(128, CustomSimilarity = typeof(MySim))] โ plug in any ISimilarity<float> struct |
| SIMD | All 9 metrics use Vector<float> / TensorPrimitives SIMD, auto-adapts to SSE4/AVX2/AVX-512 |
- Code-First Declarative Modeling โ Annotate entity classes with attributes; the framework auto-discovers and registers
QuiverSet<T>collections via reflection โ zero configuration. - Multiple ANN Index Algorithms โ Built-in Flat (brute-force), HNSW, IVF, and KDTree indexes, covering small-scale exact search to million-scale approximate search.
- 9 Distance Metrics + Custom Similarity โ Cosine, Euclidean, DotProduct, Manhattan, Chebyshev, Pearson, Hamming, Jaccard, Canberra. Or plug in your own
ISimilarity<float>. - Binary-First Persistence โ Primary storage is always high-performance binary. JSON and XML are available as export/import side channels (
ExportAsync/ImportAsync) for readable backups and interoperability. WAL incremental persistence reduces complexity from O(N) to O(ฮ). - Concurrency Safe โ
QuiverSet<T>usesReaderWriterLockSliminternally; concurrent reads and writes are safe out-of-the-box. - SIMD Accelerated โ All similarity implementations use
TensorPrimitives+Vector<float>SIMD, auto-adapting to SSE4/AVX2/AVX-512. - Lazy-loading Page Cache โ Optional
EntityCache = EntityCacheMode.LazyPagingwith LRU eviction andMaxCachedPages/PageSizecontrols. Entity objects load on demand; vector indexes remain resident for full search performance. - CompactMemory โ
CompactMemory()/CompactMemoryAsync()onQuiverSet<T>andCompactAllMemoryAsync()onQuiverDbContextflush dirty pages and release all cached pages on demand. - Schema Migration โ Property rename and value transform via
ConfigureMigration<T>(). Adding/removing fields requires no configuration.
Typical Use Cases: Semantic search ยท RAG ยท Face recognition ยท Image-to-image search ยท Recommendation systems ยท Multimodal retrieval
๐ Quick Start
1. Define Entity
using Vorcyc.Quiver;
public class Document
{
[QuiverKey]
public string Id { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
[QuiverVector(384, DistanceMetric.Cosine)]
public float[] Embedding { get; set; } = [];
}
2. Define Database Context
public class MyDocumentDb : QuiverDbContext
{
public QuiverSet<Document> Documents { get; set; } = null!;
public MyDocumentDb() : base(new QuiverDbOptions
{
DatabasePath = "documents.vdb",
DefaultMetric = DistanceMetric.Cosine
})
{ }
}
3. Use It
await using var db = new MyDocumentDb();
await db.LoadAsync();
// Add
db.Documents.Add(new Document
{
Id = "doc-001",
Title = "Introduction to Vector Databases",
Category = "Tutorial",
Embedding = new float[384] // embedding vector from your model
});
// Search Top-5
float[] queryVector = new float[384];
var results = db.Documents.Search(e => e.Embedding, queryVector, topK: 5);
foreach (var r in results)
Console.WriteLine($"{r.Entity.Title} โ {r.Similarity:F4}");
// Auto-saved on DisposeAsync
๐ Distance Metrics
| Metric | Range | Use Case |
|---|---|---|
Cosine |
[-1, 1] | Text embeddings, semantic search |
Euclidean |
(0, 1] | Spatial coordinates, physical distances |
DotProduct |
$(-\infty, +\infty)$ | Pre-normalized vectors, MIPS |
Manhattan |
(0, 1] | Sparse features, recommendation systems |
Chebyshev |
(0, 1] | Feature deviation detection, grid distances |
Pearson |
[-1, 1] | Text embeddings (de-biased), TF-IDF, ratings |
Hamming |
[0, 1] | Binary hash codes, LSH, SimHash fingerprints |
Jaccard |
[0, 1] | BoW/TF-IDF sparse features, histograms |
Canberra |
[0, 1] | Sparse data (weight-sensitive), chemical fingerprints |
Or define your own: [QuiverVector(128, CustomSimilarity = typeof(MyMetric))]
๐๏ธ Index Types
| Index | Search Speed | Accuracy | Best For |
|---|---|---|---|
| Flat | O(nรd) | 100% | < 10K entries, exact search |
| HNSW | O(log n) | ~95-99%+ | 10Kโ10M, universal preferred |
| IVF | O(n/kรd) | ~90-99% | 100K+, high throughput |
| KDTree | O(log n) | 100% | < 10K, dimensions < 20 |
// HNSW example
[QuiverVector(768, DistanceMetric.Cosine)]
[QuiverIndex(VectorIndexType.HNSW, M = 32, EfConstruction = 300, EfSearch = 100)]
public float[] Embedding { get; set; } = [];
// IVF example
[QuiverVector(128, DistanceMetric.Cosine)]
[QuiverIndex(VectorIndexType.IVF, NumClusters = 100, NumProbes = 15)]
public float[] Feature { get; set; } = [];
๐ง CRUD Operations
// Add
db.Documents.Add(entity);
db.Documents.AddRange(batch); // Atomic two-phase commit
await db.Documents.AddRangeAsync(batch);
// Upsert (insert or update, single write lock)
db.Documents.Upsert(entity);
// Remove
db.Documents.Remove(entity); // By entity (matched by key)
db.Documents.RemoveByKey("doc-001"); // By key directly
// Find
Document? doc = db.Documents.Find("doc-001"); // O(1)
// Exists
bool exists = db.Documents.Exists("doc-001"); // By key, O(1)
bool hasTutorial = db.Documents.Exists(e => e.Category == "Tutorial"); // By predicate, O(n) short-circuit
// Clear
db.Documents.Clear();
// Count
int count = db.Documents.Count;
๐ Vector Search
// Top-K
var results = db.Documents.Search(e => e.Embedding, queryVector, topK: 10);
// Threshold
var results = db.Documents.SearchByThreshold(e => e.Embedding, queryVector, threshold: 0.85f);
// Filtered
var results = db.Documents.Search(
e => e.Embedding, queryVector, topK: 10,
filter: e => e.Category == "Tutorial",
overFetchMultiplier: 4);
// Top-1
var best = db.Documents.SearchTop1(e => e.Embedding, queryVector);
// Async (all methods have Async variants)
var results = await db.Documents.SearchAsync(e => e.Embedding, queryVector, topK: 10, ct);
// Default field shorthand (single vector field entities)
var results = db.Documents.Search(queryVector, topK: 5);
๐พ Persistence
await db.SaveAsync(); // Full binary snapshot
await db.SaveChangesAsync(); // WAL incremental, O(ฮ)
await db.CompactAsync(); // Full snapshot + clear WAL
await db.LoadAsync(); // Load snapshot + replay WAL
// Export / Import side channels
await db.ExportAsync("backup.json", ExportFormat.Json);
await db.ImportAsync("backup.json", ExportFormat.Json);
| Storage | Role | Description |
|---|---|---|
| Binary | Primary (always) | Smallest size, fastest I/O, zero-copy via MemoryMarshal |
| Json | Export/Import only | Human-readable via ExportAsync / ImportAsync |
| Xml | Export/Import only | Compatible format via ExportAsync / ImportAsync |
WAL Mode
var options = new QuiverDbOptions
{
DatabasePath = "mydata.vdb",
EnableWal = true,
WalCompactionThreshold = 10_000,
WalFlushToDisk = true
};
๐ Schema Migration
When entity structures evolve, Quiver handles differences transparently during LoadAsync.
Automatic (zero config):
- New field added โ gets CLR default value
- Old field removed โ silently skipped
Property Renaming & Value Transform:
public class MyDb : QuiverDbContext
{
public QuiverSet<Document> Documents { get; set; } = null!;
public MyDb() : base(new QuiverDbOptions { DatabasePath = "my.db" })
{
ConfigureMigration<Document>(m => m
.RenameProperty("OldTitle", "Title")
.TransformValue("Score", v => v is int i ? (double)i : v));
}
}
| Scenario | Handling | Config Required |
|---|---|---|
| Add field | Default value | โ None |
| Remove field | Silently skipped | โ None |
| Rename field | RenameProperty() |
โ Yes |
| Change type/format | TransformValue() |
โ Yes |
โ๏ธ Configuration
| Property | Type | Default | Description |
|---|---|---|---|
DatabasePath |
string? |
null |
Storage path; null = in-memory mode |
DefaultMetric |
DistanceMetric |
Cosine |
Default distance metric |
EntityCache |
EntityCacheMode |
FullMemory |
FullMemory (always resident) / LazyPaging (LRU page cache, requires DatabasePath) |
EnableWal |
bool |
false |
Enable WAL incremental persistence |
WalCompactionThreshold |
int |
10,000 |
Auto-compact threshold |
WalFlushToDisk |
bool |
true |
fsync after WAL write |
MaxCachedPages |
int |
16 |
Max pages in memory per QuiverSet |
PageSize |
int |
512 |
Entities per page |
๐ Concurrency
QuiverSet<T> uses ReaderWriterLockSlim:
- Read operations (Search / Find / Exists / Count) โ shared lock, parallel execution โ
- Write operations (Add / Upsert / Remove / Clear) โ exclusive lock ๐
No external locking needed.
โป๏ธ Lifecycle
// Recommended: await using (auto-save on dispose)
await using var db = new MyDocumentDb();
await db.LoadAsync();
// ... use db ...
// Scope ends โ DisposeAsync โ auto-save โ release resources
| Disposal | Auto-Save | Recommended |
|---|---|---|
DisposeAsync() |
โ Yes | โ
Use with await using |
Dispose() |
โ No | Manual control scenarios |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- System.IO.Hashing (>= 10.0.5)
- System.Numerics.Tensors (>= 10.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.