use crate::cypher::ast::*;
use crate::cypher::record::NamedRecord;
use crate::cypher::record_view::{view_to_named, RecordView};
use crate::types::{GraphError, Value};
use super::*;
pub(in crate::cypher::eval) fn eval_list_comprehension(
variable: &str,
list_expr: &Expr,
filter: Option<&Expr>,
map_expr: Option<&Expr>,
record: &dyn RecordView,
ecx: EvalCx<'_>,
) -> crate::types::Result<Value> {
let list_val = eval_expr(list_expr, record, ecx)?;
let items = match list_val {
Value::List(items) => items,
Value::Null => return Ok(Value::List(vec![])),
_ => {
return Err(GraphError::semantic(
"list comprehension requires a list input".to_string(),
))
}
};
let base = view_to_named(record);
let mut results = Vec::new();
for item in items {
let mut local = base.clone();
local.set(variable.to_string(), item.clone());
if let Some(pred) = filter {
if !eval_predicate(pred, &local, ecx)? {
continue;
}
}
let val = match map_expr {
Some(expr) => eval_expr(expr, &local, ecx)?,
None => item,
};
results.push(val);
}
Ok(Value::List(results))
}
pub(in crate::cypher::eval) fn eval_quantifier(
kind: QuantifierKind,
variable: &str,
list_expr: &Expr,
predicate: &Expr,
record: &dyn RecordView,
ecx: EvalCx<'_>,
) -> crate::types::Result<Value> {
let list_val = eval_expr(list_expr, record, ecx)?;
let items = match list_val {
Value::List(items) => items,
Value::Null => return Ok(Value::Null),
_ => {
return Err(GraphError::semantic(
"quantifier requires a list input".to_string(),
))
}
};
let mut true_count: usize = 0;
let mut false_count: usize = 0;
let mut null_count: usize = 0;
let base = view_to_named(record);
for item in &items {
let mut local = base.clone();
local.set(variable.to_string(), item.clone());
let val = eval_expr(predicate, &local, ecx)?;
match val {
Value::Bool(true) => true_count += 1,
Value::Bool(false) => false_count += 1,
_ => null_count += 1,
}
}
match kind {
QuantifierKind::All => {
if false_count > 0 {
Ok(Value::Bool(false))
} else if null_count > 0 {
Ok(Value::Null)
} else {
Ok(Value::Bool(true))
}
}
QuantifierKind::Any => {
if true_count > 0 {
Ok(Value::Bool(true))
} else if null_count > 0 {
Ok(Value::Null)
} else {
Ok(Value::Bool(false))
}
}
QuantifierKind::None => {
if true_count > 0 {
Ok(Value::Bool(false))
} else if null_count > 0 {
Ok(Value::Null)
} else {
Ok(Value::Bool(true))
}
}
QuantifierKind::Single => {
if true_count == 1 && null_count == 0 {
Ok(Value::Bool(true))
} else if null_count > 0 && true_count <= 1 {
Ok(Value::Null)
} else {
Ok(Value::Bool(false))
}
}
}
}
pub(in crate::cypher::eval) fn eval_pattern_comprehension(
path_variable: Option<&str>,
pattern: &crate::cypher::ast::Pattern,
where_clause: Option<&Expr>,
map_expr: &Expr,
record: &dyn RecordView,
ecx: EvalCx<'_>,
) -> crate::types::Result<Value> {
use crate::cypher::executor::exec_correlated_subquery;
use crate::cypher::ir::LogicalOp;
use crate::cypher::planner::plan_patterns;
let conn = ecx.conn;
let mut pat = pattern.clone();
if let Some(pv) = path_variable {
pat.path_variable = Some(pv.to_string());
}
let mut op = plan_patterns(conn, std::slice::from_ref(&pat))?;
if let Some(predicate) = where_clause {
op = LogicalOp::Filter {
input: Box::new(op),
predicate: predicate.clone(),
};
}
let outer_base = view_to_named(record);
let mut outer_rec = NamedRecord::new();
for (k, v) in &outer_base.fields {
if k.starts_with("_anon_") || k.starts_with("_path_rel_") {
continue;
}
match v {
Value::Node(n) => {
outer_rec.set(k.clone(), Value::I64(n.id.0 as i64));
outer_rec.set(format!("{k}.__id"), Value::I64(n.id.0 as i64));
outer_rec.set(format!("{k}.__label"), Value::String(n.labels.join(":")));
outer_rec.set(
format!("{k}.__labels"),
Value::List(n.labels.iter().map(|l| Value::String(l.clone())).collect()),
);
for (pk, pv) in &n.properties {
outer_rec.set(format!("{k}.{pk}"), pv.clone());
}
}
_ => {
let base = k.split('.').next().unwrap_or(k);
if base.starts_with("_anon_") || base.starts_with("_path_rel_") {
continue;
}
outer_rec.set(k.clone(), v.clone());
}
}
}
let rows = exec_correlated_subquery(conn, &op, &outer_rec)?;
let mut results = Vec::new();
for row in &rows {
let mut merged = outer_base.clone();
for (k, v) in &row.fields {
merged.set(k.clone(), v.clone());
}
let val = eval_expr(map_expr, &merged, ecx)?;
results.push(val);
}
Ok(Value::List(results))
}