use super::super::ast::*;
use crate::datatypes::values::Value;
use crate::graph::core::pattern_matching::PatternElement;
use petgraph::graph::NodeIndex;
use std::collections::HashMap;
pub(super) fn anchor_element_id(query: &mut CypherQuery, params: &HashMap<String, Value>) {
for i in 0..query.clauses.len() {
let mut predicates: Vec<Predicate> = Vec::new();
match &query.clauses[i] {
Clause::Match(m) | Clause::OptionalMatch(m) => {
if let Some(w) = &m.where_clause {
predicates.push(w.predicate.clone());
}
}
_ => continue,
}
if let Some(Clause::Where(w)) = query.clauses.get(i + 1) {
predicates.push(w.predicate.clone());
}
if predicates.is_empty() {
continue;
}
let (Clause::Match(clause) | Clause::OptionalMatch(clause)) = &mut query.clauses[i] else {
continue;
};
let vars = node_variables(&clause.patterns);
if vars.is_empty() {
continue;
}
let mut anchors: Vec<(String, NodeIndex)> = Vec::new();
for predicate in &predicates {
collect_anchors(predicate, &vars, params, &mut anchors);
}
if !anchors.is_empty() {
clause.node_anchors = anchors;
}
}
}
fn node_variables(patterns: &[crate::graph::core::pattern_matching::Pattern]) -> Vec<String> {
let mut vars = Vec::new();
for pattern in patterns {
for element in &pattern.elements {
if let PatternElement::Node(np) = element {
if let Some(var) = &np.variable {
if !vars.iter().any(|v| v == var) {
vars.push(var.clone());
}
}
}
}
}
vars
}
fn collect_anchors(
predicate: &Predicate,
vars: &[String],
params: &HashMap<String, Value>,
out: &mut Vec<(String, NodeIndex)>,
) {
match predicate {
Predicate::And(left, right) => {
collect_anchors(left, vars, params, out);
collect_anchors(right, vars, params, out);
}
Predicate::Comparison {
left,
operator: ComparisonOp::Equals,
right,
} => {
if let Some((var, slot)) = anchor_from_operands(left, right, vars, params)
.or_else(|| anchor_from_operands(right, left, vars, params))
{
if !out.iter().any(|(v, _)| *v == var) {
out.push((var, slot));
}
}
}
_ => {}
}
}
fn anchor_from_operands(
call: &Expression,
value: &Expression,
vars: &[String],
params: &HashMap<String, Value>,
) -> Option<(String, NodeIndex)> {
let Expression::FunctionCall { name, args, .. } = call else {
return None;
};
if !name.eq_ignore_ascii_case("elementId") {
return None;
}
let Some(Expression::Variable(var)) = args.first() else {
return None;
};
if !vars.iter().any(|v| v == var) {
return None;
}
let slot = slot_from_expression(value, params)?;
Some((var.clone(), NodeIndex::new(slot)))
}
fn slot_from_expression(expr: &Expression, params: &HashMap<String, Value>) -> Option<usize> {
let value = match expr {
Expression::Literal(v) => v,
Expression::Parameter(name) => params.get(name.as_str())?,
_ => return None,
};
match value {
Value::String(s) => s.parse::<usize>().ok(),
Value::Int64(n) => usize::try_from(*n).ok(),
_ => None,
}
}