v1.1.0 · source-available · Elastic License 2.0

The vector database
that thinks in graphs.

SwarnDB is a Rust-native vector database where a vector and a graph node are the same object. Get a fast, accurate vector store by default, and a real typed graph the moment you need it, queried together in one engine.

one identity · vector id = node idSIMD · AVX2 / NEONgRPC + REST · dual APIwritten in Rust
built forKnowledge graphsAgent memoryGraphRAGRecommendationsFraud & threat graphs
Why SwarnDB

One identity. One store.
Vector and graph node, the same object.

A production-grade vector database in Rust, where the graph is a first-class citizen, not a bolt-on. A fast vector store out of the box, and a real typed graph when your problem grows past nearest-neighbor.

02 / 05

A real, typed graph, not inferred from similarity

Directed, typed edges that carry confidence and provenance, which you create explicitly with put_edge and bulk import, or have an LLM extract. This is structure you author, not similarity you hope is structure.

03 / 05

Scope by structure, then rank by meaning

One composable query seeds candidates by similarity, walks typed edges across the graph, and ranks the surviving frontier exactly by meaning. Similarity and structure in a single plan, inside one engine.

04 / 05

Vector-only by default

Every collection is a fast, accurate HNSW store with nothing turned on. Four distance metrics, per-query ef_search, batch search, and correct filter-then-search. The graph is opt-in per collection; if you never want it, you never see it.

05 / 05

Smaller in memory, safe on crash

SQ8 scalar quantization cuts index memory by roughly 75% with exact final ranking and fast restart. Write-ahead logging means a crash never costs committed data, and collections come back queryable in seconds.

The one idea

The vector id is the node id.

In SwarnDB, the id of a vector is the id of its graph node. There is no foreign key, no mirror table, no eventual consistency between two stores. The thing you searched for and the thing you traverse from are literally the same row.

That single decision makes one query possible: scope by structure, then rank by meaning, in one plan.

no foreign keyno mirror tableno sync jobone storage pathone crash-recovery path
# mode="hybrid" turns on the first-class typed graph.
client.collections.create(
    "articles", dimension=384, distance_metric="cosine", mode="hybrid"
)

# The id each insert returns is the node's id. Vector and node are one object.
a = client.vectors.insert("articles", vector=[0.1, 0.2, 0.3, ...], metadata={"topic": "physics"})
b = client.vectors.insert("articles", vector=[0.3, 0.1, 0.4, ...], metadata={"topic": "math"})

# Typed edges carry provenance, not just a pointer.
client.graph.put_edge("articles", source=a, target=b, edge_type="CITES",
                      provenance={"doc_id": "paper-1"})

# One composable hybrid query:
# seed by similarity -> walk the graph -> rank the frontier exactly by meaning.
result = (
    client.graph.query("articles")
    .vector_similar([0.1, 0.2, 0.3, ...], k=20)
    .traverse("CITES", direction="outgoing")
    .vector_rank([0.1, 0.2, 0.3, ...], k=10)
    .return_nodes()
)
for node in result.nodes:
    print(node.id, node.label)
First-class typed graph

Edges you author, not edges you guess.

Directed, typed edges that carry confidence and provenance. You create them explicitly with put_edge or bulk import, or have an LLM extract them, then traverse them in one query. Opt-in per collection via mode="hybrid".

typed · directedsimilarity → traverse → rank
CITES, MENTIONS: real edge types you control. The lit path shows one traverse step.
01
Vector id = node id
The id an insert returns is the node's id. One identity, one store, one crash-recovery path for both.
02
Typed, directed edges with provenance
Every edge carries a type, a direction, a confidence, a manual-vs-extracted flag, and where it came from.
03
You author the structure
Create edges with put_edge, import them in bulk from CSV or JSONL, or have an LLM extract them with your key.
04
Opt-in per collection
The graph turns on with mode="hybrid" at create time. Vector-only collections are completely unchanged.
also availablePrefer not to author edges? An optional auto-similarity mode (mode="auto_similarity") builds similarity edges for you, off by default. The first-class typed graph above is SwarnDB's graph.
Hybrid query builder

Scope by structure, then rank by meaning.

One composable plan seeds candidates by similarity, walks typed edges across the graph, and ranks the surviving frontier exactly by meaning. Similarity and structure in a single plan, inside one engine.

