use super::*;
#[test]
fn test_repeat_times_n_hop() {
let (store, _dir) = open_rocks_store();
let mut graph = create_tinkerpop_modern_graph(&store);
let body =
LogicalPlan { steps: vec![LogicalStep::Out(LogicalOutStep { labels: smallvec![], end_vertex_ids: None })] };
let logical_plan = LogicalPlan {
steps: vec![
LogicalStep::V(LogicalVStep { ids: smallvec![1] }),
LogicalStep::Repeat(LogicalRepeatStep { body, until: None, times: Some(2), emit: EmitSpec::Never }),
],
};
let mut builder: PhysicalPlanBuilder = Default::default();
let physical_plan = builder.build(&logical_plan, &graph.schema).unwrap();
let mut results = Vec::new();
while let Ok(Some(t)) = physical_plan.next(&mut graph) {
results.push(t.as_ref().value.clone());
}
assert_eq!(results.len(), 2);
assert!(results.contains(&GValue::Vertex(3)));
assert!(results.contains(&GValue::Vertex(5)));
}
#[test]
fn test_repeat_until_short_circuit() {
let (store, _dir) = open_rocks_store();
let mut graph = create_tinkerpop_modern_graph(&store);
let body =
LogicalPlan { steps: vec![LogicalStep::Out(LogicalOutStep { labels: smallvec![], end_vertex_ids: None })] };
let until_plan = LogicalPlan {
steps: vec![LogicalStep::HasLabel(LogicalHasLabelStep {
pred: PrimitivePredicate::Eq(Primitive::String(SmolStr::new("software"))),
})],
};
let logical_plan = LogicalPlan {
steps: vec![
LogicalStep::V(LogicalVStep { ids: smallvec![1] }),
LogicalStep::Repeat(LogicalRepeatStep {
body,
until: Some(until_plan),
times: None,
emit: EmitSpec::Never,
}),
],
};
let mut builder: PhysicalPlanBuilder = Default::default();
let physical_plan = builder.build(&logical_plan, &graph.schema).unwrap();
let mut results = Vec::new();
while let Ok(Some(t)) = physical_plan.next(&mut graph) {
results.push(t.as_ref().value.clone());
}
results.sort_by_key(|v| if let GValue::Vertex(id) = v { *id } else { 0 });
assert_eq!(results.len(), 3);
assert_eq!(results[0], GValue::Vertex(3));
assert_eq!(results[1], GValue::Vertex(3));
assert_eq!(results[2], GValue::Vertex(5));
}
#[test]
fn test_repeat_emit_all_intermediates() {
let (store, _dir) = open_rocks_store();
let mut graph = create_tinkerpop_modern_graph(&store);
let body =
LogicalPlan { steps: vec![LogicalStep::Out(LogicalOutStep { labels: smallvec![], end_vertex_ids: None })] };
let logical_plan = LogicalPlan {
steps: vec![
LogicalStep::V(LogicalVStep { ids: smallvec![1] }),
LogicalStep::Repeat(LogicalRepeatStep { body, until: None, times: Some(3), emit: EmitSpec::Always }),
],
};
let mut builder: PhysicalPlanBuilder = Default::default();
let physical_plan = builder.build(&logical_plan, &graph.schema).unwrap();
let mut results = Vec::new();
while let Ok(Some(t)) = physical_plan.next(&mut graph) {
results.push(t.as_ref().value.clone());
}
assert_eq!(results.len(), 5);
let mut ids: Vec<i64> = results.iter().map(|v| if let GValue::Vertex(id) = v { *id } else { 0 }).collect();
ids.sort();
assert_eq!(ids, vec![2, 3, 3, 4, 5]);
}
#[test]
fn test_repeat_emit_if() {
let (store, _dir) = open_rocks_store();
let mut graph = create_tinkerpop_modern_graph(&store);
let body =
LogicalPlan { steps: vec![LogicalStep::Out(LogicalOutStep { labels: smallvec![], end_vertex_ids: None })] };
let emit_cond = LogicalPlan {
steps: vec![LogicalStep::HasLabel(LogicalHasLabelStep {
pred: PrimitivePredicate::Eq(Primitive::String(SmolStr::new("person"))),
})],
};
let logical_plan = LogicalPlan {
steps: vec![
LogicalStep::V(LogicalVStep { ids: smallvec![1] }),
LogicalStep::Repeat(LogicalRepeatStep { body, until: None, times: Some(3), emit: EmitSpec::If(emit_cond) }),
],
};
let mut builder: PhysicalPlanBuilder = Default::default();
let physical_plan = builder.build(&logical_plan, &graph.schema).unwrap();
let mut results = Vec::new();
while let Ok(Some(t)) = physical_plan.next(&mut graph) {
results.push(t.as_ref().value.clone());
}
let mut ids: Vec<i64> = results.iter().map(|v| if let GValue::Vertex(id) = v { *id } else { 0 }).collect();
ids.sort();
assert_eq!(ids, vec![2, 4]);
}
#[test]
fn test_repeat_path_tracking() {
let (store, _dir) = open_rocks_store();
let mut graph = create_tinkerpop_modern_graph(&store);
let body =
LogicalPlan { steps: vec![LogicalStep::Out(LogicalOutStep { labels: smallvec![], end_vertex_ids: None })] };
let logical_plan = LogicalPlan {
steps: vec![
LogicalStep::V(LogicalVStep { ids: smallvec![1] }),
LogicalStep::Repeat(LogicalRepeatStep { body, until: None, times: Some(2), emit: EmitSpec::Never }),
LogicalStep::Path(crate::planner::logical_step::PathStep {}),
],
};
let mut builder: PhysicalPlanBuilder = Default::default();
let physical_plan = builder.build(&logical_plan, &graph.schema).unwrap();
let mut results = Vec::new();
while let Ok(Some(t)) = physical_plan.next(&mut graph) {
results.push(t.as_ref().value.clone());
}
assert_eq!(results.len(), 2);
for res in &results {
if let GValue::Path(path) = res {
assert_eq!(path.len(), 3, "each path should have 3 elements");
assert_eq!(path[0].0, GValue::Vertex(1));
} else {
panic!("Expected Path, got {:?}", res);
}
}
}
#[test]
fn test_repeat_cycle_terminates_with_times() {
let (store, _dir) = open_rocks_store();
let mut graph = create_tinkerpop_modern_graph(&store);
let vadas_id = graph.get_vertex(2).unwrap().unwrap();
let marko_id = graph.get_vertex(1).unwrap().unwrap();
let knows_label_id = graph.schema.read().unwrap().edge_label_id("knows").unwrap();
graph
.add_edge(&EdgeKey {
primary_id: vadas_id,
direction: Direction::OUT,
label_id: knows_label_id,
secondary_id: marko_id,
rank: 0,
})
.unwrap();
graph.commit().unwrap();
let body =
LogicalPlan { steps: vec![LogicalStep::Out(LogicalOutStep { labels: smallvec![], end_vertex_ids: None })] };
let logical_plan = LogicalPlan {
steps: vec![
LogicalStep::V(LogicalVStep { ids: smallvec![1] }),
LogicalStep::Repeat(LogicalRepeatStep { body, until: None, times: Some(5), emit: EmitSpec::Never }),
],
};
let mut builder: PhysicalPlanBuilder = Default::default();
let physical_plan = builder.build(&logical_plan, &graph.schema).unwrap();
let mut results = Vec::new();
while let Ok(Some(t)) = physical_plan.next(&mut graph) {
results.push(t.as_ref().value.clone());
}
assert!(!results.is_empty(), "cycle with times(5) must produce results");
}