Skip to content

Commit a07da04

Browse files
authored
Lower & evaluate graph match label negation,conjunction disjunction (#568)
1 parent 328f2d1 commit a07da04

File tree

6 files changed

+143
-55
lines changed

6 files changed

+143
-55
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- *BREAKING* partiql-ast: Removes disused `Sexp` AST node
1414

1515
### Added
16+
- Added lowering and evaluation of graph `MATCH` label negation, conjunction, and disjunction
1617

1718
### Removed
1819

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ pub enum LabelFilter<GT: GraphTypes> {
2525
#[default]
2626
Always,
2727
Named(GT::Label),
28+
Negated(Box<LabelFilter<GT>>),
29+
Disjunction(Vec<LabelFilter<GT>>),
30+
Conjunction(Vec<LabelFilter<GT>>),
2831
Never,
2932
}
3033

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

Lines changed: 94 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ impl GraphPlanConvert<StringGraphTypes, SimpleGraphTypes> for SimpleGraphEngine
8181
LabelFilter::Never
8282
}
8383
}
84+
LabelFilter::Negated(inner) => {
85+
LabelFilter::Negated(Box::new(self.convert_label_filter(inner)))
86+
}
87+
LabelFilter::Conjunction(inner) => {
88+
let inner = inner.iter().map(|l| self.convert_label_filter(l));
89+
LabelFilter::Conjunction(inner.collect())
90+
}
91+
LabelFilter::Disjunction(inner) => {
92+
let inner = inner.iter().map(|l| self.convert_label_filter(l));
93+
LabelFilter::Disjunction(inner.collect())
94+
}
8495
}
8596
}
8697

@@ -100,6 +111,17 @@ impl GraphPlanConvert<SimpleGraphTypes, StringGraphTypes> for SimpleGraphEngine
100111
LabelFilter::Named(l) => {
101112
LabelFilter::Named(self.graph.labels.resolve(&l.0).to_string())
102113
}
114+
LabelFilter::Negated(inner) => {
115+
LabelFilter::Negated(Box::new(self.convert_label_filter(inner)))
116+
}
117+
LabelFilter::Conjunction(inner) => {
118+
let inner = inner.iter().map(|l| self.convert_label_filter(l));
119+
LabelFilter::Conjunction(inner.collect())
120+
}
121+
LabelFilter::Disjunction(inner) => {
122+
let inner = inner.iter().map(|l| self.convert_label_filter(l));
123+
LabelFilter::Disjunction(inner.collect())
124+
}
103125
}
104126
}
105127

