Skip to content

Commit 6302b87

Browse files
MuhammadTahaNaveedZainab-Saad
authored andcommitted
Add optional parameter '=' in property constraints (apache#1516)
- The '=' operator checks if the original property value(as a whole) is equal to the given value. e.g MATCH (n {school:{addr:{city:'Toronto'}}}) tranforms into either(in case age.enable_containment is off) `properties.school.addr.city = 'Toronto'` or(in case age.enable_containment is on) `properties @> {school:{addr:{city:'Toronto'}}}` But MATCH (n ={school:{addr:{city:'Toronto'}}}) will tranform into either(in case age.enable_containment is off) `properties.school = {addr:{city:'Toronto'}}` or(in case age.enable_containment is on) `properties @>> {school:{addr:{city:'Toronto'}}}` - Added @>> and <<@ operators. Unlike @> and <@, these operators does not recurse into sub-objects. - Added regression tests. - Added changes in sql files to version update template file.
1 parent 2450963 commit 6302b87

File tree

13 files changed

+660
-21
lines changed

13 files changed

+660
-21
lines changed

Diff for: age--1.5.0--y.y.y.sql

+60
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,63 @@
2828
-- Please add all additions, deletions, and modifications to the end of this
2929
-- file. We need to keep the order of these changes.
3030

31+
CREATE FUNCTION ag_catalog.agtype_contains_top_level(agtype, agtype)
32+
RETURNS boolean
33+
LANGUAGE c
34+
IMMUTABLE
35+
RETURNS NULL ON NULL INPUT
36+
PARALLEL SAFE
37+
AS 'MODULE_PATHNAME';
38+
39+
CREATE OPERATOR @>> (
40+
LEFTARG = agtype,
41+
RIGHTARG = agtype,
42+
FUNCTION = ag_catalog.agtype_contains_top_level,
43+
COMMUTATOR = '<<@',
44+
RESTRICT = contsel,
45+
JOIN = contjoinsel
46+
);
47+
48+
CREATE FUNCTION ag_catalog.agtype_contained_by_top_level(agtype, agtype)
49+
RETURNS boolean
50+
LANGUAGE c
51+
IMMUTABLE
52+
RETURNS NULL ON NULL INPUT
53+
PARALLEL SAFE
54+
AS 'MODULE_PATHNAME';
55+
56+
CREATE OPERATOR <<@ (
57+
LEFTARG = agtype,
58+
RIGHTARG = agtype,
59+
FUNCTION = ag_catalog.agtype_contained_by_top_level,
60+
COMMUTATOR = '@>>',
61+
RESTRICT = contsel,
62+
JOIN = contjoinsel
63+
);
64+
65+
/*
66+
* Since there is no option to add or drop operator from class,
67+
* we have to drop and recreate the whole operator class.
68+
* Reference: https://www.postgresql.org/docs/current/sql-alteropclass.html
69+
*/
70+
71+
DROP OPERATOR CLASS ag_catalog.gin_agtype_ops;
72+
73+
CREATE OPERATOR CLASS ag_catalog.gin_agtype_ops
74+
DEFAULT FOR TYPE agtype USING gin AS
75+
OPERATOR 7 @>(agtype, agtype),
76+
OPERATOR 8 <@(agtype, agtype),
77+
OPERATOR 9 ?(agtype, agtype),
78+
OPERATOR 10 ?|(agtype, agtype),
79+
OPERATOR 11 ?&(agtype, agtype),
80+
OPERATOR 12 @>>(agtype, agtype),
81+
OPERATOR 13 <<@(agtype, agtype),
82+
FUNCTION 1 ag_catalog.gin_compare_agtype(text,text),
83+
FUNCTION 2 ag_catalog.gin_extract_agtype(agtype, internal),
84+
FUNCTION 3 ag_catalog.gin_extract_agtype_query(agtype, internal, int2,
85+
internal, internal),
86+
FUNCTION 4 ag_catalog.gin_consistent_agtype(internal, int2, agtype, int4,
87+
internal, internal),
88+
FUNCTION 6 ag_catalog.gin_triconsistent_agtype(internal, int2, agtype, int4,
89+
internal, internal, internal),
90+
STORAGE text;

Diff for: regress/expected/cypher_match.out

+245-1
Original file line numberDiff line numberDiff line change
@@ -3293,6 +3293,248 @@ $$) AS (n1 agtype, n2 agtype, n3 agtype, e1 agtype);
32933293
{"id": 844424930131970, "label": "Object", "properties": {}}::vertex | {"id": 844424930131971, "label": "Object", "properties": {}}::vertex | {"id": 844424930131970, "label": "Object", "properties": {}}::vertex | {"id": 1125899906842625, "label": "knows", "end_id": 844424930131971, "start_id": 844424930131970, "properties": {}}::edge
32943294
(1 row)
32953295

