Skip to content

Commit

Permalink
Experimental support for MATCH as expr.
Browse files Browse the repository at this point in the history
- Still doesn't allow edge as first part of a non-nested pattern
- Requires parens around entire MATCH expression
  • Loading branch information
jpschorr committed Sep 28, 2022
1 parent f4931e5 commit a1215a5
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 247 deletions.
14 changes: 7 additions & 7 deletions partiql-ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ pub enum Expr {
Like(AstNode<Like>),
Between(AstNode<Between>),
In(AstNode<In>),
/// <expr> MATCH <graph_pattern>
GraphMatch(AstNode<GraphMatch>),
Case(AstNode<Case>),
/// Constructors
Struct(AstNode<Struct>),
Expand Down Expand Up @@ -535,8 +537,6 @@ pub enum FromClause {
FromLet(AstNode<FromLet>),
/// <from_source> JOIN \[INNER | LEFT | RIGHT | FULL\] <from_source> ON <expr>
Join(AstNode<Join>),
/// <expr> MATCH <graph_pattern>
GraphMatch(AstNode<GraphMatch>),
}

#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -606,7 +606,7 @@ pub struct GraphMatch {
///
/// Fig. 5. Table of edge patterns:
/// https://arxiv.org/abs/2112.06217
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GraphMatchDirection {
Left,
Expand All @@ -633,7 +633,7 @@ pub enum GraphMatchPatternPart {
}

/// A quantifier for graph edges or patterns. (e.g., the `{2,5}` in `MATCH (x)->{2,5}(y)`)
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GraphMatchQuantifier {
pub lower: u32,
Expand All @@ -649,7 +649,7 @@ pub struct GraphMatchQuantifier {
///
/// Fig. 7. Table of restrictors:
/// https://arxiv.org/abs/2112.06217
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GraphMatchRestrictor {
Trail,
Expand All @@ -670,7 +670,7 @@ pub struct GraphMatchNode {
}

/// A single edge in a graph pattern.
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GraphMatchEdge {
/// edge direction
Expand Down Expand Up @@ -712,7 +712,7 @@ pub struct GraphMatchPattern {
///
/// Fig. 8. Table of restrictors:
/// https://arxiv.org/abs/2112.06217
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GraphMatchSelector {
AnyShortest,
Expand Down
109 changes: 63 additions & 46 deletions partiql-parser/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,111 +684,122 @@ mod tests {

#[test]
fn no_labels() {
parse!(r#"SELECT 1 FROM my_graph MATCH ()"#);
parse!(r#"SELECT 1 FROM my_graph MATCH () WHERE contains_value('1')"#);
parse!(r#"SELECT x.info AS info FROM my_graph MATCH (x) WHERE x.name LIKE 'foo'"#);
//parse!(r#"SELECT 1 FROM g MATCH -[]-> "#);
parse!(r#"SELECT 1 FROM (my_graph MATCH ())"#);
parse!(r#"SELECT 1 FROM (my_graph MATCH ()) WHERE contains_value('1')"#);
parse!(r#"SELECT x.info AS info FROM (my_graph MATCH (x)) WHERE x.name LIKE 'foo'"#);
// TODO fails due to edge first
// parse!(r#"SELECT 1 FROM (g MATCH -[]->) "#);
}

#[test]
fn lone_match_expr() {
parse!(r#"(MyGraph MATCH (x))"#);
parse!(r#"(MyGraph MATCH (x), (y) )"#);
// TODO fails due to edge first
//parse!(r#"(MyGraph MATCH (x), -[u]-> )"#);
}

#[test]
fn labelled_nodes() {
parse!(r#"SELECT x AS target FROM my_graph MATCH (x:Label) WHERE x.has_data = true"#);
parse!(r#"SELECT x AS target FROM (my_graph MATCH (x:Label)) WHERE x.has_data = true"#);
}

#[test]
fn edges() {
parse!(r#"SELECT a,b FROM g MATCH (a:A) -[e:E]-> (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) -> (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) ~[e:E]~ (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) ~ (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) <-[e:E]- (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) <- (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) ~[e:E]~> (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) ~> (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) <~[e:E]~ (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) <~ (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) <-[e:E]-> (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) <-> (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) -[e:E]- (b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A) - (b:B)"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]-> (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) -> (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~[e:E]~ (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~ (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-[e:E]- (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <- (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~[e:E]~> (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~> (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <~[e:E]~ (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <~ (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-[e:E]-> (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-> (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]- (b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A) - (b:B))"#);
}

#[test]
fn quantifiers() {
parse!(r#"SELECT a,b FROM g MATCH (a:A)-[:edge]->*(b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A)<-[:edge]-+(b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A)~[:edge]~{5,}(b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A)-[e:edge]-{2,6}(b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A)->*(b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A)<-+(b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A)~{5,}(b:B)"#);
parse!(r#"SELECT a,b FROM g MATCH (a:A)-{2,6}(b:B)"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A)-[:edge]->*(b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-[:edge]-+(b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A)~[:edge]~{5,}(b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A)-[e:edge]-{2,6}(b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A)->*(b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-+(b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A)~{5,}(b:B))"#);
parse!(r#"SELECT a,b FROM (g MATCH (a:A)-{2,6}(b:B))"#);
}

#[test]
fn patterns() {
parse!(
r#"SELECT the_a.name AS src, the_b.name AS dest FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE the_y.score > 10"#
r#"SELECT the_a.name AS src, the_b.name AS dest FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b)) WHERE the_y.score > 10"#
);
parse!(r#""SELECT a,b FROM g MATCH (a)-[:has]->()-[:contains]->(b)""#);
parse!(r#""SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))""#);
parse!(r#"SELECT a,b FROM (g MATCH (a) -[:has]-> (x), (x)-[:contains]->(b))"#);
}

#[test]
fn path_var() {
parse!(r#"SELECT a,b FROM g MATCH p = (a:A) -[e:E]-> (b:B)"#);
parse!(r#"SELECT a,b FROM (g MATCH p = (a:A) -[e:E]-> (b:B))"#);
}

#[test]
fn paranthesized() {
parse!(r#"SELECT a,b FROM g MATCH [(a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner]{2,5}"#);
parse!(r#"SELECT a,b FROM g MATCH pathVar = (a:A)[()-[e:Edge]->()]{1,3}(b:B)"#);
parse!(
r#"SELECT a,b FROM (g MATCH [(a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner]{2,5})"#
);
parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)[()-[e:Edge]->()]{1,3}(b:B))"#);

// brackets
parse!(r#"SELECT a,b FROM g MATCH pathVar = (a:A)[-[e:Edge]->]*(b:B)"#);
parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)[-[e:Edge]->]*(b:B))"#);
// parens
parse!(r#"SELECT a,b FROM g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B)"#);
parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B))"#);
}

#[test]
fn filters() {
parse!(
r#"SELECT u as banCandidate FROM g MATCH (p:Post Where p.isFlagged = true) <-[:createdPost]- (u:User WHERE u.isBanned = false AND u.karma < 20) -[:createdComment]->(c:Comment WHERE c.isFlagged = true) WHERE p.title LIKE '%considered harmful%'"#
r#"SELECT u as banCandidate FROM (g MATCH (p:Post Where p.isFlagged = true) <-[:createdPost]- (u:User WHERE u.isBanned = false AND u.karma < 20) -[:createdComment]->(c:Comment WHERE c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%'"#
);
}

#[test]
fn restrictors() {
parse!(
r#"SELECT p FROM g MATCH TRAIL p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH TRAIL p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
parse!(
r#"SELECT p FROM g MATCH SIMPLE p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH SIMPLE p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
parse!(
r#"SELECT p FROM g MATCH ACYCLIC p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH ACYCLIC p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
}

#[test]
fn selectors() {
parse!(
r#"SELECT p FROM g MATCH ANY SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH ANY SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
parse!(
r#"SELECT p FROM g MATCH ALL SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH ALL SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
parse!(
r#"SELECT p FROM g MATCH ANY p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH ANY p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
parse!(
r#"SELECT p FROM g MATCH ANY 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH ANY 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
parse!(
r#"SELECT p FROM g MATCH SHORTEST 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH SHORTEST 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
parse!(
r#"SELECT p FROM g MATCH SHORTEST 5 GROUP p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
r#"SELECT p FROM (g MATCH SHORTEST 5 GROUP p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
);
}

Expand All @@ -799,10 +810,16 @@ mod tests {
);
}

#[test]
fn union() {
parse!(r#"(MyGraph MATCH (x)) UNION SELECT * FROM tbl1"#);
parse!(r#"SELECT * FROM tbl1 UNION (MyGraph MATCH (x))"#);
}

#[test]
fn etc() {
parse!("SELECT * FROM g MATCH ALL SHORTEST [ (x)-[e]->*(y) ]");
parse!("SELECT * FROM g MATCH ALL SHORTEST [ TRAIL (x)-[e]->*(y) ]");
parse!("SELECT * FROM (g MATCH ALL SHORTEST [ (x)-[e]->*(y) ])");
parse!("SELECT * FROM (g MATCH ALL SHORTEST [ TRAIL (x)-[e]->*(y) ])");
}
}

Expand Down
Loading

0 comments on commit a1215a5

Please sign in to comment.