1use crate::query::GraphResult;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum GraphValueType {
10 String,
11 Integer,
12 Float,
13 Boolean,
14 DateTime,
15 Duration,
16}
17
18#[derive(Debug, Clone)]
20pub struct GraphPropertyDef {
21 pub name: String,
22 pub value_type: GraphValueType,
23 pub nullable: bool,
24 pub default: Option<serde_json::Value>,
25}
26
27impl GraphPropertyDef {
28 pub fn new(name: &str, value_type: GraphValueType) -> Self {
29 Self {
30 name: name.to_string(),
31 value_type,
32 nullable: false,
33 default: None,
34 }
35 }
36
37 pub fn nullable(mut self) -> Self {
38 self.nullable = true;
39 self
40 }
41
42 pub fn with_default(mut self, value: serde_json::Value) -> Self {
43 self.default = Some(value);
44 self
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum RelationDirection {
51 Outgoing,
52 Incoming,
53 Undirected,
54}
55
56#[derive(Debug, Clone)]
58pub struct GraphNodeModel {
59 pub label: String,
60 pub properties: Vec<GraphPropertyDef>,
61}
62
63impl GraphNodeModel {
64 pub fn new(label: &str) -> Self {
65 Self {
66 label: label.to_string(),
67 properties: Vec::new(),
68 }
69 }
70
71 pub fn property(mut self, name: &str, value_type: GraphValueType) -> Self {
72 self.properties
73 .push(GraphPropertyDef::new(name, value_type));
74 self
75 }
76
77 pub fn match_clause(&self, alias: &str) -> String {
79 format!("MATCH ({}:{})", alias, self.label)
80 }
81
82 pub fn create_clause(&self, alias: &str) -> String {
84 let prop_names: Vec<String> = self
85 .properties
86 .iter()
87 .map(|p| format!("{}: ${}", p.name, p.name))
88 .collect();
89 if prop_names.is_empty() {
90 format!("CREATE ({}:{})", alias, self.label)
91 } else {
92 format!(
93 "CREATE ({}:{} {{{}}})",
94 alias,
95 self.label,
96 prop_names.join(", ")
97 )
98 }
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct GraphRelationModel {
105 pub rel_type: String,
106 pub direction: RelationDirection,
107 pub from_label: String,
108 pub to_label: String,
109 pub properties: Vec<GraphPropertyDef>,
110}
111
112impl GraphRelationModel {
113 pub fn new(rel_type: &str, from_label: &str, to_label: &str) -> Self {
114 Self {
115 rel_type: rel_type.to_string(),
116 direction: RelationDirection::Outgoing,
117 from_label: from_label.to_string(),
118 to_label: to_label.to_string(),
119 properties: Vec::new(),
120 }
121 }
122
123 pub fn direction(mut self, dir: RelationDirection) -> Self {
124 self.direction = dir;
125 self
126 }
127
128 pub fn property(mut self, name: &str, value_type: GraphValueType) -> Self {
129 self.properties
130 .push(GraphPropertyDef::new(name, value_type));
131 self
132 }
133
134 pub fn match_clause(&self, from: &str, rel: &str, to: &str) -> String {
136 match self.direction {
137 RelationDirection::Outgoing => format!(
138 "MATCH ({from}:{from_label})-[{rel}:{rel_type}]->({to}:{to_label})",
139 from = from,
140 from_label = self.from_label,
141 rel = rel,
142 rel_type = self.rel_type,
143 to = to,
144 to_label = self.to_label
145 ),
146 RelationDirection::Incoming => format!(
147 "MATCH ({from}:{from_label})<-[{rel}:{rel_type}]-({to}:{to_label})",
148 from = from,
149 from_label = self.from_label,
150 rel = rel,
151 rel_type = self.rel_type,
152 to = to,
153 to_label = self.to_label
154 ),
155 RelationDirection::Undirected => format!(
156 "MATCH ({from}:{from_label})-[{rel}:{rel_type}]-({to}:{to_label})",
157 from = from,
158 from_label = self.from_label,
159 rel = rel,
160 rel_type = self.rel_type,
161 to = to,
162 to_label = self.to_label
163 ),
164 }
165 }
166}
167
168pub fn extract_node_properties(result: &GraphResult) -> Option<&serde_json::Value> {
170 result.as_node().map(|n| &n.properties)
171}