Skip to content
Permalink
Browse files
feat: Implement OPTIONAL MATCH (#175)
* feat: Implement `OPTIONAL MATCH`

* Reflect review.

* Reflect review

* Reflect review

* Reflect review

* Reflect review

* Reflect Review

* Reflect review

* Reflect review

* Fix broken
  • Loading branch information
emotionbug committed Jan 26, 2022
1 parent 5f63870 commit 8a660da40fe6bf2a6facb8e94d49319a9a5f0d6b
Showing 9 changed files with 359 additions and 50 deletions.
@@ -889,6 +889,68 @@ $$) AS (i agtype);
{"id": 1688849860263938, "label": "v2", "properties": {"id": "middle"}}::vertex
(3 rows)

--
-- Optional Match
--
SELECT * FROM cypher('cypher_match', $$
CREATE (:opt_match_v {name: 'someone'})-[:opt_match_e]->(:opt_match_v {name: 'somebody'}),
(:opt_match_v {name: 'anybody'})-[:opt_match_e]->(:opt_match_v {name: 'nobody'})
$$) AS (u agtype);
u
---
(0 rows)

SELECT * FROM cypher('cypher_match', $$
MATCH (u:opt_match_v)
OPTIONAL MATCH (u)-[m]-(l)
RETURN u.name as u, type(m), l.name as l
ORDER BY u, m, l
$$) AS (u agtype, m agtype, l agtype);
u | m | l
------------+---------------+------------
"someone" | "opt_match_e" | "somebody"
"somebody" | "opt_match_e" | "someone"
"anybody" | "opt_match_e" | "nobody"
"nobody" | "opt_match_e" | "anybody"
(4 rows)

SELECT * FROM cypher('cypher_match', $$
OPTIONAL MATCH (n:opt_match_v)-[r]->(p), (m:opt_match_v)-[s]->(q)
WHERE id(n) <> id(m)
RETURN n.name as n, type(r) AS r, p.name as p,
m.name AS m, type(s) AS s, q.name AS q
ORDER BY n, p, m, q
$$) AS (n agtype, r agtype, p agtype, m agtype, s agtype, q agtype);
n | r | p | m | s | q
-----------+---------------+------------+-----------+---------------+------------
"someone" | "opt_match_e" | "somebody" | "anybody" | "opt_match_e" | "nobody"
"anybody" | "opt_match_e" | "nobody" | "someone" | "opt_match_e" | "somebody"
(2 rows)

SELECT * FROM cypher('cypher_match', $$
MATCH (n:opt_match_v), (m:opt_match_v)
WHERE id(n) <> id(m)
OPTIONAL MATCH (n)-[r]->(p), (m)-[s]->(q)
RETURN n.name AS n, type(r) AS r, p.name AS p,
m.name AS m, type(s) AS s, q.name AS q
ORDER BY n, p, m, q
$$) AS (n agtype, r agtype, p agtype, m agtype, s agtype, q agtype);
n | r | p | m | s | q
------------+---------------+------------+------------+---------------+------------
"someone" | "opt_match_e" | "somebody" | "anybody" | "opt_match_e" | "nobody"
"someone" | | | "somebody" | |
"someone" | | | "nobody" | |
"somebody" | | | "someone" | |
"somebody" | | | "anybody" | |
"somebody" | | | "nobody" | |
"anybody" | "opt_match_e" | "nobody" | "someone" | "opt_match_e" | "somebody"
"anybody" | | | "somebody" | |
"anybody" | | | "nobody" | |
"nobody" | | | "someone" | |
"nobody" | | | "somebody" | |
"nobody" | | | "anybody" | |
(12 rows)

--
-- JIRA: AGE2-544
--
@@ -981,7 +1043,7 @@ $$) as (f agtype, t agtype);
-- Clean up
--
SELECT drop_graph('cypher_match', true);
NOTICE: drop cascades to 14 other objects
NOTICE: drop cascades to 16 other objects
DETAIL: drop cascades to table cypher_match._ag_label_vertex
drop cascades to table cypher_match._ag_label_edge
drop cascades to table cypher_match.v
@@ -996,6 +1058,8 @@ drop cascades to table cypher_match.self
drop cascades to table cypher_match.duplicate
drop cascades to table cypher_match.dup_edge
drop cascades to table cypher_match.other_v
drop cascades to table cypher_match.opt_match_v
drop cascades to table cypher_match.opt_match_e
NOTICE: graph "cypher_match" has been dropped
drop_graph
------------
@@ -456,6 +456,39 @@ SELECT * FROM cypher('cypher_match', $$
RETURN u SKIP 7 LIMIT 3
$$) AS (i agtype);


--
-- Optional Match
--
SELECT * FROM cypher('cypher_match', $$
CREATE (:opt_match_v {name: 'someone'})-[:opt_match_e]->(:opt_match_v {name: 'somebody'}),
(:opt_match_v {name: 'anybody'})-[:opt_match_e]->(:opt_match_v {name: 'nobody'})
$$) AS (u agtype);

SELECT * FROM cypher('cypher_match', $$
MATCH (u:opt_match_v)
OPTIONAL MATCH (u)-[m]-(l)
RETURN u.name as u, type(m), l.name as l
ORDER BY u, m, l
$$) AS (u agtype, m agtype, l agtype);

SELECT * FROM cypher('cypher_match', $$
OPTIONAL MATCH (n:opt_match_v)-[r]->(p), (m:opt_match_v)-[s]->(q)
WHERE id(n) <> id(m)
RETURN n.name as n, type(r) AS r, p.name as p,
m.name AS m, type(s) AS s, q.name AS q
ORDER BY n, p, m, q
$$) AS (n agtype, r agtype, p agtype, m agtype, s agtype, q agtype);

SELECT * FROM cypher('cypher_match', $$
MATCH (n:opt_match_v), (m:opt_match_v)
WHERE id(n) <> id(m)
OPTIONAL MATCH (n)-[r]->(p), (m)-[s]->(q)
RETURN n.name AS n, type(r) AS r, p.name AS p,
m.name AS m, type(s) AS s, q.name AS q
ORDER BY n, p, m, q
$$) AS (n agtype, r agtype, p agtype, m agtype, s agtype, q agtype);

--
-- JIRA: AGE2-544
--
@@ -129,6 +129,7 @@ void out_cypher_match(StringInfo str, const ExtensibleNode *node)

WRITE_NODE_FIELD(pattern);
WRITE_NODE_FIELD(where);
WRITE_BOOL_FIELD(optional);
}

// serialization function for the cypher_create ExtensibleNode.
@@ -578,6 +578,8 @@ static Query *analyze_cypher_and_coerce(List *stmt, RangeTblFunction *rtfunc,
ListCell *lc2;
ListCell *lc3;

int attr_count = 0;

pstate = make_parsestate(parent_pstate);

query = makeNode(Query);
@@ -597,8 +599,18 @@ static Query *analyze_cypher_and_coerce(List *stmt, RangeTblFunction *rtfunc,
pstate->p_lateral_active = false;
pstate->p_expr_kind = EXPR_KIND_NONE;

// ALIAS Syntax makes `RESJUNK`. So, It must be skipping.
foreach(lt, subquery->targetList)
{
TargetEntry *te = lfirst(lt);
if (!te->resjunk)
{
attr_count++;
}
}

// check the number of attributes first
if (list_length(subquery->targetList) != rtfunc->funccolcount)
if (attr_count != rtfunc->funccolcount)
{
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),

0 comments on commit 8a660da

Please sign in to comment.