Skip to main content

Graph

Struct Graph 

Source
pub struct Graph { /* private fields */ }
Expand description

Copy-on-Write graph with snapshot support.

Graph uses persistent data structures to enable O(1) snapshot creation and lock-free reads. Mutations are serialized via RwLock but don’t block existing snapshots.

§Creating a Graph

use interstellar::storage::cow::Graph;

let graph = Graph::new();

§Snapshots

Snapshots are O(1) and don’t hold locks:

use interstellar::storage::cow::Graph;

let graph = Graph::new();
let snap = graph.snapshot();

// snap can be sent to another thread, outlive the graph, etc.

Implementations§

Source§

impl Graph

Source

pub fn new() -> Self

Create a new empty COW graph.

§Example
use interstellar::storage::cow::Graph;

let graph = Graph::new();
assert_eq!(graph.vertex_count(), 0);
Source

pub fn in_memory() -> Self

Convenience alias for Graph::new().

This method exists for API compatibility with code that expects a .in_memory() constructor. Since Graph is always in-memory (for persistent storage use PersistentGraph), this is equivalent to Graph::new().

§Example
use interstellar::storage::cow::Graph;

let graph = Graph::in_memory();
assert_eq!(graph.vertex_count(), 0);
Source

pub fn new_arc() -> Arc<Self>

Create a new in-memory graph wrapped in an Arc.

Convenience constructor for entry points that require an Arc<Graph>, such as [Graph::query], [Graph::execute_script], [Graph::gql], and [Graph::gql_with_params].

§Example
use interstellar::Graph;

let graph = Graph::new_arc();
// graph is Arc<Graph>; query/gql methods dispatch through the Arc receiver
Source

pub fn in_memory_with_schema(schema: GraphSchema) -> Self

Create a new in-memory graph with a schema for validation.

Convenience alias for Graph::with_schema().

§Example
use interstellar::storage::cow::Graph;
use interstellar::schema::{SchemaBuilder, PropertyType, ValidationMode};

let schema = SchemaBuilder::new()
    .mode(ValidationMode::Strict)
    .vertex("Person")
        .property("name", PropertyType::String)
        .done()
    .build();

let graph = Graph::in_memory_with_schema(schema);
Source

pub fn with_schema(schema: GraphSchema) -> Self

Create a new COW graph with a schema.

§Example
use interstellar::storage::cow::Graph;
use interstellar::schema::{SchemaBuilder, PropertyType, ValidationMode};

let schema = SchemaBuilder::new()
    .mode(ValidationMode::Strict)
    .vertex("Person")
        .property("name", PropertyType::String)
        .done()
    .build();

let graph = Graph::with_schema(schema);
Source

pub fn snapshot(&self) -> GraphSnapshot

Create a snapshot of the current graph state.

This is an O(1) operation that creates a shared reference to the current state. The snapshot will not reflect any mutations made after this call.

§Thread Safety

This method briefly acquires a read lock to clone the state. The returned snapshot does not hold any locks.

§Example
use interstellar::storage::cow::Graph;
use interstellar::storage::GraphStorage;
use std::collections::HashMap;

let graph = Graph::new();
let v1 = graph.add_vertex("person", HashMap::new());

let snap = graph.snapshot();
assert_eq!(snap.vertex_count(), 1);

// Mutations after snapshot don't affect it
graph.add_vertex("person", HashMap::new());
assert_eq!(snap.vertex_count(), 1); // Still 1
Source

pub fn as_storage_mut(&self) -> GraphMutWrapper<'_>

Create a mutable storage wrapper for this graph.

Returns a GraphMutWrapper that implements both GraphStorage and GraphStorageMut, allowing use with APIs that require mutable storage access (like the GQL mutation engine).

§Example
use interstellar::storage::{Graph, GraphStorage, GraphStorageMut};
use std::collections::HashMap;

let graph = Graph::new();
let mut wrapper = graph.as_storage_mut();

// Use with APIs requiring GraphStorageMut
let id = wrapper.add_vertex("person", HashMap::new());
assert!(wrapper.get_vertex(id).is_some());
Source

pub fn gremlin(&self, graph_arc: Arc<Graph>) -> CowTraversalSource<'_>

Create a Gremlin traversal source for this graph.

The returned CowTraversalSource provides a fluent Gremlin-style API for both reads and mutations. Any mutations in the traversal are automatically executed when terminal steps are called.

Terminal methods like next() and to_list() now return typed results:

  • g.v().next() returns Option<GraphVertex>
  • g.e().next() returns Option<GraphEdge>
  • g.v().values("name").next() returns Option<Value>
