use crate::traversal::step::{execute_traversal_from, Step};
use crate::traversal::{ExecutionContext, Traversal, Traverser};
use crate::value::Value;
use std::collections::VecDeque;
#[derive(Clone, Default)]
pub struct RepeatConfig {
pub times: Option<usize>,
pub until: Option<Traversal<Value, Value>>,
pub emit: bool,
pub emit_if: Option<Traversal<Value, Value>>,
pub emit_first: bool,
}
impl RepeatConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_times(mut self, n: usize) -> Self {
self.times = Some(n);
self
}
pub fn with_until(mut self, condition: Traversal<Value, Value>) -> Self {
self.until = Some(condition);
self
}
pub fn with_emit(mut self) -> Self {
self.emit = true;
self
}
pub fn with_emit_if(mut self, condition: Traversal<Value, Value>) -> Self {
self.emit = true;
self.emit_if = Some(condition);
self
}
pub fn with_emit_first(mut self) -> Self {
self.emit_first = true;
self
}
}
impl std::fmt::Debug for RepeatConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RepeatConfig")
.field("times", &self.times)
.field("until", &self.until.is_some())
.field("emit", &self.emit)
.field("emit_if", &self.emit_if.is_some())
.field("emit_first", &self.emit_first)
.finish()
}
}
#[derive(Clone)]
pub struct RepeatStep {
sub: Traversal<Value, Value>,
config: RepeatConfig,
}
impl RepeatStep {
pub fn new(sub: Traversal<Value, Value>) -> Self {
Self {
sub,
config: RepeatConfig::default(),
}
}
pub fn with_config(sub: Traversal<Value, Value>, config: RepeatConfig) -> Self {
Self { sub, config }
}
#[allow(dead_code)] pub(crate) fn satisfies_until(&self, ctx: &ExecutionContext, traverser: &Traverser) -> bool {
match &self.config.until {
Some(until_trav) => {
let sub_input = Box::new(std::iter::once(traverser.clone()));
let mut results = execute_traversal_from(ctx, until_trav, sub_input);
results.next().is_some()
}
None => false,
}
}
#[allow(dead_code)] pub(crate) fn should_emit(&self, ctx: &ExecutionContext, traverser: &Traverser) -> bool {
if !self.config.emit {
return false;
}
match &self.config.emit_if {
Some(emit_trav) => {
let sub_input = Box::new(std::iter::once(traverser.clone()));
let mut results = execute_traversal_from(ctx, emit_trav, sub_input);
results.next().is_some()
}
None => true, }
}
pub fn sub(&self) -> &Traversal<Value, Value> {
&self.sub
}
pub fn config(&self) -> &RepeatConfig {
&self.config
}
}
impl std::fmt::Debug for RepeatStep {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RepeatStep")
.field("sub_steps", &self.sub.step_count())
.field("config", &self.config)
.finish()
}
}
impl Step for RepeatStep {
type Iter<'a>
= RepeatIterator<'a>
where
Self: 'a;
fn apply<'a>(
&'a self,
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
) -> Self::Iter<'a> {
RepeatIterator::new(
ctx,
input,
self.sub.clone(),
self.config.clone(),
self.clone(),
)
}
fn name(&self) -> &'static str {
"repeat"
}
fn is_barrier(&self) -> bool {
true
}
fn category(&self) -> crate::traversal::explain::StepCategory {
crate::traversal::explain::StepCategory::Branch
}
fn describe(&self) -> Option<String> {
use crate::traversal::explain::format_traversal_steps;
let mut lines = Vec::new();
lines.push(format!(
"body: {}",
format_traversal_steps(self.sub.steps())
));
if let Some(ref until_trav) = self.config.until {
lines.push(format!(
"until: {}",
format_traversal_steps(until_trav.steps())
));
}
if let Some(n) = self.config.times {
lines.push(format!("times: {n}"));
}
if self.config.emit {
lines.push("emit: true".to_string());
}
Some(lines.join("\n"))
}
fn apply_streaming(
&self,
_ctx: crate::traversal::context::StreamingContext,
input: Traverser,
) -> Box<dyn Iterator<Item = Traverser> + Send + 'static> {
Box::new(std::iter::once(input))
}
}
pub struct RepeatIterator<'a> {
ctx: &'a ExecutionContext<'a>,
frontier: VecDeque<(Traverser, usize)>,
sub: Traversal<Value, Value>,
config: RepeatConfig,
step: RepeatStep,
emit_buffer: VecDeque<Traverser>,
initialized: bool,
input: Option<Box<dyn Iterator<Item = Traverser> + 'a>>,
}
impl<'a> RepeatIterator<'a> {
fn new(
ctx: &'a ExecutionContext<'a>,
input: Box<dyn Iterator<Item = Traverser> + 'a>,
sub: Traversal<Value, Value>,
config: RepeatConfig,
step: RepeatStep,
) -> Self {
Self {
ctx,
frontier: VecDeque::new(),
sub,
config,
step,
emit_buffer: VecDeque::new(),
initialized: false,
input: Some(input),
}
}
fn process_frontier(&mut self) {
let current_frontier: Vec<_> = self.frontier.drain(..).collect();
for (traverser, loop_count) in current_frontier {
if let Some(max_times) = self.config.times {
if loop_count >= max_times {
if !self.config.emit {
self.emit_buffer.push_back(traverser);
}
continue;
}
}
if self.step.satisfies_until(self.ctx, &traverser) {
self.emit_buffer.push_back(traverser);
continue;
}
let sub_input = Box::new(std::iter::once(traverser.clone()));
let results: Vec<_> = execute_traversal_from(self.ctx, &self.sub, sub_input).collect();
if results.is_empty() {
if !self.config.emit {
self.emit_buffer.push_back(traverser);
}
} else {
for result in results {
let mut new_traverser = result;
new_traverser.inc_loops();
if self.step.should_emit(self.ctx, &new_traverser) {
self.emit_buffer.push_back(new_traverser.clone());
}
self.frontier.push_back((new_traverser, loop_count + 1));
}
}
}
}
}
impl<'a> Iterator for RepeatIterator<'a> {
type Item = Traverser;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(t) = self.emit_buffer.pop_front() {
return Some(t);
}
if !self.initialized {
self.initialized = true;
if let Some(input) = self.input.take() {
for t in input {
if self.config.emit_first && self.config.emit {
self.emit_buffer.push_back(t.clone());
}
self.frontier.push_back((t, 0));
}
}
continue;
}
if !self.frontier.is_empty() {
self.process_frontier();
continue;
}
return None;
}
}
}
use crate::traversal::source::BoundTraversal;
pub struct RepeatTraversal<'g, In> {
snapshot: &'g dyn crate::traversal::SnapshotLike,
base: Traversal<In, Value>,
sub: Traversal<Value, Value>,
config: RepeatConfig,
track_paths: bool,
}
impl<'g, In> RepeatTraversal<'g, In> {
pub(crate) fn new(
snapshot: &'g dyn crate::traversal::SnapshotLike,
base: Traversal<In, Value>,
sub: Traversal<Value, Value>,
track_paths: bool,
) -> Self {
Self {
snapshot,
base,
sub,
config: RepeatConfig::default(),
track_paths,
}
}
pub fn times(mut self, n: usize) -> Self {
self.config.times = Some(n);
self
}
pub fn until(mut self, condition: Traversal<Value, Value>) -> Self {
self.config.until = Some(condition);
self
}
pub fn emit(mut self) -> Self {
self.config.emit = true;
self
}
pub fn emit_if(mut self, condition: Traversal<Value, Value>) -> Self {
self.config.emit = true;
self.config.emit_if = Some(condition);
self
}
pub fn emit_first(mut self) -> Self {
self.config.emit_first = true;
self
}
fn finalize(self) -> BoundTraversal<'g, In, Value> {
let repeat_step = RepeatStep::with_config(self.sub, self.config);
let mut bound = BoundTraversal::new(self.snapshot, self.base.add_step(repeat_step));
if self.track_paths {
bound = bound.with_path();
}
bound
}
}
impl<'g, In> RepeatTraversal<'g, In> {
pub fn to_list(self) -> Vec<Value> {
self.finalize().to_list()
}
pub fn to_set(self) -> std::collections::HashSet<Value> {
self.finalize().to_set()
}
pub fn next(self) -> Option<Value> {
self.finalize().next()
}
pub fn has_next(self) -> bool {
self.finalize().has_next()
}
pub fn count(self) -> u64 {
self.finalize().count()
}
pub fn take(self, n: usize) -> Vec<Value> {
self.finalize().take(n)
}
pub fn iterate(self) {
self.finalize().iterate()
}
}
impl<'g, In> RepeatTraversal<'g, In> {
pub fn has_label(self, label: impl Into<String>) -> BoundTraversal<'g, In, Value> {
self.finalize().has_label(label)
}
pub fn has_label_any<I, S>(self, labels: I) -> BoundTraversal<'g, In, Value>
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.finalize().has_label_any(labels)
}
pub fn has(self, key: impl Into<String>) -> BoundTraversal<'g, In, Value> {
self.finalize().has(key)
}
pub fn has_value(
self,
key: impl Into<String>,
value: impl Into<Value>,
) -> BoundTraversal<'g, In, Value> {
self.finalize().has_value(key, value)
}
pub fn dedup(self) -> BoundTraversal<'g, In, Value> {
self.finalize().dedup()
}
pub fn limit(self, count: usize) -> BoundTraversal<'g, In, Value> {
self.finalize().limit(count)
}
pub fn skip(self, count: usize) -> BoundTraversal<'g, In, Value> {
self.finalize().skip(count)
}
pub fn values(self, key: impl Into<String>) -> BoundTraversal<'g, In, Value> {
self.finalize().values(key)
}
pub fn id(self) -> BoundTraversal<'g, In, Value> {
self.finalize().id()
}
pub fn label(self) -> BoundTraversal<'g, In, Value> {
self.finalize().label()
}
pub fn out(self) -> BoundTraversal<'g, In, Value> {
self.finalize().out()
}
pub fn out_labels(self, labels: &[&str]) -> BoundTraversal<'g, In, Value> {
self.finalize().out_labels(labels)
}
pub fn in_(self) -> BoundTraversal<'g, In, Value> {
self.finalize().in_()
}
pub fn in_labels(self, labels: &[&str]) -> BoundTraversal<'g, In, Value> {
self.finalize().in_labels(labels)
}
pub fn both(self) -> BoundTraversal<'g, In, Value> {
self.finalize().both()
}
pub fn both_labels(self, labels: &[&str]) -> BoundTraversal<'g, In, Value> {
self.finalize().both_labels(labels)
}
pub fn out_e(self) -> BoundTraversal<'g, In, Value> {
self.finalize().out_e()
}
pub fn in_e(self) -> BoundTraversal<'g, In, Value> {
self.finalize().in_e()
}
pub fn both_e(self) -> BoundTraversal<'g, In, Value> {
self.finalize().both_e()
}
pub fn path(self) -> BoundTraversal<'g, In, Value> {
self.finalize().path()
}
pub fn as_(self, label: &str) -> BoundTraversal<'g, In, Value> {
self.finalize().as_(label)
}
pub fn select(self, labels: &[&str]) -> BoundTraversal<'g, In, Value> {
self.finalize().select(labels)
}
pub fn select_one(self, label: &str) -> BoundTraversal<'g, In, Value> {
self.finalize().select_one(label)
}
pub fn where_(self, sub: Traversal<Value, Value>) -> BoundTraversal<'g, In, Value> {
self.finalize().where_(sub)
}
pub fn not(self, sub: Traversal<Value, Value>) -> BoundTraversal<'g, In, Value> {
self.finalize().not(sub)
}
pub fn union(self, branches: Vec<Traversal<Value, Value>>) -> BoundTraversal<'g, In, Value> {
self.finalize().union(branches)
}
pub fn coalesce(self, branches: Vec<Traversal<Value, Value>>) -> BoundTraversal<'g, In, Value> {
self.finalize().coalesce(branches)
}
pub fn choose(
self,
condition: Traversal<Value, Value>,
if_true: Traversal<Value, Value>,
if_false: Traversal<Value, Value>,
) -> BoundTraversal<'g, In, Value> {
self.finalize().choose(condition, if_true, if_false)
}
pub fn optional(self, sub: Traversal<Value, Value>) -> BoundTraversal<'g, In, Value> {
self.finalize().optional(sub)
}
pub fn local(self, sub: Traversal<Value, Value>) -> BoundTraversal<'g, In, Value> {
self.finalize().local(sub)
}
pub fn identity(self) -> BoundTraversal<'g, In, Value> {
self.finalize()
}
pub fn loops(self) -> BoundTraversal<'g, In, Value> {
self.finalize().loops()
}
}
impl<'g, In> std::fmt::Debug for RepeatTraversal<'g, In> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RepeatTraversal")
.field("base_steps", &self.base.step_count())
.field("sub_steps", &self.sub.step_count())
.field("config", &self.config)
.field("track_paths", &self.track_paths)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::Graph;
use crate::traversal::filter::HasLabelStep;
use crate::traversal::step::{DynStep, IdentityStep};
use crate::traversal::SnapshotLike;
use crate::value::VertexId;
use std::collections::HashMap;
fn create_test_graph() -> Graph {
let graph = Graph::new();
let v1 = graph.add_vertex("person", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("Alice".to_string()));
props
});
let v2 = graph.add_vertex("person", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("Bob".to_string()));
props
});
let v3 = graph.add_vertex("company", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("TechCorp".to_string()));
props
});
graph.add_edge(v1, v2, "knows", HashMap::new()).unwrap();
graph.add_edge(v2, v3, "works_at", HashMap::new()).unwrap();
graph
}
#[test]
fn repeat_config_default_has_none_values() {
let config = RepeatConfig::default();
assert_eq!(config.times, None);
assert!(config.until.is_none());
assert!(!config.emit);
assert!(config.emit_if.is_none());
assert!(!config.emit_first);
}
#[test]
fn repeat_config_new_equals_default() {
let new_config = RepeatConfig::new();
let default_config = RepeatConfig::default();
assert_eq!(new_config.times, default_config.times);
assert_eq!(new_config.emit, default_config.emit);
assert_eq!(new_config.emit_first, default_config.emit_first);
}
#[test]
fn repeat_config_with_times() {
let config = RepeatConfig::new().with_times(5);
assert_eq!(config.times, Some(5));
assert!(!config.emit);
assert!(config.until.is_none());
}
#[test]
fn repeat_config_with_times_zero() {
let config = RepeatConfig::new().with_times(0);
assert_eq!(config.times, Some(0));
}
#[test]
fn repeat_config_with_until() {
let condition: Traversal<Value, Value> =
Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_until(condition);
assert!(config.until.is_some());
assert_eq!(config.times, None);
}
#[test]
fn repeat_config_with_emit() {
let config = RepeatConfig::new().with_emit();
assert!(config.emit);
assert!(config.emit_if.is_none());
assert!(!config.emit_first);
}
#[test]
fn repeat_config_with_emit_if() {
let condition: Traversal<Value, Value> =
Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_emit_if(condition);
assert!(config.emit); assert!(config.emit_if.is_some());
}
#[test]
fn repeat_config_with_emit_first() {
let config = RepeatConfig::new().with_emit().with_emit_first();
assert!(config.emit);
assert!(config.emit_first);
}
#[test]
fn repeat_config_builder_chain() {
let condition: Traversal<Value, Value> =
Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new()
.with_times(3)
.with_until(condition)
.with_emit()
.with_emit_first();
assert_eq!(config.times, Some(3));
assert!(config.until.is_some());
assert!(config.emit);
assert!(config.emit_first);
}
#[test]
fn repeat_config_is_clone() {
let config = RepeatConfig::new().with_times(2).with_emit();
let cloned = config.clone();
assert_eq!(cloned.times, Some(2));
assert!(cloned.emit);
}
#[test]
fn repeat_config_debug_format() {
let config = RepeatConfig::new().with_times(3).with_emit();
let debug_str = format!("{:?}", config);
assert!(debug_str.contains("RepeatConfig"));
assert!(debug_str.contains("times"));
assert!(debug_str.contains("Some(3)"));
assert!(debug_str.contains("emit"));
assert!(debug_str.contains("true"));
}
#[test]
fn repeat_config_times_can_be_accessed() {
let config = RepeatConfig::new().with_times(10);
if let Some(n) = config.times {
assert_eq!(n, 10);
} else {
panic!("Expected times to be Some(10)");
}
}
#[test]
fn repeat_config_all_fields_accessible() {
let until_cond: Traversal<Value, Value> =
Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let emit_cond: Traversal<Value, Value> =
Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig {
times: Some(5),
until: Some(until_cond),
emit: true,
emit_if: Some(emit_cond),
emit_first: true,
};
assert_eq!(config.times, Some(5));
assert!(config.until.is_some());
assert!(config.emit);
assert!(config.emit_if.is_some());
assert!(config.emit_first);
}
mod repeat_step_tests {
use super::*;
#[test]
fn repeat_step_new_creates_with_default_config() {
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let step = RepeatStep::new(sub);
assert_eq!(step.config().times, None);
assert!(!step.config().emit);
assert!(!step.config().emit_first);
assert!(step.config().until.is_none());
assert!(step.config().emit_if.is_none());
}
#[test]
fn repeat_step_with_config_stores_config() {
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_times(5).with_emit();
let step = RepeatStep::with_config(sub, config);
assert_eq!(step.config().times, Some(5));
assert!(step.config().emit);
}
#[test]
fn repeat_step_sub_accessor() {
let sub: Traversal<Value, Value> =
Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let sub: Traversal<Value, Value> = sub.add_step(IdentityStep::new());
let step = RepeatStep::new(sub);
assert_eq!(step.sub().step_count(), 2);
}
#[test]
fn repeat_step_config_accessor() {
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_times(3);
let step = RepeatStep::with_config(sub, config);
assert_eq!(step.config().times, Some(3));
}
#[test]
fn repeat_step_is_clone() {
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_times(2).with_emit();
let step = RepeatStep::with_config(sub, config);
let cloned = step.clone();
assert_eq!(cloned.config().times, Some(2));
assert!(cloned.config().emit);
assert_eq!(cloned.sub().step_count(), 1);
}
#[test]
fn repeat_step_clone_box_returns_dyn_step() {
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let step = RepeatStep::new(sub);
let boxed = DynStep::clone_box(&step);
assert_eq!(boxed.dyn_name(), "repeat");
}
#[test]
fn repeat_step_name_is_repeat() {
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let step = RepeatStep::new(sub);
assert_eq!(step.name(), "repeat");
}
#[test]
fn repeat_step_debug_format() {
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_times(3);
let step = RepeatStep::with_config(sub, config);
let debug_str = format!("{:?}", step);
assert!(debug_str.contains("RepeatStep"));
assert!(debug_str.contains("sub_steps"));
assert!(debug_str.contains("config"));
}
#[test]
fn repeat_step_can_be_boxed_as_dyn_step() {
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let step = RepeatStep::new(sub);
let boxed: Box<dyn DynStep> = Box::new(step);
assert_eq!(boxed.dyn_name(), "repeat");
}
#[test]
fn repeat_step_can_be_stored_in_vec() {
let sub1 = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let sub2 = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let steps: Vec<Box<dyn DynStep>> = vec![
Box::new(RepeatStep::new(sub1)),
Box::new(RepeatStep::new(sub2)),
];
assert_eq!(steps.len(), 2);
assert_eq!(steps[0].dyn_name(), "repeat");
assert_eq!(steps[1].dyn_name(), "repeat");
}
#[test]
fn repeat_step_apply_with_times_terminates() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_times(2);
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))];
let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 1);
assert_eq!(output[0].value, Value::Vertex(VertexId(0)));
assert_eq!(output[0].loops, 2);
}
#[test]
fn repeat_step_apply_with_times_zero_emits_immediately() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_times(0);
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))];
let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 1);
assert_eq!(output[0].loops, 0);
}
}
mod satisfies_until_tests {
use super::*;
#[test]
fn satisfies_until_returns_false_when_no_until_set() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let step = RepeatStep::new(sub);
let traverser = Traverser::from_vertex(VertexId(0));
assert!(!step.satisfies_until(&ctx, &traverser));
}
#[test]
fn satisfies_until_returns_true_when_condition_produces_results() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let until_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("person"));
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_until(until_cond);
let step = RepeatStep::with_config(sub, config);
let traverser = Traverser::from_vertex(VertexId(0));
assert!(step.satisfies_until(&ctx, &traverser));
}
#[test]
fn satisfies_until_returns_false_when_condition_produces_no_results() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let until_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("company"));
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_until(until_cond);
let step = RepeatStep::with_config(sub, config);
let traverser = Traverser::from_vertex(VertexId(0));
assert!(!step.satisfies_until(&ctx, &traverser));
}
#[test]
fn satisfies_until_works_with_company_vertex() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let until_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("company"));
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_until(until_cond);
let step = RepeatStep::with_config(sub, config);
let traverser = Traverser::from_vertex(VertexId(2));
assert!(step.satisfies_until(&ctx, &traverser));
}
}
mod should_emit_tests {
use super::*;
#[test]
fn should_emit_returns_false_when_emit_not_set() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let step = RepeatStep::new(sub);
let traverser = Traverser::from_vertex(VertexId(0));
assert!(!step.should_emit(&ctx, &traverser));
}
#[test]
fn should_emit_returns_true_when_emit_set_without_condition() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_emit();
let step = RepeatStep::with_config(sub, config);
let traverser = Traverser::from_vertex(VertexId(0));
assert!(step.should_emit(&ctx, &traverser));
}
#[test]
fn should_emit_returns_true_when_emit_if_condition_produces_results() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let emit_if_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("person"));
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_emit_if(emit_if_cond);
let step = RepeatStep::with_config(sub, config);
let traverser = Traverser::from_vertex(VertexId(0));
assert!(step.should_emit(&ctx, &traverser));
}
#[test]
fn should_emit_returns_false_when_emit_if_condition_produces_no_results() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let emit_if_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("company"));
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_emit_if(emit_if_cond);
let step = RepeatStep::with_config(sub, config);
let traverser = Traverser::from_vertex(VertexId(0));
assert!(!step.should_emit(&ctx, &traverser));
}
#[test]
fn should_emit_works_with_emit_and_emit_if_together() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let emit_if_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("company"));
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_emit_if(emit_if_cond);
let step = RepeatStep::with_config(sub, config);
let traverser = Traverser::from_vertex(VertexId(2));
assert!(step.should_emit(&ctx, &traverser));
let traverser = Traverser::from_vertex(VertexId(0));
assert!(!step.should_emit(&ctx, &traverser));
}
#[test]
fn should_emit_emits_all_when_emit_true_and_no_emit_if() {
let graph = create_test_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(IdentityStep::new());
let config = RepeatConfig::new().with_emit();
let step = RepeatStep::with_config(sub, config);
for id in [VertexId(0), VertexId(1), VertexId(2)] {
let traverser = Traverser::from_vertex(id);
assert!(step.should_emit(&ctx, &traverser));
}
}
}
mod repeat_iterator_tests {
use super::*;
use crate::traversal::navigation::OutStep;
fn create_chain_graph() -> Graph {
let graph = Graph::new();
let v0 = graph.add_vertex("person", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("Alice".to_string()));
props
});
let v1 = graph.add_vertex("person", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("Bob".to_string()));
props
});
let v2 = graph.add_vertex("company", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("TechCorp".to_string()));
props
});
graph.add_edge(v0, v1, "knows", HashMap::new()).unwrap();
graph.add_edge(v1, v2, "works_at", HashMap::new()).unwrap();
graph
}
#[test]
fn repeat_out_times_1_traverses_one_hop() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_times(1);
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))]; let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 1);
assert_eq!(output[0].value, Value::Vertex(VertexId(1))); assert_eq!(output[0].loops, 1);
}
#[test]
fn repeat_out_times_2_traverses_two_hops() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_times(2);
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))]; let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 1);
assert_eq!(output[0].value, Value::Vertex(VertexId(2))); assert_eq!(output[0].loops, 2);
}
#[test]
fn repeat_out_times_3_exhausts_at_leaf() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_times(3);
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))]; let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 1);
assert_eq!(output[0].value, Value::Vertex(VertexId(2))); assert_eq!(output[0].loops, 2); }
#[test]
fn repeat_out_until_company_terminates_correctly() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let until_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("company"));
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_until(until_cond);
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))]; let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 1);
assert_eq!(output[0].value, Value::Vertex(VertexId(2))); }
#[test]
fn repeat_out_emit_includes_intermediates() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_times(2).with_emit();
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))]; let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 2);
assert_eq!(output[0].value, Value::Vertex(VertexId(1))); assert_eq!(output[0].loops, 1);
assert_eq!(output[1].value, Value::Vertex(VertexId(2))); assert_eq!(output[1].loops, 2);
}
#[test]
fn repeat_out_emit_first_includes_starting_vertex() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new()
.with_times(1)
.with_emit()
.with_emit_first();
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))]; let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 2);
assert_eq!(output[0].value, Value::Vertex(VertexId(0))); assert_eq!(output[0].loops, 0);
assert_eq!(output[1].value, Value::Vertex(VertexId(1))); assert_eq!(output[1].loops, 1);
}
#[test]
fn repeat_out_emit_if_only_emits_matching() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let emit_if_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("company"));
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_times(2).with_emit_if(emit_if_cond);
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(0))]; let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 1);
assert_eq!(output[0].value, Value::Vertex(VertexId(2))); }
#[test]
fn repeat_handles_multiple_input_traversers() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_times(1);
let step = RepeatStep::with_config(sub, config);
let input = vec![
Traverser::from_vertex(VertexId(0)), Traverser::from_vertex(VertexId(1)), ];
let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 2);
let values: Vec<_> = output.iter().map(|t| &t.value).collect();
assert!(values.contains(&&Value::Vertex(VertexId(1)))); assert!(values.contains(&&Value::Vertex(VertexId(2)))); }
#[test]
fn repeat_handles_empty_input() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_times(2);
let step = RepeatStep::with_config(sub, config);
let input: Vec<Traverser> = vec![];
let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert!(output.is_empty());
}
#[test]
fn repeat_from_leaf_with_no_outgoing_edges() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let ctx = ExecutionContext::new(snapshot.storage(), snapshot.interner());
let sub = Traversal::<Value, Value>::new().add_step(OutStep::new());
let config = RepeatConfig::new().with_times(2);
let step = RepeatStep::with_config(sub, config);
let input = vec![Traverser::from_vertex(VertexId(2))]; let output: Vec<Traverser> = step.apply(&ctx, Box::new(input.into_iter())).collect();
assert_eq!(output.len(), 1);
assert_eq!(output[0].value, Value::Vertex(VertexId(2)));
assert_eq!(output[0].loops, 0); }
}
mod repeat_traversal_builder_tests {
use super::*;
use crate::traversal::TraversalSource;
fn create_chain_graph() -> Graph {
let graph = Graph::new();
let v0 = graph.add_vertex("person", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("Alice".to_string()));
props
});
let v1 = graph.add_vertex("person", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("Bob".to_string()));
props
});
let v2 = graph.add_vertex("company", {
let mut props = HashMap::new();
props.insert("name".to_string(), Value::String("TechCorp".to_string()));
props
});
graph.add_edge(v0, v1, "knows", HashMap::new()).unwrap();
graph.add_edge(v1, v2, "works_at", HashMap::new()).unwrap();
graph
}
#[test]
fn repeat_traversal_builder_times_configures_correctly() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let builder = builder.times(2);
assert_eq!(builder.config.times, Some(2));
}
#[test]
fn repeat_traversal_builder_emit_configures_correctly() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let builder = builder.emit();
assert!(builder.config.emit);
assert!(!builder.config.emit_first);
}
#[test]
fn repeat_traversal_builder_emit_first_configures_correctly() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let builder = builder.emit().emit_first();
assert!(builder.config.emit);
assert!(builder.config.emit_first);
}
#[test]
fn repeat_traversal_builder_until_configures_correctly() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let until_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("company"));
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let builder = builder.until(until_cond);
assert!(builder.config.until.is_some());
}
#[test]
fn repeat_traversal_builder_emit_if_configures_correctly() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let emit_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("person"));
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let builder = builder.emit_if(emit_cond);
assert!(builder.config.emit);
assert!(builder.config.emit_if.is_some());
}
#[test]
fn repeat_traversal_builder_chained_config() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let builder = builder.times(3).emit().emit_first();
assert_eq!(builder.config.times, Some(3));
assert!(builder.config.emit);
assert!(builder.config.emit_first);
}
#[test]
fn repeat_traversal_builder_to_list_executes() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(1))); }
#[test]
fn repeat_traversal_builder_count_executes() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let count = builder.times(2).count();
assert_eq!(count, 1);
}
#[test]
fn repeat_traversal_builder_next_executes() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let result = builder.times(1).next();
assert!(result.is_some());
assert_eq!(result.unwrap(), Value::Vertex(VertexId(1))); }
#[test]
fn repeat_traversal_builder_continuation_has_label() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(2).has_label("company").to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(2)));
}
#[test]
fn repeat_traversal_builder_continuation_dedup() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(2).dedup().to_list();
assert_eq!(results.len(), 1);
}
#[test]
fn repeat_traversal_builder_debug_format() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false)
.times(2)
.emit();
let debug_str = format!("{:?}", builder);
assert!(debug_str.contains("RepeatTraversal"));
assert!(debug_str.contains("base_steps"));
assert!(debug_str.contains("sub_steps"));
assert!(debug_str.contains("config"));
}
#[test]
fn repeat_traversal_builder_to_set_executes() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(2).emit().to_set();
assert_eq!(results.len(), 2);
assert!(results.contains(&Value::Vertex(VertexId(1))));
assert!(results.contains(&Value::Vertex(VertexId(2))));
}
#[test]
fn repeat_traversal_builder_has_next_returns_true() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
assert!(builder.times(1).has_next());
}
#[test]
fn repeat_traversal_builder_has_next_returns_false_for_empty() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(2)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let result = builder.times(1).has_label("nonexistent").has_next();
assert!(!result);
}
#[test]
fn repeat_traversal_builder_take_executes() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(2).emit().take(1);
assert_eq!(results.len(), 1);
}
#[test]
fn repeat_traversal_builder_iterate_consumes() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
builder.times(2).iterate();
}
#[test]
fn repeat_traversal_builder_continuation_has_label_any() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder
.times(2)
.emit()
.has_label_any(vec!["person", "company"])
.to_list();
assert_eq!(results.len(), 2);
}
#[test]
fn repeat_traversal_builder_continuation_has() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).has("name").to_list();
assert_eq!(results.len(), 1); }
#[test]
fn repeat_traversal_builder_continuation_has_value() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).has_value("name", "Bob").to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(1)));
}
#[test]
fn repeat_traversal_builder_continuation_limit() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(2).emit().limit(1).to_list();
assert_eq!(results.len(), 1);
}
#[test]
fn repeat_traversal_builder_continuation_skip() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(2).emit().skip(1).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(2))); }
#[test]
fn repeat_traversal_builder_continuation_id() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).id().to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Int(1)); }
#[test]
fn repeat_traversal_builder_continuation_label() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(2).label().to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::String("company".to_string())); }
#[test]
fn repeat_traversal_builder_continuation_out() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).out().to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(2))); }
#[test]
fn repeat_traversal_builder_continuation_out_labels() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).out_labels(&["works_at"]).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(2)));
}
#[test]
fn repeat_traversal_builder_continuation_in() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(2)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::InStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).in_().to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(0))); }
#[test]
fn repeat_traversal_builder_continuation_in_labels() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(2)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::InStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).in_labels(&["knows"]).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(0)));
}
#[test]
fn repeat_traversal_builder_continuation_both() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(1)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).both().to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(1))); }
#[test]
fn repeat_traversal_builder_continuation_both_labels() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(1)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).both_labels(&["works_at"]).to_list();
assert_eq!(results.len(), 1);
}
#[test]
fn repeat_traversal_builder_continuation_out_e() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).out_e().to_list();
assert_eq!(results.len(), 1);
}
#[test]
fn repeat_traversal_builder_continuation_in_e() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(2)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::InStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).in_e().to_list();
assert_eq!(results.len(), 1);
}
#[test]
fn repeat_traversal_builder_continuation_both_e() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(1)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).both_e().to_list();
assert_eq!(results.len(), 1);
}
#[test]
fn repeat_traversal_builder_continuation_path() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, true);
let results = builder.times(2).path().to_list();
assert_eq!(results.len(), 1);
if let Value::List(path) = &results[0] {
assert!(path.len() >= 2); } else {
panic!("Expected path to be a List");
}
}
#[test]
fn repeat_traversal_builder_continuation_as_and_select() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, true);
let results = builder.times(1).as_("end").select(&["end"]).to_list();
assert_eq!(results.len(), 1);
}
#[test]
fn repeat_traversal_builder_continuation_select_one() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, true);
let results = builder
.times(1)
.as_("result")
.select_one("result")
.to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(1))); }
#[test]
fn repeat_traversal_builder_continuation_where() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let where_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("person"));
let results = builder.times(2).emit().where_(where_cond).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(1)));
}
#[test]
fn repeat_traversal_builder_continuation_not() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let not_cond =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("person"));
let results = builder.times(2).emit().not(not_cond).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(2)));
}
#[test]
fn repeat_traversal_builder_continuation_union() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let id_branch = Traversal::<Value, Value>::new()
.add_step(crate::traversal::transform::metadata::IdStep::new());
let label_branch = Traversal::<Value, Value>::new()
.add_step(crate::traversal::transform::metadata::LabelStep::new());
let results = builder
.times(1)
.union(vec![id_branch, label_branch])
.to_list();
assert_eq!(results.len(), 2); }
#[test]
fn repeat_traversal_builder_continuation_coalesce() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let first_branch = Traversal::<Value, Value>::new().add_step(
crate::traversal::transform::values::ValuesStep::new("nonexistent"),
);
let fallback_branch = Traversal::<Value, Value>::new()
.add_step(crate::traversal::transform::values::ValuesStep::new("name"));
let results = builder
.times(1)
.coalesce(vec![first_branch, fallback_branch])
.to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::String("Bob".to_string()));
}
#[test]
fn repeat_traversal_builder_continuation_choose() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let condition =
Traversal::<Value, Value>::new().add_step(HasLabelStep::single("person"));
let if_true = Traversal::<Value, Value>::new()
.add_step(crate::traversal::transform::metadata::IdStep::new());
let if_false = Traversal::<Value, Value>::new()
.add_step(crate::traversal::transform::metadata::LabelStep::new());
let results = builder
.times(1)
.choose(condition, if_true, if_false)
.to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Int(1));
}
#[test]
fn repeat_traversal_builder_continuation_optional() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let optional_sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let results = builder.times(1).optional(optional_sub).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(2))); }
#[test]
fn repeat_traversal_builder_continuation_local() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let local_sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::step::IdentityStep::new());
let results = builder.times(1).local(local_sub).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(1))); }
#[test]
fn repeat_traversal_builder_continuation_identity() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).identity().to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::Vertex(VertexId(1))); }
#[test]
fn repeat_traversal_builder_continuation_loops() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(2).emit().loops().to_list();
assert_eq!(results.len(), 2);
assert!(results.contains(&Value::Int(1)));
assert!(results.contains(&Value::Int(2)));
}
#[test]
fn repeat_traversal_builder_continuation_values() {
let graph = create_chain_graph();
let snapshot = graph.snapshot();
let base: Traversal<(), Value> =
Traversal::with_source(TraversalSource::Vertices(vec![VertexId(0)]));
let sub = Traversal::<Value, Value>::new()
.add_step(crate::traversal::navigation::OutStep::new());
let builder = RepeatTraversal::new(snapshot.as_dyn(), base, sub, false);
let results = builder.times(1).values("name").to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0], Value::String("Bob".to_string()));
}
}
}