Skip to content

Commit fbe3dfe

Browse files
authored
Add value filtering of paths and subpaths (#573)
1 parent eabe171 commit fbe3dfe

File tree

14 files changed

+489
-128
lines changed

14 files changed

+489
-128
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414

1515
### Added
1616
- Added lowering and evaluation of graph `MATCH` label negation, conjunction, and disjunction
17+
- Added lowering and evaluation of graph `MATCH` `WHERE` clauses
1718

1819
### Removed
1920

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

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ 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, PathMatch,
53-
PathPatternMatch, StepFilter, TripleFilter, ValueFilter,
52+
BindSpec, DirectionFilter, EdgeFilter, LabelFilter, NodeFilter, NodeMatch,
53+
PathPatternMatch, TripleFilter, TripleStepFilter, TripleStepMatch, ValueFilter,
5454
};
5555
use crate::eval::graph::string_graph::StringGraphTypes;
5656
use crate::eval::graph::types::GraphTypes;
@@ -62,8 +62,8 @@ mod tests {
6262
use partiql_value::datum::DatumTupleRef;
6363
use partiql_value::{tuple, BindingsName, DateTime, Value};
6464

65-
impl<GT: GraphTypes> From<PathMatch<GT>> for PathPatternMatch<GT> {
66-
fn from(value: PathMatch<GT>) -> Self {
65+
impl<GT: GraphTypes> From<TripleStepMatch<GT>> for PathPatternMatch<GT> {
66+
fn from(value: TripleStepMatch<GT>) -> Self {
6767
Self::Match(value)
6868
}
6969
}
@@ -189,7 +189,7 @@ mod tests {
189189
BindSpec("e".to_string()),
190190
BindSpec(fresh.node()),
191191
);
192-
let spec = StepFilter {
192+
let spec = TripleStepFilter {
193193
dir: DirectionFilter::LUR,
194194
triple: TripleFilter {
195195
lhs: NodeFilter::any(),
@@ -198,7 +198,11 @@ mod tests {
198198
},
199199
};
200200

201-
let matcher: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
201+
let matcher: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
202+
binders,
203+
spec,
204+
filter: ValueFilter::Always,
205+
};
202206

203207
test_graph(matcher.into(), "<< >>")
204208
}
@@ -210,10 +214,10 @@ mod tests {
210214
// Query: (graph MATCH (:foo) -[]- ())
211215
let binders = (
212216
BindSpec(fresh.node()),
213-
BindSpec(fresh.node()),
217+
BindSpec(fresh.edge()),
214218
BindSpec(fresh.node()),
215219
);
216-
let spec = StepFilter {
220+
let spec = TripleStepFilter {
217221
dir: DirectionFilter::LUR,
218222
triple: TripleFilter {
219223
lhs: NodeFilter::labeled("foo".to_string()),
@@ -222,7 +226,11 @@ mod tests {
222226
},
223227
};
224228

225-
let matcher: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
229+
let matcher: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
230+
binders,
231+
spec,
232+
filter: ValueFilter::Always,
233+
};
226234

227235
test_graph(matcher.into(), "<< >>")
228236
}
@@ -235,7 +243,7 @@ mod tests {
235243
BindSpec("z".to_string()),
236244
BindSpec("y".to_string()),
237245
);
238-
let spec = StepFilter {
246+
let spec = TripleStepFilter {
239247
dir: DirectionFilter::L,
240248
triple: TripleFilter {
241249
lhs: NodeFilter::any(),
@@ -244,7 +252,11 @@ mod tests {
244252
},
245253
};
246254

247-
let matcher: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
255+
let matcher: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
256+
binders,
257+
spec,
258+
filter: ValueFilter::Always,
259+
};
248260

249261
test_graph(matcher.into(), "<< {'x': 2, 'z': 1.2, 'y': 1} >>")
250262
}
@@ -259,7 +271,7 @@ mod tests {
259271
BindSpec(fresh.edge()),
260272
BindSpec(fresh.node()),
261273
);
262-
let spec = StepFilter {
274+
let spec = TripleStepFilter {
263275
dir: DirectionFilter::R,
264276
triple: TripleFilter {
265277
lhs: NodeFilter::any(),
@@ -268,7 +280,11 @@ mod tests {
268280
},
269281
};
270282

271-
let matcher: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
283+
let matcher: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
284+
binders,
285+
spec,
286+
filter: ValueFilter::Always,
287+
};
272288

273289
test_graph(matcher.into(), "<< { }, { } >>")
274290
}
@@ -283,7 +299,7 @@ mod tests {
283299
BindSpec("z".to_string()),
284300
BindSpec(fresh.node()),
285301
);
286-
let spec = StepFilter {
302+
let spec = TripleStepFilter {
287303
dir: DirectionFilter::LR,
288304
triple: TripleFilter {
289305
lhs: NodeFilter::any(),
@@ -292,7 +308,11 @@ mod tests {
292308
},
293309
};
294310

295-
let matcher: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
311+
let matcher: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
312+
binders,
313+
spec,
314+
filter: ValueFilter::Always,
315+
};
296316

297317
test_graph(
298318
matcher.into(),
@@ -308,35 +328,46 @@ mod tests {
308328
BindSpec("z1".to_string()),
309329
BindSpec("y1".to_string()),
310330
);
311-
let spec = StepFilter {
331+
let spec = TripleStepFilter {
312332
dir: DirectionFilter::LUR,
313333
triple: TripleFilter {
314334
lhs: NodeFilter::labeled("b".to_string()),
315335
e: EdgeFilter::any(),
316336
rhs: NodeFilter::labeled("a".to_string()),
317337
},
318338
};
319-
let matcher1: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
339+
let matcher1: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
340+
binders,
341+
spec,
342+
filter: Default::default(),
343+
};
320344

321345
let binders = (
322346
BindSpec("y1".to_string()),
323347
BindSpec("z2".to_string()),
324348
BindSpec("y2".to_string()),
325349
);
326-
let spec = StepFilter {
350+
let spec = TripleStepFilter {
327351
dir: DirectionFilter::LUR,
328352
triple: TripleFilter {
329353
lhs: NodeFilter::labeled("a".to_string()),
330354
e: EdgeFilter::any(),
331355
rhs: NodeFilter::labeled("b".to_string()),
332356
},
333357
};
334-
let matcher2: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
358+
let matcher2: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
359+
binders,
360+
spec,
361+
filter: Default::default(),
362+
};
335363

336-
let pattern_match = PathPatternMatch::Concat(vec![
337-
PathPatternMatch::Match(matcher1),
338-
PathPatternMatch::Match(matcher2),
339-
]);
364+
let pattern_match = PathPatternMatch::Concat(
365+
vec![
366+
PathPatternMatch::Match(matcher1),
367+
PathPatternMatch::Match(matcher2),
368+
],
369+
Default::default(),
370+
);
340371

341372
test_graph(
342373
pattern_match,
@@ -354,35 +385,46 @@ mod tests {
354385
BindSpec(fresh.edge()),
355386
BindSpec("x2".to_string()),
356387
);
357-
let spec = StepFilter {
388+
let spec = TripleStepFilter {
358389
dir: DirectionFilter::LUR,
359390
triple: TripleFilter {
360391
lhs: NodeFilter::any(),
361392
e: EdgeFilter::any(),
362393
rhs: NodeFilter::any(),
363394
},
364395
};
365-
let matcher1: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
396+
let matcher1: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
397+
binders,
398+
spec,
399+
filter: Default::default(),
400+
};
366401

367402
let binders = (
368403
BindSpec("x2".to_string()),
369404
BindSpec(fresh.edge()),
370405
BindSpec("x1".to_string()),
371406
);
372-
let spec = StepFilter {
407+
let spec = TripleStepFilter {
373408
dir: DirectionFilter::LUR,
374409
triple: TripleFilter {
375410
lhs: NodeFilter::any(),
376411
e: EdgeFilter::any(),
377412
rhs: NodeFilter::any(),
378413
},
379414
};
380-
let matcher2: PathMatch<StringGraphTypes> = PathMatch { binders, spec };
415+
let matcher2: TripleStepMatch<StringGraphTypes> = TripleStepMatch {
416+
binders,
417+
spec,
418+
filter: Default::default(),
419+
};
381420

382-
let pattern_match = PathPatternMatch::Concat(vec![
383-
PathPatternMatch::Match(matcher1),
384-
PathPatternMatch::Match(matcher2),
385-
]);
421+
let pattern_match = PathPatternMatch::Concat(
422+
vec![
423+
PathPatternMatch::Match(matcher1),
424+
PathPatternMatch::Match(matcher2),
425+
],
426+
Default::default(),
427+
);
386428
test_graph(
387429
pattern_match,
388430
"<< { 'x1': 3, 'x2': 3 }, \

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

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use crate::eval::graph::plan::{
2-
BindSpec, DirectionFilter, GraphPlanConvert, NodeFilter, NodeMatch, PathMatch, StepFilter,
3-
TripleFilter,
2+
BindSpec, DirectionFilter, GraphPlanConvert, NodeFilter, NodeMatch, TripleFilter,
3+
TripleStepFilter, TripleStepMatch, ValueFilter,
44
};
5-
use crate::eval::graph::result::Triple;
5+
use crate::eval::graph::result::{PathPatternNodes, Triple};
66
use crate::eval::graph::string_graph::StringGraphTypes;
77
use crate::eval::graph::types::GraphTypes;
88
use crate::eval::EvalContext;
9+
use fxhash::FxBuildHasher;
10+
use indexmap::IndexSet;
911
use partiql_value::Value;
1012

1113
/// A graph 'engine'; Exposes scanning, node access, and plan conversion to a target graph.
@@ -14,15 +16,28 @@ pub trait GraphEngine<GT: GraphTypes>:
1416
+ GraphPlanConvert<StringGraphTypes, GT>
1517
+ GraphPlanConvert<GT, StringGraphTypes>
1618
+ GraphAccess<GT>
19+
+ GraphFilter<GT>
1720
{
1821
}
1922

2023
/// A trait to scan paths and nodes for a graph.
2124
pub trait GraphScan<GT: GraphTypes> {
22-
fn scan(&self, spec: &PathMatch<GT>, ctx: &dyn EvalContext) -> Vec<Triple<GT>>;
25+
fn scan(&self, spec: &TripleStepMatch<GT>, ctx: &dyn EvalContext) -> Vec<Triple<GT>>;
2326
fn get(&self, spec: &NodeMatch<GT>, ctx: &dyn EvalContext) -> Vec<GT::NodeId>;
2427
}
2528

29+
type FxIndexSet<T> = IndexSet<T, FxBuildHasher>;
30+
31+
pub trait GraphFilter<GT: GraphTypes> {
32+
fn filter_path_nodes(
33+
&self,
34+
binders: &[BindSpec<GT>],
35+
spec: &ValueFilter,
36+
bindings: FxIndexSet<PathPatternNodes<GT>>,
37+
ctx: &dyn EvalContext,
38+
) -> FxIndexSet<PathPatternNodes<GT>>;
39+
}
40+
2641
/// A trait to retrieve named nodes and edges for a graph.
2742
pub trait GraphAccess<GT: GraphTypes> {
2843
fn node(&self, id: &GT::NodeId) -> &Option<Value>;
@@ -35,27 +50,31 @@ pub trait TripleScan<GT: GraphTypes> {
3550
&self,
3651
binders: &(BindSpec<GT>, BindSpec<GT>, BindSpec<GT>),
3752
spec: &TripleFilter<GT>,
53+
filter: &ValueFilter,
3854
ctx: &dyn EvalContext,
3955
) -> impl Iterator<Item = Triple<GT>>;
4056

4157
fn scan_directed_to_from(
4258
&self,
4359
binders: &(BindSpec<GT>, BindSpec<GT>, BindSpec<GT>),
4460
spec: &TripleFilter<GT>,
61+
filter: &ValueFilter,
4562
ctx: &dyn EvalContext,
4663
) -> impl Iterator<Item = Triple<GT>>;
4764

4865
fn scan_directed_both(
4966
&self,
5067
binders: &(BindSpec<GT>, BindSpec<GT>, BindSpec<GT>),
5168
spec: &TripleFilter<GT>,
69+
filter: &ValueFilter,
5270
ctx: &dyn EvalContext,
5371
) -> impl Iterator<Item = Triple<GT>>;
5472

5573
fn scan_undirected(
5674
&self,
5775
binders: &(BindSpec<GT>, BindSpec<GT>, BindSpec<GT>),
5876
spec: &TripleFilter<GT>,
77+
filter: &ValueFilter,
5978
ctx: &dyn EvalContext,
6079
) -> impl Iterator<Item = Triple<GT>>;
6180
fn get(
@@ -71,10 +90,11 @@ where
7190
GT: GraphTypes,
7291
T: TripleScan<GT>,
7392
{
74-
fn scan(&self, spec: &PathMatch<GT>, ctx: &dyn EvalContext) -> Vec<Triple<GT>> {
75-
let PathMatch {
93+
fn scan(&self, spec: &TripleStepMatch<GT>, ctx: &dyn EvalContext) -> Vec<Triple<GT>> {
94+
let TripleStepMatch {
7695
binders,
77-
spec: StepFilter { dir, triple },
96+
spec: TripleStepFilter { dir, triple },
97+
filter,
7898
} = spec;
7999
let (to_from, undirected, from_to) = match dir {
80100
DirectionFilter::L => (true, false, false),
@@ -88,17 +108,17 @@ where
88108

89109
let mut result = vec![];
90110
if undirected {
91-
result.extend(self.scan_undirected(binders, triple, ctx));
111+
result.extend(self.scan_undirected(binders, triple, filter, ctx));
92112
}
93113
match (from_to, to_from) {
94114
(true, true) => {
95-
result.extend(self.scan_directed_both(binders, triple, ctx));
115+
result.extend(self.scan_directed_both(binders, triple, filter, ctx));
96116
}
97117
(true, false) => {
98-
result.extend(self.scan_directed_from_to(binders, triple, ctx));
118+
result.extend(self.scan_directed_from_to(binders, triple, filter, ctx));
99119
}
100120
(false, true) => {
101-
result.extend(self.scan_directed_to_from(binders, triple, ctx));
121+
result.extend(self.scan_directed_to_from(binders, triple, filter, ctx));
102122
}
103123
(false, false) => {}
104124
}

0 commit comments

Comments
 (0)