@@ -148,17 +170,13 @@ impl TripleScan<SimpleGraphTypes> for SimpleGraph {
148170
// scan directed triples left to right and right to left
149171
self.g_dir
150172
.iter()
151-
.filter(move |(_, e, _)| EdgeMatcher::matches(self, &spec.e, e))
173+
.filter(move |(_, e, _)| self.edge_matches(&spec.e, e))
152174
.flat_map(move |(l, e, r)| {
153175
let mut res = Vec::with_capacity(2);
154-
if NodeMatcher::matches(self, &spec.lhs, l)
155-
&& NodeMatcher::matches(self, &spec.rhs, r)
156-
{
176+
if self.node_matches(&spec.lhs, l) && self.node_matches(&spec.rhs, r) {
157177
res.push(build_triple(&(*l, *e, *r)))
158178
}
159-
if NodeMatcher::matches(self, &spec.rhs, l)
160-
&& NodeMatcher::matches(self, &spec.lhs, r)
161-
{
179+
if self.node_matches(&spec.rhs, l) && self.node_matches(&spec.lhs, r) {
162180
res.push(reverse_triple(&(*l, *e, *r)))
163181
}
164182

@@ -173,17 +191,13 @@ impl TripleScan<SimpleGraphTypes> for SimpleGraph {
173191
// scan undirected triples
174192
self.g_undir
175193
.iter()
176-
.filter(move |(_, e, _)| EdgeMatcher::matches(self, &spec.e, e))
194+
.filter(move |(_, e, _)| self.edge_matches(&spec.e, e))
177195
.flat_map(move |(l, e, r)| {
178196
let mut res = Vec::with_capacity(2);
179-
if NodeMatcher::matches(self, &spec.lhs, l)
180-
&& NodeMatcher::matches(self, &spec.rhs, r)
181-
{
197+
if self.node_matches(&spec.lhs, l) && self.node_matches(&spec.rhs, r) {
182198
res.push(build_triple(&(*l, *e, *r)))
183199
}
184-
if NodeMatcher::matches(self, &spec.rhs, l)
185-
&& NodeMatcher::matches(self, &spec.lhs, r)
186-
{
200+
if self.node_matches(&spec.rhs, l) && self.node_matches(&spec.lhs, r) {
187201
res.push(reverse_triple(&(*l, *e, *r)))
188202
}
189203

@@ -194,7 +208,7 @@ impl TripleScan<SimpleGraphTypes> for SimpleGraph {
194208
fn get(&self, spec: &NodeFilter<SimpleGraphTypes>) -> Vec<GNodeId> {
195209
(0..self.nodes.len())
196210
.map(GNodeId)
197-
.filter(|node| NodeMatcher::matches(self, spec, node))
211+
.filter(|node| self.node_matches(spec, node))
198212
.collect()
199213
}
200214
}
@@ -222,11 +236,17 @@ trait TripleMatcher<GT: GraphTypes> {
222236
}
223237

224238
trait NodeMatcher<GT: GraphTypes> {
225-
fn matches(&self, spec: &NodeFilter<GT>, node: &GT::NodeId) -> bool;
239+
fn node_matches(&self, spec: &NodeFilter<GT>, node: &GT::NodeId) -> bool;
240+
fn node_label_matches(&self, spec: &LabelFilter<GT>, node: &GT::NodeId) -> bool;
241+
#[allow(dead_code)] // TODO implement value filters for `where`
242+
fn node_value_matches(&self, spec: &ValueFilter, node: &GT::NodeId) -> bool;
226243
}
227244

228245
trait EdgeMatcher<GT: GraphTypes> {
229-
fn matches(&self, spec: &EdgeFilter<GT>, edge: &GT::EdgeId) -> bool;
246+
fn edge_matches(&self, spec: &EdgeFilter<GT>, edge: &GT::EdgeId) -> bool;
247+
fn edge_label_matches(&self, spec: &LabelFilter<GT>, edge: &GT::EdgeId) -> bool;
248+
#[allow(dead_code)] // TODO implement value filters for `where`
249+
fn edge_value_matches(&self, spec: &ValueFilter, edge: &GT::EdgeId) -> bool;
230250
}
231251

232252
impl TripleMatcher<SimpleGraphTypes> for SimpleGraph {
@@ -236,32 +256,82 @@ impl TripleMatcher<SimpleGraphTypes> for SimpleGraph {
236256
spec: &TripleFilter<SimpleGraphTypes>,
237257
triple: &Triple<SimpleGraphTypes>,
238258
) -> bool {
239-
NodeMatcher::matches(self, &spec.lhs, &triple.lhs)
240-
&& EdgeMatcher::matches(self, &spec.e, &triple.e)
241-
&& NodeMatcher::matches(self, &spec.rhs, &triple.rhs)
259+
self.node_matches(&spec.lhs, &triple.lhs)
260+
&& self.edge_matches(&spec.e, &triple.e)
261+
&& self.node_matches(&spec.rhs, &triple.rhs)
242262
}
243263
}
244264

245265
impl NodeMatcher<SimpleGraphTypes> for SimpleGraph {
246266
#[inline]
247-
fn matches(&self, spec: &NodeFilter<SimpleGraphTypes>, node: &GNodeId) -> bool {
267+
fn node_matches(&self, spec: &NodeFilter<SimpleGraphTypes>, node: &GNodeId) -> bool {
248268
let NodeFilter { label, filter } = spec;
249269
match (label, filter) {
250270
(LabelFilter::Never, _) => false,
251271
(LabelFilter::Always, ValueFilter::Always) => true,
252-
(LabelFilter::Named(l), ValueFilter::Always) => self.nodes[node.0].labels.0.contains(l),
272+
//TODO when ValueFilter has other variants:
273+
// (LabelFilter::Always, v) => self.node_value_matches(v, node),
274+
(l, ValueFilter::Always) => self.node_label_matches(l, node),
275+
//TODO when ValueFilter has other variants:
276+
// (l, v) => self.node_label_matches(l, node) && self.node_value_matches(v, node),
277+
}
278+
}
279+
280+
fn node_label_matches(&self, spec: &LabelFilter<SimpleGraphTypes>, node: &GNodeId) -> bool {
281+
match spec {
282+
LabelFilter::Never => false,
283+
LabelFilter::Always => true,
284+
LabelFilter::Named(l) => self.nodes[node.0].labels.0.contains(l),
285+
LabelFilter::Negated(inner) => !self.node_label_matches(inner.as_ref(), node),
286+
LabelFilter::Disjunction(inner) => {
287+
inner.iter().any(|l| self.node_label_matches(l, node))
288+
}
289+
LabelFilter::Conjunction(inner) => {
290+
inner.iter().all(|l| self.node_label_matches(l, node))
291+
}
292+
}
293+
}
294+
295+
fn node_value_matches(&self, spec: &ValueFilter, _: &GNodeId) -> bool {
296+
match spec {
297+
ValueFilter::Always => true,
253298
}
254299
}
255300
}
256301

257302
impl EdgeMatcher<SimpleGraphTypes> for SimpleGraph {
258303
#[inline]
259-
fn matches(&self, spec: &EdgeFilter<SimpleGraphTypes>, edge: &GEdgeId) -> bool {
304+
fn edge_matches(&self, spec: &EdgeFilter<SimpleGraphTypes>, edge: &GEdgeId) -> bool {
260305
let EdgeFilter { label, filter } = spec;
261306
match (label, filter) {
262307
(LabelFilter::Never, _) => false,
263308
(LabelFilter::Always, ValueFilter::Always) => true,
264-
(LabelFilter::Named(l), ValueFilter::Always) => self.edges[edge.0].labels.0.contains(l),
309+
//TODO when ValueFilter has other variants:
310+
// (LabelFilter::Always, v) => self.edge_value_matches(v, edge),
311+
(l, ValueFilter::Always) => self.edge_label_matches(l, edge),
312+
//TODO when ValueFilter has other variants:
313+
// (l, v) => self.edge_label_matches(l, edge) && self.edge_value_matches(v, edge),
314+
}
315+
}
316+
317+
fn edge_label_matches(&self, spec: &LabelFilter<SimpleGraphTypes>, edge: &GEdgeId) -> bool {
318+
match spec {
319+
LabelFilter::Never => false,
320+
LabelFilter::Always => true,
321+
LabelFilter::Named(l) => self.edges[edge.0].labels.0.contains(l),
322+
LabelFilter::Negated(inner) => !self.edge_label_matches(inner.as_ref(), edge),
323+
LabelFilter::Disjunction(inner) => {
324+
inner.iter().any(|l| self.edge_label_matches(l, edge))
325+
}
326+
LabelFilter::Conjunction(inner) => {
327+
inner.iter().all(|l| self.edge_label_matches(l, edge))
328+
}
329+
}
330+
}
331+
332+
fn edge_value_matches(&self, spec: &ValueFilter, _: &GEdgeId) -> bool {
333+
match spec {
334+
ValueFilter::Always => true,
265335
}
266336
}
267337
}

partiql-eval/src/plan.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,17 @@ fn plan_graph_plan(
813813
logical::graph::LabelFilter::Always => physical::LabelFilter::Always,
814814
logical::graph::LabelFilter::Never => physical::LabelFilter::Never,
815815
logical::graph::LabelFilter::Named(n) => physical::LabelFilter::Named(n.clone()),
816+
logical::graph::LabelFilter::Negated(inner) => {
817+
physical::LabelFilter::Negated(Box::new(plan_label_filter(inner)?))
818+
}
819+
logical::graph::LabelFilter::Conjunction(inner) => {
820+
let inner: Result<Vec<_>, _> = inner.iter().map(plan_label_filter).collect();
821+
physical::LabelFilter::Conjunction(inner?)
822+
}
823+
logical::graph::LabelFilter::Disjunction(inner) => {
824+
let inner: Result<Vec<_>, _> = inner.iter().map(plan_label_filter).collect();
825+
physical::LabelFilter::Disjunction(inner?)
826+
}
816827
})
817828
}
818829

partiql-logical-planner/src/graph.rs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -270,35 +270,6 @@ impl GraphToLogical {
270270
}
271271
}
272272

273-
fn plan_graph_pattern_label(
274-
&self,
275-
label: Option<&ast::GraphMatchLabel>,
276-
) -> Result<LabelFilter, String> {
277-
if let Some(label) = label {
278-
match label {
279-
ast::GraphMatchLabel::Name(n) => Ok(LabelFilter::Named(n.value.clone())),
280-
ast::GraphMatchLabel::Wildcard => Ok(LabelFilter::Always),
281-
ast::GraphMatchLabel::Negated(_) => {
282-
not_yet_implemented_result!(
283-
"MATCH expression label negation is not yet supported."
284-
);
285-
}
286-
ast::GraphMatchLabel::Conjunction(_) => {
287-
not_yet_implemented_result!(
288-
"MATCH expression label conjunction is not yet supported."
289-
);
290-
}
291-
ast::GraphMatchLabel::Disjunction(_) => {
292-
not_yet_implemented_result!(
293-
"MATCH expression label disjunction is not yet supported."
294-
);
295-
}
296-
}
297-
} else {
298-
Ok(LabelFilter::Always)
299-
}
300-
}
301-
302273
fn plan_graph_pattern_part_node(
303274
&self,
304275
node: &ast::GraphMatchNode,
@@ -310,7 +281,7 @@ impl GraphToLogical {
310281
None => self.graph_id.node(),
311282
Some(v) => v.value.clone(),
312283
};
313-
let label = self.plan_graph_pattern_label(node.label.as_deref())?;
284+
let label = plan_graph_pattern_label(node.label.as_deref())?;
314285
let node_match = NodeMatch {
315286
binder: BindSpec(binder),
316287
spec: NodeFilter {
@@ -341,7 +312,7 @@ impl GraphToLogical {
341312
None => self.graph_id.node(),
342313
Some(v) => v.value.clone(),
343314
};
344-
let label = self.plan_graph_pattern_label(edge.label.as_deref())?;
315+
let label = plan_graph_pattern_label(edge.label.as_deref())?;
345316
let edge_match = EdgeMatch {
346317
binder: BindSpec(binder),
347318
spec: EdgeFilter {
@@ -352,3 +323,32 @@ impl GraphToLogical {
352323
Ok(vec![MatchElement::Edge(direction, edge_match)])
353324
}
354325
}
326+
327+
fn plan_graph_pattern_label(label: Option<&ast::GraphMatchLabel>) -> Result<LabelFilter, String> {
328+
if let Some(label) = label {
329+
match label {
330+
ast::GraphMatchLabel::Name(n) => Ok(LabelFilter::Named(n.value.clone())),
331+
ast::GraphMatchLabel::Wildcard => Ok(LabelFilter::Always),
332+
ast::GraphMatchLabel::Negated(l) => {
333+
let inner = plan_graph_pattern_label(Some(l.as_ref()))?;
334+
Ok(LabelFilter::Negated(Box::new(inner)))
335+
}
336+
ast::GraphMatchLabel::Conjunction(inner) => {
337+
let inner: Result<Vec<_>, _> = inner
338+
.iter()
339+
.map(|l| plan_graph_pattern_label(Some(l)))
340+
.collect();
341+
Ok(LabelFilter::Conjunction(inner?))
342+
}
343+
ast::GraphMatchLabel::Disjunction(inner) => {
344+
let inner: Result<Vec<_>, _> = inner
345+
.iter()
346+
.map(|l| plan_graph_pattern_label(Some(l)))
347+
.collect();
348+
Ok(LabelFilter::Disjunction(inner?))
349+
}
350+
}
351+
} else {
352+
Ok(LabelFilter::Always)
353+
}
354+
}

partiql-logical/src/graph/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ pub enum LabelFilter {
3030
#[default]
3131
Always,
3232
Named(String),
33+
Negated(Box<LabelFilter>),
34+
Conjunction(Vec<LabelFilter>),
35+
Disjunction(Vec<LabelFilter>),
3336
Never,
3437
}
3538

0 commit comments

Comments
 (0)