1use std::{collections::BTreeMap, fmt::Debug, ops::Bound, sync::Arc};
2
3use self::vertex_info::InternalVertexInfo;
4
5use super::InterpretedQuery;
6use crate::ir::{
7 EdgeKind, EdgeParameters, Eid, FieldValue, IREdge, IRFold, IRQueryComponent, IRVertex, Output,
8 Recursive, Vid,
9};
10
11mod candidates;
12mod dynamic;
13mod filters;
14mod sealed;
15mod vertex_info;
16
17pub use candidates::{CandidateValue, Range};
18pub use dynamic::DynamicallyResolvedValue;
19pub use vertex_info::{RequiredProperty, VertexInfo};
20
21#[non_exhaustive]
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct QueryInfo<'a> {
25 query: &'a InterpretedQuery,
26}
27
28impl<'a> QueryInfo<'a> {
29 fn new(query: &'a InterpretedQuery) -> Self {
30 Self { query }
31 }
32
33 #[allow(dead_code)] #[inline]
36 pub fn outputs(&self) -> &BTreeMap<Arc<str>, Output> {
37 &self.query.indexed_query.outputs
38 }
39
40 #[allow(dead_code)] #[inline]
43 pub fn variables(&self) -> &Arc<BTreeMap<Arc<str>, FieldValue>> {
44 &self.query.arguments
45 }
46}
47
48#[non_exhaustive]
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct ResolveInfo {
52 query: InterpretedQuery,
53 current_vid: Vid,
54 vertex_completed: bool,
55}
56
57#[non_exhaustive]
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct ResolveEdgeInfo {
61 query: InterpretedQuery,
62 current_vid: Vid,
63 target_vid: Vid,
64 crossing_eid: Eid,
65}
66
67impl ResolveInfo {
68 pub(crate) fn new(query: InterpretedQuery, current_vid: Vid, vertex_completed: bool) -> Self {
69 Self { query, current_vid, vertex_completed }
70 }
71
72 pub(crate) fn into_inner(self) -> InterpretedQuery {
73 self.query
74 }
75
76 #[allow(dead_code)] #[inline]
79 pub fn query(&self) -> QueryInfo<'_> {
80 QueryInfo::new(&self.query)
81 }
82}
83
84impl sealed::__Sealed for ResolveInfo {}
85impl sealed::__Sealed for NeighborInfo {}
86
87impl InternalVertexInfo for ResolveInfo {
88 #[inline]
89 fn query(&self) -> &InterpretedQuery {
90 &self.query
91 }
92
93 #[inline]
94 fn non_binding_filters(&self) -> bool {
95 false }
97
98 #[inline]
99 fn execution_frontier(&self) -> Bound<Vid> {
100 if self.vertex_completed {
101 Bound::Included(self.current_vid)
102 } else {
103 Bound::Excluded(self.current_vid)
105 }
106 }
107
108 #[inline]
109 fn current_vertex(&self) -> &IRVertex {
110 &self.current_component().vertices[&self.current_vid]
111 }
112
113 #[inline]
114 fn current_component(&self) -> &IRQueryComponent {
115 self.starting_component()
118 }
119
120 #[inline]
121 fn starting_component(&self) -> &IRQueryComponent {
122 &self.query.indexed_query.vids[&self.current_vid]
123 }
124
125 #[inline]
126 fn query_variables(&self) -> &BTreeMap<Arc<str>, FieldValue> {
127 self.query.arguments.as_ref()
128 }
129
130 fn make_non_folded_edge_info(&self, edge: &IREdge) -> EdgeInfo {
131 let neighboring_info = NeighborInfo {
132 query: self.query.clone(),
133 execution_frontier: self.execution_frontier(),
134 starting_vertex: self.current_vid,
135 neighbor_vertex: edge.to_vid,
136 neighbor_path: vec![edge.eid],
137 within_optional_scope: edge.optional,
138 locally_non_binding_filters: check_locally_non_binding_filters_for_edge(edge),
139 };
140 EdgeInfo {
141 eid: edge.eid,
142 parameters: edge.parameters.clone(),
143 optional: edge.optional,
144 recursive: edge.recursive.clone(),
145 folded: FoldState::None,
146 destination: neighboring_info,
147 }
148 }
149
150 fn make_folded_edge_info(&self, fold: &IRFold) -> EdgeInfo {
151 let at_least_one_element_required =
152 filters::fold_requires_at_least_one_element(&self.query.arguments, fold);
153
154 let neighboring_info = NeighborInfo {
155 query: self.query.clone(),
156 execution_frontier: self.execution_frontier(),
157 starting_vertex: self.current_vid,
158 neighbor_vertex: fold.to_vid,
159 neighbor_path: vec![fold.eid],
160 within_optional_scope: !at_least_one_element_required,
161 locally_non_binding_filters: false,
162 };
163 EdgeInfo {
164 eid: fold.eid,
165 parameters: fold.parameters.clone(),
166 optional: false,
167 recursive: None,
168 folded: if at_least_one_element_required {
169 FoldState::FoldedMandatory
170 } else {
171 FoldState::FoldedOptional
172 },
173 destination: neighboring_info,
174 }
175 }
176}
177
178impl ResolveEdgeInfo {
179 pub(crate) fn new(
180 query: InterpretedQuery,
181 current_vid: Vid,
182 target_vid: Vid,
183 crossing_eid: Eid,
184 ) -> Self {
185 Self { query, current_vid, target_vid, crossing_eid }
186 }
187
188 pub(crate) fn into_inner(self) -> InterpretedQuery {
189 self.query
190 }
191
192 #[allow(dead_code)] #[inline]
195 pub fn query(&self) -> QueryInfo<'_> {
196 QueryInfo::new(&self.query)
197 }
198
199 #[inline]
201 pub fn eid(&self) -> Eid {
202 self.crossing_eid
203 }
204
205 #[inline]
207 pub fn origin_vid(&self) -> Vid {
208 self.current_vid
209 }
210
211 #[allow(dead_code)] #[inline]
214 pub fn destination_vid(&self) -> Vid {
215 self.target_vid
216 }
217
218 #[allow(dead_code)] #[inline]
221 pub fn destination(&self) -> NeighborInfo {
222 self.edge().destination
223 }
224
225 #[allow(dead_code)] #[inline]
228 pub fn edge(&self) -> EdgeInfo {
229 let eid = self.eid();
230 match &self.query.indexed_query.eids[&eid] {
231 EdgeKind::Regular(regular) => {
232 debug_assert_eq!(
233 self.target_vid, regular.to_vid,
234 "expected Vid {:?} but got {:?} in edge {regular:?}",
235 self.target_vid, regular.to_vid
236 );
237 EdgeInfo {
238 eid,
239 parameters: regular.parameters.clone(),
240 optional: regular.optional,
241 recursive: regular.recursive.clone(),
242 folded: FoldState::None,
243 destination: NeighborInfo {
244 query: self.query.clone(),
245 execution_frontier: Bound::Excluded(self.target_vid),
246 starting_vertex: self.origin_vid(),
247 neighbor_vertex: regular.to_vid,
248 neighbor_path: vec![eid],
249 within_optional_scope: regular.optional,
250 locally_non_binding_filters: check_locally_non_binding_filters_for_edge(
251 regular,
252 ),
253 },
254 }
255 }
256 EdgeKind::Fold(fold) => {
257 debug_assert_eq!(
258 self.target_vid, fold.to_vid,
259 "expected Vid {:?} but got {:?} in fold {fold:?}",
260 self.target_vid, fold.to_vid
261 );
262
263 let within_optional_scope = false;
267 let folded = FoldState::FoldedMandatory;
268
269 EdgeInfo {
270 eid,
271 parameters: fold.parameters.clone(),
272 optional: false,
273 recursive: None,
274 folded,
275 destination: NeighborInfo {
276 query: self.query.clone(),
277 execution_frontier: Bound::Excluded(self.target_vid),
278 starting_vertex: self.origin_vid(),
279 neighbor_vertex: fold.to_vid,
280 neighbor_path: vec![eid],
281 within_optional_scope,
282 locally_non_binding_filters: false,
283 },
284 }
285 }
286 }
287 }
288}
289
290#[non_exhaustive]
292#[derive(Debug, Clone, PartialEq, Eq)]
293pub struct EdgeInfo {
294 eid: Eid,
295 parameters: EdgeParameters,
296 optional: bool,
297 recursive: Option<Recursive>,
298 folded: FoldState,
299 destination: NeighborInfo,
300}
301
302impl EdgeInfo {
303 #[allow(dead_code)] #[inline]
306 pub fn eid(&self) -> Eid {
307 self.eid
308 }
309
310 #[allow(dead_code)] #[inline]
313 pub fn parameters(&self) -> &EdgeParameters {
314 &self.parameters
315 }
316
317 #[allow(dead_code)] #[inline]
320 pub fn destination(&self) -> &NeighborInfo {
321 &self.destination
322 }
323
324 #[inline]
326 pub fn is_mandatory(&self) -> bool {
327 self.folded != FoldState::FoldedOptional && !self.optional && self.recursive.is_none()
328 }
329}
330
331#[non_exhaustive]
333#[derive(Debug, Clone, PartialEq, Eq)]
334pub struct NeighborInfo {
335 query: InterpretedQuery,
336 execution_frontier: Bound<Vid>, starting_vertex: Vid,
338 neighbor_vertex: Vid,
339 neighbor_path: Vec<Eid>,
340
341 within_optional_scope: bool,
355
356 locally_non_binding_filters: bool,
359}
360
361impl InternalVertexInfo for NeighborInfo {
362 #[inline]
363 fn query(&self) -> &InterpretedQuery {
364 &self.query
365 }
366
367 #[inline]
368 fn non_binding_filters(&self) -> bool {
369 self.within_optional_scope || self.locally_non_binding_filters
370 }
371
372 #[inline]
373 fn execution_frontier(&self) -> Bound<Vid> {
374 self.execution_frontier
375 }
376
377 #[inline]
378 fn current_vertex(&self) -> &IRVertex {
379 &self.current_component().vertices[&self.neighbor_vertex]
380 }
381
382 #[inline]
383 fn current_component(&self) -> &IRQueryComponent {
384 &self.query.indexed_query.vids[&self.neighbor_vertex]
385 }
386
387 #[inline]
388 fn starting_component(&self) -> &IRQueryComponent {
389 &self.query.indexed_query.vids[&self.starting_vertex]
390 }
391
392 #[inline]
393 fn query_variables(&self) -> &BTreeMap<Arc<str>, FieldValue> {
394 self.query.arguments.as_ref()
395 }
396
397 fn make_non_folded_edge_info(&self, edge: &IREdge) -> EdgeInfo {
398 let mut neighbor_path = self.neighbor_path.clone();
399 neighbor_path.push(edge.eid);
400
401 let neighboring_info = NeighborInfo {
402 query: self.query.clone(),
403 execution_frontier: self.execution_frontier,
404 starting_vertex: self.starting_vertex,
405 neighbor_vertex: edge.to_vid,
406 neighbor_path,
407 within_optional_scope: self.within_optional_scope,
408 locally_non_binding_filters: check_locally_non_binding_filters_for_edge(edge),
409 };
410 EdgeInfo {
411 eid: edge.eid,
412 parameters: edge.parameters.clone(),
413 optional: edge.optional,
414 recursive: edge.recursive.clone(),
415 folded: FoldState::None,
416 destination: neighboring_info,
417 }
418 }
419
420 fn make_folded_edge_info(&self, fold: &IRFold) -> EdgeInfo {
421 let at_least_one_element_required =
422 filters::fold_requires_at_least_one_element(&self.query.arguments, fold);
423
424 let mut neighbor_path = self.neighbor_path.clone();
425 neighbor_path.push(fold.eid);
426 let neighboring_info = NeighborInfo {
427 query: self.query.clone(),
428 execution_frontier: self.execution_frontier,
429 starting_vertex: self.starting_vertex,
430 neighbor_vertex: fold.to_vid,
431 neighbor_path,
432 within_optional_scope: self.within_optional_scope || !at_least_one_element_required,
433 locally_non_binding_filters: false,
434 };
435 EdgeInfo {
436 eid: fold.eid,
437 parameters: fold.parameters.clone(),
438 optional: false,
439 recursive: None,
440 folded: if at_least_one_element_required {
441 FoldState::FoldedMandatory
442 } else {
443 FoldState::FoldedOptional
444 },
445 destination: neighboring_info,
446 }
447 }
448}
449
450fn check_locally_non_binding_filters_for_edge(edge: &IREdge) -> bool {
458 edge.recursive.as_ref().map(|r| r.depth.get() >= 2).unwrap_or(false)
459}
460
461#[derive(Debug, Clone, Copy, PartialEq, Eq)]
462pub(super) enum FoldState {
463 None,
465
466 FoldedOptional,
468
469 FoldedMandatory,
474}
475
476#[cfg(test)]
477mod tests;