Skip to content

Commit 672d0c2

Browse files
committed
Add lowering from AST to logical plan for graph MATCH
1 parent 2bf9bbf commit 672d0c2

File tree

12 files changed

+419
-120
lines changed

12 files changed

+419
-120
lines changed

partiql-ast/src/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,7 @@ pub struct GraphMatchEdge {
945945
#[derive(Visit, Clone, Debug, PartialEq)]
946946
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
947947
pub struct GraphMatchPattern {
948+
/// an optional restrictor for the entire pattern match
948949
#[visit(skip)]
949950
pub restrictor: Option<GraphMatchRestrictor>,
950951
/// an optional quantifier for the entire pattern match

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

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,67 @@ impl BindEvalExpr for EvalGraphMatch {
4646
#[cfg(test)]
4747
mod tests {
4848
use crate::eval::expr::{BindEvalExpr, EvalGlobalVarRef, EvalGraphMatch};
49-
use crate::eval::graph::bind_name::FreshBinder;
5049
use crate::eval::graph::plan::{
51-
BindSpec, DirectionFilter, EdgeFilter, ElementFilterBuilder, NodeFilter, NodeMatch,
52-
PathMatch, PathPatternMatch, StepFilter, TripleFilter,
50+
BindSpec, DirectionFilter, EdgeFilter, LabelFilter, NodeFilter, NodeMatch, PathMatch,
51+
PathPatternMatch, StepFilter, TripleFilter, ValueFilter,
5352
};
5453
use crate::eval::graph::string_graph::StringGraphTypes;
54+
use crate::eval::graph::types::GraphTypes;
5555
use crate::eval::{BasicContext, MapBindings};
5656
use crate::test_value::TestValue;
5757
use partiql_catalog::context::SystemContext;
5858
use partiql_common::pretty::ToPretty;
59-
59+
use partiql_logical::graph::bind_name::FreshBinder;
6060
use partiql_value::{tuple, BindingsName, DateTime, Value};
6161

62+
impl<GT: GraphTypes> From<PathMatch<GT>> for PathPatternMatch<GT> {
63+
fn from(value: PathMatch<GT>) -> Self {
64+
Self::Match(value)
65+
}
66+
}
67+
68+
impl<GT: GraphTypes> From<NodeMatch<GT>> for PathPatternMatch<GT> {
69+
fn from(value: NodeMatch<GT>) -> Self {
70+
Self::Node(value)
71+
}
72+
}
73+
74+
pub trait ElementFilterBuilder<GT: GraphTypes> {
75+
fn any() -> Self;
76+
fn labeled(label: GT::Label) -> Self;
77+
}
78+
79+
impl<GT: GraphTypes> ElementFilterBuilder<GT> for NodeFilter<GT> {
80+
fn any() -> Self {
81+
Self {
82+
label: LabelFilter::Always,
83+
filter: ValueFilter::Always,
84+
}
85+
}
86+
87+
fn labeled(label: GT::Label) -> Self {
88+
Self {
89+
label: LabelFilter::Named(label),
90+
filter: ValueFilter::Always,
91+
}
92+
}
93+
}
94+
95+
impl<GT: GraphTypes> ElementFilterBuilder<GT> for EdgeFilter<GT> {
96+
fn any() -> Self {
97+
Self {
98+
label: LabelFilter::Always,
99+
filter: ValueFilter::Always,
100+
}
101+
}
102+
fn labeled(label: GT::Label) -> Self {
103+
Self {
104+
label: LabelFilter::Named(label),
105+
filter: ValueFilter::Always,
106+
}
107+
}
108+
}
109+
62110
/*
63111
A simple 3-node, 3-edge graph which is intended to be able to be exactly matched by:
64112
```(graph MATCH

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use crate::eval::graph::bind_name::BindNameExt;
21
use crate::eval::graph::engine::GraphEngine;
32
use crate::eval::graph::result::{
4-
GraphElement, NodeBinding, PathBinding, PathPatternBinding, PathPatternNodes,
3+
GraphElement, NodeBinding, PathBinding, PathPatternBinding, PathPatternNodes, Triple,
54
};
5+
use partiql_logical::graph::bind_name::BindNameExt;
66

77
use fxhash::FxBuildHasher;
88
use indexmap::IndexMap;
@@ -92,7 +92,20 @@ impl<GT: GraphTypes, G: GraphEngine<GT>> GraphEvaluator<GT, G> {
9292
fn eval_path_pattern(&self, matcher: PathPatternMatch<GT>) -> PathPatternBinding<GT> {
9393
match matcher {
9494
PathPatternMatch::Node(n) => self.eval_node(n).into(),
95-
PathPatternMatch::Match(m) => self.eval_path(m).into(),
95+
PathPatternMatch::Match(m) => {
96+
let PathBinding {
97+
matcher,
98+
mut bindings,
99+
} = self.eval_path(m);
100+
101+
// if edge is cyclic, filter triples
102+
let (n1, _, n2) = &matcher.binders;
103+
if n1 == n2 {
104+
bindings.retain(|Triple { lhs, e: _, rhs }| lhs == rhs)
105+
}
106+
107+
PathBinding { matcher, bindings }.into()
108+
}
96109
PathPatternMatch::Concat(ms) => ms
97110
.into_iter()
98111
.map(|p| self.eval_path_pattern(p))

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
pub(crate) mod bind_name;
21
pub(crate) mod engine;
32
pub(crate) mod evaluator;
43
pub(crate) mod plan;

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

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::fmt::Debug;
33
use std::hash::Hash;
44

55
/// A plan specification for an edge's direction filtering.
6-
#[allow(dead_code)] // TODO remove once graph planning is implemented
76
#[allow(clippy::upper_case_acronyms)]
87
#[derive(Debug, Clone, Copy)]
98
pub enum DirectionFilter {
@@ -66,29 +65,13 @@ pub struct StepFilter<GT: GraphTypes> {
6665
pub triple: TripleFilter<GT>,
6766
}
6867

69-
/// A plan specification for 'path patterns' (i.e., sequences of 'node edge node's) matching.
70-
#[allow(dead_code)] // TODO remove once graph planning is implemented
71-
#[derive(Debug, Clone)]
72-
pub struct PathPatternFilter<GT: GraphTypes> {
73-
pub head: NodeFilter<GT>,
74-
pub tail: Vec<(DirectionFilter, EdgeFilter<GT>, NodeFilter<GT>)>,
75-
}
76-
7768
/// A plan specification for node matching.
7869
#[derive(Debug, Clone)]
7970
pub struct NodeMatch<GT: GraphTypes> {
8071
pub binder: BindSpec<GT>,
8172
pub spec: NodeFilter<GT>,
8273
}
8374

84-
/// A plan specification for edge matching.
85-
#[allow(dead_code)] // TODO remove once graph planning is implemented
86-
#[derive(Debug, Clone)]
87-
pub struct EdgeMatch<GT: GraphTypes> {
88-
pub binder: BindSpec<GT>,
89-
pub spec: EdgeFilter<GT>,
90-
}
91-
9275
/// A plan specification for path (i.e., node, edge, node) matching.
9376
#[derive(Debug, Clone)]
9477
pub struct PathMatch<GT: GraphTypes> {
@@ -97,63 +80,13 @@ pub struct PathMatch<GT: GraphTypes> {
9780
}
9881

9982
/// A plan specification for path patterns (i.e., sequences of [`PathMatch`]s) matching.
100-
#[allow(dead_code)] // TODO remove once graph planning is implemented
10183
#[derive(Debug, Clone)]
10284
pub enum PathPatternMatch<GT: GraphTypes> {
10385
Node(NodeMatch<GT>),
10486
Match(PathMatch<GT>),
10587
Concat(Vec<PathPatternMatch<GT>>),
10688
}
10789

108-
impl<GT: GraphTypes> From<PathMatch<GT>> for PathPatternMatch<GT> {
109-
fn from(value: PathMatch<GT>) -> Self {
110-
Self::Match(value)
111-
}
112-
}
113-
114-
impl<GT: GraphTypes> From<NodeMatch<GT>> for PathPatternMatch<GT> {
115-
fn from(value: NodeMatch<GT>) -> Self {
116-
Self::Node(value)
117-
}
118-
}
119-
120-
#[allow(dead_code)] // TODO remove once graph planning is implemented
121-
pub trait ElementFilterBuilder<GT: GraphTypes> {
122-
fn any() -> Self;
123-
fn labeled(label: GT::Label) -> Self;
124-
}
125-
126-
impl<GT: GraphTypes> ElementFilterBuilder<GT> for NodeFilter<GT> {
127-
fn any() -> Self {
128-
Self {
129-
label: LabelFilter::Always,
130-
filter: ValueFilter::Always,
131-
}
132-
}
133-
134-
fn labeled(label: GT::Label) -> Self {
135-
Self {
136-
label: LabelFilter::Named(label),
137-
filter: ValueFilter::Always,
138-
}
139-
}
140-
}
141-
142-
impl<GT: GraphTypes> ElementFilterBuilder<GT> for EdgeFilter<GT> {
143-
fn any() -> Self {
144-
Self {
145-
label: LabelFilter::Always,
146-
filter: ValueFilter::Always,
147-
}
148-
}
149-
fn labeled(label: GT::Label) -> Self {
150-
Self {
151-
label: LabelFilter::Named(label),
152-
filter: ValueFilter::Always,
153-
}
154-
}
155-
}
156-
15790
/// A trait for converting between plans parameterized by different [`GraphTypes`]
15891
pub trait GraphPlanConvert<In: GraphTypes, Out: GraphTypes>: Debug {
15992
fn convert_pathpattern_match(&self, matcher: &PathPatternMatch<In>) -> PathPatternMatch<Out> {
@@ -211,13 +144,6 @@ pub trait GraphPlanConvert<In: GraphTypes, Out: GraphTypes>: Debug {
211144
}
212145
}
213146

214-
#[allow(dead_code)] // TODO remove once graph planning is implemented
215-
fn convert_edge_match(&self, edge: &EdgeMatch<In>) -> EdgeMatch<Out> {
216-
EdgeMatch {
217-
binder: self.convert_binder(&edge.binder),
218-
spec: self.convert_edge_filter(&edge.spec),
219-
}
220-
}
221147
fn convert_label_filter(&self, node: &LabelFilter<In>) -> LabelFilter<Out>;
222148
fn convert_binder(&self, binder: &BindSpec<In>) -> BindSpec<Out>;
223149
}

partiql-eval/src/plan.rs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -794,37 +794,37 @@ impl<'c> EvaluatorPlanner<'c> {
794794
}
795795

796796
fn plan_graph_plan(
797-
pattern: &partiql_logical::PathPatternMatch,
797+
pattern: &logical::graph::PathPatternMatch,
798798
) -> Result<eval::graph::plan::PathPatternMatch<StringGraphTypes>, PlanningError> {
799799
use eval::graph::plan as physical;
800800
use partiql_logical as logical;
801801

802802
fn plan_bind_spec(
803-
pattern: &logical::BindSpec,
803+
pattern: &logical::graph::BindSpec,
804804
) -> Result<physical::BindSpec<StringGraphTypes>, PlanningError> {
805805
Ok(physical::BindSpec(pattern.0.clone()))
806806
}
807807

808808
fn plan_label_filter(
809-
pattern: &logical::LabelFilter,
809+
pattern: &logical::graph::LabelFilter,
810810
) -> Result<physical::LabelFilter<StringGraphTypes>, PlanningError> {
811811
Ok(match pattern {
812-
logical::LabelFilter::Always => physical::LabelFilter::Always,
813-
logical::LabelFilter::Never => physical::LabelFilter::Never,
814-
logical::LabelFilter::Named(n) => physical::LabelFilter::Named(n.clone()),
812+
logical::graph::LabelFilter::Always => physical::LabelFilter::Always,
813+
logical::graph::LabelFilter::Never => physical::LabelFilter::Never,
814+
logical::graph::LabelFilter::Named(n) => physical::LabelFilter::Named(n.clone()),
815815
})
816816
}
817817

818818
fn plan_value_filter(
819-
pattern: &logical::ValueFilter,
819+
pattern: &logical::graph::ValueFilter,
820820
) -> Result<physical::ValueFilter, PlanningError> {
821821
Ok(match pattern {
822-
logical::ValueFilter::Always => physical::ValueFilter::Always,
822+
logical::graph::ValueFilter::Always => physical::ValueFilter::Always,
823823
})
824824
}
825825

826826
fn plan_node_filter(
827-
pattern: &logical::NodeFilter,
827+
pattern: &logical::graph::NodeFilter,
828828
) -> Result<physical::NodeFilter<StringGraphTypes>, PlanningError> {
829829
Ok(physical::NodeFilter {
830830
label: plan_label_filter(&pattern.label)?,
@@ -833,7 +833,7 @@ fn plan_graph_plan(
833833
}
834834

835835
fn plan_edge_filter(
836-
pattern: &logical::EdgeFilter,
836+
pattern: &logical::graph::EdgeFilter,
837837
) -> Result<physical::EdgeFilter<StringGraphTypes>, PlanningError> {
838838
Ok(physical::EdgeFilter {
839839
label: plan_label_filter(&pattern.label)?,
@@ -842,16 +842,16 @@ fn plan_graph_plan(
842842
}
843843

844844
fn plan_step_filter(
845-
pattern: &logical::StepFilter,
845+
pattern: &logical::graph::StepFilter,
846846
) -> Result<physical::StepFilter<StringGraphTypes>, PlanningError> {
847847
let dir = match pattern.dir {
848-
logical::DirectionFilter::L => physical::DirectionFilter::L,
849-
logical::DirectionFilter::R => physical::DirectionFilter::R,
850-
logical::DirectionFilter::U => physical::DirectionFilter::U,
851-
logical::DirectionFilter::LU => physical::DirectionFilter::LU,
852-
logical::DirectionFilter::UR => physical::DirectionFilter::UR,
853-
logical::DirectionFilter::LR => physical::DirectionFilter::LR,
854-
logical::DirectionFilter::LUR => physical::DirectionFilter::LUR,
848+
logical::graph::DirectionFilter::L => physical::DirectionFilter::L,
849+
logical::graph::DirectionFilter::R => physical::DirectionFilter::R,
850+
logical::graph::DirectionFilter::U => physical::DirectionFilter::U,
851+
logical::graph::DirectionFilter::LU => physical::DirectionFilter::LU,
852+
logical::graph::DirectionFilter::UR => physical::DirectionFilter::UR,
853+
logical::graph::DirectionFilter::LR => physical::DirectionFilter::LR,
854+
logical::graph::DirectionFilter::LUR => physical::DirectionFilter::LUR,
855855
};
856856
Ok(physical::StepFilter {
857857
dir,
@@ -860,7 +860,7 @@ fn plan_graph_plan(
860860
}
861861

862862
fn plan_triple_filter(
863-
pattern: &logical::TripleFilter,
863+
pattern: &logical::graph::TripleFilter,
864864
) -> Result<physical::TripleFilter<StringGraphTypes>, PlanningError> {
865865
Ok(physical::TripleFilter {
866866
lhs: plan_node_filter(&pattern.lhs)?,
@@ -870,7 +870,7 @@ fn plan_graph_plan(
870870
}
871871

872872
fn plan_node_match(
873-
pattern: &logical::NodeMatch,
873+
pattern: &logical::graph::NodeMatch,
874874
) -> Result<physical::NodeMatch<StringGraphTypes>, PlanningError> {
875875
Ok(physical::NodeMatch {
876876
binder: plan_bind_spec(&pattern.binder)?,
@@ -879,7 +879,7 @@ fn plan_graph_plan(
879879
}
880880

881881
fn plan_path_match(
882-
pattern: &logical::PathMatch,
882+
pattern: &logical::graph::PathMatch,
883883
) -> Result<physical::PathMatch<StringGraphTypes>, PlanningError> {
884884
let (l, m, r) = &pattern.binders;
885885
let binders = (plan_bind_spec(l)?, plan_bind_spec(m)?, plan_bind_spec(r)?);
@@ -890,16 +890,16 @@ fn plan_graph_plan(
890890
}
891891

892892
fn plan_path_pattern_match(
893-
pattern: &logical::PathPatternMatch,
893+
pattern: &logical::graph::PathPatternMatch,
894894
) -> Result<physical::PathPatternMatch<StringGraphTypes>, PlanningError> {
895895
Ok(match pattern {
896-
logical::PathPatternMatch::Node(n) => {
896+
logical::graph::PathPatternMatch::Node(n) => {
897897
physical::PathPatternMatch::Node(plan_node_match(n)?)
898898
}
899-
logical::PathPatternMatch::Match(m) => {
899+
logical::graph::PathPatternMatch::Match(m) => {
900900
physical::PathPatternMatch::Match(plan_path_match(m)?)
901901
}
902-
logical::PathPatternMatch::Concat(ms) => {
902+
logical::graph::PathPatternMatch::Concat(ms) => {
903903
let ms: Result<Vec<_>, _> = ms.iter().map(plan_path_pattern_match).collect();
904904
physical::PathPatternMatch::Concat(ms?)
905905
}

0 commit comments

Comments
 (0)