Skip to main content

nexus_sdk/
query_builder.rs

1//! Query builder for constructing Cypher queries in a type-safe manner
2
3use crate::models::Value;
4use std::collections::HashMap;
5
6/// Query builder for constructing Cypher queries
7#[derive(Debug, Clone)]
8pub struct QueryBuilder {
9    parts: Vec<String>,
10    params: HashMap<String, Value>,
11    current_clause: Option<ClauseType>,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15enum ClauseType {
16    Match,
17    Create,
18    Merge,
19    Where,
20    Return,
21    Set,
22    Delete,
23    With,
24    OrderBy,
25    Limit,
26    Skip,
27}
28
29impl QueryBuilder {
30    /// Create a new query builder
31    pub fn new() -> Self {
32        Self {
33            parts: Vec::new(),
34            params: HashMap::new(),
35            current_clause: None,
36        }
37    }
38
39    /// Add a MATCH clause
40    ///
41    /// # Example
42    ///
43    /// ```no_run
44    /// use nexus_sdk::query_builder::QueryBuilder;
45    ///
46    /// let query = QueryBuilder::new()
47    ///     .match_("(n:Person)")
48    ///     .build();
49    /// ```
50    pub fn match_(mut self, pattern: &str) -> Self {
51        self.parts.push(format!("MATCH {}", pattern));
52        self.current_clause = Some(ClauseType::Match);
53        self
54    }
55
56    /// Add a CREATE clause
57    ///
58    /// # Example
59    ///
60    /// ```no_run
61    /// use nexus_sdk::query_builder::QueryBuilder;
62    ///
63    /// let query = QueryBuilder::new()
64    ///     .create("(n:Person {name: $name})")
65    ///     .param("name", "Alice")
66    ///     .build();
67    /// ```
68    pub fn create(mut self, pattern: &str) -> Self {
69        self.parts.push(format!("CREATE {}", pattern));
70        self.current_clause = Some(ClauseType::Create);
71        self
72    }
73
74    /// Add a MERGE clause
75    pub fn merge(mut self, pattern: &str) -> Self {
76        self.parts.push(format!("MERGE {}", pattern));
77        self.current_clause = Some(ClauseType::Merge);
78        self
79    }
80
81    /// Add a WHERE clause
82    ///
83    /// # Example
84    ///
85    /// ```no_run
86    /// use nexus_sdk::query_builder::QueryBuilder;
87    ///
88    /// let query = QueryBuilder::new()
89    ///     .match_("(n:Person)")
90    ///     .where_("n.age > $min_age")
91    ///     .param("min_age", 18)
92    ///     .build();
93    /// ```
94    pub fn where_(mut self, condition: &str) -> Self {
95        self.parts.push(format!("WHERE {}", condition));
96        self.current_clause = Some(ClauseType::Where);
97        self
98    }
99
100    /// Add a RETURN clause
101    ///
102    /// # Example
103    ///
104    /// ```no_run
105    /// use nexus_sdk::query_builder::QueryBuilder;
106    ///
107    /// let query = QueryBuilder::new()
108    ///     .match_("(n:Person)")
109    ///     .return_("n")
110    ///     .build();
111    /// ```
112    pub fn return_(mut self, items: &str) -> Self {
113        self.parts.push(format!("RETURN {}", items));
114        self.current_clause = Some(ClauseType::Return);
115        self
116    }
117
118    /// Add a SET clause
119    pub fn set(mut self, assignments: &str) -> Self {
120        self.parts.push(format!("SET {}", assignments));
121        self.current_clause = Some(ClauseType::Set);
122        self
123    }
124
125    /// Add a DELETE clause
126    pub fn delete(mut self, items: &str) -> Self {
127        self.parts.push(format!("DELETE {}", items));
128        self.current_clause = Some(ClauseType::Delete);
129        self
130    }
131
132    /// Add a WITH clause
133    pub fn with(mut self, items: &str) -> Self {
134        self.parts.push(format!("WITH {}", items));
135        self.current_clause = Some(ClauseType::With);
136        self
137    }
138
139    /// Add an ORDER BY clause
140    ///
141    /// # Example
142    ///
143    /// ```no_run
144    /// use nexus_sdk::query_builder::QueryBuilder;
145    ///
146    /// let query = QueryBuilder::new()
147    ///     .match_("(n:Person)")
148    ///     .return_("n")
149    ///     .order_by("n.name ASC")
150    ///     .build();
151    /// ```
152    pub fn order_by(mut self, expression: &str) -> Self {
153        self.parts.push(format!("ORDER BY {}", expression));
154        self.current_clause = Some(ClauseType::OrderBy);
155        self
156    }
157
158    /// Add a LIMIT clause
159    ///
160    /// # Example
161    ///
162    /// ```no_run
163    /// use nexus_sdk::query_builder::QueryBuilder;
164    ///
165    /// let query = QueryBuilder::new()
166    ///     .match_("(n:Person)")
167    ///     .return_("n")
168    ///     .limit(10)
169    ///     .build();
170    /// ```
171    pub fn limit(mut self, count: usize) -> Self {
172        self.parts.push(format!("LIMIT {}", count));
173        self.current_clause = Some(ClauseType::Limit);
174        self
175    }
176
177    /// Add a SKIP clause
178    pub fn skip(mut self, count: usize) -> Self {
179        self.parts.push(format!("SKIP {}", count));
180        self.current_clause = Some(ClauseType::Skip);
181        self
182    }
183
184    /// Add a parameter to the query
185    ///
186    /// # Example
187    ///
188    /// ```no_run
189    /// use nexus_sdk::query_builder::QueryBuilder;
190    ///
191    /// let query = QueryBuilder::new()
192    ///     .match_("(n:Person)")
193    ///     .where_("n.name = $name")
194    ///     .param("name", "Alice")
195    ///     .build();
196    /// ```
197    pub fn param<T: Into<Value>>(mut self, name: &str, value: T) -> Self {
198        self.params.insert(name.to_string(), value.into());
199        self
200    }
201
202    /// Build the final query string
203    pub fn build(self) -> BuiltQuery {
204        BuiltQuery {
205            query: self.parts.join(" "),
206            params: if self.params.is_empty() {
207                None
208            } else {
209                Some(self.params)
210            },
211        }
212    }
213}
214
215impl Default for QueryBuilder {
216    fn default() -> Self {
217        Self::new()
218    }
219}
220
221/// Built query ready for execution
222#[derive(Debug, Clone)]
223pub struct BuiltQuery {
224    query: String,
225    params: Option<HashMap<String, Value>>,
226}
227
228impl BuiltQuery {
229    /// Get the query string
230    pub fn query(&self) -> &str {
231        &self.query
232    }
233
234    /// Get the parameters
235    pub fn params(&self) -> Option<&HashMap<String, Value>> {
236        self.params.as_ref()
237    }
238
239    /// Convert to query string and parameters tuple
240    pub fn into_parts(self) -> (String, Option<HashMap<String, Value>>) {
241        (self.query, self.params)
242    }
243}