§Example
use interstellar::storage::cow::Graph;
use std::sync::Arc;

let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));

// Create vertices - next() returns Option<GraphVertex>
let alice = g.add_v("Person").property("name", "Alice").next();
let bob = g.add_v("Person").property("name", "Bob").next();

// Read - count() returns u64
assert_eq!(g.v().count(), 2);
Source

pub fn typed_gremlin<'a>( &self, snapshot: &'a GraphSnapshot, graph_arc: Arc<Graph>, ) -> TypedTraversalSource<'a>

Create a typed traversal source for read-only traversals.

The typed source returns GraphVertex and GraphEdge objects directly from terminal methods like next() and to_list(), without requiring an Arc<Graph> parameter.

This is useful when you want type-safe traversals that track the output type at compile time.

§Arguments
  • graph_arc - An Arc-wrapped reference to the same graph
§Example
use interstellar::prelude::*;
use std::sync::Arc;
use std::collections::HashMap;

let graph = Arc::new(Graph::new());
graph.add_vertex("person", HashMap::from([
    ("name".to_string(), "Alice".into()),
]));

// Create typed traversal source
let snapshot = graph.snapshot();
let g = graph.typed_gremlin(&snapshot, Arc::clone(&graph));

// next() returns Option<GraphVertex> directly
let v = g.v().next().unwrap();
assert_eq!(v.label(), Some("person".to_string()));
Source

pub fn vertex_count(&self) -> u64

Returns the total number of vertices in the graph.

Source

pub fn edge_count(&self) -> u64

Returns the total number of edges in the graph.

Source

pub fn version(&self) -> u64

Returns the current version number.

Source

pub fn schema(&self) -> Option<GraphSchema>

Get the current schema, if one is set.

Source

pub fn set_schema(&self, schema: Option<GraphSchema>)

Set or replace the graph schema.

Source

pub fn create_index(&self, spec: IndexSpec) -> Result<(), IndexError>

Creates a new property index and populates it with existing data.

The index will be automatically maintained as vertices and edges are added, updated, or removed.

§Arguments
  • spec - The index specification defining what to index
§Errors

Returns IndexError::AlreadyExists if an index with the same name exists. Returns IndexError::DuplicateValue if creating a unique index and duplicate values exist.

§Example
use interstellar::storage::cow::Graph;
use interstellar::index::IndexBuilder;
use std::collections::HashMap;

let graph = Graph::new();

// Add some data first
graph.add_vertex("person", HashMap::from([
    ("age".to_string(), 30i64.into()),
]));

// Create a B+ tree index for range queries
graph.create_index(
    IndexBuilder::vertex()
        .label("person")
        .property("age")
        .build()
        .unwrap()
).unwrap();
Source

pub fn drop_index(&self, name: &str) -> Result<(), IndexError>

Drops an index by name.

§Errors

Returns IndexError::NotFound if no index with that name exists.

Source

pub fn list_indexes(&self) -> Vec<IndexSpec>

Returns a vector of all index specifications.

Source

pub fn has_index(&self, name: &str) -> bool

Checks if an index with the given name exists.

Source

pub fn index_count(&self) -> usize

Returns the number of indexes.

Source

pub fn supports_indexes(&self) -> bool

Returns whether this storage supports indexes.

Source

pub fn vertices_by_property( &self, label: Option<&str>, property: &str, value: &Value, ) -> Box<dyn Iterator<Item = Vertex> + '_>

Lookup vertices by indexed property value.

If an applicable index exists, uses it for O(log n) or O(1) lookup. Otherwise falls back to O(n) scan.

Source

pub fn edges_by_property( &self, label: Option<&str>, property: &str, value: &Value, ) -> Box<dyn Iterator<Item = Edge> + '_>

Lookup edges by indexed property value.

If an applicable index exists, uses it for O(log n) or O(1) lookup. Otherwise falls back to O(n) scan.

Source

pub fn vertices_by_property_range( &self, label: Option<&str>, property: &str, start: Bound<&Value>, end: Bound<&Value>, ) -> Box<dyn Iterator<Item = Vertex> + '_>

Lookup vertices by property range, using indexes if available.

Source

pub fn add_vertex( &self, label: &str, properties: HashMap<String, Value>, ) -> VertexId

Add a vertex to the graph.

This triggers copy-on-write: only the modified paths in the persistent data structure are copied. Existing snapshots continue to see the old state.

