Skip to main content

nova_boot_graphdb/
builders.rs

1use crate::types::GraphQuery;
2
3pub(crate) fn sanitize_symbol(value: &str) -> String {
4    let cleaned = value
5        .chars()
6        .filter(|c| c.is_ascii_alphanumeric() || *c == '_')
7        .collect::<String>();
8    if cleaned.is_empty() {
9        "Node".to_string()
10    } else {
11        cleaned
12    }
13}
14
15pub struct CypherQueryBuilder {
16    query: String,
17}
18
19impl CypherQueryBuilder {
20    pub fn new() -> Self {
21        Self {
22            query: String::new(),
23        }
24    }
25
26    pub fn raw(mut self, fragment: impl AsRef<str>) -> Self {
27        if !self.query.is_empty() {
28            self.query.push(' ');
29        }
30        self.query.push_str(fragment.as_ref());
31        self
32    }
33
34    pub fn match_node(mut self, alias: &str, label: &str) -> Self {
35        self.query.push_str(&format!("MATCH ({alias}:{label}) "));
36        self
37    }
38
39    pub fn where_eq(mut self, alias: &str, field: &str, value: &str) -> Self {
40        self.query
41            .push_str(&format!("WHERE {alias}.{field} = '{value}' "));
42        self
43    }
44
45    pub fn return_fields(mut self, fields: impl AsRef<str>) -> Self {
46        self.query.push_str(&format!("RETURN {}", fields.as_ref()));
47        self
48    }
49
50    pub fn build(self) -> GraphQuery {
51        GraphQuery::Cypher(self.query.trim().to_string())
52    }
53}
54
55impl Default for CypherQueryBuilder {
56    fn default() -> Self {
57        Self::new()
58    }
59}
60
61pub struct GraphQlQueryBuilder {
62    root: String,
63    fields: Vec<String>,
64    args: Vec<(String, String)>,
65}
66
67impl GraphQlQueryBuilder {
68    pub fn new(root: impl Into<String>) -> Self {
69        Self {
70            root: root.into(),
71            fields: Vec::new(),
72            args: Vec::new(),
73        }
74    }
75
76    pub fn arg(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
77        self.args.push((key.into(), value.into()));
78        self
79    }
80
81    pub fn field(mut self, field: impl Into<String>) -> Self {
82        self.fields.push(field.into());
83        self
84    }
85
86    pub fn build(self) -> GraphQuery {
87        let args = if self.args.is_empty() {
88            String::new()
89        } else {
90            let joined = self
91                .args
92                .into_iter()
93                .map(|(k, v)| format!("{k}: \"{v}\""))
94                .collect::<Vec<_>>()
95                .join(", ");
96            format!("({joined})")
97        };
98
99        let fields = if self.fields.is_empty() {
100            "id".to_string()
101        } else {
102            self.fields.join(" ")
103        };
104
105        GraphQuery::GraphQl(format!(
106            "query {{ {}{} {{ {} }} }}",
107            self.root, args, fields
108        ))
109    }
110}