use crate::datatypes::values::Value;
use crate::graph::constraints::EntityKind;
use crate::graph::core::membership::MembershipSet;
use crate::graph::core::pattern_matching::{ParamLabel, 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,
pub optimizer_tags: Vec<String>,
}
#[derive(Debug, Clone)]
pub enum Clause {
Match(MatchClause),
OptionalMatch(MatchClause),
Where(WhereClause),
Return(ReturnClause),
With(WithClause),
OrderBy(OrderByClause),
Skip(SkipClause),
Limit(LimitClause),
Unwind(UnwindClause),
LoadCsv(LoadCsvClause),
Union(UnionClause),
Create(CreateClause),
Set(SetClause),
Delete(DeleteClause),
Remove(RemoveClause),
Merge(MergeClause),
Foreach {
variable: String,
list: Expression,
body: Vec<Clause>,
},
Call(CallClause),
Schema(SchemaCommand),
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,
sort_keys: Vec<FusedSortKey>,
limit: usize,
},
FusedCountAll {
alias: String,
},
FusedCountAllEdges {
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_types: Option<Vec<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_keys: Vec<FusedSortKey>,
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>,
pub where_clause: Option<WhereClause>,
pub node_anchors: Vec<(String, petgraph::graph::NodeIndex)>,
}
#[derive(Debug, Clone)]
pub struct PathAssignment {
pub variable: String,
pub pattern_index: usize,
pub is_shortest_path: bool,
pub all_shortest: 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: MembershipSet,
},
StartsWith {
expr: Expression,
pattern: Expression,
},
EndsWith {
expr: Expression,
pattern: Expression,
},
Contains {
expr: Expression,
pattern: Expression,
},
Exists {
patterns: Vec<Pattern>,
pattern_groups: Vec<usize>,
where_clause: Option<Box<Predicate>>,
},
InExpression {
expr: Expression,
list_expr: Expression,
},
LabelCheck {
variable: String,
label: String,
label_param: Option<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>,
pattern_groups: Vec<usize>,
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 FusedSortKey {
pub expression: Expression,
pub ascending: bool,
pub nulls: NullsPlacement,
pub return_item: Option<usize>,
}
#[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,
pub consume_source: bool,
}
#[derive(Debug, Clone)]
pub struct LoadCsvClause {
pub with_headers: bool,
pub source: Expression,
pub variable: String,
pub field_terminator: Option<u8>,
}
#[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)>,
pub label_params: Vec<ParamLabel>,
}
#[derive(Debug, Clone)]
pub struct CreateEdgePattern {
pub variable: Option<String>,
pub connection_type: String,
pub direction: CreateEdgeDirection,
pub properties: Vec<(String, Expression)>,
pub type_param: Option<String>,
}
#[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,
label_param: Option<String>,
},
Map {
variable: String,
expression: Expression,
replace: bool,
},
}
#[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,
label_param: Option<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, PartialEq)]
pub struct YieldItem {
pub name: String,
pub alias: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SchemaCommand {
CreateIndex(CreateIndex),
UnsupportedIndexType {
index_type: DdlIndexType,
name: Option<String>,
},
DropIndex(DropIndex),
ShowIndexes,
ShowProcedures {
yield_items: Vec<YieldItem>,
},
ShowFunctions {
yield_items: Vec<YieldItem>,
},
Constraint(ConstraintCommand),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DdlIndexType {
Unspecified,
Range,
Text,
Point,
Fulltext,
Vector,
Lookup,
}
impl DdlIndexType {
pub fn keyword(self) -> &'static str {
match self {
DdlIndexType::Unspecified => "INDEX",
DdlIndexType::Range => "RANGE",
DdlIndexType::Text => "TEXT",
DdlIndexType::Point => "POINT",
DdlIndexType::Fulltext => "FULLTEXT",
DdlIndexType::Vector => "VECTOR",
DdlIndexType::Lookup => "LOOKUP",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DdlTarget {
Node {
variable: Option<String>,
label: String,
},
Relationship {
variable: Option<String>,
rel_type: String,
},
}
impl DdlTarget {
pub fn variable(&self) -> Option<&str> {
match self {
DdlTarget::Node { variable, .. } | DdlTarget::Relationship { variable, .. } => {
variable.as_deref()
}
}
}
pub fn entity(&self) -> EntityKind {
match self {
DdlTarget::Node { .. } => EntityKind::Node,
DdlTarget::Relationship { .. } => EntityKind::Relationship,
}
}
pub fn type_name(&self) -> &str {
match self {
DdlTarget::Node { label, .. } => label,
DdlTarget::Relationship { rel_type, .. } => rel_type,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateIndex {
pub name: Option<String>,
pub index_type: DdlIndexType,
pub if_not_exists: bool,
pub target: DdlTarget,
pub properties: Vec<String>,
pub has_options: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DropIndex {
pub selector: DropIndexSelector,
pub if_exists: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DropIndexSelector {
Name(String),
Descriptor {
target: DdlTarget,
properties: Vec<String>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConstraintCommand {
Create(CreateConstraint),
Drop { name: String, if_exists: bool },
Show,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateConstraint {
pub name: Option<String>,
pub if_not_exists: bool,
pub target: DdlTarget,
pub properties: Vec<String>,
pub requirement: ConstraintRequirement,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConstraintRequirement {
Unique,
NotNull,
Key,
PropertyType(String),
}
impl ConstraintRequirement {
pub fn keyword(&self) -> &str {
match self {
ConstraintRequirement::Unique => "IS UNIQUE",
ConstraintRequirement::NotNull => "IS NOT NULL",
ConstraintRequirement::Key => "IS NODE KEY",
ConstraintRequirement::PropertyType(ty) => ty,
}
}
}
pub fn is_aggregate_function_name(name: &str) -> bool {
matches!(
name,
"count"
| "sum"
| "avg"
| "mean"
| "average"
| "min"
| "max"
| "collect"
| "std"
| "stdev"
| "variance"
| "var_samp"
| "median"
| "mode"
| "percentile_cont"
| "percentile_disc"
)
}
pub fn is_aggregate_expression(expr: &Expression) -> bool {
match expr {
Expression::FunctionCall { name, args, .. } => {
if is_aggregate_function_name(name.as_str()) {
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::ListLiteral(items) => items.iter().any(is_aggregate_expression),
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 { .. })
}