reddb_server/storage/query/parser/
graph.rs1use super::super::ast::{
4 CompareOp, EdgeDirection, EdgePattern, FieldRef, GraphPattern, GraphQuery, NodePattern,
5 Projection, PropertyFilter, QueryExpr,
6};
7use super::super::lexer::Token;
8use super::error::ParseError;
9use super::Parser;
10
11impl<'a> Parser<'a> {
12 pub fn parse_match_query(&mut self) -> Result<QueryExpr, ParseError> {
14 self.expect(Token::Match)?;
15
16 let pattern = self.parse_graph_pattern()?;
17
18 let filter = if self.consume(&Token::Where)? {
19 Some(self.parse_filter()?)
20 } else {
21 None
22 };
23
24 self.expect(Token::Return)?;
25 let return_ = self.parse_return_list()?;
26
27 Ok(QueryExpr::Graph(GraphQuery {
28 alias: None,
29 pattern,
30 filter,
31 return_,
32 }))
33 }
34
35 pub fn parse_graph_pattern(&mut self) -> Result<GraphPattern, ParseError> {
37 let mut pattern = GraphPattern::new();
38
39 let first_node = self.parse_node_pattern()?;
41 pattern.nodes.push(first_node);
42
43 while self.peek() == &Token::Dash || self.peek() == &Token::ArrowLeft {
45 let (edge, next_node) =
46 self.parse_edge_and_node(pattern.nodes.last().unwrap().alias.clone())?;
47 pattern.edges.push(edge);
48 pattern.nodes.push(next_node);
49 }
50
51 Ok(pattern)
52 }
53
54 pub fn parse_node_pattern(&mut self) -> Result<NodePattern, ParseError> {
56 self.expect(Token::LParen)?;
57
58 let alias = self.expect_ident()?;
59
60 let node_label = if self.consume(&Token::Colon)? {
63 Some(self.expect_ident_or_keyword()?.to_lowercase())
64 } else {
65 None
66 };
67
68 let properties = if self.consume(&Token::LBrace)? {
69 self.parse_property_filters()?
70 } else {
71 Vec::new()
72 };
73
74 self.expect(Token::RParen)?;
75
76 Ok(NodePattern {
77 alias,
78 node_label,
79 properties,
80 })
81 }
82
83 fn parse_edge_and_node(
85 &mut self,
86 from_alias: String,
87 ) -> Result<(EdgePattern, NodePattern), ParseError> {
88 let incoming = self.consume(&Token::ArrowLeft)?;
90 if !incoming {
91 self.expect(Token::Dash)?;
92 }
93
94 self.expect(Token::LBracket)?;
96
97 let alias = if let Token::Ident(name) = self.peek() {
98 let name = name.clone();
99 self.advance()?;
100 Some(name)
101 } else {
102 None
103 };
104
105 let edge_label = if self.consume(&Token::Colon)? {
106 Some(self.expect_ident_or_keyword()?.to_lowercase())
107 } else {
108 None
109 };
110
111 let (min_hops, max_hops) = if self.consume(&Token::Star)? {
113 if let Token::Integer(_) = self.peek() {
114 let min = self.parse_integer()? as u32;
115 if self.consume(&Token::DotDot)? {
116 let max = self.parse_integer()? as u32;
117 (min, max)
118 } else {
119 (min, min)
120 }
121 } else {
122 (1, u32::MAX) }
124 } else {
125 (1, 1) };
127
128 self.expect(Token::RBracket)?;
129
130 let direction = if incoming {
132 self.expect(Token::Dash)?;
133 EdgeDirection::Incoming
134 } else if self.consume(&Token::Arrow)? {
135 EdgeDirection::Outgoing
136 } else {
137 self.expect(Token::Dash)?;
138 EdgeDirection::Both
139 };
140
141 let next_node = self.parse_node_pattern()?;
143
144 let edge = EdgePattern {
145 alias,
146 from: from_alias,
147 to: next_node.alias.clone(),
148 edge_label,
149 direction,
150 min_hops,
151 max_hops,
152 };
153
154 Ok((edge, next_node))
155 }
156
157 pub fn parse_property_filters(&mut self) -> Result<Vec<PropertyFilter>, ParseError> {
159 let mut filters = Vec::new();
160
161 loop {
162 let name = self.expect_ident()?;
163 self.expect(Token::Colon)?;
164 let value = self.parse_value()?;
165
166 filters.push(PropertyFilter {
167 name,
168 op: CompareOp::Eq,
169 value,
170 });
171
172 if !self.consume(&Token::Comma)? {
173 break;
174 }
175 }
176
177 self.expect(Token::RBrace)?;
178 Ok(filters)
179 }
180
181 pub fn parse_return_list(&mut self) -> Result<Vec<Projection>, ParseError> {
183 let mut projections = Vec::new();
184 loop {
185 let proj = self.parse_graph_projection()?;
186 projections.push(proj);
187
188 if !self.consume(&Token::Comma)? {
189 break;
190 }
191 }
192 Ok(projections)
193 }
194
195 fn parse_graph_projection(&mut self) -> Result<Projection, ParseError> {
197 let first = self.expect_ident()?;
198
199 let field = if self.consume(&Token::Dot)? {
200 let prop = self.expect_ident()?;
201 FieldRef::NodeProperty {
202 alias: first,
203 property: prop,
204 }
205 } else {
206 FieldRef::NodeId { alias: first }
208 };
209
210 let alias = if self.consume(&Token::As)? {
211 Some(self.expect_ident()?)
212 } else {
213 None
214 };
215
216 Ok(Projection::Field(field, alias))
217 }
218
219 pub fn parse_node_label(&self, name: &str) -> Result<String, ParseError> {
225 let canonical = match name.to_uppercase().as_str() {
226 "HOST" => "host",
227 "SERVICE" => "service",
228 "CREDENTIAL" => "credential",
229 "VULNERABILITY" | "VULN" => "vulnerability",
230 "ENDPOINT" => "endpoint",
231 "TECHNOLOGY" | "TECH" => "technology",
232 "USER" => "user",
233 "DOMAIN" => "domain",
234 "CERTIFICATE" | "CERT" => "certificate",
235 other => return Ok(other.to_lowercase()),
239 };
240 Ok(canonical.to_string())
241 }
242
243 pub fn parse_edge_label(&self, name: &str) -> Result<String, ParseError> {
245 let canonical = match name.to_uppercase().as_str() {
246 "HAS_SERVICE" => "has_service",
247 "HAS_ENDPOINT" => "has_endpoint",
248 "USES_TECH" | "USES_TECHNOLOGY" => "uses_tech",
249 "AUTH_ACCESS" | "AUTH" => "auth_access",
250 "AFFECTED_BY" => "affected_by",
251 "CONTAINS" => "contains",
252 "CONNECTS_TO" | "CONNECTS" => "connects_to",
253 "RELATED_TO" | "RELATED" => "related_to",
254 "HAS_USER" => "has_user",
255 "HAS_CERT" | "HAS_CERTIFICATE" => "has_cert",
256 other => return Ok(other.to_lowercase()),
257 };
258 Ok(canonical.to_string())
259 }
260}