use std::collections::HashSet;
use std::marker::PhantomData;
use std::sync::Arc;
use crate::error::TraversalError;
use crate::graph_elements::{GraphEdge, GraphVertex, InMemoryEdge, InMemoryVertex};
use crate::storage::cow::Graph;
use crate::storage::interner::StringInterner;
use crate::storage::GraphStorage;
use crate::traversal::context::SnapshotLike;
use crate::traversal::filter::{
DedupStep, HasLabelStep, HasStep, HasValueStep, LimitStep, RangeStep, SkipStep,
};
use crate::traversal::markers::{Edge, OutputMarker, Scalar, Vertex};
use crate::traversal::navigation::{
BothEStep, BothStep, InEStep, InStep, InVStep, OutEStep, OutStep, OutVStep,
};
use crate::traversal::step::{StartStep, Step};
use crate::traversal::transform::{IdStep, LabelStep, ValuesStep};
use crate::traversal::{ExecutionContext, Traversal, TraversalSource, Traverser};
use crate::value::Value;
pub struct TypedTraversalSource<'g> {
storage: &'g dyn GraphStorage,
interner: &'g StringInterner,
graph: Arc<Graph>,
}
impl<'g> TypedTraversalSource<'g> {
pub fn new<S: SnapshotLike + ?Sized>(snapshot: &'g S, graph: Arc<Graph>) -> Self {
Self {
storage: snapshot.storage(),
interner: snapshot.interner(),
graph,
}
}
pub fn v(&self) -> TypedTraversal<'g, Vertex> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: Arc::clone(&self.graph),
traversal: Traversal::with_source(TraversalSource::AllVertices),
track_paths: false,
_marker: PhantomData,
}
}
pub fn e(&self) -> TypedTraversal<'g, Edge> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: Arc::clone(&self.graph),
traversal: Traversal::with_source(TraversalSource::AllEdges),
track_paths: false,
_marker: PhantomData,
}
}
pub fn inject<T, I>(&self, values: I) -> TypedTraversal<'g, Scalar>
where
I: IntoIterator<Item = T>,
T: Into<Value>,
{
let values: Vec<Value> = values.into_iter().map(Into::into).collect();
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: Arc::clone(&self.graph),
traversal: Traversal::with_source(TraversalSource::Inject(values)),
track_paths: false,
_marker: PhantomData,
}
}
#[inline]
pub fn graph(&self) -> &Arc<Graph> {
&self.graph
}
}
pub struct TypedTraversal<'g, Marker: OutputMarker> {
storage: &'g dyn GraphStorage,
interner: &'g StringInterner,
graph: Arc<Graph>,
traversal: Traversal<(), Value>,
track_paths: bool,
_marker: PhantomData<Marker>,
}
impl<'g, Marker: OutputMarker> TypedTraversal<'g, Marker> {
pub fn with_path(mut self) -> Self {
self.track_paths = true;
self
}
#[inline]
pub fn is_tracking_paths(&self) -> bool {
self.track_paths
}
fn add_step(self, step: impl Step) -> Self {
Self {
traversal: self.traversal.add_step(step),
..self
}
}
fn execute(self) -> TypedTraversalExecutor {
let ctx = if self.track_paths {
ExecutionContext::with_path_tracking(self.storage, self.interner)
} else {
ExecutionContext::new(self.storage, self.interner)
};
let (source, steps) = self.traversal.into_steps();
let mut current: Vec<Traverser> = match source {
Some(src) => {
let start_step = StartStep::new(src);
start_step
.apply(&ctx, Box::new(std::iter::empty()))
.collect()
}
None => Vec::new(),
};
for step in &steps {
current = step
.apply_dyn(&ctx, Box::new(current.into_iter()))
.collect();
}
TypedTraversalExecutor {
results: current.into_iter(),
}
}
#[inline]
pub fn graph(&self) -> &Arc<Graph> {
&self.graph
}
pub fn next_value(self) -> Option<Value> {
self.execute().next().map(|t| t.value)
}
pub fn to_value_list(self) -> Vec<Value> {
self.execute().map(|t| t.value).collect()
}
}
struct TypedTraversalExecutor {
results: std::vec::IntoIter<Traverser>,
}
impl Iterator for TypedTraversalExecutor {
type Item = Traverser;
fn next(&mut self) -> Option<Self::Item> {
self.results.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.results.size_hint()
}
}
impl ExactSizeIterator for TypedTraversalExecutor {
fn len(&self) -> usize {
self.results.len()
}
}
impl<'g> TypedTraversal<'g, Vertex> {
pub fn next(self) -> Option<InMemoryVertex> {
let graph = Arc::clone(&self.graph);
self.execute().find_map(|t| match t.value {
Value::Vertex(id) => Some(GraphVertex::new(id, Arc::clone(&graph))),
_ => None,
})
}
pub fn to_list(self) -> Vec<InMemoryVertex> {
let graph = Arc::clone(&self.graph);
self.execute()
.filter_map(|t| match t.value {
Value::Vertex(id) => Some(GraphVertex::new(id, Arc::clone(&graph))),
_ => None,
})
.collect()
}
pub fn one(self) -> Result<InMemoryVertex, TraversalError> {
let graph = Arc::clone(&self.graph);
let ids: Vec<_> = self
.execute()
.filter_map(|t| match t.value {
Value::Vertex(id) => Some(id),
_ => None,
})
.take(2)
.collect();
match ids.len() {
1 => Ok(GraphVertex::new(ids[0], graph)),
n => Err(TraversalError::NotOne(n)),
}
}
#[allow(clippy::mutable_key_type)]
pub fn to_set(self) -> HashSet<InMemoryVertex> {
self.to_list().into_iter().collect()
}
pub fn has_next(self) -> bool {
self.execute().any(|t| matches!(t.value, Value::Vertex(_)))
}
pub fn count(self) -> u64 {
self.execute()
.filter(|t| matches!(t.value, Value::Vertex(_)))
.count() as u64
}
pub fn take(self, n: usize) -> Vec<InMemoryVertex> {
let graph = Arc::clone(&self.graph);
self.execute()
.filter_map(|t| match t.value {
Value::Vertex(id) => Some(GraphVertex::new(id, Arc::clone(&graph))),
_ => None,
})
.take(n)
.collect()
}
pub fn iter(self) -> impl Iterator<Item = InMemoryVertex> {
let graph = Arc::clone(&self.graph);
self.execute().filter_map(move |t| match t.value {
Value::Vertex(id) => Some(GraphVertex::new(id, Arc::clone(&graph))),
_ => None,
})
}
}
impl<'g> TypedTraversal<'g, Edge> {
pub fn next(self) -> Option<InMemoryEdge> {
let graph = Arc::clone(&self.graph);
self.execute().find_map(|t| match t.value {
Value::Edge(id) => Some(GraphEdge::new(id, Arc::clone(&graph))),
_ => None,
})
}
pub fn to_list(self) -> Vec<InMemoryEdge> {
let graph = Arc::clone(&self.graph);
self.execute()
.filter_map(|t| match t.value {
Value::Edge(id) => Some(GraphEdge::new(id, Arc::clone(&graph))),
_ => None,
})
.collect()
}
pub fn one(self) -> Result<InMemoryEdge, TraversalError> {
let graph = Arc::clone(&self.graph);
let ids: Vec<_> = self
.execute()
.filter_map(|t| match t.value {
Value::Edge(id) => Some(id),
_ => None,
})
.take(2)
.collect();
match ids.len() {
1 => Ok(GraphEdge::new(ids[0], graph)),
n => Err(TraversalError::NotOne(n)),
}
}
#[allow(clippy::mutable_key_type)]
pub fn to_set(self) -> HashSet<InMemoryEdge> {
self.to_list().into_iter().collect()
}
pub fn has_next(self) -> bool {
self.execute().any(|t| matches!(t.value, Value::Edge(_)))
}
pub fn count(self) -> u64 {
self.execute()
.filter(|t| matches!(t.value, Value::Edge(_)))
.count() as u64
}
pub fn take(self, n: usize) -> Vec<InMemoryEdge> {
let graph = Arc::clone(&self.graph);
self.execute()
.filter_map(|t| match t.value {
Value::Edge(id) => Some(GraphEdge::new(id, Arc::clone(&graph))),
_ => None,
})
.take(n)
.collect()
}
pub fn iter(self) -> impl Iterator<Item = InMemoryEdge> {
let graph = Arc::clone(&self.graph);
self.execute().filter_map(move |t| match t.value {
Value::Edge(id) => Some(GraphEdge::new(id, Arc::clone(&graph))),
_ => None,
})
}
}
impl<'g> TypedTraversal<'g, Scalar> {
pub fn next(self) -> Option<Value> {
self.execute().next().map(|t| t.value)
}
pub fn to_list(self) -> Vec<Value> {
self.execute().map(|t| t.value).collect()
}
pub fn one(self) -> Result<Value, TraversalError> {
let results: Vec<_> = self.execute().take(2).collect();
match results.len() {
1 => Ok(results.into_iter().next().unwrap().value),
n => Err(TraversalError::NotOne(n)),
}
}
pub fn to_set(self) -> HashSet<Value> {
self.to_list().into_iter().collect()
}
pub fn has_next(self) -> bool {
self.execute().next().is_some()
}
pub fn count(self) -> u64 {
self.execute().count() as u64
}
pub fn take(self, n: usize) -> Vec<Value> {
self.execute().take(n).map(|t| t.value).collect()
}
pub fn iter(self) -> impl Iterator<Item = Value> {
self.execute().map(|t| t.value)
}
pub fn sum(self) -> Value {
let mut int_sum: i64 = 0;
let mut float_sum: f64 = 0.0;
let mut has_float = false;
for traverser in self.execute() {
match traverser.value {
Value::Int(n) => int_sum += n,
Value::Float(f) => {
has_float = true;
float_sum += f;
}
_ => {}
}
}
if has_float {
Value::Float(int_sum as f64 + float_sum)
} else {
Value::Int(int_sum)
}
}
pub fn min(self) -> Option<Value> {
self.execute()
.map(|t| t.value)
.min_by(|a, b| a.to_comparable().cmp(&b.to_comparable()))
}
pub fn max(self) -> Option<Value> {
self.execute()
.map(|t| t.value)
.max_by(|a, b| a.to_comparable().cmp(&b.to_comparable()))
}
}
impl<'g> TypedTraversal<'g, Vertex> {
pub fn out(self) -> TypedTraversal<'g, Vertex> {
self.add_step(OutStep::new())
}
pub fn out_labels(self, labels: &[&str]) -> TypedTraversal<'g, Vertex> {
self.add_step(OutStep::with_labels(
labels.iter().map(|s| s.to_string()).collect(),
))
}
pub fn in_(self) -> TypedTraversal<'g, Vertex> {
self.add_step(InStep::new())
}
pub fn in_labels(self, labels: &[&str]) -> TypedTraversal<'g, Vertex> {
self.add_step(InStep::with_labels(
labels.iter().map(|s| s.to_string()).collect(),
))
}
pub fn both(self) -> TypedTraversal<'g, Vertex> {
self.add_step(BothStep::new())
}
pub fn both_labels(self, labels: &[&str]) -> TypedTraversal<'g, Vertex> {
self.add_step(BothStep::with_labels(
labels.iter().map(|s| s.to_string()).collect(),
))
}
pub fn out_e(self) -> TypedTraversal<'g, Edge> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(OutEStep::new()),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn out_e_labels(self, labels: &[&str]) -> TypedTraversal<'g, Edge> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(OutEStep::with_labels(
labels.iter().map(|s| s.to_string()).collect(),
)),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn in_e(self) -> TypedTraversal<'g, Edge> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(InEStep::new()),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn in_e_labels(self, labels: &[&str]) -> TypedTraversal<'g, Edge> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(InEStep::with_labels(
labels.iter().map(|s| s.to_string()).collect(),
)),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn both_e(self) -> TypedTraversal<'g, Edge> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(BothEStep::new()),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn both_e_labels(self, labels: &[&str]) -> TypedTraversal<'g, Edge> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(BothEStep::with_labels(
labels.iter().map(|s| s.to_string()).collect(),
)),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn values(self, key: &str) -> TypedTraversal<'g, Scalar> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(ValuesStep::new(key)),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn id(self) -> TypedTraversal<'g, Scalar> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(IdStep),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn label(self) -> TypedTraversal<'g, Scalar> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(LabelStep),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn has_label(self, label: impl Into<String>) -> TypedTraversal<'g, Vertex> {
self.add_step(HasLabelStep::single(label))
}
pub fn has(self, key: impl Into<String>) -> TypedTraversal<'g, Vertex> {
self.add_step(HasStep::new(key))
}
pub fn has_value(self, key: &str, value: impl Into<Value>) -> TypedTraversal<'g, Vertex> {
self.add_step(HasValueStep::new(key, value.into()))
}
pub fn limit(self, n: usize) -> TypedTraversal<'g, Vertex> {
self.add_step(LimitStep::new(n))
}
pub fn skip(self, n: usize) -> TypedTraversal<'g, Vertex> {
self.add_step(SkipStep::new(n))
}
pub fn range(self, start: usize, end: usize) -> TypedTraversal<'g, Vertex> {
self.add_step(RangeStep::new(start, end))
}
pub fn dedup(self) -> TypedTraversal<'g, Vertex> {
self.add_step(DedupStep::new())
}
}
impl<'g> TypedTraversal<'g, Edge> {
pub fn out_v(self) -> TypedTraversal<'g, Vertex> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(OutVStep),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn in_v(self) -> TypedTraversal<'g, Vertex> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(InVStep),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn both_v(self) -> TypedTraversal<'g, Vertex> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self
.traversal
.add_step(crate::traversal::navigation::BothVStep),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn values(self, key: &str) -> TypedTraversal<'g, Scalar> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(ValuesStep::new(key)),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn id(self) -> TypedTraversal<'g, Scalar> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(IdStep),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn label(self) -> TypedTraversal<'g, Scalar> {
TypedTraversal {
storage: self.storage,
interner: self.interner,
graph: self.graph,
traversal: self.traversal.add_step(LabelStep),
track_paths: self.track_paths,
_marker: PhantomData,
}
}
pub fn has_label(self, label: impl Into<String>) -> TypedTraversal<'g, Edge> {
self.add_step(HasLabelStep::single(label))
}
pub fn has(self, key: impl Into<String>) -> TypedTraversal<'g, Edge> {
self.add_step(HasStep::new(key))
}
pub fn has_value(self, key: &str, value: impl Into<Value>) -> TypedTraversal<'g, Edge> {
self.add_step(HasValueStep::new(key, value.into()))
}
pub fn limit(self, n: usize) -> TypedTraversal<'g, Edge> {
self.add_step(LimitStep::new(n))
}
pub fn skip(self, n: usize) -> TypedTraversal<'g, Edge> {
self.add_step(SkipStep::new(n))
}
pub fn range(self, start: usize, end: usize) -> TypedTraversal<'g, Edge> {
self.add_step(RangeStep::new(start, end))
}
pub fn dedup(self) -> TypedTraversal<'g, Edge> {
self.add_step(DedupStep::new())
}
}
impl<'g> TypedTraversal<'g, Scalar> {
pub fn limit(self, n: usize) -> TypedTraversal<'g, Scalar> {
self.add_step(LimitStep::new(n))
}
pub fn skip(self, n: usize) -> TypedTraversal<'g, Scalar> {
self.add_step(SkipStep::new(n))
}
pub fn range(self, start: usize, end: usize) -> TypedTraversal<'g, Scalar> {
self.add_step(RangeStep::new(start, end))
}
pub fn dedup(self) -> TypedTraversal<'g, Scalar> {
self.add_step(DedupStep::new())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
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()),
("age".to_string(), 25i64.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 typed_source_v() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let vertices = g.v().to_list();
assert_eq!(vertices.len(), 3);
}
#[test]
fn typed_source_e() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let edges = g.e().to_list();
assert_eq!(edges.len(), 2);
}
#[test]
fn typed_source_inject() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let values = g.inject([1i64, 2, 3]).to_list();
assert_eq!(values.len(), 3);
assert_eq!(values[0], Value::Int(1));
}
#[test]
fn vertex_next() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let v: Option<InMemoryVertex> = g.v().next();
assert!(v.is_some());
assert!(v.unwrap().label().is_some());
}
#[test]
fn vertex_to_list() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let vertices: Vec<InMemoryVertex> = g.v().to_list();
assert_eq!(vertices.len(), 3);
}
#[test]
fn vertex_one() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let result = g.v().one();
assert!(result.is_err());
let result = g.v().limit(1).one();
assert!(result.is_ok());
}
#[test]
fn vertex_count() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
assert_eq!(g.v().count(), 3);
}
#[test]
fn vertex_has_next() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
assert!(g.v().has_next());
}
#[test]
fn vertex_take() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let vertices = g.v().take(2);
assert_eq!(vertices.len(), 2);
}
#[test]
fn edge_next() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let e: Option<InMemoryEdge> = g.e().next();
assert!(e.is_some());
assert_eq!(e.unwrap().label(), Some("knows".to_string()));
}
#[test]
fn edge_to_list() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let edges: Vec<InMemoryEdge> = g.e().to_list();
assert_eq!(edges.len(), 2);
}
#[test]
fn edge_count() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
assert_eq!(g.e().count(), 2);
}
#[test]
fn scalar_next() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let name: Option<Value> = g.v().values("name").next();
assert!(matches!(name, Some(Value::String(_))));
}
#[test]
fn scalar_to_list() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let names: Vec<Value> = g.v().values("name").to_list();
assert_eq!(names.len(), 3);
}
#[test]
fn scalar_sum() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let total = g.v().values("age").sum();
assert_eq!(total, Value::Int(55));
}
#[test]
fn vertex_out_navigation() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let friends: Vec<InMemoryVertex> = g.v().out().to_list();
assert_eq!(friends.len(), 2);
}
#[test]
fn vertex_in_navigation() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let known: Vec<InMemoryVertex> = g.v().in_().to_list();
assert_eq!(known.len(), 2);
}
#[test]
fn vertex_to_edge_transformation() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let edges: Vec<InMemoryEdge> = g.v().out_e().to_list();
assert_eq!(edges.len(), 2);
assert!(edges.iter().all(|e| e.label() == Some("knows".to_string())));
}
#[test]
fn edge_to_vertex_transformation() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let sources: Vec<InMemoryVertex> = g.e().out_v().to_list();
assert_eq!(sources.len(), 2);
let targets: Vec<InMemoryVertex> = g.e().in_v().to_list();
assert_eq!(targets.len(), 2);
}
#[test]
fn vertex_to_scalar_transformation() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let names: Vec<Value> = g.v().values("name").to_list();
assert_eq!(names.len(), 3);
let ids: Vec<Value> = g.v().id().to_list();
assert_eq!(ids.len(), 3);
let labels: Vec<Value> = g.v().label().to_list();
assert_eq!(labels.len(), 3);
assert!(labels
.iter()
.all(|l| *l == Value::String("person".to_string())));
}
#[test]
fn vertex_has_label_filter() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let people: Vec<InMemoryVertex> = g.v().has_label("person").to_list();
assert_eq!(people.len(), 3);
let software: Vec<InMemoryVertex> = g.v().has_label("software").to_list();
assert_eq!(software.len(), 0);
}
#[test]
fn vertex_has_value_filter() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let alice: Vec<InMemoryVertex> = g.v().has_value("name", "Alice").to_list();
assert_eq!(alice.len(), 1);
assert_eq!(
alice[0].property("name"),
Some(Value::String("Alice".to_string()))
);
}
#[test]
fn vertex_limit_filter() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let limited: Vec<InMemoryVertex> = g.v().limit(2).to_list();
assert_eq!(limited.len(), 2);
}
#[test]
fn vertex_dedup_filter() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let unique: Vec<InMemoryVertex> = g.v().out().dedup().to_list();
assert_eq!(unique.len(), 2);
}
#[test]
fn chained_navigation() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let fof: Vec<InMemoryVertex> = g.v().out().out().to_list();
assert_eq!(fof.len(), 1);
assert_eq!(
fof[0].property("name"),
Some(Value::String("Charlie".to_string()))
);
}
#[test]
fn edge_chain_navigation() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let source_names: Vec<Value> = g.e().out_v().values("name").to_list();
assert_eq!(source_names.len(), 2);
}
#[test]
fn escape_hatch_next_value() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let v: Option<Value> = g.v().next_value();
assert!(matches!(v, Some(Value::Vertex(_))));
let e: Option<Value> = g.e().next_value();
assert!(matches!(e, Some(Value::Edge(_))));
}
#[test]
fn escape_hatch_to_value_list() {
let graph = test_graph();
let snapshot = graph.snapshot();
let g = TypedTraversalSource::new(&snapshot, graph.clone());
let values: Vec<Value> = g.v().to_value_list();
assert_eq!(values.len(), 3);
assert!(values.iter().all(|v| matches!(v, Value::Vertex(_))));
}
}