Expand description
§Interstellar
A high-performance Rust graph traversal library with a Gremlin-style fluent API.
Interstellar provides a type-safe, ergonomic interface for graph operations with support for both in-memory and persistent (memory-mapped) storage backends.
§Features
- Gremlin-style Fluent API: Chainable traversal steps with lazy evaluation
- Dual Storage Backends: In-memory (HashMap-based) and memory-mapped (persistent)
- Anonymous Traversals: Composable fragments via the
__factory module - Rich Predicate System: Filtering with
p::eq,p::gt,p::within,p::regex, and more - GQL Query Language: Declarative queries as an alternative to the programmatic API
- Thread-Safe: Snapshot-based concurrency for safe parallel reads
§Quick Start
use interstellar::prelude::*;
use std::collections::HashMap;
// Create a new graph
let graph = Graph::new();
// Add vertices with properties
let alice = graph.add_vertex("person", HashMap::from([
("name".to_string(), Value::from("Alice")),
("age".to_string(), Value::from(30i64)),
]));
let bob = graph.add_vertex("person", HashMap::from([
("name".to_string(), Value::from("Bob")),
("age".to_string(), Value::from(25i64)),
]));
// Add an edge
graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
// Create a snapshot for read access
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
// Traverse: find all people Alice knows
let friends = g.v_ids([alice])
.out_labels(&["knows"])
.values("name")
.to_list();
assert_eq!(friends, vec![Value::String("Bob".to_string())]);§Simpler Quick Start
For the simplest setup, use the convenience constructor:
use interstellar::prelude::*;
// Create an empty in-memory graph
let graph = Graph::in_memory();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
// Count vertices (0 in empty graph)
assert_eq!(g.v().count(), 0);§Module Overview
| Module | Description |
|---|---|
[graph] | Graph container with snapshot-based concurrency (Graph, GraphSnapshot) |
storage | Storage backends (Graph, MmapGraph) |
traversal | Fluent traversal API, steps, predicates (p), anonymous traversals (__) |
value | Core value types (Value, VertexId, EdgeId) |
error | Error types (StorageError, TraversalError, MutationError) |
[gql] | GQL query language parser and compiler (requires gql feature) |
algorithms | Graph algorithms (BFS, DFS, shortest path) |
§Traversal API Overview
The traversal API follows the Gremlin pattern with source, navigation, filter, transform, branch, and terminal steps:
use interstellar::prelude::*;
let graph = Graph::in_memory();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
// Source steps - where traversals begin
let _ = g.v(); // All vertices
let _ = g.e(); // All edges
// g.v_ids([id1, id2]) // Specific vertices
// g.inject([1, 2, 3]) // Inject arbitrary values
// Navigation steps - traverse the graph structure
// .out("label") // Follow outgoing edges
// .in_("label") // Follow incoming edges
// .both("label") // Both directions
// .out_e() / .in_e() / .both_e() // Get edge objects
// .out_v() / .in_v() / .other_v() // Get edge endpoints
// Filter steps - narrow down results
// .has("key") // Has property
// .has_value("key", value) // Property equals value
// .has_where("key", p::gt(30)) // Property matches predicate
// .has_label("person") // Filter by label
// .dedup() // Remove duplicates
// .limit(10) // Take first N
// Transform steps - modify or extract data
// .values("name") // Extract property value
// .value_map() // All properties as map
// .id() / .label() // Element metadata
// .map(|v| ...) // Custom transformation
// Terminal steps - execute and collect results
let count = g.v().count(); // Count results
let list = g.v().to_list(); // Collect all results
// .first() // First result (Option)
// .one() // Exactly one (Result)§Predicates
The p module provides predicates for filtering:
use interstellar::prelude::*;
// Comparison predicates
let _ = p::eq(30); // Equals
let _ = p::gt(30); // Greater than
let _ = p::between(20, 40); // Range [20, 40)
// Collection predicates
let _ = p::within([1i64, 2, 3]); // In set
// String predicates
let _ = p::containing("sub"); // Contains substring
let _ = p::regex(r"^\d+$"); // Regex match
// Logical predicates
let _ = p::and(p::gt(20), p::lt(40));
let _ = p::or(p::lt(20), p::gt(40));
let _ = p::not(p::eq(30));§Anonymous Traversals
The __ module provides anonymous traversal fragments for composition:
use interstellar::prelude::*;
let graph = Graph::in_memory();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
// Use in branch steps
let _ = g.v().union(vec![
__.out_labels(&["knows"]),
__.out_labels(&["created"]),
]);
// Use in repeat
let _ = g.v().repeat(__.out_labels(&["parent"])).times(3);
// Use in where clauses
// g.v().where_(__.out("knows").count().is_(p::gt(3)))§GQL Query Language
For declarative queries, enable the gql feature and use the GQL interface:
[dependencies]
interstellar = { version = "0.1", features = ["gql"] }use interstellar::prelude::*;
use std::collections::HashMap;
let graph = Graph::new();
graph.add_vertex("Person", HashMap::from([
("name".to_string(), Value::from("Alice")),
]));
let snapshot = graph.snapshot();
// Execute a GQL query (requires `gql` feature)
let results = graph.gql("MATCH (n:Person) RETURN n.name").unwrap();
assert_eq!(results.len(), 1);§Error Handling
Interstellar uses Result types throughout. See the error module for details
on error types and recovery patterns:
use interstellar::prelude::*;
let graph = Graph::in_memory();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
// Handle "exactly one" requirement
match g.v().one() {
Ok(vertex) => println!("Found: {:?}", vertex),
Err(TraversalError::NotOne(0)) => println!("No vertices found"),
Err(TraversalError::NotOne(n)) => println!("Too many: {}", n),
Err(e) => println!("Error: {}", e),
}§Storage Backends
§In-Memory (Default)
The COW (Copy-on-Write) graph for development and small graphs:
use interstellar::storage::Graph;
let graph = Graph::new();
// Use directly with traversal API§Memory-Mapped (Persistent)
Persistent storage with write-ahead logging. Enable with the mmap feature:
[dependencies]
interstellar = { version = "0.1", features = ["mmap"] }use interstellar::storage::MmapGraph;
let graph = MmapGraph::open("my_graph.db").unwrap();
// Data persists across restarts§Feature Flags
| Feature | Description | Default |
|---|---|---|
graphson | GraphSON import/export support | Yes |
mmap | Memory-mapped persistent storage (not available on WASM) | No |
gql | GQL query language support | No |
full-text | Full-text search with Tantivy (not available on WASM) | No |
full | Enable all features | No |
Note: In-memory graph storage is always available (core functionality).
§WASM Support
Interstellar supports WebAssembly targets (wasm32-unknown-unknown).
The following features work on WASM:
- Core in-memory
Graph - Full traversal API
- GQL query language (with
gqlfeature) - GraphSON serialization (string-based only; file I/O excluded)
Build for WASM:
cargo build --target wasm32-unknown-unknown
cargo build --target wasm32-unknown-unknown --features gql§Thread Safety
Graph uses a readers-writer lock for safe concurrent access:
- Multiple
GraphSnapshots can exist simultaneously (shared reads) - [
GraphMut] requires exclusive access (exclusive writes) - Snapshots see a consistent view of the graph
use interstellar::prelude::*;
use std::sync::Arc;
use std::thread;
let graph = Arc::new(Graph::in_memory());
// Multiple threads can read concurrently
let handles: Vec<_> = (0..4).map(|_| {
let g = Arc::clone(&graph);
thread::spawn(move || {
let snap = g.snapshot();
snap.gremlin().v().count()
})
}).collect();
for handle in handles {
let _ = handle.join().unwrap();
}§Examples
The examples/ directory contains comprehensive demonstrations:
basic_traversal.rs- Getting started with traversalsnavigation_steps.rs- Graph navigation patternsfilter_steps.rs- Filtering and predicatesbranch_steps.rs- Branching and conditional logicrepeat_steps.rs- Iterative traversalsbritish_royals.rs- Real-world family tree queriesnba.rs- Sports analytics queries
Run examples with:
cargo run --example basic_traversal
cargo run --example british_royalsRe-exports§
pub use graph_elements::GraphEdge;pub use graph_elements::GraphVertex;pub use graph_elements::GraphVertexTraversal;pub use graph_access::GraphAccess;pub use prelude::*;
Modules§
- algorithms
- error
- Error types for storage and traversal operations.
- graph_
access - Trait for graph access operations needed by GraphVertex/GraphEdge.
- graph_
elements - Rich graph element types with graph references.
- graphson
- GraphSON 3.0 import/export support.
- index
- Property index support for efficient lookups.
- prelude
- The prelude module re-exports commonly used types.
- schema
- Graph schema definition and validation.
- storage
- Storage backends for graph data.
- traversal
- Traversal engine core types.
- value
- Value types and element identifiers for graph data.
Macros§
- impl_
filter_ step - Helper macro to implement
Stepfor simple filter steps. - impl_
flatmap_ step - Helper macro to implement
Stepfor flatmap steps (1:N mappings). - props
- Creates a property map for vertices and edges.