Skip to main content

Crate interstellar

Crate interstellar 

Source
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

ModuleDescription
[graph]Graph container with snapshot-based concurrency (Graph, GraphSnapshot)
storageStorage backends (Graph, MmapGraph)
traversalFluent traversal API, steps, predicates (p), anonymous traversals (__)
valueCore value types (Value, VertexId, EdgeId)
errorError types (StorageError, TraversalError, MutationError)
[gql]GQL query language parser and compiler (requires gql feature)
algorithmsGraph 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

FeatureDescriptionDefault
graphsonGraphSON import/export supportYes
mmapMemory-mapped persistent storage (not available on WASM)No
gqlGQL query language supportNo
full-textFull-text search with Tantivy (not available on WASM)No
fullEnable all featuresNo

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 gql feature)
  • 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 traversals
  • navigation_steps.rs - Graph navigation patterns
  • filter_steps.rs - Filtering and predicates
  • branch_steps.rs - Branching and conditional logic
  • repeat_steps.rs - Iterative traversals
  • british_royals.rs - Real-world family tree queries
  • nba.rs - Sports analytics queries

Run examples with:

cargo run --example basic_traversal
cargo run --example british_royals

Re-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 Step for simple filter steps.
impl_flatmap_step
Helper macro to implement Step for flatmap steps (1:N mappings).
props
Creates a property map for vertices and edges.