use marsdb_graph::Direction;
use crate::ast::{CompareOp, Expr, NodePattern, Pattern, PropAccess, RelDirection};
use crate::ir::LogicalPlan;
struct VarNamer {
next: usize,
}
impl VarNamer {
fn new() -> Self {
Self { next: 0 }
}
fn name(&mut self, given: &Option<String>) -> String {
match given {
Some(v) => v.clone(),
None => {
let n = format!("__anon{}", self.next);
self.next += 1;
n
}
}
}
}
pub fn build_match_plan(pattern: &Pattern, where_clause: &Option<Expr>) -> LogicalPlan {
let mut namer = VarNamer::new();
let start_var = namer.name(&pattern.start.var);
let mut plan = scan_for(&start_var, &pattern.start);
let mut from_var = start_var;
for (rel, node) in &pattern.hops {
let to_var = namer.name(&node.var);
let direction = match rel.direction {
RelDirection::Right => Direction::Out,
RelDirection::Left => Direction::In,
};
plan = LogicalPlan::Expand {
input: Box::new(plan),
from_var: from_var.clone(),
to_var: to_var.clone(),
rel_var: rel.var.clone(),
rel_label: rel.rel_type.clone(),
direction,
};
plan = wrap_node_filters(plan, &to_var, node);
from_var = to_var;
}
if let Some(expr) = where_clause {
plan = LogicalPlan::Filter {
input: Box::new(plan),
predicate: expr.clone(),
};
}
plan
}
fn scan_for(var: &str, node: &NodePattern) -> LogicalPlan {
let base = match &node.label {
Some(label) => LogicalPlan::NodeByLabelScan {
var: var.to_string(),
label: label.clone(),
},
None => LogicalPlan::AllNodesScan { var: var.to_string() },
};
wrap_node_filters(base, var, node)
}
fn wrap_node_filters(plan: LogicalPlan, var: &str, node: &NodePattern) -> LogicalPlan {
let mut plan = plan;
for (key, lit) in &node.props {
let predicate = Expr::Compare(
PropAccess {
var: var.to_string(),
prop: key.clone(),
},
CompareOp::Eq,
lit.clone(),
);
plan = LogicalPlan::Filter {
input: Box::new(plan),
predicate,
};
}
plan
}