Skip to content

Commit 4256f4e

Browse files
committed
Implement map projection (apache#1710)
Adds support for openCypher Map Projection specification.
1 parent edcd465 commit 4256f4e

10 files changed

Lines changed: 544 additions & 2 deletions

File tree

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ REGRESS = scan \
102102
cypher_union \
103103
cypher_call \
104104
cypher_merge \
105-
cypher_subquery \
105+
cypher_subquery \
106106
age_global_graph \
107107
age_load \
108108
index \
@@ -111,6 +111,7 @@ REGRESS = scan \
111111
name_validation \
112112
jsonb_operators \
113113
list_comprehension \
114+
map_projection \
114115
drop
115116

116117
srcdir=`pwd`
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
LOAD 'age';
20+
SET search_path TO ag_catalog;
21+
SELECT create_graph('map_proj');
22+
NOTICE: graph "map_proj" has been created
23+
create_graph
24+
--------------
25+
26+
(1 row)
27+
28+
SELECT * FROM cypher('map_proj',
29+
$$
30+
CREATE
31+
(tom:Actor {name:'Tom Hanks', age:60}),
32+
(bale:Actor {name:'Christian Bale', age:50}),
33+
(tom)-[:ACTED_IN {during: 1990}]->(:Movie {title:'Forrest Gump'}),
34+
(tom)-[:ACTED_IN {during: 1995}]->(:Movie {title:'Finch'}),
35+
(tom)-[:ACTED_IN {during: 1999}]->(:Movie {title:'The Circle'}),
36+
(bale)-[:ACTED_IN {during: 2002}]->(:Movie {title:'The Prestige'}),
37+
(bale)-[:ACTED_IN {during: 2008}]->(:Movie {title:'The Dark Knight'})
38+
$$) as (a agtype);
39+
a
40+
---
41+
(0 rows)
42+
43+
-- all property selection
44+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .* } $$) as (a agtype);
45+
a
46+
----------------------------
47+
{"age": 50, "name": "Bob"}
48+
(1 row)
49+
50+
-- property selector
51+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .name } $$) as (a agtype);
52+
a
53+
-----------------
54+
{"name": "Bob"}
55+
(1 row)
56+
57+
-- literal entry
58+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { name:'Tom' } $$) as (a agtype);
59+
a
60+
-----------------
61+
{"name": "Tom"}
62+
(1 row)
63+
64+
-- variable selector
65+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map, 'Tom' as name RETURN map { name } $$) as (a agtype);
66+
a
67+
-----------------
68+
{"name": "Tom"}
69+
(1 row)
70+
71+
-- duplicate all property selector
72+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .*, .* } $$) as (a agtype);
73+
a
74+
----------------------------
75+
{"age": 50, "name": "Bob"}
76+
(1 row)
77+
78+
-- name being selected twice
79+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .name, .* } $$) as (a agtype);
80+
a
81+
----------------------------
82+
{"age": 50, "name": "Bob"}
83+
(1 row)
84+
85+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .name, .name } $$) as (a agtype);
86+
a
87+
-----------------
88+
{"name": "Bob"}
89+
(1 row)
90+
91+
-- name being selected twice with different value
92+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { name:'Tom', .* } $$) as (a agtype);
93+
a
94+
----------------------------
95+
{"age": 50, "name": "Tom"}
96+
(1 row)
97+
98+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map, 'Tom' as name RETURN map { name, .* } $$) as (a agtype);
99+
a
100+
----------------------------
101+
{"age": 50, "name": "Tom"}
102+
(1 row)
103+
104+
-- new entry added
105+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .name, .age, height:180 } $$) as (a agtype);
106+
a
107+
-------------------------------------------
108+
{"age": 50, "name": "Bob", "height": 180}
109+
(1 row)
110+
111+
-- NULL as a map
112+
SELECT * FROM cypher('map_proj', $$ WITH NULL AS map RETURN map { .name } $$) as (a agtype);
113+
a
114+
----
115+
{}
116+
(1 row)
117+
118+
-- vertex as a map
119+
SELECT * FROM cypher('map_proj', $$ MATCH (n:Actor) RETURN n { .name } $$) as (a agtype);
120+
a
121+
----------------------------
122+
{"name": "Tom Hanks"}
123+
{"name": "Christian Bale"}
124+
(2 rows)
125+
126+
-- edge as a map
127+
SELECT * FROM cypher('map_proj', $$ MATCH ()-[e:ACTED_IN]->() RETURN e { .during } $$) as (a agtype);
128+
a
129+
------------------
130+
{"during": 1990}
131+
{"during": 1995}
132+
{"during": 1999}
133+
{"during": 2002}
134+
{"during": 2008}
135+
(5 rows)
136+
137+
-- syntax error
138+
SELECT * FROM cypher('map_proj', $$ WITH 12 AS map RETURN map { .name } $$) as (a agtype);
139+
ERROR: properties() argument must be a vertex, an edge or null
140+
SELECT * FROM cypher('map_proj', $$ WITH [] AS map RETURN map { .name } $$) as (a agtype);
141+
ERROR: properties() argument must resolve to an object
142+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob'} AS map RETURN map { 'name' } $$) as (a agtype);
143+
ERROR: syntax error at or near "'name'"
144+
LINE 1: ...p_proj', $$ WITH {name:'Bob'} AS map RETURN map { 'name' } $...
145+
^
146+
-- advanced
147+
SELECT * FROM cypher('map_proj',
148+
$$
149+
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
150+
WITH a, collect(m { .title }) AS movies
151+
RETURN collect(a { .name, movies })
152+
$$) as (a agtype);
153+
a
154+
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
155+
[{"name": "Christian Bale", "movies": [{"title": "The Prestige"}, {"title": "The Dark Knight"}]}, {"name": "Tom Hanks", "movies": [{"title": "Forrest Gump"}, {"title": "Finch"}, {"title": "The Circle"}]}]
156+
(1 row)
157+
158+
-- drop
159+
SELECT drop_graph('map_proj', true);
160+
NOTICE: drop cascades to 5 other objects
161+
DETAIL: drop cascades to table map_proj._ag_label_vertex
162+
drop cascades to table map_proj._ag_label_edge
163+
drop cascades to table map_proj."Actor"
164+
drop cascades to table map_proj."ACTED_IN"
165+
drop cascades to table map_proj."Movie"
166+
NOTICE: graph "map_proj" has been dropped
167+
drop_graph
168+
------------
169+
170+
(1 row)
171+

