Install
| Product | Versions |
|---|---|
| .NET |
README
HnswLite
A pure C# implementation of Hierarchical Navigable Small World (HNSW) graphs for approximate nearest neighbor search. HnswLite ships as an embeddable library, a REST server, a React dashboard, and SDKs in three languages.
Note: This library is in its early stages of development. We welcome your patience, constructive feedback, and contributions! Please be kind and considerate when reporting issues or suggesting improvements. I am not an expert on this topic and relied heavily on available AI tools to build this library. Pull requests are greatly appreciated!
Overview
HnswLite implements the Hierarchical Navigable Small World algorithm, which provides fast approximate nearest-neighbor search with excellent recall rates. The library is designed to be embeddable, extensible, and easy to use from any .NET application β or from Python / JavaScript / any HTTP client via the REST server.
Repository layout
| Path | Purpose |
|---|---|
src/HnswIndex/ |
Core library (HnswLite on NuGet) |
src/HnswIndex.RamStorage/ |
In-memory storage provider |
src/HnswIndex.SqliteStorage/ |
SQLite storage provider |
src/HnswIndex.Server/ |
Standalone REST server (Watson 7) |
src/Test.Shared/ + src/Test.{Automated,XUnit,NUnit,MSTest}/ |
Touchstone-driven test suites |
dashboard/ |
React 19 + Vite dashboard |
sdk/csharp/, sdk/python/, sdk/js/ |
Client SDKs with 100% endpoint coverage |
docker/ |
compose.yaml for server + dashboard, plus factory-reset scripts |
Key features
- Pure C# implementation β no native dependencies.
- Thread-safe, async/await with cancellation tokens throughout.
- Unified
IStorageProviderinterface β build-your-own backend by implementing one interface. - Multiple distance metrics β Euclidean, Cosine, Dot Product, with SIMD acceleration via
System.Numerics.Vector<float>. - Batch operations β efficient bulk insert and remove.
- Persistence by default β the SQLite storage provider writes a self-describing
.dbfile; the REST server reloads every index on startup. - Paginated enumeration contract across every GET collection endpoint (
EnumerationQuery/EnumerationResult<T>). - OPTIONS preflight + CORS out of the box in the REST server.
New in v1.1.0
See CHANGELOG.md for the full list. Highlights:
- Multi-target
net8.0+net10.0. - Unified
IStorageProviderwithRamStorageProviderandSqliteStorageProvider. - Watson web server upgraded to
7.0.11with CORS + OPTIONS preflight. - Every GET-collection endpoint now paginated via
EnumerationQuery/EnumerationResult<T>. - Default server
StorageTypeis SQLite β indexes persist across restarts, metadata lives inside the.dbfile (no separate manifest). - New
GET /v1.0/indexes/{name}/vectors(paginated) andGET /v1.0/indexes/{name}/vectors/{guid}endpoints. - SIMD distance functions + a dozen internal performance fixes β see PERFORMANCE_IMPROVEMENTS.md.
- New dashboard, new SDKs (C#, Python, JS), new Touchstone test suite (53 cases Γ 4 runners).
Use cases
- Semantic search β find similar documents / sentences from embeddings.
- Recommendation systems β discover similar items / users / content.
- Image similarity β search on feature vectors.
- Anomaly detection β identify outliers by neighbour distance.
- Clustering β group similar items.
- RAG β retrieval-augmented generation for LLM applications.
- Duplicate detection β find near-duplicate content at scale.
Performance & scalability
Recommended limits
- Vector dimensions: 50β1000 (optimal: 128β768).
- Dataset size: up to 1β10M vectors depending on dimension and RAM.
- Memory usage: approximately
(vector_count Γ dimension Γ 4 bytes) + (vector_count Γ M Γ 32 bytes).
These are estimates. The library has not been exhaustively load-tested.
Parameters
Mβ connections per vector (default 16). More connections β better recall, more memory. 16β32 works well for most cases.EfConstructionβ construction search depth (default 200). Higher β better graph quality, slower builds. Drop to 50β100 for fast batch insertion.Efβ search depth (default 50β200). Higher β better recall, slower search.Seedβ fix for reproducible builds.
Tips
- Use
AddNodesAsync(...)/RemoveNodesAsync(...)for batches β they acquire the write lock once. - Prefer
SqliteStorageProviderfor persistence;RamStorageProviderfor ephemeral in-memory indexes. - For high-dimensional embeddings use
CosineDistance.
Simple example (embedded)
using Hnsw;
using Hnsw.RamStorage;
using HnswIndex.SqliteStorage;
// RAM
using RamStorageProvider ram = new RamStorageProvider();
HnswIndex index = new HnswIndex(128, ram);
// Or SQLite (persistent)
using SqliteStorageProvider sqlite = new SqliteStorageProvider("my-index.db");
HnswIndex persistentIndex = new HnswIndex(128, sqlite);
// Configure
index.M = 16;
index.EfConstruction = 200;
index.DistanceFunction = new CosineDistance();
// Add a single vector
Guid id = Guid.NewGuid();
List<float> vector = new List<float>(128); // your 128-d embedding
await index.AddAsync(id, vector);
// Add a batch
Dictionary<Guid, List<float>> batch = new Dictionary<Guid, List<float>>();
for (int i = 0; i < 1000; i++) batch[Guid.NewGuid()] = GenerateRandomVector(128);
await index.AddNodesAsync(batch);
// Search
List<float> query = new List<float>(128);
IEnumerable<VectorResult> neighbors = await index.GetTopKAsync(query, count: 10);
foreach (VectorResult r in neighbors)
Console.WriteLine($"id={r.GUID} distance={r.Distance:F4}");
// Export / import state
HnswState state = await index.ExportStateAsync();
HnswIndex restored = new HnswIndex(128, new RamStorageProvider());
await restored.ImportStateAsync(state);
Best practices
- Resource management.
IStorageProviderisIDisposableβ useusingto guarantee flush on scope exit (important for SQLite). - Prefer batches. Calling
AddNodesAsyncis substantially faster than a loop ofAddAsyncbecause it acquires the write lock once. - Tune
Efat search time.IEnumerable<VectorResult> quick = await index.GetTopKAsync(query, 10, ef: 50); // fast, lower recall IEnumerable<VectorResult> better = await index.GetTopKAsync(query, 10, ef: 400); // slower, higher recall
Custom storage backend
Implement IStorageProvider (which aggregates IHnswStorage, IHnswLayerStorage, and IDisposable). See RamStorageProvider and SqliteStorageProvider as reference implementations. The server and dashboard are completely provider-agnostic.
REST server
cd src/HnswIndex.Server
dotnet run -- --setup # writes hnswindex.json with a generated admin API key
dotnet run
The server listens on http://localhost:8080 by default. Authentication uses the x-api-key header (configurable via Server.AdminApiKeyHeader). OPTIONS pre-flight is unauthenticated and served by Watson's preflight hook; CORS headers are emitted on every response and configured under the Cors block in hnswindex.json.
Full endpoint reference: REST_API.md. Interactive reference: HNSW Index.postman_collection.json.
Dashboard
React 19 + Vite 6 + TypeScript dashboard at dashboard/. Pages include Indices, Vectors (browse / edit / add / delete), Search, Request History with an activity chart, API Explorer, Server Info, Settings, plus a login flow.
# Local development
cd dashboard
npm install
HNSWLITE_SERVER_URL=http://localhost:8080 npm run dev
# Production build (static assets in dashboard/dist)
npm run build
SDKs
| Language | Directory | Package | Runtime |
|---|---|---|---|
| C# | sdk/csharp/ |
HnswLite.Sdk |
.NET 8 or .NET 10 |
| Python | sdk/python/ |
hnswlite-sdk |
Python 3.9+ |
| JavaScript / TypeScript | sdk/js/ |
hnswlite-sdk |
Node 18+ (native fetch) |
Each SDK has 100% endpoint coverage and a test harness. See sdk/README.md for the method matrix and per-language READMEs.
Docker
cd docker
docker compose up -d
- Server:
http://localhost:8080/ - Dashboard:
http://localhost:8081/dashboard/
Factory reset (with RESET confirmation):
cd docker/factory
./reset.sh # or reset.bat on Windows
See docker/README.md for image tags and environment overrides.
Bugs, feedback, or enhancement requests
- Bug reports: please file an issue with reproduction steps.
- Feature requests: open a discussion or create an issue.
- Questions: use the discussions forum.
- Contributions: pull requests welcome.
License
MIT. See LICENSE.md.
Acknowledgments
Based on Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs by Yu. A. Malkov and D. A. Yashunin.
Changes from Previous Version
Adoption Assessment
Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.