use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CypherQuery {
pub reading_clauses: Vec<ReadingClause>,
pub where_clause: Option<WhereClause>,
pub with_clause: Option<WithClause>,
pub post_with_reading_clauses: Vec<ReadingClause>,
pub post_with_where_clause: Option<WhereClause>,
pub return_clause: ReturnClause,
pub limit: Option<u64>,
pub order_by: Option<OrderByClause>,
pub skip: Option<u64>,
}
impl CypherQuery {
pub fn get_node_labels(&self) -> Vec<String> {
let mut labels = Vec::new();
for clause in &self.reading_clauses {
if let ReadingClause::Match(match_clause) = clause {
for pattern in &match_clause.patterns {
match pattern {
GraphPattern::Node(node) => {
for label in &node.labels {
if !labels.contains(label) {
labels.push(label.clone());
}
}
}
GraphPattern::Path(path) => {
for label in &path.start_node.labels {
if !labels.contains(label) {
labels.push(label.clone());
}
}
for segment in &path.segments {
for label in &segment.end_node.labels {
if !labels.contains(label) {
labels.push(label.clone());
}
}
}
}
}
}
}
}
labels
}
pub fn get_relationship_types(&self) -> Vec<String> {
let mut types = Vec::new();
for clause in &self.reading_clauses {
if let ReadingClause::Match(match_clause) = clause {
for pattern in &match_clause.patterns {
self.collect_relationship_types_from_pattern(pattern, &mut types);
}
}
}
types
}
fn collect_relationship_types_from_pattern(
&self,
pattern: &GraphPattern,
types: &mut Vec<String>,
) {
if let GraphPattern::Path(path) = pattern {
for segment in &path.segments {
for rel_type in &segment.relationship.types {
if !types.contains(rel_type) {
types.push(rel_type.clone());
}
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ReadingClause {
Match(MatchClause),
Unwind(UnwindClause),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MatchClause {
pub patterns: Vec<GraphPattern>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UnwindClause {
pub expression: ValueExpression,
pub alias: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GraphPattern {
Node(NodePattern),
Path(PathPattern),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NodePattern {
pub variable: Option<String>,
pub labels: Vec<String>,
pub properties: HashMap<String, PropertyValue>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PathPattern {
pub start_node: NodePattern,
pub segments: Vec<PathSegment>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PathSegment {
pub relationship: RelationshipPattern,
pub end_node: NodePattern,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RelationshipPattern {
pub variable: Option<String>,
pub types: Vec<String>,
pub direction: RelationshipDirection,
pub properties: HashMap<String, PropertyValue>,
pub length: Option<LengthRange>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum RelationshipDirection {
Outgoing,
Incoming,
Undirected,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LengthRange {
pub min: Option<u32>,
pub max: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PropertyValue {
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Null,
Parameter(String),
Property(PropertyRef),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PropertyRef {
pub variable: String,
pub property: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WhereClause {
pub expression: BooleanExpression,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum BooleanExpression {
Comparison {
left: ValueExpression,
operator: ComparisonOperator,
right: ValueExpression,
},
And(Box<BooleanExpression>, Box<BooleanExpression>),
Or(Box<BooleanExpression>, Box<BooleanExpression>),
Not(Box<BooleanExpression>),
Exists(PropertyRef),
In {
expression: ValueExpression,
list: Vec<ValueExpression>,
},
Like {
expression: ValueExpression,
pattern: String,
},
ILike {
expression: ValueExpression,
pattern: String,
},
Contains {
expression: ValueExpression,
substring: String,
},
StartsWith {
expression: ValueExpression,
prefix: String,
},
EndsWith {
expression: ValueExpression,
suffix: String,
},
IsNull(ValueExpression),
IsNotNull(ValueExpression),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub enum DistanceMetric {
L2,
#[default]
Cosine,
Dot,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ComparisonOperator {
Equal,
NotEqual,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ValueExpression {
Variable(String),
Property(PropertyRef),
Literal(PropertyValue),
ScalarFunction {
name: String,
args: Vec<ValueExpression>,
},
AggregateFunction {
name: String,
args: Vec<ValueExpression>,
distinct: bool,
},
Arithmetic {
left: Box<ValueExpression>,
operator: ArithmeticOperator,
right: Box<ValueExpression>,
},
VectorDistance {
left: Box<ValueExpression>,
right: Box<ValueExpression>,
metric: DistanceMetric,
},
VectorSimilarity {
left: Box<ValueExpression>,
right: Box<ValueExpression>,
metric: DistanceMetric,
},
Parameter(String),
VectorLiteral(Vec<f32>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum FunctionType {
Aggregate,
Scalar,
Unknown,
}
pub fn classify_function(name: &str) -> FunctionType {
match name.to_lowercase().as_str() {
"count" | "sum" | "avg" | "min" | "max" | "collect" => FunctionType::Aggregate,
"tolower" | "lower" | "toupper" | "upper" => FunctionType::Scalar,
_ => FunctionType::Unknown,
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ArithmeticOperator {
Add,
Subtract,
Multiply,
Divide,
Modulo,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WithClause {
pub items: Vec<ReturnItem>,
pub order_by: Option<OrderByClause>,
pub limit: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReturnClause {
pub distinct: bool,
pub items: Vec<ReturnItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReturnItem {
pub expression: ValueExpression,
pub alias: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OrderByClause {
pub items: Vec<OrderByItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OrderByItem {
pub expression: ValueExpression,
pub direction: SortDirection,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SortDirection {
Ascending,
Descending,
}
impl NodePattern {
pub fn new(variable: Option<String>) -> Self {
Self {
variable,
labels: Vec::new(),
properties: HashMap::new(),
}
}
pub fn with_label<S: Into<String>>(mut self, label: S) -> Self {
self.labels.push(label.into());
self
}
pub fn with_property<S: Into<String>>(mut self, key: S, value: PropertyValue) -> Self {
self.properties.insert(key.into(), value);
self
}
}
impl RelationshipPattern {
pub fn new(direction: RelationshipDirection) -> Self {
Self {
variable: None,
types: Vec::new(),
direction,
properties: HashMap::new(),
length: None,
}
}
pub fn with_variable<S: Into<String>>(mut self, variable: S) -> Self {
self.variable = Some(variable.into());
self
}
pub fn with_type<S: Into<String>>(mut self, rel_type: S) -> Self {
self.types.push(rel_type.into());
self
}
pub fn with_property<S: Into<String>>(mut self, key: S, value: PropertyValue) -> Self {
self.properties.insert(key.into(), value);
self
}
}
impl PropertyRef {
pub fn new<S: Into<String>>(variable: S, property: S) -> Self {
Self {
variable: variable.into(),
property: property.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_pattern_creation() {
let node = NodePattern::new(Some("n".to_string()))
.with_label("Person")
.with_property("name", PropertyValue::String("John".to_string()));
assert_eq!(node.variable, Some("n".to_string()));
assert_eq!(node.labels, vec!["Person"]);
assert_eq!(node.properties.len(), 1);
}
#[test]
fn test_relationship_pattern_creation() {
let rel = RelationshipPattern::new(RelationshipDirection::Outgoing)
.with_variable("r")
.with_type("KNOWS");
assert_eq!(rel.variable, Some("r".to_string()));
assert_eq!(rel.types, vec!["KNOWS"]);
assert_eq!(rel.direction, RelationshipDirection::Outgoing);
}
#[test]
fn test_property_ref() {
let prop_ref = PropertyRef::new("n", "name");
assert_eq!(prop_ref.variable, "n");
assert_eq!(prop_ref.property, "name");
}
}