regress/sql/map_projection.sql

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
LOAD 'age';
21+
SET search_path TO ag_catalog;
22+
23+
SELECT create_graph('map_proj');
24+
25+
SELECT * FROM cypher('map_proj',
26+
$$
27+
CREATE
28+
(tom:Actor {name:'Tom Hanks', age:60}),
29+
(bale:Actor {name:'Christian Bale', age:50}),
30+
(tom)-[:ACTED_IN {during: 1990}]->(:Movie {title:'Forrest Gump'}),
31+
(tom)-[:ACTED_IN {during: 1995}]->(:Movie {title:'Finch'}),
32+
(tom)-[:ACTED_IN {during: 1999}]->(:Movie {title:'The Circle'}),
33+
(bale)-[:ACTED_IN {during: 2002}]->(:Movie {title:'The Prestige'}),
34+
(bale)-[:ACTED_IN {during: 2008}]->(:Movie {title:'The Dark Knight'})
35+
$$) as (a agtype);
36+
37+
-- all property selection
38+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .* } $$) as (a agtype);
39+
-- property selector
40+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .name } $$) as (a agtype);
41+
-- literal entry
42+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { name:'Tom' } $$) as (a agtype);
43+
-- variable selector
44+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map, 'Tom' as name RETURN map { name } $$) as (a agtype);
45+
-- duplicate all property selector
46+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .*, .* } $$) as (a agtype);
47+
-- name being selected twice
48+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .name, .* } $$) as (a agtype);
49+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .name, .name } $$) as (a agtype);
50+
-- name being selected twice with different value
51+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { name:'Tom', .* } $$) as (a agtype);
52+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map, 'Tom' as name RETURN map { name, .* } $$) as (a agtype);
53+
-- new entry added
54+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob', age:50} AS map RETURN map { .name, .age, height:180 } $$) as (a agtype);
55+
-- NULL as a map
56+
SELECT * FROM cypher('map_proj', $$ WITH NULL AS map RETURN map { .name } $$) as (a agtype);
57+
-- vertex as a map
58+
SELECT * FROM cypher('map_proj', $$ MATCH (n:Actor) RETURN n { .name } $$) as (a agtype);
59+
-- edge as a map
60+
SELECT * FROM cypher('map_proj', $$ MATCH ()-[e:ACTED_IN]->() RETURN e { .during } $$) as (a agtype);
61+
-- syntax error
62+
SELECT * FROM cypher('map_proj', $$ WITH 12 AS map RETURN map { .name } $$) as (a agtype);
63+
SELECT * FROM cypher('map_proj', $$ WITH [] AS map RETURN map { .name } $$) as (a agtype);
64+
SELECT * FROM cypher('map_proj', $$ WITH {name:'Bob'} AS map RETURN map { 'name' } $$) as (a agtype);
65+
-- advanced
66+
SELECT * FROM cypher('map_proj',
67+
$$
68+
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
69+
WITH a, collect(m { .title }) AS movies
70+
RETURN collect(a { .name, movies })
71+
$$) as (a agtype);
72+
73+
-- drop
74+
SELECT drop_graph('map_proj', true);

src/backend/nodes/ag_nodes.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ const char *node_names[] = {
4545
"cypher_bool_const",
4646
"cypher_param",
4747
"cypher_map",
48+
"cypher_map_projection",
49+
"cypher_map_projection_element",
4850
"cypher_list",
4951
"cypher_comparison_aexpr",
5052
"cypher_comparison_boolexpr",
@@ -111,6 +113,7 @@ const ExtensibleNodeMethods node_methods[] = {
111113
DEFINE_NODE_METHODS(cypher_bool_const),
112114
DEFINE_NODE_METHODS(cypher_param),
113115
DEFINE_NODE_METHODS(cypher_map),
116+
DEFINE_NODE_METHODS(cypher_map_projection),
114117
DEFINE_NODE_METHODS(cypher_list),
115118
DEFINE_NODE_METHODS(cypher_comparison_aexpr),
116119
DEFINE_NODE_METHODS(cypher_comparison_boolexpr),

src/backend/nodes/cypher_outfuncs.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,15 @@ void out_cypher_map(StringInfo str, const ExtensibleNode *node)
251251
WRITE_LOCATION_FIELD(location);
252252
}
253253

254+
void out_cypher_map_projection(StringInfo str, const ExtensibleNode *node)
255+
{
256+
DEFINE_AG_NODE(cypher_map_projection);
257+
258+
WRITE_NODE_FIELD(map_var);
259+
WRITE_NODE_FIELD(map_elements);
260+
WRITE_LOCATION_FIELD(location);
261+
}
262+
254263
// serialization function for the cypher_list ExtensibleNode.
255264
void out_cypher_list(StringInfo str, const ExtensibleNode *node)
256265
{

0 commit comments

Comments
 (0)