use crate::graph::GraphStorage;
use crate::traversal::optimizer::plan::Plan;
use crate::traversal::optimizer::rule::OptimizationRule;
use crate::traversal::step::GraphStep;
use crate::traversal::step::flatbarrier_steps::step_barrier::lazy_barrier_step;
#[derive(Default)]
pub(crate) struct LazyBarrierRule;
impl LazyBarrierRule {
pub fn new() -> Self {
Self
}
}
fn yields_vertices<'g, S: GraphStorage + 'g>(step: &GraphStep<'g, S>) -> bool {
matches!(
step,
GraphStep::FlatMap(name, flatmap)
if flatmap.is_adjacency()
&& matches!(
*name,
"out" | "in" | "both" | "out_v" | "in_v" | "both_v" | "other_v"
)
)
}
impl<'g, S: GraphStorage + 'g> OptimizationRule<'g, S> for LazyBarrierRule {
fn name(&self) -> &'static str {
"lazy_barrier"
}
fn apply(&self, steps: &mut Plan<'g, S>) -> bool {
let mut modified = false;
let mut previous: Option<usize> = None;
let mut index = 0;
while index < steps.len() {
match &steps[index] {
GraphStep::Modulator(_, _) => {}
GraphStep::FlatMap(_, next) if next.is_adjacency() => {
if let Some(first) = previous
&& yields_vertices(&steps[first])
{
let next = next.clone_box();
steps.insert_synthetic(index, lazy_barrier_step(next));
modified = true;
index += 1;
}
previous = Some(index);
}
_ => previous = None,
}
index += 1;
}
modified
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::GraphSource;
fn names(steps: &[GraphStep<'_, &crate::graph::TraversalGraph>]) -> Vec<String> {
steps.iter().map(|step| step.get_name()).collect()
}
#[test]
fn inserts_between_adjacent_expansions_only() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let g = g_lock.traversal();
let mut traversal = g.v(None).out(None).out(None).in_(None).values("name");
let modified = LazyBarrierRule::new().apply_to_steps(&mut traversal.steps);
assert!(modified);
assert_eq!(
names(&traversal.steps),
vec![
"v()",
"out()",
"lazy_barrier()",
"out()",
"lazy_barrier()",
"in()",
"values(\"name\")"
]
);
}
#[test]
fn leaves_other_shapes_alone() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let untouched = |mut steps: Vec<GraphStep<'_, &crate::graph::TraversalGraph>>| {
assert!(!LazyBarrierRule::new().apply_to_steps(&mut steps));
};
untouched(g_lock.traversal().v(None).out(None).take_steps());
untouched(g_lock.traversal().v(None).out(None).count().take_steps());
untouched(
g_lock
.traversal()
.v(None)
.out(None)
.as_("a")
.out(None)
.take_steps(),
);
untouched(
g_lock
.traversal()
.v(None)
.out(None)
.values("name")
.out(None)
.take_steps(),
);
untouched(g_lock.traversal().v(None).out_e(None).in_v().take_steps());
untouched(
g_lock
.traversal()
.v(None)
.out(None)
.barrier()
.out(None)
.take_steps(),
);
}
#[test]
fn edge_then_vertex_steps_get_one_barrier_after_the_vertices() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let g = g_lock.traversal();
let mut traversal = g.v(None).out_e(None).in_v().out(None);
assert!(LazyBarrierRule::new().apply_to_steps(&mut traversal.steps));
assert_eq!(
names(&traversal.steps),
vec!["v()", "out_e()", "in_v()", "lazy_barrier()", "out()"]
);
}
}