01vector_similar(k)
Seed by similarity
Pull the k nearest candidates from the HNSW index. This is the entry frontier for the walk.
02traverse(edge_type, direction)
Walk typed edges
Expand across directed, typed edges: single-hop, k-hop, or shortest path. Direction is outgoing, incoming, or both.
03vector_rank(k)
Rank the frontier
Rank the surviving nodes exactly by meaning over the candidate set the graph produced. Default mode is vector_rank.
04return_nodes()
Return nodes
Materialize the final, ranked nodes. One plan, one round trip, one copy of the data.
Single-hop and k-hop
Traverse one edge or expand k hops outward. The frontier grows under your control, not by accident.
Shortest path
Find the shortest typed path between two nodes, then rank what it surfaces.
Correct filter-then-search
Fix the qualified set first, then rank exactly within it. You get the true top-k among qualified items.
Edges, provenance & curation

You always know where a fact came from.

Every edge carries its type, confidence, a manual-vs-extracted flag, and a provenance record. Full edge CRUD with verify, reject, and audit history, plus CSV and JSONL bulk import.

edge A → BCITES · 0.94
typeCITES, MENTIONS, FOLLOWS
confidence0.0 to 1.0
sourcemanual or extracted
provenancedoc, chunk, model
statusverified / rejected
historyfull audit trail
Curate by hand
Create, read, update, delete. Verify or reject any edge, with a full audit history behind it.
Import in bulk
Load authored edges from CSV or JSONL, each one keeping its type, confidence, and provenance.
# Author and curate edges with full provenance.
client.graph.put_edge("articles", source=a, target=b, edge_type="CITES",
                      provenance={"doc_id": "paper-1", "chunk": 4})

# Review what an LLM proposed: verify or reject, with an audit trail.
client.graph.verify_edge("articles", edge_id)
client.graph.reject_edge("articles", edge_id)

# Bulk import authored edges from CSV or JSONL.
client.graph.bulk_import_edges("articles", data, format="csv")
Capabilities

What you can build on top.

A fast vector engine, an opt-in typed graph that shares one identity with your vectors, and server-side math, all backed by the same crash-safe store. Index for speed, traverse authored edges for structure, compute for shape.

Vector engine

index
  • HNSW index
    Configurable ef_construction, ef_search, M; per-query ef_search.
  • SQ8 quantization
    Roughly 75% less index memory, exact final ranking, fast restart.
  • Filter-then-search
    Adaptive pre-filter (B-tree, hash, bitmap), then exact top-k within it.
  • Batch search
    Many queries in one round trip with shared overhead.
  • File-based bulk ingest
    bulk_insert_from_path, memory-mapped; working memory bounded by the index.

First-class typed graph

graph
  • Vector id = node id
    One identity, one store, one crash-recovery path.
  • Directed typed edges
    Each edge carries confidence, a manual-vs-extracted flag, and provenance.
  • You author the edges
    Create via put_edge, CSV/JSONL bulk import, or LLM extraction.
  • Opt-in per collection
    Turn it on with mode="hybrid"; vector-only collections are unchanged.
  • Not inferred from similarity
    Only the node identity is shared with the vector, never the edges.

Math & internals

math
  • 15+ math operations
    Ghost vectors, SLERP, k-means, PCA, MMR, drift, and more, server-side.
  • jemalloc memory release
    Freed memory is returned to the OS, not held by the process.
  • WAL crash recovery
    A crash never costs committed data; collections come back queryable in seconds.
  • SIMD distance kernels
    AVX2, SSE4.1, NEON, scalar fallback, chosen at runtime.
  • Dual API
    Same engine over gRPC (50051) and REST (8080).
Math engine

15+ vector operations, first-class.