3296+
--
3297+
-- Issue 1461
3298+
--
3299+
-- Using the test_enable_containment graph for these tests
3300+
SELECT * FROM cypher('test_enable_containment', $$ CREATE p=(:Customer)-[:bought {store:'Amazon', addr:{city: 'Vancouver', street: 30}}]->(y:Product) RETURN p $$) as (a agtype);
3301+
a
3302+
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3303+
[{"id": 844424930131970, "label": "Customer", "properties": {}}::vertex, {"id": 1125899906842625, "label": "bought", "end_id": 1407374883553281, "start_id": 844424930131970, "properties": {"addr": {"city": "Vancouver", "street": 30}, "store": "Amazon"}}::edge, {"id": 1407374883553281, "label": "Product", "properties": {}}::vertex]::path
3304+
(1 row)
3305+
3306+
-- With enable_containment on
3307+
SET age.enable_containment = on;
3308+
-- Should return 0
3309+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={addr:[{city:'Toronto'}]}) RETURN x $$) as (a agtype);
3310+
count
3311+
-------
3312+
0
3313+
(1 row)
3314+
3315+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={school:{program:{major:'Psyc'}}}) RETURN x $$) as (a agtype);
3316+
count
3317+
-------
3318+
0
3319+
(1 row)
3320+
3321+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={name:'Bob',school:{program:{degree:'BSc'}}}) RETURN x $$) as (a agtype);
3322+
count
3323+
-------
3324+
0
3325+
(1 row)
3326+
3327+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={school:{program:{major:'Cs'}}}) RETURN x $$) as (a agtype);
3328+
count
3329+
-------
3330+
0
3331+
(1 row)
3332+
3333+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={name:'Bob',school:{program:{degree:'PHd'}}}) RETURN x $$) as (a agtype);
3334+
count
3335+
-------
3336+
0
3337+
(1 row)
3338+
3339+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={phone:[987654321]}) RETURN x $$) as (a agtype);
3340+
count
3341+
-------
3342+
0
3343+
(1 row)
3344+
3345+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={phone:[654765876]}) RETURN x $$) as (a agtype);
3346+
count
3347+
-------
3348+
0
3349+
(1 row)
3350+
3351+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x)-[:bought ={store: 'Amazon', addr:{city: 'Vancouver'}}]->() RETURN x $$) as (a agtype);
3352+
count
3353+
-------
3354+
0
3355+
(1 row)
3356+
3357+
-- Should return 1
3358+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={addr: [{city: 'Vancouver', street: 30},{city: 'Toronto', street: 40}]}) RETURN x $$) as (a agtype);
3359+
count
3360+
-------
3361+
1
3362+
(1 row)
3363+
3364+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={school: { name: 'XYZ College',program: { major: 'Psyc', degree: 'BSc'}}}) RETURN x $$) as (a agtype);
3365+
count
3366+
-------
3367+
1
3368+
(1 row)
3369+
3370+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={phone: [ 123456789, 987654321, 456987123 ]}) RETURN x $$) as (a agtype);
3371+
count
3372+
-------
3373+
1
3374+
(1 row)
3375+
3376+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={school: { name: 'XYZ College',program: { major: 'Psyc', degree: 'BSc'} },phone: [ 123456789, 987654321, 456987123 ]}) RETURN x $$) as (a agtype);
3377+
count
3378+
-------
3379+
1
3380+
(1 row)
3381+
3382+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH p=(x:Customer)-[:bought ={store: 'Amazon'}]->() RETURN p $$) as (a agtype);
3383+
count
3384+
-------
3385+
1
3386+
(1 row)
3387+
3388+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH p=(x:Customer)-[:bought ={store: 'Amazon', addr:{city: 'Vancouver', street: 30}}]->() RETURN p $$) as (a agtype);
3389+
count
3390+
-------
3391+
1
3392+
(1 row)
3393+
3394+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH p=(x:Customer)-[:bought {store: 'Amazon', addr:{city: 'Vancouver'}}]->() RETURN p $$) as (a agtype);
3395+
count
3396+
-------
3397+
1
3398+
(1 row)
3399+
3400+
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (costs off) MATCH (x:Customer)-[:bought ={store: 'Amazon', addr:{city: 'Vancouver', street: 30}}]->(y:Product) RETURN 0 $$) as (a agtype);
3401+
QUERY PLAN
3402+
-------------------------------------------------------------------------------------------------------------------------------
3403+
Hash Join
3404+
Hash Cond: (y.id = _age_default_alias_0.end_id)
3405+
-> Seq Scan on "Product" y
3406+
-> Hash
3407+
-> Hash Join
3408+
Hash Cond: (x.id = _age_default_alias_0.start_id)
3409+
-> Seq Scan on "Customer" x
3410+
-> Hash
3411+
-> Seq Scan on bought _age_default_alias_0
3412+
Filter: (properties @>> '{"addr": {"city": "Vancouver", "street": 30}, "store": "Amazon"}'::agtype)
3413+
(10 rows)
3414+
3415+
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (costs off) MATCH (x:Customer ={school: { name: 'XYZ College',program: { major: 'Psyc', degree: 'BSc'} },phone: [ 123456789, 987654321, 456987123 ]}) RETURN 0 $$) as (a agtype);
3416+
QUERY PLAN
3417+
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
3418+
Seq Scan on "Customer" x
3419+
Filter: (properties @>> '{"phone": [123456789, 987654321, 456987123], "school": {"name": "XYZ College", "program": {"major": "Psyc", "degree": "BSc"}}}'::agtype)
3420+
(2 rows)
3421+
3422+
-- With enable_containment off
3423+
SET age.enable_containment = off;
3424+
-- Should return 0
3425+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={addr:[{city:'Toronto'}]}) RETURN x $$) as (a agtype);
3426+
count
3427+
-------
3428+
0
3429+
(1 row)
3430+
3431+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={school:{program:{major:'Psyc'}}}) RETURN x $$) as (a agtype);
3432+
count
3433+
-------
3434+
0
3435+
(1 row)
3436+
3437+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={name:'Bob',school:{program:{degree:'BSc'}}}) RETURN x $$) as (a agtype);
3438+
count
3439+
-------
3440+
0
3441+
(1 row)
3442+
3443+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={school:{program:{major:'Cs'}}}) RETURN x $$) as (a agtype);
3444+
count
3445+
-------
3446+
0
3447+
(1 row)
3448+
3449+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={name:'Bob',school:{program:{degree:'PHd'}}}) RETURN x $$) as (a agtype);
3450+
count
3451+
-------
3452+
0
3453+
(1 row)
3454+
3455+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={phone:[987654321]}) RETURN x $$) as (a agtype);
3456+
count
3457+
-------
3458+
0
3459+
(1 row)
3460+
3461+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={phone:[654765876]}) RETURN x $$) as (a agtype);
3462+
count
3463+
-------
3464+
0
3465+
(1 row)
3466+
3467+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x)-[:bought ={store: 'Amazon', addr:{city: 'Vancouver'}}]->() RETURN x $$) as (a agtype);
3468+
count
3469+
-------
3470+
0
3471+
(1 row)
3472+
3473+
-- Should return 1
3474+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={addr: [{city: 'Vancouver', street: 30},{city: 'Toronto', street: 40}]}) RETURN x $$) as (a agtype);
3475+
count
3476+
-------
3477+
1
3478+
(1 row)
3479+
3480+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={school: { name: 'XYZ College',program: { major: 'Psyc', degree: 'BSc'}}}) RETURN x $$) as (a agtype);
3481+
count
3482+
-------
3483+
1
3484+
(1 row)
3485+
3486+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={phone: [ 123456789, 987654321, 456987123 ]}) RETURN x $$) as (a agtype);
3487+
count
3488+
-------
3489+
1
3490+
(1 row)
3491+
3492+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer ={school: { name: 'XYZ College',program: { major: 'Psyc', degree: 'BSc'} },phone: [ 123456789, 987654321, 456987123 ]}) RETURN x $$) as (a agtype);
3493+
count
3494+
-------
3495+
1
3496+
(1 row)
3497+
3498+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH p=(x:Customer)-[:bought ={store: 'Amazon'}]->() RETURN p $$) as (a agtype);
3499+
count
3500+
-------
3501+
1
3502+
(1 row)
3503+
3504+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH p=(x:Customer)-[:bought ={store: 'Amazon', addr:{city: 'Vancouver', street: 30}}]->() RETURN p $$) as (a agtype);
3505+
count
3506+
-------
3507+
1
3508+
(1 row)
3509+
3510+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH p=(x:Customer)-[:bought {store: 'Amazon', addr:{city: 'Vancouver'}}]->() RETURN p $$) as (a agtype);
3511+
count
3512+
-------
3513+
1
3514+
(1 row)
3515+
3516+
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (costs off) MATCH (x:Customer)-[:bought ={store: 'Amazon', addr:{city: 'Vancouver', street: 30}}]->(y:Product) RETURN 0 $$) as (a agtype);
3517+
QUERY PLAN
3518+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3519+
Hash Join
3520+
Hash Cond: (y.id = _age_default_alias_0.end_id)
3521+
-> Seq Scan on "Product" y
3522+
-> Hash
3523+
-> Hash Join
3524+
Hash Cond: (x.id = _age_default_alias_0.start_id)
3525+
-> Seq Scan on "Customer" x
3526+
-> Hash
3527+
-> Seq Scan on bought _age_default_alias_0
3528+
Filter: ((agtype_access_operator(VARIADIC ARRAY[properties, '"store"'::agtype]) = '"Amazon"'::agtype) AND (agtype_access_operator(VARIADIC ARRAY[properties, '"addr"'::agtype]) = '{"city": "Vancouver", "street": 30}'::agtype))
3529+
(10 rows)
3530+
3531+
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (costs off) MATCH (x:Customer ={school: { name: 'XYZ College',program: { major: 'Psyc', degree: 'BSc'} },phone: [ 123456789, 987654321, 456987123 ]}) RETURN 0 $$) as (a agtype);
3532+
QUERY PLAN
3533+
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3534+
Seq Scan on "Customer" x
3535+
Filter: ((agtype_access_operator(VARIADIC ARRAY[properties, '"school"'::agtype]) = '{"name": "XYZ College", "program": {"major": "Psyc", "degree": "BSc"}}'::agtype) AND (agtype_access_operator(VARIADIC ARRAY[properties, '"phone"'::agtype]) = '[123456789, 987654321, 456987123]'::agtype))
3536+
(2 rows)
3537+
32963538
--
32973539
-- Clean up
32983540
--
@@ -3335,10 +3577,12 @@ NOTICE: graph "test_retrieve_var" has been dropped
33353577
(1 row)
33363578

33373579
SELECT drop_graph('test_enable_containment', true);
3338-
NOTICE: drop cascades to 3 other objects
3580+
NOTICE: drop cascades to 5 other objects
33393581
DETAIL: drop cascades to table test_enable_containment._ag_label_vertex
33403582
drop cascades to table test_enable_containment._ag_label_edge
33413583
drop cascades to table test_enable_containment."Customer"
3584+
drop cascades to table test_enable_containment.bought
3585+
drop cascades to table test_enable_containment."Product"
33423586
NOTICE: graph "test_enable_containment" has been dropped
33433587
drop_graph
33443588
------------

0 commit comments

Comments
 (0)