ruvector/crates/ruvector-graph
rUv 1e1740a876
docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities (#570)
* docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities

Compares HelixDB (LMDB/heed, compiled type-safe HelixQL, graph-vector
thesis, graph-vector-bench) against RuVector's redb/Cypher/hybrid stack
and proposes 7 prioritized, opt-in improvements: optional schema layer
with load-time validation, first-class typed graph-vector binding and a
unified search-then-traverse operator, in-query embed(), unified
ANN+BM25+graph RRF hybrid, a reproducible benchmark harness, schema-driven
typed SDK codegen, and an object-storage tier research spike.

https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF

* feat(ruvector-graph): native schema layer + typed search-then-traverse (ADR-252 P1/P2/P4)

Implements the HelixDB-inspired improvements natively in ruvector-graph:

- schema.rs: opt-in GraphSchema (N::/E::/V:: equivalents) with load-time
  validation (self-consistency, node required/typed props + strict mode,
  edge from/to label constraints, vector dimension checks), higher-is-better
  distance metrics (cosine/dot/euclidean), and reciprocal_rank_fusion (P4).
- typed_graph.rs: TypedGraph wrapper validating mutations pre-storage, plus a
  fused typed search_then_traverse operator (HelixQL SearchV<T>(q,k)::In/Out<E>)
  with optimized bounded-heap top-k selection (O(n log k)).

Pure-Rust, no new deps, WASM-safe. 13 new tests, 148/148 lib tests green,
clippy clean. Schemaless mode remains the default (opt-in coexistence).

https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF

* perf(ruvector-graph): optimize search_then_traverse + add criterion bench (ADR-252)

Hot-path optimizations for the typed search-then-traverse operator:
- GraphDB::with_node / node_ids_by_label: zero-copy borrow scoring, eliminating
  per-candidate Node + embedding clones (get_nodes_by_label cloned everything).
- Fused single-pass cosine (q.c and c.c in one read of the candidate) + hoisted
  query norm out of the per-candidate loop.
- Bounded top-k min-heap (O(n log k)); clone id only for heap winners.
- Rayon parallel scan over DashMap for >=4096 candidates (per-thread heaps,
  bounded merge); serial path below threshold.

Adds benches/typed_graph_bench.rs (criterion). Measured vs first cut (128-dim,
k=10): 10k 7.2ms->3.08ms (2.34x), 50k 74.3ms->28.5ms (2.61x), 1k 539us->432us.
New parallel-vs-reference correctness test. 149/149 lib tests green, clippy clean.

https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF

* feat(ruvector-graph): HNSW push-down for search_then_traverse (ADR-252 P2)

Adds an opt-in ANN path to the typed search-then-traverse operator, removing
the O(n) full-label scan for indexed vector types:

- TypedGraph::build_vector_index(vector_type) builds a per-vector-type
  HybridIndex (HNSW under hnsw_rs, exact FlatIndex otherwise), holding only the
  bound label's nodes so searches stay label-scoped. Kept current incrementally
  via create_node -> index_node.
- search_then_traverse routes through the index when present: ~O(log n)
  approximate search, over-fetch (max(4k, k+32)), then exact rescore with the
  schema metric so ANN results carry identical higher-is-better score semantics
  to the brute-force path. Brute force remains the default.
- Parallel brute-force path refactored to capture &GraphDB (not &self) so it
  stays Send+Sync independent of the index's thread-safety bounds.

Bench (50k nodes, 128-dim, k=10): brute-force parallel scan 27.6ms -> HNSW
push-down 1.05ms (~26x; ~70x vs first cut). 151/151 lib tests green (3 new
HNSW tests), clippy clean.

https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF

* feat(ruvector-graph): inline embed() + tri-modal BM25/ANN/graph hybrid (ADR-252 P3/P4)

P3 - inline embedding (HelixQL Embed()):
- embed.rs: Embedder trait + dependency-free deterministic HashEmbedder
  (feature-hashing, explicit opt-in, never a silent fallback per ADR-194).
- TypedGraph::with_embedder / embed / create_node_from_text (embed-at-insert,
  dimension-validated) / search_text (embed-at-query).

P4 - tri-modal hybrid query:
- bm25.rs: self-contained Okapi-BM25 inverted index.
- TypedGraph::build_text_index + hybrid_search_text fusing ANN vector + BM25
  keyword + graph traversal via reciprocal rank fusion in one typed call.
- Refactored search_then_traverse into shared rank_seeds/expand helpers.

Bench: hash_embed_256 717ns; tri_modal_hybrid over 10k docs (embed+HNSW+BM25+
RRF+traverse) 1.63ms end-to-end. 164/164 lib tests green (+13), clippy clean.

https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF

* feat(ruvector-graph): schema-driven typed SDK codegen (ADR-252 P6)

codegen.rs generates typed client stubs from a GraphSchema:
- generate_typescript: interfaces with typed/optional properties (@indexed
  hints), edge from->to constraints, and a VectorTypes manifest + VectorTypeName.
- generate_python: TypedDict classes + VECTOR_TYPES manifest.
- generate_rust: serde-ready structs.
Deterministic (schema elements sorted) for check-in/diff. Adds *_schemas_sorted
accessors to GraphSchema. Closes HelixDB's schema->typed-SDK DX advantage.

168/168 lib tests green (+4), clippy clean.

https://claude.ai/code/session_01BrEtcS3KZykinsv9RoBGrF

* docs(adr): renumber ADR-252 -> ADR-253 (252 taken by FastGRNN training pipeline)

ADR-252 was already merged to main as the tiny-dancer FastGRNN training
pipeline. Renumber this HelixDB comparison to ADR-253 to resolve the collision.

Co-Authored-By: claude-flow <ruv@ruv.net>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: ruv <ruvnet@users.noreply.github.com>
2026-06-15 12:28:46 -04:00
..
benches docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities (#570) 2026-06-15 12:28:46 -04:00
examples fix: Resolve CI build failures 2025-11-26 15:25:47 +00:00
fuzz feat(quality): ADR-144 monorepo quality analysis — Phase 1 critical fixes (#336) 2026-04-06 21:19:13 -04:00
src docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities (#570) 2026-06-15 12:28:46 -04:00
tests chore(workspace): cargo fmt — mechanical whitespace fix across 427 files 2026-04-24 10:44:02 -04:00
ARCHITECTURE.md feat: Add Neo4j-compatible hypergraph database package (ruvector-graph) 2025-11-25 23:11:54 +00:00
Cargo.toml docs(adr): ADR-252 HelixDB vs RuVector comparison and improvement opportunities (#570) 2026-06-15 12:28:46 -04:00
README.md docs: optimize 12 crate READMEs and add SONA learning loop diagram 2026-02-27 03:38:42 +00:00

Ruvector Graph

Crates.io Documentation License: MIT Rust

A graph database with Cypher queries, hyperedges, and vector search -- all in one crate.

[dependencies]
ruvector-graph = "0.1.1"

Most graph databases make you choose: you can have relationships or vector search, a query language or raw traversals, pairwise edges or nothing. ruvector-graph gives you all of them together. Write familiar Cypher queries like Neo4j, attach vector embeddings to any node for semantic search, and model complex group relationships with hyperedges that connect three or more nodes at once. It runs on servers, in browsers via WASM, and across clusters with built-in RAFT consensus. Part of the RuVector ecosystem.

ruvector-graph Neo4j / Typical Graph DB Vector DB + Custom Glue
Query language Full Cypher parser built-in Cypher (Neo4j) or proprietary No graph queries
Hyperedges Native -- one edge connects N nodes Pairwise only -- workarounds needed Not applicable
Vector search HNSW on every node, semantic similarity Separate plugin or not available Vectors only, no graph structure
SIMD acceleration SimSIMD hardware-optimized ops JVM-based Varies
Browser / WASM default-features = false, features = ["wasm"] Server only Server only
Distributed Built-in RAFT consensus + federation Enterprise tier (paid) Varies
Cost Free, open source (MIT) Community or paid license Varies

Key Features

Feature What It Does Why It Matters
Cypher Engine Parse and execute Cypher queries -- MATCH (a)-[:KNOWS]->(b) Use a query language you already know instead of raw traversal code
Hypergraph Model Edges connect any number of nodes, not just pairs Model meetings, co-authorships, reactions -- any group relationship -- natively
Vector Embeddings Attach embeddings to nodes, run HNSW similarity search Combine "who is connected to whom" with "what is semantically similar"
Property Graph Rich JSON properties on every node and edge Store real data on your graph elements, not just IDs
Label Indexes Roaring bitmap indexes for fast label lookups Filter millions of nodes by label in microseconds
SIMD Optimized Hardware-accelerated distance calculations via SimSIMD Faster vector operations without changing your code
Distributed Mode RAFT consensus for multi-node deployments Scale out without bolting on a separate coordination layer
Federation Cross-cluster graph queries Query across data centers as if they were one graph
Compression ZSTD and LZ4 for storage Smaller on disk without sacrificing read speed
WASM Compatible Run in browsers with WebAssembly Same graph engine on server and client

Installation

[dependencies]
ruvector-graph = "0.1.1"

Feature Flags

[dependencies]
# Full feature set
ruvector-graph = { version = "0.1.1", features = ["full"] }

# Minimal WASM-compatible build
ruvector-graph = { version = "0.1.1", default-features = false, features = ["wasm"] }

# Distributed deployment
ruvector-graph = { version = "0.1.1", features = ["distributed"] }

Available features:

  • full (default): Complete feature set with all optimizations
  • simd: SIMD-optimized operations
  • storage: Persistent storage with redb
  • async-runtime: Tokio async support
  • compression: ZSTD/LZ4 compression
  • distributed: RAFT consensus support
  • federation: Cross-cluster federation
  • wasm: WebAssembly-compatible minimal build
  • metrics: Prometheus monitoring

Quick Start

Create a Graph

use ruvector_graph::{Graph, Node, Edge, GraphConfig};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a new graph
    let config = GraphConfig::default();
    let graph = Graph::new(config)?;

    // Create nodes
    let alice = graph.create_node(Node {
        labels: vec!["Person".to_string()],
        properties: serde_json::json!({
            "name": "Alice",
            "age": 30
        }),
        ..Default::default()
    })?;

    let bob = graph.create_node(Node {
        labels: vec!["Person".to_string()],
        properties: serde_json::json!({
            "name": "Bob",
            "age": 25
        }),
        ..Default::default()
    })?;

    // Create relationship
    graph.create_edge(Edge {
        label: "KNOWS".to_string(),
        source: alice.id,
        target: bob.id,
        properties: serde_json::json!({
            "since": 2020
        }),
        ..Default::default()
    })?;

    Ok(())
}

Cypher Queries

use ruvector_graph::{Graph, CypherExecutor};

// Execute Cypher query
let executor = CypherExecutor::new(&graph);
let results = executor.execute("
    MATCH (p:Person)-[:KNOWS]->(friend:Person)
    WHERE p.name = 'Alice'
    RETURN friend.name AS name, friend.age AS age
")?;

for row in results {
    println!("Friend: {} (age {})", row["name"], row["age"]);
}

Vector-Enhanced Graph

use ruvector_graph::{Graph, VectorConfig};

// Enable vector embeddings on nodes
let config = GraphConfig {
    vector_config: Some(VectorConfig {
        dimensions: 384,
        distance_metric: DistanceMetric::Cosine,
        ..Default::default()
    }),
    ..Default::default()
};

let graph = Graph::new(config)?;

// Create node with embedding
let node = graph.create_node(Node {
    labels: vec!["Document".to_string()],
    properties: serde_json::json!({"title": "Introduction to Graphs"}),
    embedding: Some(vec![0.1, 0.2, 0.3, /* ... 384 dims */]),
    ..Default::default()
})?;

// Semantic similarity search
let similar = graph.search_similar_nodes(
    vec![0.1, 0.2, 0.3, /* query vector */],
    10,  // top-k
    Some(vec!["Document".to_string()]),  // filter by labels
)?;

Hyperedges

use ruvector_graph::{Graph, Hyperedge};

// Create a hyperedge connecting multiple nodes
let meeting = graph.create_hyperedge(Hyperedge {
    label: "PARTICIPATED_IN".to_string(),
    nodes: vec![alice.id, bob.id, charlie.id],
    properties: serde_json::json!({
        "event": "Team Meeting",
        "date": "2024-01-15"
    }),
    ..Default::default()
})?;

API Overview

Core Types

// Node in the graph
pub struct Node {
    pub id: NodeId,
    pub labels: Vec<String>,
    pub properties: serde_json::Value,
    pub embedding: Option<Vec<f32>>,
}

// Edge connecting two nodes
pub struct Edge {
    pub id: EdgeId,
    pub label: String,
    pub source: NodeId,
    pub target: NodeId,
    pub properties: serde_json::Value,
}

// Hyperedge connecting multiple nodes
pub struct Hyperedge {
    pub id: HyperedgeId,
    pub label: String,
    pub nodes: Vec<NodeId>,
    pub properties: serde_json::Value,
}

Graph Operations

impl Graph {
    // Node operations
    pub fn create_node(&self, node: Node) -> Result<Node>;
    pub fn get_node(&self, id: &NodeId) -> Result<Option<Node>>;
    pub fn update_node(&self, node: Node) -> Result<Node>;
    pub fn delete_node(&self, id: &NodeId) -> Result<bool>;

    // Edge operations
    pub fn create_edge(&self, edge: Edge) -> Result<Edge>;
    pub fn get_edge(&self, id: &EdgeId) -> Result<Option<Edge>>;
    pub fn delete_edge(&self, id: &EdgeId) -> Result<bool>;

    // Traversal
    pub fn neighbors(&self, id: &NodeId, direction: Direction) -> Result<Vec<Node>>;
    pub fn traverse(&self, start: &NodeId, config: TraversalConfig) -> Result<Vec<Path>>;

    // Vector search
    pub fn search_similar_nodes(&self, query: Vec<f32>, k: usize, labels: Option<Vec<String>>) -> Result<Vec<Node>>;
}

Performance

Benchmarks (1M Nodes, 10M Edges)

Operation               Latency (p50)    Throughput
-----------------------------------------------------
Node lookup             ~0.1ms           100K ops/s
Edge traversal          ~0.5ms           50K ops/s
1-hop neighbors         ~1ms             20K ops/s
Cypher simple query     ~5ms             5K ops/s
Vector similarity       ~2ms             10K ops/s

Documentation

License

MIT License - see LICENSE for details.


Part of RuVector - Built by rUv

Star on GitHub

Documentation | Crates.io | GitHub