Server-side, no round-trip. Every operation runs inside the engine and is exposed through gRPC MathService and REST POST /api/v1/collections/{id}/math/*. On a hybrid collection, several of these (analogy, diversity via MMR, cone, isolation, centroid, and interpolation) run exactly over the candidate set a graph query produced.

ghost
Ghost vectors
Synthetic vectors representing absent concepts in a space.
∠ cone
Cone search
Angular proximity search within a cone aperture.
slerp
SLERP interpolation
Spherical linear interpolation between vectors.
Σ/n
Centroid computation
Weighted and unweighted centroids of vector sets.
Δt
Vector drift detection
Track how vector representations change over time.
k-means
K-means clustering
Partition vectors into k clusters.
PCA
PCA
Dimensionality reduction via principal component analysis.
A:B::C:?
Analogy completion
Vector arithmetic for analogy tasks.
MMR
Max marginal relevance
Diversity-aware result re-ranking.
‖v‖
Vector normalization
L2 normalization for angular similarity.
showing 10 named operations · 15+ total including SIMD distance kernels, batch ops, and vector arithmetic
on hybrid collections: run over a graph-built frontier, not the whole collection
Optional LLM extraction (BYOK)

Turn text into typed edges, on your terms.

Point a hybrid collection at any OpenAI-compatible model with your own key. It proposes typed entities and edges with full provenance. Preview the cost before you spend, then verify, reject, or re-extract.

off by defaultyou supply and own the keyhybrid collections only
01
Configure
Set the base URL, your key, the model, and an ontology. Off until you do this; you supply and own the key.
02
Preview cost
cost_preview returns estimated_cost_usd for the chunks. See the spend before any token is bought.
03
Extract
start_extraction proposes typed entities and edges, each with full provenance back to its source chunk.
04
Verify, reject, re-extract
Curate proposals: keep the good ones, drop the rest, re-run where you need to. Nothing lands unreviewed.
# Point a hybrid collection at any OpenAI-compatible model. You own the key.
client.extraction.set_llm_config(
    "articles",
    base_url="https://openrouter.ai/api/v1",
    api_key="sk-or-...",
    model_name="openai/gpt-4o-mini",
    temperature=0.0,
    max_tokens=2048,
)
client.extraction.set_ontology("articles", base_template="research-papers", replace=False)

# Preview the cost before you spend a single token.
estimate = client.extraction.cost_preview("articles", chunks)
print(f"Estimated cost: ${estimate.estimated_cost_usd}")

# Then run it. Proposed entities and edges come back for verify / reject / re-extract.
result = client.extraction.start_extraction("articles", chunks)
Getting started

Vector-only by default, graph when you need it.

# pip install swarndb
from swarndb import SwarnDBClient

with SwarnDBClient(host="localhost", port=50051) as client:
    # Vector-only by default.
    client.collections.create("articles", dimension=384,
                               distance_metric="cosine")

    client.vectors.insert("articles", vector=[0.1, 0.2, 0.3, ...],
                           metadata={"topic": "physics"})
    client.vectors.insert("articles", vector=[0.3, 0.1, 0.4, ...],
                           metadata={"topic": "math"})

    results = client.search.query("articles", vector=[0.1, 0.2, 0.3, ...], k=10)
    for r in results.results:
        print(r.id, r.score)  # distance score, lower is more similar
Benchmarks · vector-only

Sub-7ms p95 at 99% recall.

Vector-only search on DBpedia 1M (1536-dim float32, cosine) on a 32-core, 64 GB host with 8 concurrent searcher threads, 1,000 queries per setting averaged across 3 iterations. Default HNSW (M=16, ef_construction=200). Measured 2026-05-20; not re-run for v1.1.0.

vector-only, single host
Peak QPS
0
Recall @ ef=800
0.0%
p50 latency
0.00ms
p95 latency
0.00ms
ef_searchQPSRecall@10p50 (ms)p95 (ms)p99 (ms)
252,39898.16%3.164.916.06
502,21498.94%3.335.266.77
1001,80199.21%4.166.858.02
2001,23399.35%6.1810.1912.26
40076099.60%10.0016.8320.48
80043799.74%17.4230.4335.90
dataset: dbpedia-1m-1536 · vector-only · index: HNSW (M=16, ef_c=200) · cosine · 8 searcher threads · 32c / 64 GB · measured 2026-05-20
These numbers are vector-only. The graph layer is the right tool where structure matters (knowledge graphs, agent memory, recommendations, fraud and threat graphs); on broad question-answering, the graph does not beat plain vector search. We position the graph for structure, not as a universal speed or recall lift.
FAQ

Questions, answered honestly.

SwarnDB is one Rust engine that is a fast vector store by default and a real typed graph when structure matters, so here is a straight account of what it does well and where it does not.

SwarnDB is a production-grade vector database written in Rust where a vector and a graph node are the same object. The id of a vector is the id of its graph node, so there is only one thing to store and reason about. By default it is a fast, accurate vector store, and when you need it, it becomes a real typed graph. It is one engine, not two systems bolted together.

Performance

SIMD, end to end.

Runtime dispatch picks the widest instruction set your CPU supports. Cosine distance is fused: dot product and norms in a single pass. PQ distance tables use SIMD gather.

Instruction setRelative throughputWidthPlatform
AVX2256-bitx86_64
SSE4.1128-bitx86_64
NEON128-bitARM / Apple Si
Scalar64-bitAll platforms
Zero-copy mmap
Memory-mapped segment files. The kernel pages in what you need.
Lock-free concurrency
DashMap + fine-grained HNSW locking. Reads never block reads.
Arena allocators
Bump allocation for per-query scratch. No malloc in the hot path.
Quickstart

Get running in one command.

Docker$ docker run -d -p 8080:8080 -p 50051:50051 sarthiai/swarndb
Python SDK$ pip install swarndb