use std::collections::HashMap;
use std::sync::Arc;
use crate::error::StorageError;
use crate::graph_access::GraphAccess;
use crate::storage::cow::Graph;
use crate::value::{EdgeId, IntoVertexId, Value, VertexId};
#[cfg(feature = "mmap")]
use crate::storage::CowMmapGraph;
pub type InMemoryVertex = GraphVertex<Arc<Graph>>;
pub type InMemoryEdge = GraphEdge<Arc<Graph>>;
#[cfg(feature = "mmap")]
pub type PersistentVertex = GraphVertex<Arc<CowMmapGraph>>;
#[cfg(feature = "mmap")]
pub type PersistentEdge = GraphEdge<Arc<CowMmapGraph>>;
#[derive(Clone)]
pub struct GraphVertex<G: GraphAccess> {
id: VertexId,
graph: G,
}
impl<G: GraphAccess> GraphVertex<G> {
pub fn new(id: VertexId, graph: G) -> Self {
Self { id, graph }
}
#[inline]
pub fn id(&self) -> VertexId {
self.id
}
pub fn label(&self) -> Option<String> {
self.graph.get_vertex(self.id).map(|v| v.label)
}
pub fn property(&self, key: &str) -> Option<Value> {
self.graph
.get_vertex(self.id)
.and_then(|v| v.properties.get(key).cloned())
}
pub fn properties(&self) -> HashMap<String, Value> {
self.graph
.get_vertex(self.id)
.map(|v| v.properties)
.unwrap_or_default()
}
pub fn exists(&self) -> bool {
self.graph.get_vertex(self.id).is_some()
}
pub fn property_set(&self, key: &str, value: impl Into<Value>) -> Result<(), StorageError> {
self.graph.set_vertex_property(self.id, key, value.into())
}
#[inline]
pub fn graph(&self) -> &G {
&self.graph
}
#[inline]
pub fn to_value(&self) -> Value {
Value::Vertex(self.id)
}
pub fn add_edge(&self, label: &str, to: &GraphVertex<G>) -> Result<GraphEdge<G>, StorageError> {
self.add_edge_to_id(label, to.id)
}
pub fn add_edge_to_id(&self, label: &str, to: VertexId) -> Result<GraphEdge<G>, StorageError> {
let edge_id = self.graph.add_edge(self.id, to, label, HashMap::new())?;
Ok(GraphEdge::new(edge_id, self.graph.clone()))
}
pub fn add_edge_with_props(
&self,
label: &str,
to: &GraphVertex<G>,
properties: HashMap<String, Value>,
) -> Result<GraphEdge<G>, StorageError> {
let edge_id = self.graph.add_edge(self.id, to.id, label, properties)?;
Ok(GraphEdge::new(edge_id, self.graph.clone()))
}
pub fn remove(&self) -> Result<(), StorageError> {
self.graph.remove_vertex(self.id)
}
pub fn out(&self, label: &str) -> GraphVertexTraversal<G> {
GraphVertexTraversal::new(self.graph.clone(), self.id).out_label(label)
}
pub fn out_all(&self) -> GraphVertexTraversal<G> {
GraphVertexTraversal::new(self.graph.clone(), self.id).out()
}
pub fn in_(&self, label: &str) -> GraphVertexTraversal<G> {
GraphVertexTraversal::new(self.graph.clone(), self.id).in_label(label)
}
pub fn in_all(&self) -> GraphVertexTraversal<G> {
GraphVertexTraversal::new(self.graph.clone(), self.id).in_step()
}
pub fn both(&self, label: &str) -> GraphVertexTraversal<G> {
GraphVertexTraversal::new(self.graph.clone(), self.id).both_label(label)
}
pub fn both_all(&self) -> GraphVertexTraversal<G> {
GraphVertexTraversal::new(self.graph.clone(), self.id).both_step()
}
}
impl<G: GraphAccess> std::fmt::Debug for GraphVertex<G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GraphVertex")
.field("id", &self.id)
.field("label", &self.label())
.finish()
}
}
impl<G: GraphAccess> PartialEq for GraphVertex<G> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<G: GraphAccess> Eq for GraphVertex<G> {}
impl<G: GraphAccess> std::hash::Hash for GraphVertex<G> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl<G: GraphAccess> IntoVertexId for GraphVertex<G> {
#[inline]
fn into_vertex_id(self) -> VertexId {
self.id
}
}
impl<G: GraphAccess> IntoVertexId for &GraphVertex<G> {
#[inline]
fn into_vertex_id(self) -> VertexId {
self.id
}
}
#[derive(Clone)]
pub struct GraphEdge<G: GraphAccess> {
id: EdgeId,
graph: G,
}
impl<G: GraphAccess> GraphEdge<G> {
pub fn new(id: EdgeId, graph: G) -> Self {
Self { id, graph }
}
#[inline]
pub fn id(&self) -> EdgeId {
self.id
}
pub fn label(&self) -> Option<String> {
self.graph.get_edge(self.id).map(|e| e.label)
}
pub fn out_v(&self) -> Option<GraphVertex<G>> {
self.graph
.get_edge(self.id)
.map(|e| GraphVertex::new(e.src, self.graph.clone()))
}
pub fn in_v(&self) -> Option<GraphVertex<G>> {
self.graph
.get_edge(self.id)
.map(|e| GraphVertex::new(e.dst, self.graph.clone()))
}
pub fn both_v(&self) -> Option<(GraphVertex<G>, GraphVertex<G>)> {
self.graph.get_edge(self.id).map(|e| {
(
GraphVertex::new(e.src, self.graph.clone()),
GraphVertex::new(e.dst, self.graph.clone()),
)
})
}
pub fn property(&self, key: &str) -> Option<Value> {
self.graph
.get_edge(self.id)
.and_then(|e| e.properties.get(key).cloned())
}
pub fn properties(&self) -> HashMap<String, Value> {
self.graph
.get_edge(self.id)
.map(|e| e.properties)
.unwrap_or_default()
}
pub fn property_set(&self, key: &str, value: impl Into<Value>) -> Result<(), StorageError> {
self.graph.set_edge_property(self.id, key, value.into())
}
pub fn exists(&self) -> bool {
self.graph.get_edge(self.id).is_some()
}
#[inline]
pub fn graph(&self) -> &G {
&self.graph
}
#[inline]
pub fn to_value(&self) -> Value {
Value::Edge(self.id)
}
pub fn remove(&self) -> Result<(), StorageError> {
self.graph.remove_edge(self.id)
}
}
impl<G: GraphAccess> std::fmt::Debug for GraphEdge<G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GraphEdge")
.field("id", &self.id)
.field("label", &self.label())
.finish()
}
}
impl<G: GraphAccess> PartialEq for GraphEdge<G> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<G: GraphAccess> Eq for GraphEdge<G> {}
impl<G: GraphAccess> std::hash::Hash for GraphEdge<G> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
#[derive(Clone, Debug)]
enum TraversalStep {
Out(Option<String>),
In(Option<String>),
Both(Option<String>),
HasLabel(String),
HasValue(String, Value),
}
#[derive(Clone)]
pub struct GraphVertexTraversal<G: GraphAccess> {
graph: G,
start_id: VertexId,
steps: Vec<TraversalStep>,
}
impl<G: GraphAccess> GraphVertexTraversal<G> {
pub(crate) fn new(graph: G, start_id: VertexId) -> Self {
Self {
graph,
start_id,
steps: Vec::new(),
}
}
pub fn out(mut self) -> Self {
self.steps.push(TraversalStep::Out(None));
self
}
pub fn out_label(mut self, label: &str) -> Self {
self.steps.push(TraversalStep::Out(Some(label.to_string())));
self
}
pub fn in_step(mut self) -> Self {
self.steps.push(TraversalStep::In(None));
self
}
pub fn in_label(mut self, label: &str) -> Self {
self.steps.push(TraversalStep::In(Some(label.to_string())));
self
}
pub fn both_step(mut self) -> Self {
self.steps.push(TraversalStep::Both(None));
self
}
pub fn both_label(mut self, label: &str) -> Self {
self.steps
.push(TraversalStep::Both(Some(label.to_string())));
self
}
pub fn has_label(mut self, label: &str) -> Self {
self.steps.push(TraversalStep::HasLabel(label.to_string()));
self
}
pub fn has_value(mut self, key: &str, value: impl Into<Value>) -> Self {
self.steps
.push(TraversalStep::HasValue(key.to_string(), value.into()));
self
}
pub fn to_list(self) -> Vec<GraphVertex<G>> {
let mut current_ids: Vec<VertexId> = vec![self.start_id];
for step in &self.steps {
let mut next_ids: Vec<VertexId> = Vec::new();
for vertex_id in ¤t_ids {
match step {
TraversalStep::Out(label_filter) => {
for edge_id in self.graph.out_edge_ids(*vertex_id) {
if let Some(edge) = self.graph.get_edge(edge_id) {
if let Some(label) = label_filter {
if &edge.label != label {
continue;
}
}
next_ids.push(edge.dst);
}
}
}
TraversalStep::In(label_filter) => {
for edge_id in self.graph.in_edge_ids(*vertex_id) {
if let Some(edge) = self.graph.get_edge(edge_id) {
if let Some(label) = label_filter {
if &edge.label != label {
continue;
}
}
next_ids.push(edge.src);
}
}
}
TraversalStep::Both(label_filter) => {
for edge_id in self.graph.out_edge_ids(*vertex_id) {
if let Some(edge) = self.graph.get_edge(edge_id) {
if let Some(label) = label_filter {
if &edge.label != label {
continue;
}
}
next_ids.push(edge.dst);
}
}
for edge_id in self.graph.in_edge_ids(*vertex_id) {
if let Some(edge) = self.graph.get_edge(edge_id) {
if let Some(label) = label_filter {
if &edge.label != label {
continue;
}
}
next_ids.push(edge.src);
}
}
}
TraversalStep::HasLabel(label) => {
if let Some(vertex) = self.graph.get_vertex(*vertex_id) {
if &vertex.label == label {
next_ids.push(*vertex_id);
}
}
}
TraversalStep::HasValue(key, value) => {
if let Some(vertex) = self.graph.get_vertex(*vertex_id) {
if vertex.properties.get(key) == Some(value) {
next_ids.push(*vertex_id);
}
}
}
}
}
current_ids = next_ids;
}
current_ids
.into_iter()
.map(|id| GraphVertex::new(id, self.graph.clone()))
.collect()
}
pub fn first(self) -> Option<GraphVertex<G>> {
self.to_list().into_iter().next()
}
pub fn count(self) -> usize {
self.to_list().len()
}
pub fn exists(self) -> bool {
self.first().is_some()
}
}
impl<G: GraphAccess> std::fmt::Debug for GraphVertexTraversal<G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GraphVertexTraversal")
.field("start_id", &self.start_id)
.field("steps", &self.steps)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_graph() -> Arc<Graph> {
let graph = Graph::new();
let alice = graph.add_vertex(
"person",
HashMap::from([
("name".to_string(), "Alice".into()),
("age".to_string(), 30i64.into()),
]),
);
let bob = graph.add_vertex(
"person",
HashMap::from([("name".to_string(), "Bob".into())]),
);
graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
Arc::new(graph)
}
#[test]
fn graph_vertex_id() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
assert_eq!(alice.id(), alice_id);
}
#[test]
fn graph_vertex_label() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
assert_eq!(alice.label(), Some("person".to_string()));
}
#[test]
fn graph_vertex_property_access() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
assert_eq!(
alice.property("name"),
Some(Value::String("Alice".to_string()))
);
assert_eq!(alice.property("age"), Some(Value::Int(30)));
assert_eq!(alice.property("nonexistent"), None);
}
#[test]
fn graph_vertex_properties() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
let props = alice.properties();
assert_eq!(props.len(), 2);
assert_eq!(props.get("name"), Some(&Value::String("Alice".to_string())));
assert_eq!(props.get("age"), Some(&Value::Int(30)));
}
#[test]
fn graph_vertex_exists() {
let graph = Arc::new(Graph::new());
let id = graph.add_vertex("person", HashMap::new());
let v = GraphVertex::new(id, graph.clone());
assert!(v.exists());
graph.remove_vertex(id).unwrap();
assert!(!v.exists());
}
#[test]
fn graph_vertex_mutation() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
assert_eq!(alice.property("age"), Some(Value::Int(30)));
alice.property_set("age", 31i64).unwrap();
assert_eq!(alice.property("age"), Some(Value::Int(31)));
let snapshot2 = graph.snapshot();
let g2 = snapshot2.gremlin();
let age = g2.v().has_value("name", "Alice").values("age").next();
assert_eq!(age, Some(Value::Int(31)));
}
#[test]
fn graph_vertex_to_value() {
let graph = Arc::new(Graph::new());
let id = graph.add_vertex("person", HashMap::new());
let v = GraphVertex::new(id, graph.clone());
assert_eq!(v.to_value(), Value::Vertex(id));
}
#[test]
fn graph_vertex_equality() {
let graph = Arc::new(Graph::new());
let id1 = graph.add_vertex("person", HashMap::new());
let id2 = graph.add_vertex("person", HashMap::new());
let v1a = GraphVertex::new(id1, graph.clone());
let v1b = GraphVertex::new(id1, graph.clone());
let v2 = GraphVertex::new(id2, graph.clone());
assert_eq!(v1a, v1b);
assert_ne!(v1a, v2);
}
#[test]
fn graph_vertex_hash() {
use std::collections::HashSet;
let graph = Arc::new(Graph::new());
let id1 = graph.add_vertex("person", HashMap::new());
let id2 = graph.add_vertex("person", HashMap::new());
let v1a = GraphVertex::new(id1, graph.clone());
let v1b = GraphVertex::new(id1, graph.clone());
let v2 = GraphVertex::new(id2, graph.clone());
let mut set = HashSet::new();
set.insert(v1a);
set.insert(v1b); set.insert(v2);
assert_eq!(set.len(), 2);
}
#[test]
fn graph_vertex_debug() {
let graph = Arc::new(Graph::new());
let id = graph.add_vertex("person", HashMap::new());
let v = GraphVertex::new(id, graph.clone());
let debug_str = format!("{:?}", v);
assert!(debug_str.contains("GraphVertex"));
assert!(debug_str.contains("person"));
}
#[test]
fn graph_edge_id() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let edge_val = g.e().next().unwrap();
let edge_id = edge_val.as_edge_id().unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
assert_eq!(edge.id(), edge_id);
}
#[test]
fn graph_edge_label() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let edge_val = g.e().next().unwrap();
let edge_id = edge_val.as_edge_id().unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
assert_eq!(edge.label(), Some("knows".to_string()));
}
#[test]
fn graph_edge_endpoints() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let edge_val = g.e().next().unwrap();
let edge_id = edge_val.as_edge_id().unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
let src = edge.out_v().unwrap();
assert_eq!(
src.property("name"),
Some(Value::String("Alice".to_string()))
);
let dst = edge.in_v().unwrap();
assert_eq!(dst.property("name"), Some(Value::String("Bob".to_string())));
let (src2, dst2) = edge.both_v().unwrap();
assert_eq!(src2.id(), src.id());
assert_eq!(dst2.id(), dst.id());
}
#[test]
fn graph_edge_property_access() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let edge_id = graph
.add_edge(
alice,
bob,
"knows",
HashMap::from([("since".to_string(), 2020i64.into())]),
)
.unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
assert_eq!(edge.property("since"), Some(Value::Int(2020)));
assert_eq!(edge.property("nonexistent"), None);
}
#[test]
fn graph_edge_properties() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let edge_id = graph
.add_edge(
alice,
bob,
"knows",
HashMap::from([
("since".to_string(), 2020i64.into()),
("weight".to_string(), 0.95f64.into()),
]),
)
.unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
let props = edge.properties();
assert_eq!(props.len(), 2);
assert_eq!(props.get("since"), Some(&Value::Int(2020)));
}
#[test]
fn graph_edge_mutation() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let edge_id = graph
.add_edge(
alice,
bob,
"knows",
HashMap::from([("since".to_string(), 2020i64.into())]),
)
.unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
assert_eq!(edge.property("since"), Some(Value::Int(2020)));
edge.property_set("since", 2021i64).unwrap();
assert_eq!(edge.property("since"), Some(Value::Int(2021)));
}
#[test]
fn graph_edge_exists() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let edge_id = graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
assert!(edge.exists());
graph.remove_edge(edge_id).unwrap();
assert!(!edge.exists());
}
#[test]
fn graph_edge_to_value() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let edge_id = graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
assert_eq!(edge.to_value(), Value::Edge(edge_id));
}
#[test]
fn graph_edge_equality() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let charlie = graph.add_vertex("person", HashMap::new());
let edge1 = graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
let edge2 = graph
.add_edge(bob, charlie, "knows", HashMap::new())
.unwrap();
let e1a = GraphEdge::new(edge1, graph.clone());
let e1b = GraphEdge::new(edge1, graph.clone());
let e2 = GraphEdge::new(edge2, graph.clone());
assert_eq!(e1a, e1b);
assert_ne!(e1a, e2);
}
fn test_graph_chain() -> Arc<Graph> {
let graph = Graph::new();
let alice = graph.add_vertex(
"person",
HashMap::from([("name".to_string(), "Alice".into())]),
);
let bob = graph.add_vertex(
"person",
HashMap::from([("name".to_string(), "Bob".into())]),
);
let charlie = graph.add_vertex(
"person",
HashMap::from([("name".to_string(), "Charlie".into())]),
);
graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
graph
.add_edge(bob, charlie, "knows", HashMap::new())
.unwrap();
Arc::new(graph)
}
#[test]
fn vertex_out_traversal() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
let friends = alice.out("knows").to_list();
assert_eq!(friends.len(), 1);
assert_eq!(
friends[0].property("name"),
Some(Value::String("Bob".to_string()))
);
}
#[test]
fn vertex_out_all_traversal() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
let friends = alice.out_all().to_list();
assert_eq!(friends.len(), 1);
}
#[test]
fn vertex_in_traversal() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let bob_val = g.v().has_value("name", "Bob").next().unwrap();
let bob_id = bob_val.as_vertex_id().unwrap();
let bob = GraphVertex::new(bob_id, graph.clone());
let knowers = bob.in_("knows").to_list();
assert_eq!(knowers.len(), 1);
assert_eq!(
knowers[0].property("name"),
Some(Value::String("Alice".to_string()))
);
}
#[test]
fn vertex_in_all_traversal() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let bob_val = g.v().has_value("name", "Bob").next().unwrap();
let bob_id = bob_val.as_vertex_id().unwrap();
let bob = GraphVertex::new(bob_id, graph.clone());
let knowers = bob.in_all().to_list();
assert_eq!(knowers.len(), 1);
}
#[test]
fn vertex_both_traversal() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let bob_val = g.v().has_value("name", "Bob").next().unwrap();
let bob_id = bob_val.as_vertex_id().unwrap();
let bob = GraphVertex::new(bob_id, graph.clone());
let neighbors = bob.both("knows").to_list();
assert_eq!(neighbors.len(), 2);
}
#[test]
fn vertex_chained_traversal() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
let fof = alice.out("knows").out_label("knows").to_list();
assert_eq!(fof.len(), 1);
assert_eq!(
fof[0].property("name"),
Some(Value::String("Charlie".to_string()))
);
}
#[test]
fn vertex_traversal_first() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
let friend = alice.out("knows").first();
assert!(friend.is_some());
assert_eq!(
friend.unwrap().property("name"),
Some(Value::String("Bob".to_string()))
);
}
#[test]
fn vertex_traversal_count() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let bob_val = g.v().has_value("name", "Bob").next().unwrap();
let bob_id = bob_val.as_vertex_id().unwrap();
let bob = GraphVertex::new(bob_id, graph.clone());
assert_eq!(bob.both("knows").count(), 2); }
#[test]
fn vertex_traversal_exists() {
let graph = test_graph_chain();
let snapshot = graph.snapshot();
let g = snapshot.gremlin();
let alice_val = g.v().has_value("name", "Alice").next().unwrap();
let alice_id = alice_val.as_vertex_id().unwrap();
let alice = GraphVertex::new(alice_id, graph.clone());
assert!(alice.out("knows").exists());
assert!(!alice.out("created").exists());
}
#[test]
fn vertex_add_edge() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let alice_v = GraphVertex::new(alice, graph.clone());
let bob_v = GraphVertex::new(bob, graph.clone());
let edge = alice_v.add_edge("knows", &bob_v).unwrap();
assert_eq!(edge.label(), Some("knows".to_string()));
let friends = alice_v.out("knows").to_list();
assert_eq!(friends.len(), 1);
assert_eq!(friends[0].id(), bob);
}
#[test]
fn vertex_add_edge_with_props() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let alice_v = GraphVertex::new(alice, graph.clone());
let bob_v = GraphVertex::new(bob, graph.clone());
let edge = alice_v
.add_edge_with_props(
"knows",
&bob_v,
HashMap::from([("since".to_string(), 2020i64.into())]),
)
.unwrap();
assert_eq!(edge.property("since"), Some(Value::Int(2020)));
}
#[test]
fn vertex_remove() {
let graph = Arc::new(Graph::new());
let id = graph.add_vertex("person", HashMap::new());
let v = GraphVertex::new(id, graph.clone());
assert!(v.exists());
v.remove().unwrap();
assert!(!v.exists());
}
#[test]
fn edge_remove() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("person", HashMap::new());
let bob = graph.add_vertex("person", HashMap::new());
let edge_id = graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
let edge = GraphEdge::new(edge_id, graph.clone());
assert!(edge.exists());
edge.remove().unwrap();
assert!(!edge.exists());
}
#[test]
fn type_alias_inmemory_vertex() {
let graph = Arc::new(Graph::new());
let id = graph.add_vertex("person", HashMap::new());
let v: InMemoryVertex = InMemoryVertex::new(id, graph);
assert!(v.exists());
}
#[test]
fn type_alias_inmemory_edge() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex("person", HashMap::new());
let b = graph.add_vertex("person", HashMap::new());
let edge_id = graph.add_edge(a, b, "knows", HashMap::new()).unwrap();
let e: InMemoryEdge = InMemoryEdge::new(edge_id, graph);
assert!(e.exists());
}
#[cfg(feature = "mmap")]
mod mmap_tests {
use super::*;
use crate::storage::CowMmapGraph;
use tempfile::tempdir;
fn temp_db_path() -> (tempfile::TempDir, std::path::PathBuf) {
let dir = tempdir().unwrap();
let path = dir.path().join("test.db");
(dir, path)
}
#[test]
fn persistent_vertex_basic() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let id = graph
.add_vertex(
"person",
HashMap::from([("name".to_string(), "Alice".into())]),
)
.unwrap();
let v: PersistentVertex = PersistentVertex::new(id, graph.clone());
assert_eq!(v.label(), Some("person".to_string()));
assert_eq!(v.property("name"), Some(Value::String("Alice".to_string())));
}
#[test]
fn persistent_edge_basic() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let a = graph.add_vertex("person", HashMap::new()).unwrap();
let b = graph.add_vertex("person", HashMap::new()).unwrap();
let edge_id = graph.add_edge(a, b, "knows", HashMap::new()).unwrap();
let e: PersistentEdge = PersistentEdge::new(edge_id, graph.clone());
assert_eq!(e.label(), Some("knows".to_string()));
}
#[test]
fn persistent_vertex_traversal() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let alice = graph
.add_vertex(
"person",
HashMap::from([("name".to_string(), "Alice".into())]),
)
.unwrap();
let bob = graph
.add_vertex(
"person",
HashMap::from([("name".to_string(), "Bob".into())]),
)
.unwrap();
graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
let alice_v: PersistentVertex = PersistentVertex::new(alice, graph.clone());
let friends = alice_v.out("knows").to_list();
assert_eq!(friends.len(), 1);
assert_eq!(
friends[0].property("name"),
Some(Value::String("Bob".to_string()))
);
}
#[test]
fn persistent_vertex_mutation() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let id = graph
.add_vertex("person", HashMap::from([("age".to_string(), 30i64.into())]))
.unwrap();
let v: PersistentVertex = PersistentVertex::new(id, graph.clone());
assert_eq!(v.property("age"), Some(Value::Int(30)));
v.property_set("age", 31i64).unwrap();
assert_eq!(v.property("age"), Some(Value::Int(31)));
}
#[test]
fn persistent_edge_endpoints_return_persistent_vertex() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let a = graph
.add_vertex(
"person",
HashMap::from([("name".to_string(), "Alice".into())]),
)
.unwrap();
let b = graph
.add_vertex(
"person",
HashMap::from([("name".to_string(), "Bob".into())]),
)
.unwrap();
let edge_id = graph.add_edge(a, b, "knows", HashMap::new()).unwrap();
let e: PersistentEdge = PersistentEdge::new(edge_id, graph.clone());
let src = e.out_v().unwrap();
let dst = e.in_v().unwrap();
assert_eq!(
src.property("name"),
Some(Value::String("Alice".to_string()))
);
assert_eq!(dst.property("name"), Some(Value::String("Bob".to_string())));
}
}
}