use crate::datatypes::values::Value;
use crate::graph::core::pattern_matching::Pattern;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OutputFormat {
Default,
Csv,
}
#[derive(Debug, Clone)]
pub struct CypherQuery {
pub clauses: Vec<Clause>,
pub explain: bool,
pub profile: bool,
pub output_format: OutputFormat,
}
#[derive(Debug, Clone)]
pub enum Clause {
Match(MatchClause),
OptionalMatch(MatchClause),
Where(WhereClause),
Return(ReturnClause),
With(WithClause),
OrderBy(OrderByClause),
Skip(SkipClause),
Limit(LimitClause),
Unwind(UnwindClause),
Union(UnionClause),
Create(CreateClause),
Set(SetClause),
Delete(DeleteClause),
Remove(RemoveClause),
Merge(MergeClause),
Call(CallClause),
CallSubquery {
import: Vec<String>,
body: Box<CypherQuery>,
},
FusedOptionalMatchAggregate {
match_clause: MatchClause,
with_clause: WithClause,
},
FusedVectorScoreTopK {
return_clause: ReturnClause,
score_item_index: usize,
descending: bool,
limit: usize,
},
FusedMatchReturnAggregate {
match_clause: MatchClause,
return_clause: ReturnClause,
top_k: Option<(usize, bool, usize)>,
candidate_emit: Option<(usize, bool, usize)>,
distinct_count: bool,
},
FusedMatchWithAggregate {
match_clause: MatchClause,
with_clause: WithClause,
secondary_match: Option<MatchClause>,
top_k: Option<AggregateTopK>,
distinct_count: bool,
},
FusedOrderByTopK {
return_clause: ReturnClause,
score_item_index: usize,
descending: bool,
limit: usize,
sort_expression: Option<Expression>,
},
FusedCountAll {
alias: String,
},
FusedCountByType {
type_alias: String,
count_alias: String,
type_as_list: bool,
},
FusedCountEdgesByType {
type_alias: String,
count_alias: String,
},
FusedCountTypedNode {
node_type: String,
alias: String,
},
FusedCountTypedEdge {
edge_type: String,
alias: String,
},
FusedCountAnchoredEdges {
anchor_idx: u32,
anchor_direction: petgraph::Direction,
edge_type: Option<String>,
alias: String,
},
FusedNodeScanAggregate {
match_clause: MatchClause,
where_predicate: Option<Predicate>,
return_clause: ReturnClause,
},
FusedNodeScanTopK {
match_clause: MatchClause,
where_predicate: Option<Predicate>,
return_clause: ReturnClause,
sort_expression: Expression,
descending: bool,
limit: usize,
},
SpatialJoin {
container_var: String,
probe_var: String,
container_type: String,
probe_type: String,
probe_kind: SpatialProbeKind,
remainder: Option<Predicate>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpatialProbeKind {
Location,
Centroid,
}
#[derive(Debug, Clone)]
pub struct AggregateTopK {
pub limit: usize,
pub descending: bool,
}
#[derive(Debug, Clone)]
pub struct MatchClause {
pub patterns: Vec<Pattern>,
pub path_assignments: Vec<PathAssignment>,
pub limit_hint: Option<usize>,
pub distinct_node_hint: Option<String>,
}
#[derive(Debug, Clone)]
pub struct PathAssignment {
pub variable: String,
pub pattern_index: usize,
pub is_shortest_path: bool,
}
#[derive(Debug, Clone)]
pub struct WhereClause {
pub predicate: Predicate,
}
#[derive(Debug, Clone)]
pub enum Predicate {
Comparison {
left: Expression,
operator: ComparisonOp,
right: Expression,
},
And(Box<Predicate>, Box<Predicate>),
Or(Box<Predicate>, Box<Predicate>),
Xor(Box<Predicate>, Box<Predicate>),
Not(Box<Predicate>),
IsNull(Expression),
IsNotNull(Expression),
In {
expr: Expression,
list: Vec<Expression>,
},
InLiteralSet {
expr: Expression,
values: std::collections::HashSet<Value>,
},
StartsWith {
expr: Expression,
pattern: Expression,
},
EndsWith {
expr: Expression,
pattern: Expression,
},
Contains {
expr: Expression,
pattern: Expression,
},
Exists {
patterns: Vec<Pattern>,
where_clause: Option<Box<Predicate>>,
},
InExpression {
expr: Expression,
list_expr: Expression,
},
LabelCheck {
variable: String,
label: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ComparisonOp {
Equals, NotEquals, LessThan, LessThanEq, GreaterThan, GreaterThanEq, RegexMatch, }
#[derive(Debug, Clone)]
pub enum Expression {
PropertyAccess {
variable: String,
property: String,
},
Variable(String),
Literal(Value),
FunctionCall {
name: String,
args: Vec<Expression>,
distinct: bool,
},
Add(Box<Expression>, Box<Expression>),
Subtract(Box<Expression>, Box<Expression>),
Multiply(Box<Expression>, Box<Expression>),
Divide(Box<Expression>, Box<Expression>),
Modulo(Box<Expression>, Box<Expression>),
Concat(Box<Expression>, Box<Expression>),
Negate(Box<Expression>),
Star,
ListLiteral(Vec<Expression>),
Case {
operand: Option<Box<Expression>>,
when_clauses: Vec<(CaseCondition, Expression)>,
else_expr: Option<Box<Expression>>,
},
Parameter(String),
ListComprehension {
variable: String,
list_expr: Box<Expression>,
filter: Option<Box<Predicate>>,
map_expr: Option<Box<Expression>>,
},
IndexAccess {
expr: Box<Expression>,
index: Box<Expression>,
},
ListSlice {
expr: Box<Expression>,
start: Option<Box<Expression>>,
end: Option<Box<Expression>>,
},
MapProjection {
variable: String,
items: Vec<MapProjectionItem>,
},
IsNull(Box<Expression>),
IsNotNull(Box<Expression>),
MapLiteral(Vec<(String, Expression)>),
QuantifiedList {
quantifier: ListQuantifier,
variable: String,
list_expr: Box<Expression>,
filter: Box<Predicate>,
},
Reduce {
accumulator: String,
init: Box<Expression>,
variable: String,
list_expr: Box<Expression>,
body: Box<Expression>,
},
PredicateExpr(Box<Predicate>),
ExprPropertyAccess {
expr: Box<Expression>,
property: String,
},
WindowFunction {
name: String,
partition_by: Vec<Expression>,
order_by: Vec<OrderItem>,
},
CountSubquery {
patterns: Vec<crate::graph::core::pattern_matching::Pattern>,
where_clause: Option<Box<Predicate>>,
},
}
#[derive(Debug, Clone)]
pub enum ListQuantifier {
Any,
All,
None,
Single,
}
#[derive(Debug, Clone)]
pub enum MapProjectionItem {
Property(String),
AllProperties,
Alias { key: String, expr: Expression },
}
#[derive(Debug, Clone)]
pub enum CaseCondition {
Predicate(Predicate),
Expression(Expression),
}
#[derive(Debug, Clone)]
pub struct ReturnClause {
pub items: Vec<ReturnItem>,
pub distinct: bool,
pub having: Option<Predicate>,
pub lazy_eligible: bool,
pub group_limit_hint: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct ReturnItem {
pub expression: Expression,
pub alias: Option<String>,
}
#[derive(Debug, Clone)]
pub struct WithClause {
pub items: Vec<ReturnItem>,
pub distinct: bool,
pub where_clause: Option<WhereClause>,
pub group_limit_hint: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct OrderByClause {
pub items: Vec<OrderItem>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NullsPlacement {
First,
Last,
}
#[derive(Debug, Clone)]
pub struct OrderItem {
pub expression: Expression,
pub ascending: bool,
pub nulls: Option<NullsPlacement>,
}
impl OrderItem {
#[inline]
pub fn effective_nulls(&self) -> NullsPlacement {
self.nulls.unwrap_or(if self.ascending {
NullsPlacement::Last
} else {
NullsPlacement::First
})
}
}
#[derive(Debug, Clone)]
pub struct SkipClause {
pub count: Expression,
}
#[derive(Debug, Clone)]
pub struct LimitClause {
pub count: Expression,
}
#[derive(Debug, Clone)]
pub struct UnwindClause {
pub expression: Expression,
pub alias: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SetOpKind {
Union,
Intersect,
Except,
}
#[derive(Debug, Clone)]
pub struct UnionClause {
pub all: bool,
pub query: Box<CypherQuery>,
pub kind: SetOpKind,
}
#[derive(Debug, Clone)]
pub struct CreateClause {
pub patterns: Vec<CreatePattern>,
}
#[derive(Debug, Clone)]
pub struct CreatePattern {
pub elements: Vec<CreateElement>,
}
#[derive(Debug, Clone)]
pub enum CreateElement {
Node(CreateNodePattern),
Edge(CreateEdgePattern),
}
#[derive(Debug, Clone)]
pub struct CreateNodePattern {
pub variable: Option<String>,
pub label: Option<String>,
pub extra_labels: Vec<String>,
pub properties: Vec<(String, Expression)>,
}
#[derive(Debug, Clone)]
pub struct CreateEdgePattern {
pub variable: Option<String>,
pub connection_type: String,
pub direction: CreateEdgeDirection,
pub properties: Vec<(String, Expression)>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CreateEdgeDirection {
Outgoing, Incoming, }
#[derive(Debug, Clone)]
pub struct SetClause {
pub items: Vec<SetItem>,
}
#[derive(Debug, Clone)]
pub enum SetItem {
Property {
variable: String,
property: String,
expression: Expression,
},
Label {
variable: String,
label: String,
},
}
#[derive(Debug, Clone)]
pub struct DeleteClause {
pub detach: bool,
pub expressions: Vec<Expression>,
}
#[derive(Debug, Clone)]
pub struct RemoveClause {
pub items: Vec<RemoveItem>,
}
#[derive(Debug, Clone)]
pub enum RemoveItem {
Property { variable: String, property: String },
Label { variable: String, label: String },
}
#[derive(Debug, Clone)]
pub struct MergeClause {
pub pattern: CreatePattern,
pub on_create: Option<Vec<SetItem>>,
pub on_match: Option<Vec<SetItem>>,
}
#[derive(Debug, Clone)]
pub struct CallClause {
pub procedure_name: String,
pub parameters: Vec<(String, Expression)>,
pub yield_items: Vec<YieldItem>,
}
#[derive(Debug, Clone)]
pub struct YieldItem {
pub name: String,
pub alias: Option<String>,
}
pub fn is_aggregate_expression(expr: &Expression) -> bool {
match expr {
Expression::FunctionCall { name, args, .. } => {
if matches!(
name.as_str(),
"count"
| "sum"
| "avg"
| "mean"
| "average"
| "min"
| "max"
| "collect"
| "std"
| "stdev"
| "variance"
| "var_samp"
| "median"
| "mode"
| "percentile_cont"
| "percentile_disc"
) {
return true;
}
args.iter().any(is_aggregate_expression)
}
Expression::Add(l, r)
| Expression::Subtract(l, r)
| Expression::Multiply(l, r)
| Expression::Divide(l, r)
| Expression::Modulo(l, r)
| Expression::Concat(l, r) => is_aggregate_expression(l) || is_aggregate_expression(r),
Expression::Negate(inner) => is_aggregate_expression(inner),
Expression::Case {
when_clauses,
else_expr,
..
} => {
when_clauses
.iter()
.any(|(_, result)| is_aggregate_expression(result))
|| else_expr
.as_ref()
.is_some_and(|e| is_aggregate_expression(e))
}
Expression::ListComprehension {
list_expr,
map_expr,
..
} => {
is_aggregate_expression(list_expr)
|| map_expr
.as_ref()
.is_some_and(|e| is_aggregate_expression(e))
}
Expression::IndexAccess { expr, index } => {
is_aggregate_expression(expr) || is_aggregate_expression(index)
}
Expression::ListSlice { expr, start, end } => {
is_aggregate_expression(expr)
|| start.as_ref().is_some_and(|s| is_aggregate_expression(s))
|| end.as_ref().is_some_and(|e| is_aggregate_expression(e))
}
Expression::MapProjection { items, .. } => items.iter().any(|item| {
if let MapProjectionItem::Alias { expr, .. } = item {
is_aggregate_expression(expr)
} else {
false
}
}),
Expression::MapLiteral(entries) => entries
.iter()
.any(|(_, expr)| is_aggregate_expression(expr)),
Expression::PredicateExpr(pred) => match pred.as_ref() {
Predicate::Comparison { left, right, .. } => {
is_aggregate_expression(left) || is_aggregate_expression(right)
}
Predicate::StartsWith { expr, pattern }
| Predicate::EndsWith { expr, pattern }
| Predicate::Contains { expr, pattern } => {
is_aggregate_expression(expr) || is_aggregate_expression(pattern)
}
Predicate::In { expr, list } => {
is_aggregate_expression(expr) || list.iter().any(is_aggregate_expression)
}
Predicate::InExpression { expr, list_expr } => {
is_aggregate_expression(expr) || is_aggregate_expression(list_expr)
}
_ => false,
},
Expression::ExprPropertyAccess { expr, .. } => is_aggregate_expression(expr),
_ => false,
}
}
pub fn is_window_expression(expr: &Expression) -> bool {
matches!(expr, Expression::WindowFunction { .. })
}