§Example
use interstellar::storage::cow::Graph;
use interstellar::storage::GraphStorage;
use std::collections::HashMap;

let graph = Graph::new();
let id = graph.add_vertex("person", HashMap::from([
    ("name".to_string(), "Alice".into()),
]));

let snap = graph.snapshot();
let vertex = snap.get_vertex(id).unwrap();
assert_eq!(vertex.label, "person");
Source

pub fn add_edge( &self, src: VertexId, dst: VertexId, label: &str, properties: HashMap<String, Value>, ) -> Result<EdgeId, StorageError>

Add an edge between two vertices.

§Errors

Returns StorageError::VertexNotFound if either vertex doesn’t exist.

§Example
use interstellar::storage::cow::Graph;
use interstellar::storage::GraphStorage;
use std::collections::HashMap;

let graph = Graph::new();
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());

let edge = graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();

let snap = graph.snapshot();
let e = snap.get_edge(edge).unwrap();
assert_eq!(e.src, alice);
assert_eq!(e.dst, bob);
Source

pub fn set_vertex_property( &self, id: VertexId, key: &str, value: Value, ) -> Result<(), StorageError>

Update a vertex’s property.

§Errors

Returns StorageError::VertexNotFound if the vertex doesn’t exist.

Source

pub fn set_edge_property( &self, id: EdgeId, key: &str, value: Value, ) -> Result<(), StorageError>

Update an edge’s property.

§Errors

Returns StorageError::EdgeNotFound if the edge doesn’t exist.

Source

pub fn remove_vertex(&self, id: VertexId) -> Result<(), StorageError>

Remove a vertex and all its incident edges.

§Errors

Returns StorageError::VertexNotFound if the vertex doesn’t exist.

Source

pub fn remove_edge(&self, id: EdgeId) -> Result<(), StorageError>

Remove an edge from the graph.

§Errors

Returns StorageError::EdgeNotFound if the edge doesn’t exist.

Source

pub fn batch<F, T>(&self, f: F) -> Result<T, BatchError>
where F: FnOnce(&mut BatchContext<'_>) -> Result<T, BatchError>,

Execute multiple operations atomically.

The closure receives a BatchContext that buffers all mutations. Only when the closure returns Ok(()) are all mutations applied. If the closure returns Err or panics, no mutations are applied.

§Example
use interstellar::storage::cow::{Graph, BatchContext};
use std::collections::HashMap;

let graph = Graph::new();

graph.batch(|ctx| {
    let alice = ctx.add_vertex("Person", HashMap::from([
        ("name".to_string(), "Alice".into()),
    ]));
    let bob = ctx.add_vertex("Person", HashMap::from([
        ("name".to_string(), "Bob".into()),
    ]));
    ctx.add_edge(alice, bob, "knows", HashMap::new())?;
    Ok(())
}).unwrap();

assert_eq!(graph.vertex_count(), 2);
assert_eq!(graph.edge_count(), 1);
Source

pub fn to_graphson(&self) -> Result<String, Error>

Export this graph to GraphSON format.

Returns a compact JSON string. For pretty-printed output, use to_graphson_pretty.

§Example
use interstellar::storage::Graph;

let graph = Graph::new();
let json = graph.to_graphson().unwrap();
assert!(json.contains("tinker:graph"));
Source

pub fn to_graphson_pretty(&self) -> Result<String, Error>

Export this graph to GraphSON format (pretty-printed).

Returns a nicely formatted JSON string with indentation.

§Example
use interstellar::storage::Graph;

let graph = Graph::new();
let json = graph.to_graphson_pretty().unwrap();
assert!(json.contains('\n')); // Contains newlines
Source

pub fn from_graphson(json: &str) -> Result<Self>

Create a graph from GraphSON data.

Deserializes a GraphSON 3.0 formatted JSON string into a new graph. The original vertex and edge IDs from the GraphSON are mapped to new IDs; the original IDs are not preserved.

§Example
use interstellar::storage::Graph;

let json = r#"{"@type": "tinker:graph", "@value": {"vertices": [], "edges": []}}"#;
let graph = Graph::from_graphson(json).unwrap();
assert_eq!(graph.vertex_count(), 0);

Trait Implementations§

Source§

impl Default for Graph

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl !Freeze for Graph

§

impl !RefUnwindSafe for Graph

§

impl Send for Graph

§

impl Sync for Graph

§

impl Unpin for Graph

§

impl UnsafeUnpin for Graph

§

impl !UnwindSafe for Graph

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V