Skip to content

Commit 3b052eb

Browse files
authored
Lower & evaluate Graph Match Modes (#575)
1 parent 5711c11 commit 3b052eb

File tree

12 files changed

+341
-136
lines changed

12 files changed

+341
-136
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
### Added
1616
- Added lowering and evaluation of graph `MATCH` label negation, conjunction, and disjunction
1717
- Added lowering and evaluation of graph `MATCH` `WHERE` clauses
18+
- Added lowering and evaluation of graph `MATCH` modes (i.e., `WALK`, `TRAIL`, `ACYCLIC`, `SIMPLE`)
1819

1920
### Removed
2021

partiql-ast/src/ast/graph.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub struct GraphMatch {
2424
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2525
pub struct GraphPattern {
2626
#[visit(skip)]
27-
pub mode: Option<GraphMatchMode>,
27+
pub match_mode: Option<GraphMatchMode>,
2828
pub patterns: Vec<AstNode<GraphPathPattern>>,
2929
#[visit(skip)]
3030
pub keep: Option<GraphPathPrefix>,
@@ -34,7 +34,9 @@ pub struct GraphPattern {
3434
#[derive(Clone, Debug, PartialEq, Eq)]
3535
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3636
pub enum GraphMatchMode {
37+
/// Edges are not allowed to bind to more than one edge variable in a path
3738
DifferentEdges,
39+
/// No restrictions on binding of edges or vertices
3840
RepeatableElements,
3941
}
4042

@@ -89,8 +91,9 @@ pub enum GraphTableExport {
8991
}
9092

9193
/// The direction of an edge
94+
///
9295
/// | Orientation | Edge pattern | Abbreviation |
93-
/// |---------------------------+--------------+--------------|
96+
/// |---------------------------|--------------|--------------|
9497
/// | Pointing left | <−[ spec ]− | <− |
9598
/// | Undirected | ~[ spec ]~ | ~ |
9699
/// | Pointing right | −[ spec ]−> | −> |
@@ -118,20 +121,17 @@ pub struct GraphMatchQuantifier {
118121
pub upper: Option<NonZeroU32>,
119122
}
120123

121-
/// A path mode
122-
/// | Keyword | Description
123-
/// |----------------+--------------
124-
/// | WALK |
125-
/// | TRAIL | No repeated edges.
126-
/// | ACYCLIC | No repeated nodes.
127-
/// | SIMPLE | No repeated nodes, except that the first and last nodes may be the same.
128-
124+
/// Filters paths. Options other than `Walk` ensure a finite number of matches.
129125
#[derive(Clone, Debug, PartialEq, Eq)]
130126
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
131127
pub enum GraphPathMode {
128+
/// No filtering of edges/nodes.
132129
Walk,
130+
/// No repeated edges.
133131
Trail,
132+
/// No repeated nodes.
134133
Acyclic,
134+
/// No repeated nodes, except that the first and last nodes may be the same.
135135
Simple,
136136
}
137137

partiql-ast/src/pretty/graph.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,14 @@ impl PrettyDoc for GraphPattern {
184184
A: Clone,
185185
{
186186
let GraphPattern {
187-
mode,
187+
match_mode,
188188
patterns,
189189
keep,
190190
where_clause,
191191
} = &self;
192192
let patterns = pretty_list(patterns, PRETTY_INDENT_MINOR_NEST, arena).group();
193193
let parts = [
194-
mode.as_ref().map(|inner| inner.pretty_doc(arena)),
194+
match_mode.as_ref().map(|inner| inner.pretty_doc(arena)),
195195
Some(patterns),
196196
keep.as_ref().map(|keep| {
197197
arena.intersperse([arena.text("KEEP"), keep.pretty_doc(arena)], arena.space())

partiql-eval/src/eval/expr/graph_match.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl BindEvalExpr for EvalGraphMatch {
4949
mod tests {
5050
use crate::eval::expr::{BindEvalExpr, EvalGlobalVarRef, EvalGraphMatch};
5151
use crate::eval::graph::plan::{
52-
BindSpec, DirectionFilter, EdgeFilter, LabelFilter, NodeFilter, NodeMatch,
52+
BindSpec, DirectionFilter, EdgeFilter, LabelFilter, NodeFilter, NodeMatch, PathMode,
5353
PathPatternMatch, TripleFilter, TripleStepFilter, TripleStepMatch, ValueFilter,
5454
};
5555
use crate::eval::graph::string_graph::StringGraphTypes;
@@ -202,6 +202,7 @@ mod tests {
202202
binders,
203203
spec,
204204
filter: ValueFilter::Always,
205+
path_mode: PathMode::Walk,
205206
};
206207

207208
test_graph(matcher.into(), "<< >>")
@@ -230,6 +231,7 @@ mod tests {
230231
binders,
231232
spec,
232233
filter: ValueFilter::Always,
234+
path_mode: PathMode::Walk,
233235
};
234236

235237
test_graph(matcher.into(), "<< >>")
@@ -256,6 +258,7 @@ mod tests {
256258
binders,
257259
spec,
258260
filter: ValueFilter::Always,
261+
path_mode: PathMode::Walk,
259262
};
260263

261264
test_graph(matcher.into(), "<< {'x': 2, 'z': 1.2, 'y': 1} >>")
@@ -284,6 +287,7 @@ mod tests {
284287
binders,
285288
spec,
286289
filter: ValueFilter::Always,
290+
path_mode: PathMode::Walk,
287291
};
288292

289293
test_graph(matcher.into(), "<< { }, { } >>")
@@ -312,6 +316,7 @@ mod tests {
312316
binders,
313317
spec,
314318
filter: ValueFilter::Always,
319+
path_mode: PathMode::Walk,
315320
};
316321

317322
test_graph(
@@ -340,6 +345,7 @@ mod tests {
340345
binders,
341346
spec,
342347
filter: Default::default(),
348+
path_mode: PathMode::Walk,
343349
};
344350

345351
let binders = (
@@ -359,6 +365,7 @@ mod tests {
359365
binders,
360366
spec,
361367
filter: Default::default(),
368+
path_mode: PathMode::Walk,
362369
};
363370

364371
let pattern_match = PathPatternMatch::Concat(
@@ -367,6 +374,7 @@ mod tests {
367374
PathPatternMatch::Match(matcher2),
368375
],
369376
Default::default(),
377+
PathMode::Walk,
370378
);
371379

372380
test_graph(
@@ -397,6 +405,7 @@ mod tests {
397405
binders,
398406
spec,
399407
filter: Default::default(),
408+
path_mode: PathMode::Walk,
400409
};
401410

402411
let binders = (
@@ -416,6 +425,7 @@ mod tests {
416425
binders,
417426
spec,
418427
filter: Default::default(),
428+
path_mode: PathMode::Walk,
419429
};
420430

421431
let pattern_match = PathPatternMatch::Concat(
@@ -424,6 +434,7 @@ mod tests {
424434
PathPatternMatch::Match(matcher2),
425435
],
426436
Default::default(),
437+
PathMode::Walk,
427438
);
428439
test_graph(
429440
pattern_match,

partiql-eval/src/eval/graph/engine.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::eval::graph::plan::{
2-
BindSpec, DirectionFilter, GraphPlanConvert, NodeFilter, NodeMatch, TripleFilter,
2+
BindSpec, DirectionFilter, GraphPlanConvert, NodeFilter, NodeMatch, PathMode, TripleFilter,
33
TripleStepFilter, TripleStepMatch, ValueFilter,
44
};
55
use crate::eval::graph::result::{PathPatternNodes, Triple};
@@ -50,6 +50,7 @@ pub trait TripleScan<GT: GraphTypes> {
5050
&self,
5151
binders: &(BindSpec<GT>, BindSpec<GT>, BindSpec<GT>),
5252
spec: &TripleFilter<GT>,
53+
allow_repeated_nodes: bool,
5354
filter: &ValueFilter,
5455
ctx: &dyn EvalContext,
5556
) -> impl Iterator<Item = Triple<GT>>;
@@ -58,6 +59,7 @@ pub trait TripleScan<GT: GraphTypes> {
5859
&self,
5960
binders: &(BindSpec<GT>, BindSpec<GT>, BindSpec<GT>),
6061
spec: &TripleFilter<GT>,
62+
allow_repeated_nodes: bool,
6163
filter: &ValueFilter,
6264
ctx: &dyn EvalContext,
6365
) -> impl Iterator<Item = Triple<GT>>;
@@ -66,6 +68,7 @@ pub trait TripleScan<GT: GraphTypes> {
6668
&self,
6769
binders: &(BindSpec<GT>, BindSpec<GT>, BindSpec<GT>),
6870
spec: &TripleFilter<GT>,
71+
allow_repeated_nodes: bool,
6972
filter: &ValueFilter,
7073
ctx: &dyn EvalContext,
7174
) -> impl Iterator<Item = Triple<GT>>;
@@ -74,6 +77,7 @@ pub trait TripleScan<GT: GraphTypes> {
7477
&self,
7578
binders: &(BindSpec<GT>, BindSpec<GT>, BindSpec<GT>),
7679
spec: &TripleFilter<GT>,
80+
allow_repeated_nodes: bool,
7781
filter: &ValueFilter,
7882
ctx: &dyn EvalContext,
7983
) -> impl Iterator<Item = Triple<GT>>;
@@ -95,6 +99,7 @@ where
9599
binders,
96100
spec: TripleStepFilter { dir, triple },
97101
filter,
102+
path_mode,
98103
} = spec;
99104
let (to_from, undirected, from_to) = match dir {
100105
DirectionFilter::L => (true, false, false),
@@ -106,19 +111,38 @@ where
106111
DirectionFilter::LUR => (true, true, true),
107112
};
108113

114+
let allow_repeated_nodes = !matches!(path_mode, PathMode::Acyclic);
109115
let mut result = vec![];
110116
if undirected {
111-
result.extend(self.scan_undirected(binders, triple, filter, ctx));
117+
result.extend(self.scan_undirected(binders, triple, allow_repeated_nodes, filter, ctx));
112118
}
113119
match (from_to, to_from) {
114120
(true, true) => {
115-
result.extend(self.scan_directed_both(binders, triple, filter, ctx));
121+
result.extend(self.scan_directed_both(
122+
binders,
123+
triple,
124+
allow_repeated_nodes,
125+
filter,
126+
ctx,
127+
));
116128
}
117129
(true, false) => {
118-
result.extend(self.scan_directed_from_to(binders, triple, filter, ctx));
130+
result.extend(self.scan_directed_from_to(
131+
binders,
132+
triple,
133+
allow_repeated_nodes,
134+
filter,
135+
ctx,
136+
));
119137
}
120138
(false, true) => {
121-
result.extend(self.scan_directed_to_from(binders, triple, filter, ctx));
139+
result.extend(self.scan_directed_to_from(
140+
binders,
141+
triple,
142+
allow_repeated_nodes,
143+
filter,
144+
ctx,
145+
));
122146
}
123147
(false, false) => {}
124148
}

0 commit comments

Comments
 (0)