//! Abstract Syntax Tree definitions for Cypher query language //! //! Represents the parsed structure of Cypher queries including: //! - Pattern matching (MATCH, OPTIONAL MATCH) //! - Filtering (WHERE) //! - Projections (RETURN, WITH) //! - Mutations (CREATE, MERGE, DELETE, SET) //! - Aggregations and ordering //! - Hyperedge support for N-ary relationships use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// Top-level query representation #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Query { pub statements: Vec, } /// Individual query statement #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Statement { Match(MatchClause), Create(CreateClause), Merge(MergeClause), Delete(DeleteClause), Set(SetClause), Remove(RemoveClause), Return(ReturnClause), With(WithClause), } /// MATCH clause for pattern matching #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct MatchClause { pub optional: bool, pub patterns: Vec, pub where_clause: Option, } /// Pattern matching expressions #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Pattern { /// Simple node pattern: (n:Label {props}) Node(NodePattern), /// Relationship pattern: (a)-[r:TYPE]->(b) Relationship(RelationshipPattern), /// Path pattern: p = (a)-[*1..5]->(b) Path(PathPattern), /// Hyperedge pattern for N-ary relationships: (a)-[r:TYPE]->(b,c,d) Hyperedge(HyperedgePattern), } /// Node pattern: (variable:Label {property: value}) #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct NodePattern { pub variable: Option, pub labels: Vec, pub properties: Option, } /// Relationship pattern: [variable:Type {properties}] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RelationshipPattern { pub variable: Option, pub rel_type: Option, pub properties: Option, pub direction: Direction, pub range: Option, /// Source node pattern pub from: Box, /// Target - can be a NodePattern or another Pattern for chained relationships /// For simple relationships like (a)-[r]->(b), this is just the node /// For chained patterns like (a)-[r]->(b)<-[s]-(c), the target is nested pub to: Box, } /// Hyperedge pattern for N-ary relationships /// Example: (person)-[r:TRANSACTION]->(account1, account2, merchant) #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct HyperedgePattern { pub variable: Option, pub rel_type: String, pub properties: Option, pub from: Box, pub to: Vec, // Multiple target nodes for N-ary relationships pub arity: usize, // Number of participating nodes (including source) } /// Relationship direction #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum Direction { Outgoing, // -> Incoming, // <- Undirected, // - } /// Relationship range for path queries: [*min..max] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct RelationshipRange { pub min: Option, pub max: Option, } /// Path pattern: p = (a)-[*]->(b) #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PathPattern { pub variable: String, pub pattern: Box, } /// Property map: {key: value, ...} pub type PropertyMap = HashMap; /// WHERE clause for filtering #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct WhereClause { pub condition: Expression, } /// CREATE clause for creating nodes and relationships #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct CreateClause { pub patterns: Vec, } /// MERGE clause for create-or-match #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct MergeClause { pub pattern: Pattern, pub on_create: Option, pub on_match: Option, } /// DELETE clause #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct DeleteClause { pub detach: bool, pub expressions: Vec, } /// SET clause for updating properties #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SetClause { pub items: Vec, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum SetItem { Property { variable: String, property: String, value: Expression, }, Variable { variable: String, value: Expression, }, Labels { variable: String, labels: Vec, }, } /// REMOVE clause for removing properties or labels #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RemoveClause { pub items: Vec, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum RemoveItem { /// Remove a property: REMOVE n.property Property { variable: String, property: String }, /// Remove labels: REMOVE n:Label1:Label2 Labels { variable: String, labels: Vec, }, } /// RETURN clause for projection #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ReturnClause { pub distinct: bool, pub items: Vec, pub order_by: Option, pub skip: Option, pub limit: Option, } /// WITH clause for chaining queries #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct WithClause { pub distinct: bool, pub items: Vec, pub where_clause: Option, pub order_by: Option, pub skip: Option, pub limit: Option, } /// Return item: expression AS alias #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ReturnItem { pub expression: Expression, pub alias: Option, } /// ORDER BY clause #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct OrderBy { pub items: Vec, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct OrderByItem { pub expression: Expression, pub ascending: bool, } /// Expression tree #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Expression { // Literals Integer(i64), Float(f64), String(String), Boolean(bool), Null, // Variables and properties Variable(String), Property { object: Box, property: String, }, // Collections List(Vec), Map(HashMap), // Operators BinaryOp { left: Box, op: BinaryOperator, right: Box, }, UnaryOp { op: UnaryOperator, operand: Box, }, // Functions and aggregations FunctionCall { name: String, args: Vec, }, Aggregation { function: AggregationFunction, expression: Box, distinct: bool, }, // Pattern predicates PatternPredicate(Box), // Case expressions Case { expression: Option>, alternatives: Vec<(Expression, Expression)>, default: Option>, }, } /// Binary operators #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BinaryOperator { // Arithmetic Add, Subtract, Multiply, Divide, Modulo, Power, // Comparison Equal, NotEqual, LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual, // Logical And, Or, Xor, // String Contains, StartsWith, EndsWith, Matches, // Regex // Collection In, // Null checking Is, IsNot, } /// Unary operators #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum UnaryOperator { Not, Minus, Plus, IsNull, IsNotNull, } /// Aggregation functions #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum AggregationFunction { Count, Sum, Avg, Min, Max, Collect, StdDev, StdDevP, Percentile, } impl Query { pub fn new(statements: Vec) -> Self { Self { statements } } /// Check if query contains only read operations pub fn is_read_only(&self) -> bool { self.statements.iter().all(|stmt| { matches!( stmt, Statement::Match(_) | Statement::Return(_) | Statement::With(_) ) }) } /// Check if query contains hyperedges pub fn has_hyperedges(&self) -> bool { self.statements.iter().any(|stmt| match stmt { Statement::Match(m) => m .patterns .iter() .any(|p| matches!(p, Pattern::Hyperedge(_))), Statement::Create(c) => c .patterns .iter() .any(|p| matches!(p, Pattern::Hyperedge(_))), Statement::Merge(m) => matches!(&m.pattern, Pattern::Hyperedge(_)), _ => false, }) } } impl Pattern { /// Get the arity of the pattern (number of nodes involved) pub fn arity(&self) -> usize { match self { Pattern::Node(_) => 1, Pattern::Relationship(_) => 2, Pattern::Path(_) => 2, // Simplified, could be variable Pattern::Hyperedge(h) => h.arity, } } } impl Expression { /// Check if expression is constant (no variables) pub fn is_constant(&self) -> bool { match self { Expression::Integer(_) | Expression::Float(_) | Expression::String(_) | Expression::Boolean(_) | Expression::Null => true, Expression::List(items) => items.iter().all(|e| e.is_constant()), Expression::Map(map) => map.values().all(|e| e.is_constant()), Expression::BinaryOp { left, right, .. } => left.is_constant() && right.is_constant(), Expression::UnaryOp { operand, .. } => operand.is_constant(), _ => false, } } /// Check if expression contains aggregation pub fn has_aggregation(&self) -> bool { match self { Expression::Aggregation { .. } => true, Expression::BinaryOp { left, right, .. } => { left.has_aggregation() || right.has_aggregation() } Expression::UnaryOp { operand, .. } => operand.has_aggregation(), Expression::FunctionCall { args, .. } => args.iter().any(|e| e.has_aggregation()), Expression::List(items) => items.iter().any(|e| e.has_aggregation()), Expression::Property { object, .. } => object.has_aggregation(), _ => false, } } }