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
impl Graph
Sourcepub fn new() -> Self
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);Sourcepub fn in_memory() -> Self
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);Sourcepub fn new_arc() -> Arc<Self>
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 receiverSourcepub fn in_memory_with_schema(schema: GraphSchema) -> Self
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);Sourcepub fn with_schema(schema: GraphSchema) -> Self
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);Sourcepub fn snapshot(&self) -> GraphSnapshot
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 1Sourcepub fn as_storage_mut(&self) -> GraphMutWrapper<'_>
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());Sourcepub fn gremlin(&self, graph_arc: Arc<Graph>) -> CowTraversalSource<'_>
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()returnsOption<GraphVertex>g.e().next()returnsOption<GraphEdge>g.v().values("name").next()returnsOption<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);Sourcepub fn typed_gremlin<'a>(
&self,
snapshot: &'a GraphSnapshot,
graph_arc: Arc<Graph>,
) -> TypedTraversalSource<'a>
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()));Sourcepub fn vertex_count(&self) -> u64
pub fn vertex_count(&self) -> u64
Returns the total number of vertices in the graph.
Sourcepub fn edge_count(&self) -> u64
pub fn edge_count(&self) -> u64
Returns the total number of edges in the graph.
Sourcepub fn schema(&self) -> Option<GraphSchema>
pub fn schema(&self) -> Option<GraphSchema>
Get the current schema, if one is set.
Sourcepub fn set_schema(&self, schema: Option<GraphSchema>)
pub fn set_schema(&self, schema: Option<GraphSchema>)
Set or replace the graph schema.
Sourcepub fn create_index(&self, spec: IndexSpec) -> Result<(), IndexError>
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();Sourcepub fn drop_index(&self, name: &str) -> Result<(), IndexError>
pub fn drop_index(&self, name: &str) -> Result<(), IndexError>
Sourcepub fn list_indexes(&self) -> Vec<IndexSpec>
pub fn list_indexes(&self) -> Vec<IndexSpec>
Returns a vector of all index specifications.
Sourcepub fn index_count(&self) -> usize
pub fn index_count(&self) -> usize
Returns the number of indexes.
Sourcepub fn supports_indexes(&self) -> bool
pub fn supports_indexes(&self) -> bool
Returns whether this storage supports indexes.
Sourcepub fn vertices_by_property(
&self,
label: Option<&str>,
property: &str,
value: &Value,
) -> Box<dyn Iterator<Item = Vertex> + '_>
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.
Sourcepub fn edges_by_property(
&self,
label: Option<&str>,
property: &str,
value: &Value,
) -> Box<dyn Iterator<Item = Edge> + '_>
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.
Sourcepub fn vertices_by_property_range(
&self,
label: Option<&str>,
property: &str,
start: Bound<&Value>,
end: Bound<&Value>,
) -> Box<dyn Iterator<Item = Vertex> + '_>
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.
Sourcepub fn add_vertex(
&self,
label: &str,
properties: HashMap<String, Value>,
) -> VertexId
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");Sourcepub fn add_edge(
&self,
src: VertexId,
dst: VertexId,
label: &str,
properties: HashMap<String, Value>,
) -> Result<EdgeId, StorageError>
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);Sourcepub fn set_vertex_property(
&self,
id: VertexId,
key: &str,
value: Value,
) -> Result<(), StorageError>
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.
Sourcepub fn set_edge_property(
&self,
id: EdgeId,
key: &str,
value: Value,
) -> Result<(), StorageError>
pub fn set_edge_property( &self, id: EdgeId, key: &str, value: Value, ) -> Result<(), StorageError>
Sourcepub fn remove_vertex(&self, id: VertexId) -> Result<(), StorageError>
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.
Sourcepub fn remove_edge(&self, id: EdgeId) -> Result<(), StorageError>
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.
Sourcepub fn batch<F, T>(&self, f: F) -> Result<T, BatchError>
pub fn batch<F, T>(&self, f: F) -> 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);Sourcepub fn to_graphson(&self) -> Result<String, Error>
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"));Sourcepub fn to_graphson_pretty(&self) -> Result<String, Error>
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 newlinesSourcepub fn from_graphson(json: &str) -> Result<Self>
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);