From c4d262f2debf2fb9ab83868e8d14e5a6767eef8c Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Wed, 14 Dec 2016 16:10:06 +0100 Subject: [PATCH 01/42] Add the constraints CIP - General outline - Describe the node uniqueness constraint --- .../CIP2016-12-14-Constraint-syntax.adoc | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc new file mode 100644 index 0000000000..d668cbab4d --- /dev/null +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -0,0 +1,174 @@ += CIP2016-12-16 - Constraints syntax +:numbered: +:toc: +:toc-placement: macro +:source-highlighter: codemirror + +*Author:* Mats Rydberg + +[abstract] +.Abstract +-- +This CIP describes syntax and semantics for Cypher constraints. +These are language constructs that impose restrictions on the shape of the data graph, and how statements are allowed to change it. +-- + +toc::[] + +== Motivation + +Constraints provide utility for shaping the data graph. + +== Background + +Cypher has a loose notion of schema, in which nodes and relationships may take very heterogeneous forms, both in terms of properties and in graph patterns. +Constraints allows us to bound the heterogeneous nature of the property graph into a more regular form. + +== Proposal + +This CIP includes the following proposed constraints: + +* Node property uniqueness constraint +* Node property existence constraint +* Relationship property existence constraint + +Each constraint is detailed in its own below section. + +Once a constraint has been created, it may not be amended. +Should a user wish to change its definition, it has to be dropped and recreated with an updated structure. + +==== Constraint names + +All constraints require the user to specify a nonempty _name_ at constraint creation time. +This name is subsequently the handle with which a user may refer to the constraint, e.g. when dropping it. + +// TODO: Should we impose restrictions on the domain of constraint names, or are all Unicode characters allowed? + +=== Syntax overview + +The syntax for all constraints follow the same basic outline. + +.Grammar definition for constraint syntax. +[source, ebnf] +---- +constraint command = create-constraint | drop-constraint ; +create-constraint = "CREATE", "CONSTRAINT", constraint-name, "FOR", constraint-pattern, "REQUIRE", constraint-expr ; +constraint-name = symbolic-name +constraint-pattern = node-pattern | simple-pattern ; +constraint-expr = uniqueness-constraint | existence-constraint ; +drop-constraint = "DROP", "CONSTRAINT", constraint-name ; +---- + +The constraint expressions vary depending on the actual constraint (see the detailed sections). + +.Example of dropping a constraint with name foo: +[source, cypher] +---- +DROP CONSTRAINT foo +---- + +=== Semantics overview + +The semantics are defined for each type of constraint, but some characteristics are shared: + +* When a statement tries to create a constraint on a graph where the data does not pass the constraint criterion, that statement will raise an error. +* When a statement tries to create a constraint with a name that already exists, that statement will raise an error. +* When a statement tries to drop a constraint referencing a name that does not exist, that statement will raise an error. +* When an updating statement tries to modify the graph in such a way that it would violate a constraint, that statement will raise an error. + +=== Node property uniqueness constraint + +This constraint enforces that there can not be duplicate values of a certain property for a certain type of node. +For example, that among nodes labeled with `:Person`, each `email` property must be unique. + +==== Syntax + +.Grammar definition for node property uniqueness constraint: +[source, ebnf] +---- +uniqueness-constraint = "UNIQUE", property-expression, { ",", property-expression } ; +---- + +.Example of single-property uniqueness constraint: +[source, cypher] +---- +CREATE CONSTRAINT unique_person_email +FOR (p:Person) +REQUIRE UNIQUE p.email +---- + +.Example of multiple-property uniqueness constraint: +[source, cypher] +---- +CREATE CONSTRAINT unique_person_details +FOR (p:Person) +REQUIRE UNIQUE p.name, p.email, p.address +---- + +==== Semantics + +A property uniqueness constraint is applied on nodes with a specific label, for one or more property keys. +The constraint applies to all nodes where the property exist; its value must be non-null. +When more than one property key is defined as part of the constraint, the uniqueness applies only to nodes where _all_ of the properties exist (are non-null). +The uniqueness mandates that two distinct nodes within the domain of the constraint can not have the same combination of values for the defined properties (respectively). + +===== Example + +Consider the graph created by the following statement: + +[source, cypher] +---- +CREATE (:Color {name: 'white', rgb: 255}) +CREATE (:Color {name: 'black', rgb: 0}) +CREATE (:Color {name: 'very, very dark grey', rgb: 0}) // rounding error! +---- + +Due to the duplication of the `rgb` property, the following attempt at creating a constraint will fail: + +[source, cypher] +---- +CREATE CONSTRAINT only_one_color_per_rgb +FOR (c:Color) +REQUIRE UNIQUE c.rgb +---- + +Suppose that we would rather like to have one color node per name _and_ RGB value (to work around the rounding errors). +We could then use the following constraint, without modifying our data: + +[source, cypher] +---- +CREATE CONSTRAINT unique_color_nodes +FOR (c:Color) +REQUIRE UNIQUE c.rgb, c.name +---- + +=== Interaction with existing features + +The main interaction between the constraints and the rest of the language happens during updating statements. +Existing constraints will cause certain updating statements to fail; in fact, that's the main purpose. + +=== Alternatives + +Plenty of alternative syntaxes have been discussed: + +* `GIVEN`, `CONSTRAIN`, `ASSERT` instead of `FOR` +* `ASSERT`, `ENFORCE`, `IMPLIES` instead of `REQUIRE` + +The use of existing expression to express uniqueness, instead of using a new keyword `UNIQUE`, on the form: +---- +FOR (p:Person), (q:Person) +REQUIRE p.email <> q.email AND p <> q +---- +which quickly becomes unwieldy for multiple properties. + +== What others do + +// TODO: SQL syntax for constraints + +== Benefits to this proposal + +Constraints make Cypher's notion of schema more well-defined, and allows users to keep graphs in a more regular, easier to manage form. + +== Caveats to this proposal + +For an implementing system, some constraints may prove challenging to enforce, as they generally require scanning through large parts of the graph to look for conflicting entities. From 370c7688c54b3c3eb172ccb9b32b431e5cc231c3 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Wed, 14 Dec 2016 16:36:32 +0100 Subject: [PATCH 02/42] Add property existence constraint syntax --- .../CIP2016-12-14-Constraint-syntax.adoc | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index d668cbab4d..1846c17824 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -142,6 +142,43 @@ FOR (c:Color) REQUIRE UNIQUE c.rgb, c.name ---- +[[existence]] +=== Property existence constraints + +Property existence constraints are defined for both nodes and relationships, but the semantics are the same. +For this reason we will go over both constraints in the same section. + +==== Syntax + +.Grammar definition for property existence constraint: +[source, ebnf] +---- +existence-constraint = "exists", "(", property-expression, ")" ; +---- + +.Example of node property existence constraint: +[source, cypher] +---- +CREATE CONSTRAINT colors_must_have_rgb +FOR (c:Color) +REQUIRE exists(c.rgb) +---- + +.Example of relationship property existence constraint: +[source, cypher] +---- +CREATE CONSTRAINT rates_have_quality +FOR ()-[l:RATED]-() +REQUIRE exists(l.rating) +---- + +==== Semantics + +Property existence constraints enforce that the value of the specified property is non-null for all entities in the constraint domain. + + +===== Example + === Interaction with existing features The main interaction between the constraints and the rest of the language happens during updating statements. From e7c621f295cf602c305675cff7d27c8b7fd6c91d Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Thu, 15 Dec 2016 10:13:46 +0100 Subject: [PATCH 03/42] Introduce the concept of domain - Define domain for uniqueness - Define domain for existence --- .../CIP2016-12-14-Constraint-syntax.adoc | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 1846c17824..f3e058a939 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -28,9 +28,9 @@ Constraints allows us to bound the heterogeneous nature of the property graph in This CIP includes the following proposed constraints: -* Node property uniqueness constraint -* Node property existence constraint -* Relationship property existence constraint +* <> +* <> +* <> Each constraint is detailed in its own below section. @@ -76,6 +76,10 @@ The semantics are defined for each type of constraint, but some characteristics * When a statement tries to drop a constraint referencing a name that does not exist, that statement will raise an error. * When an updating statement tries to modify the graph in such a way that it would violate a constraint, that statement will raise an error. +The constraints define a _domain_ within which the constraint applies. +The domain is defined by the constraint pattern. + +[[uniqueness]] === Node property uniqueness constraint This constraint enforces that there can not be duplicate values of a certain property for a certain type of node. @@ -107,10 +111,10 @@ REQUIRE UNIQUE p.name, p.email, p.address ==== Semantics -A property uniqueness constraint is applied on nodes with a specific label, for one or more property keys. -The constraint applies to all nodes where the property exist; its value must be non-null. -When more than one property key is defined as part of the constraint, the uniqueness applies only to nodes where _all_ of the properties exist (are non-null). -The uniqueness mandates that two distinct nodes within the domain of the constraint can not have the same combination of values for the defined properties (respectively). +The domain of a property uniqueness constraint is defined as all the nodes with a specific label where the specified property key(s) exist (are non-null). +When more than one property key is defined as part of the constraint, only nodes where _all_ of the properties exist are part of the domain. + +The uniqueness mandates that two distinct nodes within the domain can not have the same combination of values for the defined properties (respectively). ===== Example @@ -174,8 +178,10 @@ REQUIRE exists(l.rating) ==== Semantics -Property existence constraints enforce that the value of the specified property is non-null for all entities in the constraint domain. +The domain of a node property existence constraint are all nodes with the specified label. +Similarly, the domain of a relationship property existence constraint are all relationship with the specified type. +Property existence constraints mandates that the value of the specified property exists (is non-null) for all entities in the domain. ===== Example From d67e2eab4855e39a05162bc11ba8af43a3611270 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Thu, 15 Dec 2016 10:39:49 +0100 Subject: [PATCH 04/42] Add example for existence constraint - Use hex integers for rgb examples --- .../CIP2016-12-14-Constraint-syntax.adoc | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index f3e058a939..9201b0ec45 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -122,9 +122,9 @@ Consider the graph created by the following statement: [source, cypher] ---- -CREATE (:Color {name: 'white', rgb: 255}) -CREATE (:Color {name: 'black', rgb: 0}) -CREATE (:Color {name: 'very, very dark grey', rgb: 0}) // rounding error! +CREATE (:Color {name: 'white', rgb: 0xffffff}) +CREATE (:Color {name: 'black', rgb: 0x000000}) +CREATE (:Color {name: 'very, very dark grey', rgb: 0x000000}) // rounding error! ---- Due to the duplication of the `rgb` property, the following attempt at creating a constraint will fail: @@ -185,6 +185,32 @@ Property existence constraints mandates that the value of the specified property ===== Example +Consider the graph containing `:Color` nodes. +Each color has an integral RGB value representation in a property `rgb`. +Users may lookup color nodes to extract their RGB values for application processing. +Users may also add new color nodes to the graph. + +Suppose the query that looks up the RGB value of a color with a given name looks like this: + +[source, cypher] +---- +MATCH (c:Color {name: $name}) +WHERE exists(c.rgb) +RETURN c.rgb +---- + +The `WHERE` clause protects the application from receiving `null` values back for user-defined colors where the RGB values have not been specified correctly. +It may however be eliminated by the introduction of a node property existence constraint: + +[source, cypher] +---- +CREATE CONSTRAINT colors_must_have_rgb +FOR (c:Color) +REQUIRE exists(c.rgb) +---- + +Any updating statement that would create a `:Color` node without specifying a `rgb` property for it would now fail. + === Interaction with existing features The main interaction between the constraints and the rest of the language happens during updating statements. From e1f69ede5ec98331e6e8e93894e9b0b76e852038 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Thu, 15 Dec 2016 10:53:20 +0100 Subject: [PATCH 05/42] Add SQL examples --- .../CIP2016-12-14-Constraint-syntax.adoc | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 9201b0ec45..47a0802749 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -232,7 +232,49 @@ which quickly becomes unwieldy for multiple properties. == What others do -// TODO: SQL syntax for constraints +In SQL, the following constraints exist (http://www.w3schools.com/sql/sql_constraints.asp): + +* `NOT NULL` - Indicates that a column cannot store NULL value +* `UNIQUE` - Ensures that each row for a column must have a unique value +* `PRIMARY KEY` - A combination of a `NOT NULL` and `UNIQUE`. Ensures that a column (or combination of two or more columns) have a unique identity which helps to find a particular record in a table more easily and quickly +* `FOREIGN KEY` - Ensure the referential integrity of the data in one table to match values in another table +* `CHECK` - Ensures that the value in a column meets a specific condition +* `DEFAULT` - Specifies a default value for a column +The next chapters will describe each constraint in detail. + +The property existence constraints represent the same functionality as the `NOT NULL` SQL constraint. +The node property uniqueness constraint represents the `PRIMARY KEY` SQL constraint. + +SQL constraints may be introduced at table creation time (in a `CREATE TABLE` statement), or in an `ALTER TABLE` statement: + +.Creating a persons table in SQL Server / Oracle / MS Access: +[source, sql] +---- +CREATE TABLE Persons +( + P_Id int NOT NULL UNIQUE, + LastName varchar(255) NOT NULL, + FirstName varchar(255)) +---- + +.Creating a persons table in MySQL: +[source, sql] +---- +CREATE TABLE Persons +( + P_Id int NOT NULL, + LastName varchar(255) NOT NULL, + FirstName varchar(255) + UNIQUE (P_Id) +) +---- + +.Adding a named composite `UNIQUE` constraint in MySQL / SQL Server / Oracle / MS Access: +[source, sql] +---- +ALTER TABLE Persons +ADD CONSTRAINT uc_PersonID UNIQUE (P_Id,LastName) +---- == Benefits to this proposal From 0fb11614a44d08a3cb2bfba3b3ba5f668d86ee3a Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Thu, 15 Dec 2016 11:12:54 +0100 Subject: [PATCH 06/42] Update standardisation scope --- docs/standardisation-scope.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/standardisation-scope.adoc b/docs/standardisation-scope.adoc index 3663597885..5e6ead0a9b 100644 --- a/docs/standardisation-scope.adoc +++ b/docs/standardisation-scope.adoc @@ -44,6 +44,10 @@ It is the goal of this project to create a good and feature-rich standard langua * `allShortestPaths()` * `shortestPath()` +=== Commands + +* `CREATE CONSTRAINT` + === Operators ==== General @@ -210,7 +214,6 @@ It is the goal of this project to create a good and feature-rich standard langua === Commands -* `CREATE CONSTRAINT` * `CREATE INDEX` === Operators From 0baf002dc1c153fcecbb0e355c6afe17174ed7a8 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Thu, 15 Dec 2016 12:23:50 +0100 Subject: [PATCH 07/42] Add grammar rules for new constraint syntax --- grammar/basic-grammar.xml | 2 +- grammar/commands.xml | 45 ++++++++++++++++++++- tools/grammar/src/test/resources/cypher.txt | 13 ++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/grammar/basic-grammar.xml b/grammar/basic-grammar.xml index 6235c3a02e..3ab77ff665 100644 --- a/grammar/basic-grammar.xml +++ b/grammar/basic-grammar.xml @@ -149,7 +149,7 @@ - + : &WS; diff --git a/grammar/commands.xml b/grammar/commands.xml index abff6a6373..0c811ec163 100644 --- a/grammar/commands.xml +++ b/grammar/commands.xml @@ -47,7 +47,7 @@ xsi:schemaLocation="http://opencypher.org/grammar xmlschemas/ocGrammar.xsd " > - + @@ -60,10 +60,51 @@ + + + + + + + + + + CREATE &SP; CONSTRAINT &SP; &SP; + FOR &SP; &SP; + REQUIRE &SP; + + + + DROP &SP; CONSTRAINT &SP; + + + + + + + + + ( &var; &label; ) + ( ) - [ &var; ] - ( ) + + + + + + + - + + UNIQUE &SP; &WS; , &WS; + + + + exists ( ) + + + CREATE &SP; diff --git a/tools/grammar/src/test/resources/cypher.txt b/tools/grammar/src/test/resources/cypher.txt index 786eeadb69..8c3b14c25d 100644 --- a/tools/grammar/src/test/resources/cypher.txt +++ b/tools/grammar/src/test/resources/cypher.txt @@ -312,3 +312,16 @@ CALL db.labels() YIELD * WHERE label CONTAINS 'User' AND foo + bar = foo RETURN count(label) AS numLabels§ CALL db.labels() YIELD x WHERE label CONTAINS 'User' AND foo + bar = foo RETURN count(label) AS numLabels§ +CREATE CONSTRAINT foo +FOR (p:Person) +REQUIRE UNIQUE p.name§ +CREATE CONSTRAINT bar +FOR (p:Person) +REQUIRE UNIQUE p.name, p.email§ +CREATE CONSTRAINT baz +FOR (p:Person) +REQUIRE exists(p.name)§ +CREATE CONSTRAINT cru +FOR ()-[r:REL]-() +REQUIRE exists(r.property)§ +DROP CONSTRAINT foo_bar_baz§ From 19a896dc74f62716bd3fbbe895a68a333e9205a3 Mon Sep 17 00:00:00 2001 From: Petra Selmer Date: Wed, 15 Feb 2017 10:49:59 +0000 Subject: [PATCH 08/42] Edited the textual contents of the CIP --- .../CIP2016-12-14-Constraint-syntax.adoc | 100 +++++++++--------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 47a0802749..a38d5c6238 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -17,12 +17,12 @@ toc::[] == Motivation -Constraints provide utility for shaping the data graph. +Constraints provide the means by which various aspects of the data graph may be controlled. == Background -Cypher has a loose notion of schema, in which nodes and relationships may take very heterogeneous forms, both in terms of properties and in graph patterns. -Constraints allows us to bound the heterogeneous nature of the property graph into a more regular form. +Cypher has a loose notion of a schema, in which nodes and relationships may take very heterogeneous forms, both in terms of properties and in graph patterns. +Constraints allow us to mould the heterogeneous nature of the property graph into a more regular form. == Proposal @@ -32,7 +32,7 @@ This CIP includes the following proposed constraints: * <> * <> -Each constraint is detailed in its own below section. +Each constraint is detailed in the sections below. Once a constraint has been created, it may not be amended. Should a user wish to change its definition, it has to be dropped and recreated with an updated structure. @@ -61,7 +61,7 @@ drop-constraint = "DROP", "CONSTRAINT", constraint-name ; The constraint expressions vary depending on the actual constraint (see the detailed sections). -.Example of dropping a constraint with name foo: +.Example of dropping a constraint with name `foo`: [source, cypher] ---- DROP CONSTRAINT foo @@ -69,12 +69,12 @@ DROP CONSTRAINT foo === Semantics overview -The semantics are defined for each type of constraint, but some characteristics are shared: +The following list describes the situations in which an error will be raised: -* When a statement tries to create a constraint on a graph where the data does not pass the constraint criterion, that statement will raise an error. -* When a statement tries to create a constraint with a name that already exists, that statement will raise an error. -* When a statement tries to drop a constraint referencing a name that does not exist, that statement will raise an error. -* When an updating statement tries to modify the graph in such a way that it would violate a constraint, that statement will raise an error. +* Attempting to create a constraint on a graph where the data does not comply with the constraint criterion. +* Attempting to create a constraint with a name that already exists. +* Attempting to drop a constraint referencing a non-existent name. +* Attempting to modify the graph in such a way that it would violate a constraint. The constraints define a _domain_ within which the constraint applies. The domain is defined by the constraint pattern. @@ -82,18 +82,18 @@ The domain is defined by the constraint pattern. [[uniqueness]] === Node property uniqueness constraint -This constraint enforces that there can not be duplicate values of a certain property for a certain type of node. -For example, that among nodes labeled with `:Person`, each `email` property must be unique. +This constraint enforces that there cannot be duplicate values of some property `p` for any node labeled with some label `l`. +For example, this constraint can specify that the `email` property must be unique for all nodes labeled with `:Person`. ==== Syntax -.Grammar definition for node property uniqueness constraint: +.Grammar definition for the node property uniqueness constraint: [source, ebnf] ---- uniqueness-constraint = "UNIQUE", property-expression, { ",", property-expression } ; ---- -.Example of single-property uniqueness constraint: +.Example of a single-property uniqueness constraint: [source, cypher] ---- CREATE CONSTRAINT unique_person_email @@ -101,7 +101,7 @@ FOR (p:Person) REQUIRE UNIQUE p.email ---- -.Example of multiple-property uniqueness constraint: +.Example of a multiple-property uniqueness constraint: [source, cypher] ---- CREATE CONSTRAINT unique_person_details @@ -111,10 +111,10 @@ REQUIRE UNIQUE p.name, p.email, p.address ==== Semantics -The domain of a property uniqueness constraint is defined as all the nodes with a specific label where the specified property key(s) exist (are non-null). +The domain of a property uniqueness constraint is defined as all the nodes with a specific label where the specified property key(s) exist (i.e. are not null). When more than one property key is defined as part of the constraint, only nodes where _all_ of the properties exist are part of the domain. -The uniqueness mandates that two distinct nodes within the domain can not have the same combination of values for the defined properties (respectively). +The uniqueness constraint mandates that two distinct nodes within the domain cannot have the same combination of values for the defined properties. ===== Example @@ -127,7 +127,7 @@ CREATE (:Color {name: 'black', rgb: 0x000000}) CREATE (:Color {name: 'very, very dark grey', rgb: 0x000000}) // rounding error! ---- -Due to the duplication of the `rgb` property, the following attempt at creating a constraint will fail: +Owing to the duplication of the `rgb` property, the following attempt at creating a constraint will fail: [source, cypher] ---- @@ -149,18 +149,18 @@ REQUIRE UNIQUE c.rgb, c.name [[existence]] === Property existence constraints -Property existence constraints are defined for both nodes and relationships, but the semantics are the same. -For this reason we will go over both constraints in the same section. +Property existence constraints are defined for both nodes and relationships; these have the same semantics. +We now describe both of these. ==== Syntax -.Grammar definition for property existence constraint: +.Grammar definition for the property existence constraint: [source, ebnf] ---- existence-constraint = "exists", "(", property-expression, ")" ; ---- -.Example of node property existence constraint: +.Example of a node property existence constraint: [source, cypher] ---- CREATE CONSTRAINT colors_must_have_rgb @@ -168,7 +168,7 @@ FOR (c:Color) REQUIRE exists(c.rgb) ---- -.Example of relationship property existence constraint: +.Example of a relationship property existence constraint: [source, cypher] ---- CREATE CONSTRAINT rates_have_quality @@ -181,16 +181,16 @@ REQUIRE exists(l.rating) The domain of a node property existence constraint are all nodes with the specified label. Similarly, the domain of a relationship property existence constraint are all relationship with the specified type. -Property existence constraints mandates that the value of the specified property exists (is non-null) for all entities in the domain. +The property existence constraint mandates that the value of the specified property exists (i.e. is not null) for all entities in the domain. ===== Example Consider the graph containing `:Color` nodes. -Each color has an integral RGB value representation in a property `rgb`. -Users may lookup color nodes to extract their RGB values for application processing. -Users may also add new color nodes to the graph. +Each color is represented as an integer-type RGB value in a property `rgb`. +Users may look up nodes labeled with `:Color` to extract their RGB values for application processing. +Users may also add new `:Color`-labeled nodes to the graph. -Suppose the query that looks up the RGB value of a color with a given name looks like this: +The following query retrieves the RGB value of a color with a given `name`: [source, cypher] ---- @@ -199,8 +199,8 @@ WHERE exists(c.rgb) RETURN c.rgb ---- -The `WHERE` clause protects the application from receiving `null` values back for user-defined colors where the RGB values have not been specified correctly. -It may however be eliminated by the introduction of a node property existence constraint: +The `WHERE` clause may be used to prevent an application from retrieving `null` values for user-defined colors where the RGB values have not been specified correctly. +It may, however, be eliminated by the introduction of a node property existence constraint: [source, cypher] ---- @@ -213,54 +213,52 @@ Any updating statement that would create a `:Color` node without specifying a `r === Interaction with existing features -The main interaction between the constraints and the rest of the language happens during updating statements. -Existing constraints will cause certain updating statements to fail; in fact, that's the main purpose. +The main interaction between the constraints and the rest of the language occurs during updating statements. +Existing constraints will cause any updating statements to fail, thereby fulfilling the main purpose of this feature. === Alternatives -Plenty of alternative syntaxes have been discussed: +Alternative syntaxes have been discussed: * `GIVEN`, `CONSTRAIN`, `ASSERT` instead of `FOR` * `ASSERT`, `ENFORCE`, `IMPLIES` instead of `REQUIRE` -The use of existing expression to express uniqueness, instead of using a new keyword `UNIQUE`, on the form: +The use of an existing expression to express uniqueness -- instead of using a new keyword `UNIQUE` -- becomes unwieldy for multiple properties, as exemplified by the following: ---- FOR (p:Person), (q:Person) REQUIRE p.email <> q.email AND p <> q ---- -which quickly becomes unwieldy for multiple properties. == What others do In SQL, the following constraints exist (http://www.w3schools.com/sql/sql_constraints.asp): -* `NOT NULL` - Indicates that a column cannot store NULL value -* `UNIQUE` - Ensures that each row for a column must have a unique value -* `PRIMARY KEY` - A combination of a `NOT NULL` and `UNIQUE`. Ensures that a column (or combination of two or more columns) have a unique identity which helps to find a particular record in a table more easily and quickly -* `FOREIGN KEY` - Ensure the referential integrity of the data in one table to match values in another table +* `NOT NULL` - Indicates that a column cannot store a null value. +* `UNIQUE` - Ensures that each row for a column must have a unique value. +* `PRIMARY KEY` - A combination of a `NOT NULL` and `UNIQUE`. Ensures that a column (or a combination of two or more columns) has a unique identity, reducing the resources required to locate a specific record in a table. +* `FOREIGN KEY` - Ensures the referential integrity of the data in one table matches values in another table. * `CHECK` - Ensures that the value in a column meets a specific condition -* `DEFAULT` - Specifies a default value for a column -The next chapters will describe each constraint in detail. +* `DEFAULT` - Specifies a default value for a column. -The property existence constraints represent the same functionality as the `NOT NULL` SQL constraint. -The node property uniqueness constraint represents the `PRIMARY KEY` SQL constraint. +The property existence constraints correspond to the `NOT NULL` SQL constraint. +The node property uniqueness constraint corresponds to the `PRIMARY KEY` SQL constraint. -SQL constraints may be introduced at table creation time (in a `CREATE TABLE` statement), or in an `ALTER TABLE` statement: +SQL constraints may be introduced at table creation time in a `CREATE TABLE` statement, or in an `ALTER TABLE` statement: -.Creating a persons table in SQL Server / Oracle / MS Access: +.Creating a `Person` table in SQL Server / Oracle / MS Access: [source, sql] ---- -CREATE TABLE Persons +CREATE TABLE Person ( P_Id int NOT NULL UNIQUE, LastName varchar(255) NOT NULL, FirstName varchar(255)) ---- -.Creating a persons table in MySQL: +.Creating a `Person` table in MySQL: [source, sql] ---- -CREATE TABLE Persons +CREATE TABLE Person ( P_Id int NOT NULL, LastName varchar(255) NOT NULL, @@ -272,14 +270,14 @@ CREATE TABLE Persons .Adding a named composite `UNIQUE` constraint in MySQL / SQL Server / Oracle / MS Access: [source, sql] ---- -ALTER TABLE Persons +ALTER TABLE Person ADD CONSTRAINT uc_PersonID UNIQUE (P_Id,LastName) ---- == Benefits to this proposal -Constraints make Cypher's notion of schema more well-defined, and allows users to keep graphs in a more regular, easier to manage form. +Constraints make Cypher's notion of schema more well-defined, allowing users to maintain graphs in a more regular, easier-to-manage form. == Caveats to this proposal -For an implementing system, some constraints may prove challenging to enforce, as they generally require scanning through large parts of the graph to look for conflicting entities. +Some constraints may prove challenging to enforce in a system seeking to implement the contents of this CIP, as these generally require scanning through large parts of the graph to locate conflicting entities. From 7b273aadd707c8749fc4dc03150164254ce87be0 Mon Sep 17 00:00:00 2001 From: Petra Selmer Date: Thu, 16 Feb 2017 09:09:02 +0000 Subject: [PATCH 09/42] More textual edits to the Constraints CIP --- .../CIP2016-12-14-Constraint-syntax.adoc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index a38d5c6238..304fba0ac4 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -40,7 +40,7 @@ Should a user wish to change its definition, it has to be dropped and recreated ==== Constraint names All constraints require the user to specify a nonempty _name_ at constraint creation time. -This name is subsequently the handle with which a user may refer to the constraint, e.g. when dropping it. +This name is subsequently the handle with which a user may refer to the constraint, for example when dropping it. // TODO: Should we impose restrictions on the domain of constraint names, or are all Unicode characters allowed? @@ -118,7 +118,11 @@ The uniqueness constraint mandates that two distinct nodes within the domain can ===== Example -Consider the graph created by the following statement: +Consider the graph created by the statement below. +The graph contains nodes labeled with `:Color`. +Each color is represented as an integer-type RGB value in a property `rgb`. +Users may look up nodes labeled with `:Color` to extract their RGB values for application processing. +Users may also add new `:Color`-labeled nodes to the graph. [source, cypher] ---- @@ -136,7 +140,7 @@ FOR (c:Color) REQUIRE UNIQUE c.rgb ---- -Suppose that we would rather like to have one color node per name _and_ RGB value (to work around the rounding errors). +Suppose that we would rather like to have one color node per `name` _and_ `rgb` value (to work around the rounding errors). We could then use the following constraint, without modifying our data: [source, cypher] @@ -185,10 +189,7 @@ The property existence constraint mandates that the value of the specified prope ===== Example -Consider the graph containing `:Color` nodes. -Each color is represented as an integer-type RGB value in a property `rgb`. -Users may look up nodes labeled with `:Color` to extract their RGB values for application processing. -Users may also add new `:Color`-labeled nodes to the graph. +Consider once again the graph containing `:Color` nodes. The following query retrieves the RGB value of a color with a given `name`: From 9de7ce06d630a042569d0f37045c791bc00eb353 Mon Sep 17 00:00:00 2001 From: Petra Selmer Date: Fri, 17 Feb 2017 08:20:14 +0000 Subject: [PATCH 10/42] Amended w3 reference to denote alterations in 'quoted' text --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 304fba0ac4..f05c657908 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -232,7 +232,7 @@ REQUIRE p.email <> q.email AND p <> q == What others do -In SQL, the following constraints exist (http://www.w3schools.com/sql/sql_constraints.asp): +In SQL, the following constraints exist (inspired by http://www.w3schools.com/sql/sql_constraints.asp): * `NOT NULL` - Indicates that a column cannot store a null value. * `UNIQUE` - Ensures that each row for a column must have a unique value. From bea414b88aca52c846ee579b614c9c995fdff154 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Wed, 1 Mar 2017 14:17:44 +0100 Subject: [PATCH 11/42] Rework CIP - Specify general constraint language - Specify `UNIQUE` operator - Clearly define semantics for domain and expressions - List all concrete constraints in example section - Add several more examples --- .../CIP2016-12-14-Constraint-syntax.adoc | 174 ++++++++++-------- 1 file changed, 94 insertions(+), 80 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index f05c657908..979c38cb2e 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -26,40 +26,45 @@ Constraints allow us to mould the heterogeneous nature of the property graph int == Proposal -This CIP includes the following proposed constraints: +This CIP specifies the general syntax for constraint definition (and constraint removal), and provides several examples of possible use cases for constraints. +However, the specification does not otherwise specify or limit the space of expressible constraints that the syntax and semantics allow. -* <> -* <> -* <> - -Each constraint is detailed in the sections below. +===== Mutability Once a constraint has been created, it may not be amended. Should a user wish to change its definition, it has to be dropped and recreated with an updated structure. -==== Constraint names +===== Constraint names All constraints require the user to specify a nonempty _name_ at constraint creation time. This name is subsequently the handle with which a user may refer to the constraint, for example when dropping it. // TODO: Should we impose restrictions on the domain of constraint names, or are all Unicode characters allowed? -=== Syntax overview +=== Syntax -The syntax for all constraints follow the same basic outline. +The constraint syntax is defined as follows: .Grammar definition for constraint syntax. [source, ebnf] ---- constraint command = create-constraint | drop-constraint ; -create-constraint = "CREATE", "CONSTRAINT", constraint-name, "FOR", constraint-pattern, "REQUIRE", constraint-expr ; +create-constraint = "CREATE", "CONSTRAINT", constraint-name, "FOR", constraint-pattern, "REQUIRE", constraint-expr, { "REQUIRE", constraint-expr } ; constraint-name = symbolic-name constraint-pattern = node-pattern | simple-pattern ; -constraint-expr = uniqueness-constraint | existence-constraint ; +constraint-expr = uniqueness-expr | expression ; +uniquness-expr = "UNIQUE", property-expression, { ",", property-expression } drop-constraint = "DROP", "CONSTRAINT", constraint-name ; ---- -The constraint expressions vary depending on the actual constraint (see the detailed sections). +The constraint expression (`constraint-expr` above) is any expression that evaluates to a boolean value. +This allows for very complex concrete constraint definitions within the specified syntax. + +To that set of valid expressions, this CIP further specifies a special prefix operator `UNIQUE`, which is used to assert uniqueness of one or more property expressions. + +==== Removing constraints + +A constraint is removed by referring to its name. .Example of dropping a constraint with name `foo`: [source, cypher] @@ -67,41 +72,25 @@ The constraint expressions vary depending on the actual constraint (see the deta DROP CONSTRAINT foo ---- -=== Semantics overview +=== Semantics -The following list describes the situations in which an error will be raised: - -* Attempting to create a constraint on a graph where the data does not comply with the constraint criterion. -* Attempting to create a constraint with a name that already exists. -* Attempting to drop a constraint referencing a non-existent name. -* Attempting to modify the graph in such a way that it would violate a constraint. +The semantics for constraints follow these general rules: -The constraints define a _domain_ within which the constraint applies. -The domain is defined by the constraint pattern. +1. The constraint pattern define the constraint domain, where all entities that would be returned by a `MATCH` clause with the same pattern constitute the domain, with one notable exception (see <>). -[[uniqueness]] -=== Node property uniqueness constraint +2. The constraint expressions defined in the `REQUIRE` clauses of the constraint definition must all evaluate to `true`. Any other result raises an error (see <>). -This constraint enforces that there cannot be duplicate values of some property `p` for any node labeled with some label `l`. -For example, this constraint can specify that the `email` property must be unique for all nodes labeled with `:Person`. +3. [[domain-exception]]Entities for which a constraint expression evaluate to `null` under Cypher's ternary logic are _excluded_ from the constraint domain, even if they fit within the constraint pattern. -==== Syntax +==== Uniqueness -.Grammar definition for the node property uniqueness constraint: -[source, ebnf] ----- -uniqueness-constraint = "UNIQUE", property-expression, { ",", property-expression } ; ----- +The new operator `UNIQUE` is only valid as part of a constraint expression. +It takes as argument one or more property expressions, and asserts that the combination of the evaluated values of the expressions (forming a tuple) is unique across the constraint domain. -.Example of a single-property uniqueness constraint: -[source, cypher] ----- -CREATE CONSTRAINT unique_person_email -FOR (p:Person) -REQUIRE UNIQUE p.email ----- +The domain of the uniqueness expression is limited to entities for which _all_ properties defined as arguments to the `UNIQUE` operator exist. +In other words, property expressions which evaluate to `null` are not considered for uniqueness (see <>) above. -.Example of a multiple-property uniqueness constraint: +.Example of a constraint definition using `UNIQUE`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- CREATE CONSTRAINT unique_person_details @@ -109,14 +98,31 @@ FOR (p:Person) REQUIRE UNIQUE p.name, p.email, p.address ---- -==== Semantics +==== Errors + +The following list describes the situations in which an error will be raised: + +* Attempting to create a constraint on a graph where the data does not comply with the constraint criterion. +* Attempting to create a constraint with a name that already exists. +* Attempting to drop a constraint referencing a non-existent name. +* Attempting to modify the graph in such a way that it would violate a constraint. + +The constraints define a _domain_ within which the constraint applies. +The domain is defined by the constraint pattern. + +==== Compositionality + +It is possible to define multiple `REQUIRE` clauses within the scope of the same constraint. +The semantics between these is that of a conjunction between the constraint expressions of the clauses, such that the constraint is upheld if and only if for all `REQUIRE` clauses, the expression evaluates to `true`. + +This is useful not only for readability and logical separation of different aspects of the same constraint, but also for combining the use of the `UNIQUE` operator with other constraint expressions. -The domain of a property uniqueness constraint is defined as all the nodes with a specific label where the specified property key(s) exist (i.e. are not null). -When more than one property key is defined as part of the constraint, only nodes where _all_ of the properties exist are part of the domain. +=== Examples -The uniqueness constraint mandates that two distinct nodes within the domain cannot have the same combination of values for the defined properties. +In this section we provide several examples of constraints that are possible to express in the specified syntax. -===== Example +[NOTE] +The specification in this CIP is limited to the general syntax of constraints, and the following are simply examples of possible uses of the language defined by that syntax. None of the examples provided are to be viewed as mandatory for any Cypher implementation. Consider the graph created by the statement below. The graph contains nodes labeled with `:Color`. @@ -150,21 +156,18 @@ FOR (c:Color) REQUIRE UNIQUE c.rgb, c.name ---- -[[existence]] -=== Property existence constraints +Now, consider the following query which retrieves the RGB value of a color with a given `name`: -Property existence constraints are defined for both nodes and relationships; these have the same semantics. -We now describe both of these. - -==== Syntax - -.Grammar definition for the property existence constraint: -[source, ebnf] +[source, cypher] ---- -existence-constraint = "exists", "(", property-expression, ")" ; +MATCH (c:Color {name: $name}) +WHERE exists(c.rgb) +RETURN c.rgb ---- -.Example of a node property existence constraint: +The `WHERE` clause is here used to prevent an application from retrieving `null` values for user-defined colors where the RGB values have not been specified correctly. +It may, however, be eliminated by the introduction of a constraint asserting the existence of that property: + [source, cypher] ---- CREATE CONSTRAINT colors_must_have_rgb @@ -172,45 +175,53 @@ FOR (c:Color) REQUIRE exists(c.rgb) ---- -.Example of a relationship property existence constraint: +Any updating statement that would create a `:Color` node without specifying an `rgb` property for it would now fail. + +Alternatively, we could extend our previous constraint definition with this new requirement: + [source, cypher] ---- -CREATE CONSTRAINT rates_have_quality -FOR ()-[l:RATED]-() -REQUIRE exists(l.rating) +CREATE CONSTRAINT color_schema +FOR (c:Color) +REQUIRE UNIQUE c.rgb, c.name +REQUIRE exists(c.rgb) ---- -==== Semantics - -The domain of a node property existence constraint are all nodes with the specified label. -Similarly, the domain of a relationship property existence constraint are all relationship with the specified type. - -The property existence constraint mandates that the value of the specified property exists (i.e. is not null) for all entities in the domain. - -===== Example +This composite constraint will make sure that all `:Color` nodes has a value for their `rgb` property, and that its value is unique for each `name`. -Consider once again the graph containing `:Color` nodes. - -The following query retrieves the RGB value of a color with a given `name`: +More complex constraint definitions are considered below: +.Property value limitations [source, cypher] ---- -MATCH (c:Color {name: $name}) -WHERE exists(c.rgb) -RETURN c.rgb +CREATE CONSTRAINT road_width +FOR ()-[r:ROAD]-() +REQUIRE 5 < r.width < 50 ---- -The `WHERE` clause may be used to prevent an application from retrieving `null` values for user-defined colors where the RGB values have not been specified correctly. -It may, however, be eliminated by the introduction of a node property existence constraint: +.Cardinality +[source, cypher] +---- +CREATE CONSTRAINT spread_the_love +FOR (p:Person) +REQUIRE size((p)-[:LOVES]->()) > 3 +---- +.Endpoint requirements [source, cypher] ---- -CREATE CONSTRAINT colors_must_have_rgb -FOR (c:Color) -REQUIRE exists(c.rgb) +CREATE CONSTRAINT can_only_own_things +FOR ()-[:OWNS]->(t) +REQUIRE (t:Vehicle) OR (t:Building) OR (t:Object) ---- -Any updating statement that would create a `:Color` node without specifying a `rgb` property for it would now fail. +.Label coexistence +[source, cypher] +---- +CREATE CONSTRAINT programmers_are_people_too +FOR (p:Programmer) +REQUIRE p:Person +---- === Interaction with existing features @@ -224,7 +235,7 @@ Alternative syntaxes have been discussed: * `GIVEN`, `CONSTRAIN`, `ASSERT` instead of `FOR` * `ASSERT`, `ENFORCE`, `IMPLIES` instead of `REQUIRE` -The use of an existing expression to express uniqueness -- instead of using a new keyword `UNIQUE` -- becomes unwieldy for multiple properties, as exemplified by the following: +The use of an existing expression to express uniqueness -- instead of using the operator `UNIQUE` -- becomes unwieldy for multiple properties, as exemplified by the following: ---- FOR (p:Person), (q:Person) REQUIRE p.email <> q.email AND p <> q @@ -279,6 +290,9 @@ ADD CONSTRAINT uc_PersonID UNIQUE (P_Id,LastName) Constraints make Cypher's notion of schema more well-defined, allowing users to maintain graphs in a more regular, easier-to-manage form. +Additionally, this specification is deliberately defining a constraint _language_ within which a great deal of possible concrete constraints are made possible. +This allows different implementers of Cypher to independently choose how to limit the scope of supported constraint expressions that fit their model and targeted use cases. + == Caveats to this proposal Some constraints may prove challenging to enforce in a system seeking to implement the contents of this CIP, as these generally require scanning through large parts of the graph to locate conflicting entities. From 2282f06a4837c292bf1be9d46ea0d03b28cb3d9f Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Wed, 1 Mar 2017 14:18:31 +0100 Subject: [PATCH 12/42] Remove Motivation section --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 979c38cb2e..c323636296 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -15,10 +15,6 @@ These are language constructs that impose restrictions on the shape of the data toc::[] -== Motivation - -Constraints provide the means by which various aspects of the data graph may be controlled. - == Background Cypher has a loose notion of a schema, in which nodes and relationships may take very heterogeneous forms, both in terms of properties and in graph patterns. From 3ba5278ce2f82a934797442d2d1aec9adf672214 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Wed, 1 Mar 2017 14:21:01 +0100 Subject: [PATCH 13/42] Move Mutability and Name sections They are more appropriate under the Semantics and Syntax sections, respectively. --- .../CIP2016-12-14-Constraint-syntax.adoc | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index c323636296..f4415df00f 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -25,18 +25,6 @@ Constraints allow us to mould the heterogeneous nature of the property graph int This CIP specifies the general syntax for constraint definition (and constraint removal), and provides several examples of possible use cases for constraints. However, the specification does not otherwise specify or limit the space of expressible constraints that the syntax and semantics allow. -===== Mutability - -Once a constraint has been created, it may not be amended. -Should a user wish to change its definition, it has to be dropped and recreated with an updated structure. - -===== Constraint names - -All constraints require the user to specify a nonempty _name_ at constraint creation time. -This name is subsequently the handle with which a user may refer to the constraint, for example when dropping it. - -// TODO: Should we impose restrictions on the domain of constraint names, or are all Unicode characters allowed? - === Syntax The constraint syntax is defined as follows: @@ -58,6 +46,13 @@ This allows for very complex concrete constraint definitions within the specifie To that set of valid expressions, this CIP further specifies a special prefix operator `UNIQUE`, which is used to assert uniqueness of one or more property expressions. +==== Constraint names + +All constraints require the user to specify a nonempty _name_ at constraint creation time. +This name is subsequently the handle with which a user may refer to the constraint, for example when dropping it. + +// TODO: Should we impose restrictions on the domain of constraint names, or are all Unicode characters allowed? + ==== Removing constraints A constraint is removed by referring to its name. @@ -78,6 +73,11 @@ The semantics for constraints follow these general rules: 3. [[domain-exception]]Entities for which a constraint expression evaluate to `null` under Cypher's ternary logic are _excluded_ from the constraint domain, even if they fit within the constraint pattern. +==== Mutability + +Once a constraint has been created, it may not be amended. +Should a user wish to change its definition, it has to be dropped and recreated with an updated structure. + ==== Uniqueness The new operator `UNIQUE` is only valid as part of a constraint expression. From 9645c8d8c2c7b7f8aa6883c072aa590dc8d3441f Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Wed, 1 Mar 2017 14:26:56 +0100 Subject: [PATCH 14/42] Add cross-links Move Errors section --- .../CIP2016-12-14-Constraint-syntax.adoc | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index f4415df00f..f2680afda1 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -44,7 +44,7 @@ drop-constraint = "DROP", "CONSTRAINT", constraint-name ; The constraint expression (`constraint-expr` above) is any expression that evaluates to a boolean value. This allows for very complex concrete constraint definitions within the specified syntax. -To that set of valid expressions, this CIP further specifies a special prefix operator `UNIQUE`, which is used to assert uniqueness of one or more property expressions. +To that set of valid expressions, this CIP further specifies a special prefix operator `UNIQUE`, which is used to assert uniqueness of one or more property expressions (see <> for details). ==== Constraint names @@ -67,17 +67,27 @@ DROP CONSTRAINT foo The semantics for constraints follow these general rules: -1. The constraint pattern define the constraint domain, where all entities that would be returned by a `MATCH` clause with the same pattern constitute the domain, with one notable exception (see <>). +1. The constraint pattern define the constraint _domain_, where all entities that would be returned by a `MATCH` clause with the same pattern constitute the domain, with one notable exception (see <>). -2. The constraint expressions defined in the `REQUIRE` clauses of the constraint definition must all evaluate to `true`. Any other result raises an error (see <>). +2. The constraint expressions defined in the `REQUIRE` clauses of the constraint definition must all evaluate to `true`. 3. [[domain-exception]]Entities for which a constraint expression evaluate to `null` under Cypher's ternary logic are _excluded_ from the constraint domain, even if they fit within the constraint pattern. +==== Errors + +The following list describes the situations in which an error will be raised: + +* Attempting to create a constraint on a graph where the data does not comply with the constraint criterion. +* Attempting to create a constraint with a name that already exists. +* Attempting to drop a constraint referencing a non-existent name. +* Attempting to modify the graph in such a way that it would violate a constraint. + ==== Mutability Once a constraint has been created, it may not be amended. Should a user wish to change its definition, it has to be dropped and recreated with an updated structure. +[[uniqueness]] ==== Uniqueness The new operator `UNIQUE` is only valid as part of a constraint expression. @@ -94,18 +104,6 @@ FOR (p:Person) REQUIRE UNIQUE p.name, p.email, p.address ---- -==== Errors - -The following list describes the situations in which an error will be raised: - -* Attempting to create a constraint on a graph where the data does not comply with the constraint criterion. -* Attempting to create a constraint with a name that already exists. -* Attempting to drop a constraint referencing a non-existent name. -* Attempting to modify the graph in such a way that it would violate a constraint. - -The constraints define a _domain_ within which the constraint applies. -The domain is defined by the constraint pattern. - ==== Compositionality It is possible to define multiple `REQUIRE` clauses within the scope of the same constraint. From 76eac597c7099cde825dec7cfc1f6d9dfbdb1e7b Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Wed, 1 Mar 2017 15:53:14 +0100 Subject: [PATCH 15/42] Add example for CIR-2017-172 References #172 --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index f2680afda1..2745d1f961 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -217,6 +217,16 @@ FOR (p:Programmer) REQUIRE p:Person ---- +Assuming a function `acyclic()` that takes a path as argument and returns `true` if and only if the same node does not appear twice in the path, otherwise `false`, we may express: + +.Constraint example from CIR-2017-172 +[source, cypher] +---- +CREATE CONSTRAINT enforce_dag_acyclic_for_R_links +FOR p = ()-[:R*]-() +REQUIRE acyclic(p) +---- + === Interaction with existing features The main interaction between the constraints and the rest of the language occurs during updating statements. From efbba933949dd86443204bf5ce3d1cbeaa7c538d Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Wed, 1 Mar 2017 21:57:02 +0100 Subject: [PATCH 16/42] Support arbitrary patterns - Remove TODO - Add example using larger pattern - Add example using multiple `exists()` --- .../CIP2016-12-14-Constraint-syntax.adoc | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 2745d1f961..070a9a5bde 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -33,9 +33,8 @@ The constraint syntax is defined as follows: [source, ebnf] ---- constraint command = create-constraint | drop-constraint ; -create-constraint = "CREATE", "CONSTRAINT", constraint-name, "FOR", constraint-pattern, "REQUIRE", constraint-expr, { "REQUIRE", constraint-expr } ; +create-constraint = "CREATE", "CONSTRAINT", constraint-name, "FOR", pattern, "REQUIRE", constraint-expr, { "REQUIRE", constraint-expr } ; constraint-name = symbolic-name -constraint-pattern = node-pattern | simple-pattern ; constraint-expr = uniqueness-expr | expression ; uniquness-expr = "UNIQUE", property-expression, { ",", property-expression } drop-constraint = "DROP", "CONSTRAINT", constraint-name ; @@ -51,8 +50,6 @@ To that set of valid expressions, this CIP further specifies a special prefix op All constraints require the user to specify a nonempty _name_ at constraint creation time. This name is subsequently the handle with which a user may refer to the constraint, for example when dropping it. -// TODO: Should we impose restrictions on the domain of constraint names, or are all Unicode characters allowed? - ==== Removing constraints A constraint is removed by referring to its name. @@ -185,6 +182,22 @@ This composite constraint will make sure that all `:Color` nodes has a value for More complex constraint definitions are considered below: +.Multiple property existence using conjunction +[source, cypher] +---- +CREATE CONSTRAINT person_properties +FOR (p:Person) +REQUIRE exists(p.name) AND exists(p.email) +---- + +.Using larger pattern +[source, cypher] +---- +CREATE CONSTRAINT not_rating_own_posts +FOR (u1:User)-[:RATED]->(:Post)<-[:POSTED_BY]-(u2:User) +REQUIRE u.name <> u2.name +---- + .Property value limitations [source, cypher] ---- From 35bffe2a7d114ca244bd38fb0a2ad66e2b73bb44 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 3 Mar 2017 17:43:01 +0100 Subject: [PATCH 17/42] Introduce PRIMARY KEY constraint predicate - Rename `constrait-expr` to `constraint-predicate` - Limit scope of `UNIQUE` to single properties only - Update examples to reflect `PRIMARY KEY` --- .../CIP2016-12-14-Constraint-syntax.adoc | 79 +++++++++++++------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 070a9a5bde..89f284c5bc 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -32,23 +32,25 @@ The constraint syntax is defined as follows: .Grammar definition for constraint syntax. [source, ebnf] ---- -constraint command = create-constraint | drop-constraint ; -create-constraint = "CREATE", "CONSTRAINT", constraint-name, "FOR", pattern, "REQUIRE", constraint-expr, { "REQUIRE", constraint-expr } ; -constraint-name = symbolic-name -constraint-expr = uniqueness-expr | expression ; -uniquness-expr = "UNIQUE", property-expression, { ",", property-expression } -drop-constraint = "DROP", "CONSTRAINT", constraint-name ; +constraint command = create-constraint | drop-constraint ; +create-constraint = "CREATE", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; +constraint-name = symbolic-name +constraint-predicate = expression | unique | primary-key ; +unique = "UNIQUE", property-expression +primary-key = "PRIMARY KEY", property-expression, { ",", property-expression } +drop-constraint = "DROP", "CONSTRAINT", constraint-name ; ---- -The constraint expression (`constraint-expr` above) is any expression that evaluates to a boolean value. -This allows for very complex concrete constraint definitions within the specified syntax. +The `REQUIRE` clause works exactly like the `WHERE` clause in a standard Cypher query, with the addition of also supporting the special constraint operators `UNIQUE` and `PRIMARY KEY`. +This allows for very complex concrete constraint definitions (using custom predicates) within the specified syntax. -To that set of valid expressions, this CIP further specifies a special prefix operator `UNIQUE`, which is used to assert uniqueness of one or more property expressions (see <> for details). +For details on `UNIQUE` and `PRIMARY KEY`, see the dedicated sections below: <>, <>. ==== Constraint names -All constraints require the user to specify a nonempty _name_ at constraint creation time. +All constraints provide the user the option to specify a nonempty _name_ at constraint creation time. This name is subsequently the handle with which a user may refer to the constraint, for example when dropping it. +In the case where a name is not provided, the system will generate a unique name. ==== Removing constraints @@ -87,26 +89,50 @@ Should a user wish to change its definition, it has to be dropped and recreated [[uniqueness]] ==== Uniqueness -The new operator `UNIQUE` is only valid as part of a constraint expression. +The new operator `UNIQUE` is only valid as part of a constraint predicate. +It takes as argument a single property expression, and asserts that this property is unique across the domain of the constraint. +Following on rule <> above, entities for which the property is not defined (is `null`) are not part of the constraint domain. + +.Example of a constraint definition using `UNIQUE`, over the domain of nodes labeled with `:Person`: +[source, cypher] +---- +CREATE CONSTRAINT only_one_person_per_name +FOR (p:Person) +REQUIRE UNIQUE p.name +---- + +[[primary-key]] +==== Primary key + +The new operator `PRIMARY KEY` is only valid as part of a constraint predicate. It takes as argument one or more property expressions, and asserts that the combination of the evaluated values of the expressions (forming a tuple) is unique across the constraint domain. +It further asserts that the property expressions all exist on the entities of the domain, and thus avoids applicability of rule <> above. The domain of a primary key constraint is thus exactly defined as all entities which fit the constraint pattern. -The domain of the uniqueness expression is limited to entities for which _all_ properties defined as arguments to the `UNIQUE` operator exist. -In other words, property expressions which evaluate to `null` are not considered for uniqueness (see <>) above. +.Example of a constraint definition using `PRIMARY KEY`, over the domain of nodes labeled with `:Person`: +[source, cypher] +---- +CREATE CONSTRAINT person_details +FOR (p:Person) +REQUIRE PRIMARY KEY p.name, p.email, p.address +---- -.Example of a constraint definition using `UNIQUE`, over the domain of nodes labeled with `:Person`: +A semantically equivalent constraint is achieved by composing the use of the `UNIQUE` operator with `exists()` predicates, as exemplified by: + +.Example of a constraint definition equivalent to the above `PRIMARY KEY` example: [source, cypher] ---- -CREATE CONSTRAINT unique_person_details +CREATE CONSTRAINT person_details FOR (p:Person) -REQUIRE UNIQUE p.name, p.email, p.address +REQUIRE UNIQUE p.name +REQUIRE UNIQUE p.email +REQUIRE UNIQUE p.address +REQUIRE exists(p.name) AND exists(p.email) AND exists(p.address) ---- ==== Compositionality It is possible to define multiple `REQUIRE` clauses within the scope of the same constraint. -The semantics between these is that of a conjunction between the constraint expressions of the clauses, such that the constraint is upheld if and only if for all `REQUIRE` clauses, the expression evaluates to `true`. - -This is useful not only for readability and logical separation of different aspects of the same constraint, but also for combining the use of the `UNIQUE` operator with other constraint expressions. +The semantics between these is that of a conjunction (under standard 2-valued boolean logic) between the constraint predicates of the clauses, such that the constraint is upheld if and only if for all `REQUIRE` clauses, the joint predicate evaluates to `true`. === Examples @@ -144,7 +170,8 @@ We could then use the following constraint, without modifying our data: ---- CREATE CONSTRAINT unique_color_nodes FOR (c:Color) -REQUIRE UNIQUE c.rgb, c.name +REQUIRE UNIQUE c.rgb +REQUIRE UNIQUE c.name ---- Now, consider the following query which retrieves the RGB value of a color with a given `name`: @@ -168,17 +195,16 @@ REQUIRE exists(c.rgb) Any updating statement that would create a `:Color` node without specifying an `rgb` property for it would now fail. -Alternatively, we could extend our previous constraint definition with this new requirement: +If we also want to mandate the existence of the `name` property, we could use a `PRIMARY KEY` operator to capture all these requirements in a single constraint: [source, cypher] ---- CREATE CONSTRAINT color_schema FOR (c:Color) -REQUIRE UNIQUE c.rgb, c.name -REQUIRE exists(c.rgb) +REQUIRE PRIMARY KEY c.rgb, c.name ---- -This composite constraint will make sure that all `:Color` nodes has a value for their `rgb` property, and that its value is unique for each `name`. +This constraint will make sure that all `:Color` nodes has a value for their `rgb` and `name` properties, and that the combination is unique across all the nodes. More complex constraint definitions are considered below: @@ -269,8 +295,9 @@ In SQL, the following constraints exist (inspired by http://www.w3schools.com/sq * `CHECK` - Ensures that the value in a column meets a specific condition * `DEFAULT` - Specifies a default value for a column. -The property existence constraints correspond to the `NOT NULL` SQL constraint. -The node property uniqueness constraint corresponds to the `PRIMARY KEY` SQL constraint. +The `NOT NULL` SQL constraint is expressible using an `exists()` constraint predicate. +The `UNIQUE` SQL constraint is exactly as Cypher's `UNIQUE` constraint predicate. +The `PRIMARY KEY` SQL constraint is exactly as Cypher's `PRIMARY KEY` constraint predicate. SQL constraints may be introduced at table creation time in a `CREATE TABLE` statement, or in an `ALTER TABLE` statement: From 29a278d9ad9b08a6eb0cbed3903e9b556e5b0f07 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Tue, 7 Mar 2017 15:30:30 +0100 Subject: [PATCH 18/42] Rename constraint operator to NODE KEY - Remove erroneous example for composing `NODE KEY` with `UNIQUE` and `exists()` - Rephrase example section to describe `NODE KEY` more accurately. --- .../CIP2016-12-14-Constraint-syntax.adoc | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 89f284c5bc..6c4a48f534 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -35,16 +35,16 @@ The constraint syntax is defined as follows: constraint command = create-constraint | drop-constraint ; create-constraint = "CREATE", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; constraint-name = symbolic-name -constraint-predicate = expression | unique | primary-key ; +constraint-predicate = expression | unique | node-key ; unique = "UNIQUE", property-expression -primary-key = "PRIMARY KEY", property-expression, { ",", property-expression } +node-key = "NODE KEY", property-expression, { ",", property-expression } drop-constraint = "DROP", "CONSTRAINT", constraint-name ; ---- -The `REQUIRE` clause works exactly like the `WHERE` clause in a standard Cypher query, with the addition of also supporting the special constraint operators `UNIQUE` and `PRIMARY KEY`. +The `REQUIRE` clause works exactly like the `WHERE` clause in a standard Cypher query, with the addition of also supporting the special constraint operators `UNIQUE` and `NODE KEY`. This allows for very complex concrete constraint definitions (using custom predicates) within the specified syntax. -For details on `UNIQUE` and `PRIMARY KEY`, see the dedicated sections below: <>, <>. +For details on `UNIQUE` and `NODE KEY`, see the dedicated sections below: <>, <>. ==== Constraint names @@ -101,32 +101,31 @@ FOR (p:Person) REQUIRE UNIQUE p.name ---- -[[primary-key]] -==== Primary key +[[node-key]] +==== Node key -The new operator `PRIMARY KEY` is only valid as part of a constraint predicate. +The new operator `NODE KEY` is only valid as part of a constraint predicate. It takes as argument one or more property expressions, and asserts that the combination of the evaluated values of the expressions (forming a tuple) is unique across the constraint domain. -It further asserts that the property expressions all exist on the entities of the domain, and thus avoids applicability of rule <> above. The domain of a primary key constraint is thus exactly defined as all entities which fit the constraint pattern. +It further asserts that the property expressions all exist on the entities of the domain, and thus avoids applicability of rule <> above. +The domain of a node key constraint is thus exactly defined as all entities which fit the constraint pattern. -.Example of a constraint definition using `PRIMARY KEY`, over the domain of nodes labeled with `:Person`: +.Example of a constraint definition using `NODE KEY`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- CREATE CONSTRAINT person_details FOR (p:Person) -REQUIRE PRIMARY KEY p.name, p.email, p.address +REQUIRE NODE KEY p.name, p.email, p.address ---- -A semantically equivalent constraint is achieved by composing the use of the `UNIQUE` operator with `exists()` predicates, as exemplified by: +In the context of a single property, a semantically equivalent constraint is achieved by composing the use of the `UNIQUE` operator with `exists()` predicates, as exemplified by: -.Example of a constraint definition equivalent to the above `PRIMARY KEY` example: +.Example of a constraint definition equivalent to a `NODE KEY` on a single property `name`: [source, cypher] ---- CREATE CONSTRAINT person_details FOR (p:Person) REQUIRE UNIQUE p.name -REQUIRE UNIQUE p.email -REQUIRE UNIQUE p.address -REQUIRE exists(p.name) AND exists(p.email) AND exists(p.address) +REQUIRE exists(p.name) ---- ==== Compositionality @@ -163,17 +162,6 @@ FOR (c:Color) REQUIRE UNIQUE c.rgb ---- -Suppose that we would rather like to have one color node per `name` _and_ `rgb` value (to work around the rounding errors). -We could then use the following constraint, without modifying our data: - -[source, cypher] ----- -CREATE CONSTRAINT unique_color_nodes -FOR (c:Color) -REQUIRE UNIQUE c.rgb -REQUIRE UNIQUE c.name ----- - Now, consider the following query which retrieves the RGB value of a color with a given `name`: [source, cypher] @@ -195,16 +183,17 @@ REQUIRE exists(c.rgb) Any updating statement that would create a `:Color` node without specifying an `rgb` property for it would now fail. -If we also want to mandate the existence of the `name` property, we could use a `PRIMARY KEY` operator to capture all these requirements in a single constraint: +If we instead want to make the _combination_ of the properties `name` and `rgb` unique, while simultaneously mandating their existence, we could use a `NODE KEY` operator to capture all these requirements in a single constraint: [source, cypher] ---- CREATE CONSTRAINT color_schema FOR (c:Color) -REQUIRE PRIMARY KEY c.rgb, c.name +REQUIRE NODE KEY c.rgb, c.name ---- This constraint will make sure that all `:Color` nodes has a value for their `rgb` and `name` properties, and that the combination is unique across all the nodes. +This would allow several `:Color` nodes named `'grey'`, as long as their `rgb` values are distinct. More complex constraint definitions are considered below: @@ -297,7 +286,7 @@ In SQL, the following constraints exist (inspired by http://www.w3schools.com/sq The `NOT NULL` SQL constraint is expressible using an `exists()` constraint predicate. The `UNIQUE` SQL constraint is exactly as Cypher's `UNIQUE` constraint predicate. -The `PRIMARY KEY` SQL constraint is exactly as Cypher's `PRIMARY KEY` constraint predicate. +The `PRIMARY KEY` SQL constraint is exactly as Cypher's `NODE KEY` constraint predicate. SQL constraints may be introduced at table creation time in a `CREATE TABLE` statement, or in an `ALTER TABLE` statement: From 2b76cf1077d64535699ae61325e1f0023d212989 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Tue, 7 Mar 2017 15:47:44 +0100 Subject: [PATCH 19/42] Use ADD for constraint creation - Add missing case for when an error should be raised --- .../CIP2016-12-14-Constraint-syntax.adoc | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 6c4a48f534..9a4943ebad 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -32,8 +32,8 @@ The constraint syntax is defined as follows: .Grammar definition for constraint syntax. [source, ebnf] ---- -constraint command = create-constraint | drop-constraint ; -create-constraint = "CREATE", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; +constraint command = add-constraint | drop-constraint ; +add-constraint = "ADD", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; constraint-name = symbolic-name constraint-predicate = expression | unique | node-key ; unique = "UNIQUE", property-expression @@ -76,15 +76,16 @@ The semantics for constraints follow these general rules: The following list describes the situations in which an error will be raised: -* Attempting to create a constraint on a graph where the data does not comply with the constraint criterion. -* Attempting to create a constraint with a name that already exists. +* Attempting to add a constraint on a graph where the data does not comply with the constraint criterion. +* Attempting to add a constraint with a name that already exists. +* Attempting to add a constraint that the underlying engine does not support enforcing. * Attempting to drop a constraint referencing a non-existent name. * Attempting to modify the graph in such a way that it would violate a constraint. ==== Mutability -Once a constraint has been created, it may not be amended. -Should a user wish to change its definition, it has to be dropped and recreated with an updated structure. +Once a constraint has been added, it may not be amended. +Should a user wish to change its definition, it has to be dropped and added anew with an updated structure. [[uniqueness]] ==== Uniqueness @@ -96,7 +97,7 @@ Following on rule <> above, entities for which the property .Example of a constraint definition using `UNIQUE`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- -CREATE CONSTRAINT only_one_person_per_name +ADD CONSTRAINT only_one_person_per_name FOR (p:Person) REQUIRE UNIQUE p.name ---- @@ -112,7 +113,7 @@ The domain of a node key constraint is thus exactly defined as all entities whic .Example of a constraint definition using `NODE KEY`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- -CREATE CONSTRAINT person_details +ADD CONSTRAINT person_details FOR (p:Person) REQUIRE NODE KEY p.name, p.email, p.address ---- @@ -122,7 +123,7 @@ In the context of a single property, a semantically equivalent constraint is ach .Example of a constraint definition equivalent to a `NODE KEY` on a single property `name`: [source, cypher] ---- -CREATE CONSTRAINT person_details +ADD CONSTRAINT person_details FOR (p:Person) REQUIRE UNIQUE p.name REQUIRE exists(p.name) @@ -140,7 +141,7 @@ In this section we provide several examples of constraints that are possible to [NOTE] The specification in this CIP is limited to the general syntax of constraints, and the following are simply examples of possible uses of the language defined by that syntax. None of the examples provided are to be viewed as mandatory for any Cypher implementation. -Consider the graph created by the statement below. +Consider the graph added by the statement below. The graph contains nodes labeled with `:Color`. Each color is represented as an integer-type RGB value in a property `rgb`. Users may look up nodes labeled with `:Color` to extract their RGB values for application processing. @@ -153,11 +154,11 @@ CREATE (:Color {name: 'black', rgb: 0x000000}) CREATE (:Color {name: 'very, very dark grey', rgb: 0x000000}) // rounding error! ---- -Owing to the duplication of the `rgb` property, the following attempt at creating a constraint will fail: +Owing to the duplication of the `rgb` property, the following attempt at adding a constraint will fail: [source, cypher] ---- -CREATE CONSTRAINT only_one_color_per_rgb +ADD CONSTRAINT only_one_color_per_rgb FOR (c:Color) REQUIRE UNIQUE c.rgb ---- @@ -176,7 +177,7 @@ It may, however, be eliminated by the introduction of a constraint asserting the [source, cypher] ---- -CREATE CONSTRAINT colors_must_have_rgb +ADD CONSTRAINT colors_must_have_rgb FOR (c:Color) REQUIRE exists(c.rgb) ---- @@ -187,7 +188,7 @@ If we instead want to make the _combination_ of the properties `name` and `rgb` [source, cypher] ---- -CREATE CONSTRAINT color_schema +ADD CONSTRAINT color_schema FOR (c:Color) REQUIRE NODE KEY c.rgb, c.name ---- @@ -200,7 +201,7 @@ More complex constraint definitions are considered below: .Multiple property existence using conjunction [source, cypher] ---- -CREATE CONSTRAINT person_properties +ADD CONSTRAINT person_properties FOR (p:Person) REQUIRE exists(p.name) AND exists(p.email) ---- @@ -208,7 +209,7 @@ REQUIRE exists(p.name) AND exists(p.email) .Using larger pattern [source, cypher] ---- -CREATE CONSTRAINT not_rating_own_posts +ADD CONSTRAINT not_rating_own_posts FOR (u1:User)-[:RATED]->(:Post)<-[:POSTED_BY]-(u2:User) REQUIRE u.name <> u2.name ---- @@ -216,7 +217,7 @@ REQUIRE u.name <> u2.name .Property value limitations [source, cypher] ---- -CREATE CONSTRAINT road_width +ADD CONSTRAINT road_width FOR ()-[r:ROAD]-() REQUIRE 5 < r.width < 50 ---- @@ -224,7 +225,7 @@ REQUIRE 5 < r.width < 50 .Cardinality [source, cypher] ---- -CREATE CONSTRAINT spread_the_love +ADD CONSTRAINT spread_the_love FOR (p:Person) REQUIRE size((p)-[:LOVES]->()) > 3 ---- @@ -232,7 +233,7 @@ REQUIRE size((p)-[:LOVES]->()) > 3 .Endpoint requirements [source, cypher] ---- -CREATE CONSTRAINT can_only_own_things +ADD CONSTRAINT can_only_own_things FOR ()-[:OWNS]->(t) REQUIRE (t:Vehicle) OR (t:Building) OR (t:Object) ---- @@ -240,7 +241,7 @@ REQUIRE (t:Vehicle) OR (t:Building) OR (t:Object) .Label coexistence [source, cypher] ---- -CREATE CONSTRAINT programmers_are_people_too +ADD CONSTRAINT programmers_are_people_too FOR (p:Programmer) REQUIRE p:Person ---- @@ -250,7 +251,7 @@ Assuming a function `acyclic()` that takes a path as argument and returns `true` .Constraint example from CIR-2017-172 [source, cypher] ---- -CREATE CONSTRAINT enforce_dag_acyclic_for_R_links +ADD CONSTRAINT enforce_dag_acyclic_for_R_links FOR p = ()-[:R*]-() REQUIRE acyclic(p) ---- From 68499768ca021a968584f2e270e9d5863914ed0b Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Tue, 7 Mar 2017 16:05:37 +0100 Subject: [PATCH 20/42] Add specification for the return record --- .../CIP2016-12-14-Constraint-syntax.adoc | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 9a4943ebad..c7d033c1bb 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -25,6 +25,8 @@ Constraints allow us to mould the heterogeneous nature of the property graph int This CIP specifies the general syntax for constraint definition (and constraint removal), and provides several examples of possible use cases for constraints. However, the specification does not otherwise specify or limit the space of expressible constraints that the syntax and semantics allow. +This specification also covers the return structure of constraint commands, see <>. + === Syntax The constraint syntax is defined as follows: @@ -134,6 +136,45 @@ REQUIRE exists(p.name) It is possible to define multiple `REQUIRE` clauses within the scope of the same constraint. The semantics between these is that of a conjunction (under standard 2-valued boolean logic) between the constraint predicates of the clauses, such that the constraint is upheld if and only if for all `REQUIRE` clauses, the joint predicate evaluates to `true`. +[[return-record]] +==== Return record + +Since constraints always are named, but user-defined names are optional, the system must sometimes generate a constraint name. +In order for a user to be able to drop such a constraint, the system-generated name is therefore returned in a standard Cypher result record. +The result record has a fixed structure, with three string fields: `name`, `definition`, and `details`. + +A constraint command will always return exactly one record, if successful. +Note that also `DROP CONSTRAINT` will return a record. + +===== Name + +This field contains the name of the constraint, either user- or system-defined. + +===== Definition + +This field contains the constraint definition, which is the contents of the constraint creation command following (and including) the `FOR` clause. + +===== Details + +The contents of this field are left unspecified, to be used for implementation-specific messages and/or details. + +Consider the following constraint: +[source, Cypher] +---- +ADD CONSTRAINT myConstraint +FOR (n:Node) +REQUIRE NODE KEY n.prop1, n.prop2 +---- + +A correct result record for it could be: + +---- +name | definition | details +----------------------------------------------------------------------- +myConstraint | FOR (n:NODE) | n/a + | REQUIRE NODE KEY n.prop1, n.prop2 | +---- + === Examples In this section we provide several examples of constraints that are possible to express in the specified syntax. From aabf6857eac4d02565c1922d3edab65ecbde6ccf Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Thu, 4 May 2017 14:11:17 +0200 Subject: [PATCH 21/42] Add tests verifying NODE KEY works in grammar --- grammar/basic-grammar.xml | 2 ++ grammar/commands.xml | 15 ++++++++------- tools/grammar/src/test/resources/cypher.txt | 18 ++++++++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/grammar/basic-grammar.xml b/grammar/basic-grammar.xml index 3ab77ff665..433e9144af 100644 --- a/grammar/basic-grammar.xml +++ b/grammar/basic-grammar.xml @@ -728,6 +728,8 @@ ANY NONE SINGLE + NODE + KEY diff --git a/grammar/commands.xml b/grammar/commands.xml index 0c811ec163..52739ea5d7 100644 --- a/grammar/commands.xml +++ b/grammar/commands.xml @@ -69,7 +69,7 @@ - CREATE &SP; CONSTRAINT &SP; &SP; + ADD &SP; CONSTRAINT &SP; &SP; FOR &SP; &SP; REQUIRE &SP; @@ -91,17 +91,18 @@ - - + + + - - UNIQUE &SP; &WS; , &WS; + + UNIQUE &SP; - - exists ( ) + + NODE &SP; KEY &SP; &WS; , &WS; diff --git a/tools/grammar/src/test/resources/cypher.txt b/tools/grammar/src/test/resources/cypher.txt index 8c3b14c25d..3694daeee3 100644 --- a/tools/grammar/src/test/resources/cypher.txt +++ b/tools/grammar/src/test/resources/cypher.txt @@ -312,16 +312,22 @@ CALL db.labels() YIELD * WHERE label CONTAINS 'User' AND foo + bar = foo RETURN count(label) AS numLabels§ CALL db.labels() YIELD x WHERE label CONTAINS 'User' AND foo + bar = foo RETURN count(label) AS numLabels§ -CREATE CONSTRAINT foo +ADD CONSTRAINT foo FOR (p:Person) REQUIRE UNIQUE p.name§ -CREATE CONSTRAINT bar -FOR (p:Person) -REQUIRE UNIQUE p.name, p.email§ -CREATE CONSTRAINT baz +ADD CONSTRAINT baz FOR (p:Person) REQUIRE exists(p.name)§ -CREATE CONSTRAINT cru +ADD CONSTRAINT cru FOR ()-[r:REL]-() REQUIRE exists(r.property)§ DROP CONSTRAINT foo_bar_baz§ +ADD CONSTRAINT nodeKey +FOR (n:Node) +REQUIRE NODE KEY n.prop§ +ADD CONSTRAINT nodeKey +FOR (n:Node) +REQUIRE NODE KEY n.p1, n.p2, n.p3§ +ADD CONSTRAINT nodeKey +FOR (n:Node) +REQUIRE NODE KEY n.p1 ,n.p2, n.p3§ From 16fc677f6ea480a3d63a71ae8fc456fe392fcc64 Mon Sep 17 00:00:00 2001 From: Petra Selmer Date: Wed, 17 Jan 2018 16:23:36 +0000 Subject: [PATCH 22/42] Reformatted title --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index c7d033c1bb..d377c6c684 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -1,4 +1,4 @@ -= CIP2016-12-16 - Constraints syntax += CIP2016-12-16 Constraints syntax :numbered: :toc: :toc-placement: macro From 5f9acf5e50268b5d0d51456f4e88098fbb9ce113 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 19 Jul 2019 17:22:14 +0200 Subject: [PATCH 23/42] Use CREATE instead of ADD --- .../CIP2016-12-14-Constraint-syntax.adoc | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index d377c6c684..f8581aab37 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -34,8 +34,8 @@ The constraint syntax is defined as follows: .Grammar definition for constraint syntax. [source, ebnf] ---- -constraint command = add-constraint | drop-constraint ; -add-constraint = "ADD", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; +constraint command = create-constraint | drop-constraint ; +add-constraint = "CREATE", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; constraint-name = symbolic-name constraint-predicate = expression | unique | node-key ; unique = "UNIQUE", property-expression @@ -99,7 +99,7 @@ Following on rule <> above, entities for which the property .Example of a constraint definition using `UNIQUE`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- -ADD CONSTRAINT only_one_person_per_name +CREATE CONSTRAINT only_one_person_per_name FOR (p:Person) REQUIRE UNIQUE p.name ---- @@ -115,7 +115,7 @@ The domain of a node key constraint is thus exactly defined as all entities whic .Example of a constraint definition using `NODE KEY`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- -ADD CONSTRAINT person_details +CREATE CONSTRAINT person_details FOR (p:Person) REQUIRE NODE KEY p.name, p.email, p.address ---- @@ -125,7 +125,7 @@ In the context of a single property, a semantically equivalent constraint is ach .Example of a constraint definition equivalent to a `NODE KEY` on a single property `name`: [source, cypher] ---- -ADD CONSTRAINT person_details +CREATE CONSTRAINT person_details FOR (p:Person) REQUIRE UNIQUE p.name REQUIRE exists(p.name) @@ -161,7 +161,7 @@ The contents of this field are left unspecified, to be used for implementation-s Consider the following constraint: [source, Cypher] ---- -ADD CONSTRAINT myConstraint +CREATE CONSTRAINT myConstraint FOR (n:Node) REQUIRE NODE KEY n.prop1, n.prop2 ---- @@ -199,7 +199,7 @@ Owing to the duplication of the `rgb` property, the following attempt at adding [source, cypher] ---- -ADD CONSTRAINT only_one_color_per_rgb +CREATE CONSTRAINT only_one_color_per_rgb FOR (c:Color) REQUIRE UNIQUE c.rgb ---- @@ -218,7 +218,7 @@ It may, however, be eliminated by the introduction of a constraint asserting the [source, cypher] ---- -ADD CONSTRAINT colors_must_have_rgb +CREATE CONSTRAINT colors_must_have_rgb FOR (c:Color) REQUIRE exists(c.rgb) ---- @@ -229,7 +229,7 @@ If we instead want to make the _combination_ of the properties `name` and `rgb` [source, cypher] ---- -ADD CONSTRAINT color_schema +CREATE CONSTRAINT color_schema FOR (c:Color) REQUIRE NODE KEY c.rgb, c.name ---- @@ -242,7 +242,7 @@ More complex constraint definitions are considered below: .Multiple property existence using conjunction [source, cypher] ---- -ADD CONSTRAINT person_properties +CREATE CONSTRAINT person_properties FOR (p:Person) REQUIRE exists(p.name) AND exists(p.email) ---- @@ -250,7 +250,7 @@ REQUIRE exists(p.name) AND exists(p.email) .Using larger pattern [source, cypher] ---- -ADD CONSTRAINT not_rating_own_posts +CREATE CONSTRAINT not_rating_own_posts FOR (u1:User)-[:RATED]->(:Post)<-[:POSTED_BY]-(u2:User) REQUIRE u.name <> u2.name ---- @@ -258,7 +258,7 @@ REQUIRE u.name <> u2.name .Property value limitations [source, cypher] ---- -ADD CONSTRAINT road_width +CREATE CONSTRAINT road_width FOR ()-[r:ROAD]-() REQUIRE 5 < r.width < 50 ---- @@ -266,7 +266,7 @@ REQUIRE 5 < r.width < 50 .Cardinality [source, cypher] ---- -ADD CONSTRAINT spread_the_love +CREATE CONSTRAINT spread_the_love FOR (p:Person) REQUIRE size((p)-[:LOVES]->()) > 3 ---- @@ -274,7 +274,7 @@ REQUIRE size((p)-[:LOVES]->()) > 3 .Endpoint requirements [source, cypher] ---- -ADD CONSTRAINT can_only_own_things +CREATE CONSTRAINT can_only_own_things FOR ()-[:OWNS]->(t) REQUIRE (t:Vehicle) OR (t:Building) OR (t:Object) ---- @@ -282,7 +282,7 @@ REQUIRE (t:Vehicle) OR (t:Building) OR (t:Object) .Label coexistence [source, cypher] ---- -ADD CONSTRAINT programmers_are_people_too +CREATE CONSTRAINT programmers_are_people_too FOR (p:Programmer) REQUIRE p:Person ---- @@ -292,7 +292,7 @@ Assuming a function `acyclic()` that takes a path as argument and returns `true` .Constraint example from CIR-2017-172 [source, cypher] ---- -ADD CONSTRAINT enforce_dag_acyclic_for_R_links +CREATE CONSTRAINT enforce_dag_acyclic_for_R_links FOR p = ()-[:R*]-() REQUIRE acyclic(p) ---- @@ -308,6 +308,8 @@ Alternative syntaxes have been discussed: * `GIVEN`, `CONSTRAIN`, `ASSERT` instead of `FOR` * `ASSERT`, `ENFORCE`, `IMPLIES` instead of `REQUIRE` +* `ADD` instead of `CREATE` +** It is desirable for verb pairs for modifying operations to be consistent in the language, and recent discussions are (so far informally) suggesting `INSERT`/`DELETE` to be used for data modification, thus making `CREATE` and `DROP` available for schema modification such as constraints. The use of an existing expression to express uniqueness -- instead of using the operator `UNIQUE` -- becomes unwieldy for multiple properties, as exemplified by the following: ---- From 97ac3c986765b6b223751432e8013330f64f0dcd Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 19 Jul 2019 17:29:46 +0200 Subject: [PATCH 24/42] Make textual clarifications --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index f8581aab37..c8954dcf7a 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -70,7 +70,7 @@ The semantics for constraints follow these general rules: 1. The constraint pattern define the constraint _domain_, where all entities that would be returned by a `MATCH` clause with the same pattern constitute the domain, with one notable exception (see <>). -2. The constraint expressions defined in the `REQUIRE` clauses of the constraint definition must all evaluate to `true`. +2. The constraint expressions defined in the `REQUIRE` clauses of the constraint definition must all evaluate to `true`, at all times. 3. [[domain-exception]]Entities for which a constraint expression evaluate to `null` under Cypher's ternary logic are _excluded_ from the constraint domain, even if they fit within the constraint pattern. @@ -78,7 +78,7 @@ The semantics for constraints follow these general rules: The following list describes the situations in which an error will be raised: -* Attempting to add a constraint on a graph where the data does not comply with the constraint criterion. +* Attempting to add a constraint on a graph where the data does not comply with a constraint predicate. * Attempting to add a constraint with a name that already exists. * Attempting to add a constraint that the underlying engine does not support enforcing. * Attempting to drop a constraint referencing a non-existent name. @@ -87,7 +87,7 @@ The following list describes the situations in which an error will be raised: ==== Mutability Once a constraint has been added, it may not be amended. -Should a user wish to change its definition, it has to be dropped and added anew with an updated structure. +Should a user wish to change a constraint definition, the constraint has to be dropped and added anew with an updated structure. [[uniqueness]] ==== Uniqueness @@ -158,7 +158,7 @@ This field contains the constraint definition, which is the contents of the cons The contents of this field are left unspecified, to be used for implementation-specific messages and/or details. -Consider the following constraint: +.Example: consider the following constraint: [source, Cypher] ---- CREATE CONSTRAINT myConstraint @@ -300,7 +300,7 @@ REQUIRE acyclic(p) === Interaction with existing features The main interaction between the constraints and the rest of the language occurs during updating statements. -Existing constraints will cause any updating statements to fail, thereby fulfilling the main purpose of this feature. +Existing constraints will cause some updating statements to fail, thereby fulfilling the main purpose of this feature. === Alternatives @@ -368,7 +368,7 @@ ADD CONSTRAINT uc_PersonID UNIQUE (P_Id,LastName) Constraints make Cypher's notion of schema more well-defined, allowing users to maintain graphs in a more regular, easier-to-manage form. Additionally, this specification is deliberately defining a constraint _language_ within which a great deal of possible concrete constraints are made possible. -This allows different implementers of Cypher to independently choose how to limit the scope of supported constraint expressions that fit their model and targeted use cases. +This allows different implementers of Cypher to independently choose how to limit the scope of supported constraint expressions that fit their model and targeted use cases, while retaining a common and consistent semantic and syntactic model. == Caveats to this proposal From e6ed668fd18fe2765141b2f0c77558bda1e9fcdd Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Mon, 22 Jul 2019 17:37:40 +0200 Subject: [PATCH 25/42] Update grammar to use CREATE Add test for DROP --- grammar/commands.xml | 2 +- tools/grammar/src/test/resources/cypher.txt | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/grammar/commands.xml b/grammar/commands.xml index 52739ea5d7..f64969ff2b 100644 --- a/grammar/commands.xml +++ b/grammar/commands.xml @@ -69,7 +69,7 @@ - ADD &SP; CONSTRAINT &SP; &SP; + CREATE &SP; CONSTRAINT &SP; &SP; FOR &SP; &SP; REQUIRE &SP; diff --git a/tools/grammar/src/test/resources/cypher.txt b/tools/grammar/src/test/resources/cypher.txt index 3694daeee3..18d25f2560 100644 --- a/tools/grammar/src/test/resources/cypher.txt +++ b/tools/grammar/src/test/resources/cypher.txt @@ -312,22 +312,23 @@ CALL db.labels() YIELD * WHERE label CONTAINS 'User' AND foo + bar = foo RETURN count(label) AS numLabels§ CALL db.labels() YIELD x WHERE label CONTAINS 'User' AND foo + bar = foo RETURN count(label) AS numLabels§ -ADD CONSTRAINT foo +CREATE CONSTRAINT foo FOR (p:Person) REQUIRE UNIQUE p.name§ -ADD CONSTRAINT baz +CREATE CONSTRAINT baz FOR (p:Person) REQUIRE exists(p.name)§ -ADD CONSTRAINT cru +CREATE CONSTRAINT cru FOR ()-[r:REL]-() REQUIRE exists(r.property)§ DROP CONSTRAINT foo_bar_baz§ -ADD CONSTRAINT nodeKey +CREATE CONSTRAINT nodeKey FOR (n:Node) REQUIRE NODE KEY n.prop§ -ADD CONSTRAINT nodeKey +CREATE CONSTRAINT nodeKey FOR (n:Node) REQUIRE NODE KEY n.p1, n.p2, n.p3§ -ADD CONSTRAINT nodeKey +CREATE CONSTRAINT nodeKey FOR (n:Node) REQUIRE NODE KEY n.p1 ,n.p2, n.p3§ +DROP CONSTRAINT foo§ From 4fe30eed8b719e87634ed48432ad26f41da3a09b Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 16 Aug 2019 11:02:11 +0200 Subject: [PATCH 26/42] Add missing semicolons in BNF syntax --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index c8954dcf7a..0033851631 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -36,10 +36,10 @@ The constraint syntax is defined as follows: ---- constraint command = create-constraint | drop-constraint ; add-constraint = "CREATE", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; -constraint-name = symbolic-name +constraint-name = symbolic-name ; constraint-predicate = expression | unique | node-key ; -unique = "UNIQUE", property-expression -node-key = "NODE KEY", property-expression, { ",", property-expression } +unique = "UNIQUE", property-expression ; +node-key = "NODE KEY", property-expression, { ",", property-expression } ; drop-constraint = "DROP", "CONSTRAINT", constraint-name ; ---- From bc1c1bd9ead2223710a8ea8f016738658693b4d6 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 16 Aug 2019 11:16:00 +0200 Subject: [PATCH 27/42] Specify limitations on constraint expressions --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 0033851631..ada4206110 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -74,6 +74,8 @@ The semantics for constraints follow these general rules: 3. [[domain-exception]]Entities for which a constraint expression evaluate to `null` under Cypher's ternary logic are _excluded_ from the constraint domain, even if they fit within the constraint pattern. +4. The constraint expression must be deterministic and free of side effects (such as graph mutations). + ==== Errors The following list describes the situations in which an error will be raised: From ba0491d045e52e50c7076dac7218c97b87cb9799 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 16 Aug 2019 11:22:43 +0200 Subject: [PATCH 28/42] Add ability to specify multiple constraints in one statement --- .../CIP2016-12-14-Constraint-syntax.adoc | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index ada4206110..6f5eaa870b 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -34,13 +34,15 @@ The constraint syntax is defined as follows: .Grammar definition for constraint syntax. [source, ebnf] ---- -constraint command = create-constraint | drop-constraint ; -add-constraint = "CREATE", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; -constraint-name = symbolic-name ; -constraint-predicate = expression | unique | node-key ; -unique = "UNIQUE", property-expression ; -node-key = "NODE KEY", property-expression, { ",", property-expression } ; -drop-constraint = "DROP", "CONSTRAINT", constraint-name ; +constraint command = create-constraint | drop-constraint ; +add-constraint = "CREATE", constraint-keyword, constraint-declaration, { ",", constraint-declaration } ; +constraint-keyword = "CONSTRAINT" | "CONSTRAINTS" ; +constraint-declaration = [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; +constraint-name = symbolic-name ; +constraint-predicate = expression | unique | node-key ; +unique = "UNIQUE", property-expression ; +node-key = "NODE KEY", property-expression, { ",", property-expression } ; +drop-constraint = "DROP", "CONSTRAINT", constraint-name ; ---- The `REQUIRE` clause works exactly like the `WHERE` clause in a standard Cypher query, with the addition of also supporting the special constraint operators `UNIQUE` and `NODE KEY`. @@ -64,6 +66,11 @@ A constraint is removed by referring to its name. DROP CONSTRAINT foo ---- +==== Plurality + +The syntax allows for multiple constraint creations to be requested simultaneously, by using the `CREATE CONSTRAINTS ...` syntax. +This is a syntactic shorthand for specifying each individual constraint separately and combining the return records (see <>) as a `UNION`. + === Semantics The semantics for constraints follow these general rules: From 54c5db751dcf82f200449c33cac4b212651e2434 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 16 Aug 2019 11:27:57 +0200 Subject: [PATCH 29/42] Add example for plural syntax --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 6f5eaa870b..20daf362ec 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -280,6 +280,18 @@ FOR (p:Person) REQUIRE size((p)-[:LOVES]->()) > 3 ---- +.Property value limitations and cardinality +[source, cypher] +---- +CREATE CONSTRAINTS +road_width +FOR ()-[r:ROAD]-() +REQUIRE 5 < r.width < 50, +spread_the_love +FOR (p:Person) +REQUIRE size((p)-[:LOVES]->()) > 3 +---- + .Endpoint requirements [source, cypher] ---- From 0cca4f63638f36b7590845ae57e49c3b79b79870 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 16 Aug 2019 14:09:14 +0200 Subject: [PATCH 30/42] Explain usage of term 'constraint expression' --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 20daf362ec..4c6660ebdc 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -50,6 +50,9 @@ This allows for very complex concrete constraint definitions (using custom predi For details on `UNIQUE` and `NODE KEY`, see the dedicated sections below: <>, <>. +The term 'constraint expression' is used in the following to describe the expressions that constitute the body of the constraint predicate. +In other words, the `expression` or `property-expression` grammar references in the above syntax. + ==== Constraint names All constraints provide the user the option to specify a nonempty _name_ at constraint creation time. From 6f9e71458fe21f051c91949d671c87c0130690b4 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Thu, 1 Jul 2021 12:15:05 +0200 Subject: [PATCH 31/42] Use two blank lines before new section For improved source readability --- .../CIP2016-12-14-Constraint-syntax.adoc | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 4c6660ebdc..db45db857d 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -20,6 +20,7 @@ toc::[] Cypher has a loose notion of a schema, in which nodes and relationships may take very heterogeneous forms, both in terms of properties and in graph patterns. Constraints allow us to mould the heterogeneous nature of the property graph into a more regular form. + == Proposal This CIP specifies the general syntax for constraint definition (and constraint removal), and provides several examples of possible use cases for constraints. @@ -27,6 +28,7 @@ However, the specification does not otherwise specify or limit the space of expr This specification also covers the return structure of constraint commands, see <>. + === Syntax The constraint syntax is defined as follows: @@ -53,12 +55,14 @@ For details on `UNIQUE` and `NODE KEY`, see the dedicated sections below: <>) as a `UNION`. + === Semantics The semantics for constraints follow these general rules: @@ -86,6 +92,7 @@ The semantics for constraints follow these general rules: 4. The constraint expression must be deterministic and free of side effects (such as graph mutations). + ==== Errors The following list describes the situations in which an error will be raised: @@ -96,11 +103,13 @@ The following list describes the situations in which an error will be raised: * Attempting to drop a constraint referencing a non-existent name. * Attempting to modify the graph in such a way that it would violate a constraint. + ==== Mutability Once a constraint has been added, it may not be amended. Should a user wish to change a constraint definition, the constraint has to be dropped and added anew with an updated structure. + [[uniqueness]] ==== Uniqueness @@ -116,6 +125,7 @@ FOR (p:Person) REQUIRE UNIQUE p.name ---- + [[node-key]] ==== Node key @@ -143,11 +153,13 @@ REQUIRE UNIQUE p.name REQUIRE exists(p.name) ---- + ==== Compositionality It is possible to define multiple `REQUIRE` clauses within the scope of the same constraint. The semantics between these is that of a conjunction (under standard 2-valued boolean logic) between the constraint predicates of the clauses, such that the constraint is upheld if and only if for all `REQUIRE` clauses, the joint predicate evaluates to `true`. + [[return-record]] ==== Return record @@ -158,14 +170,17 @@ The result record has a fixed structure, with three string fields: `name`, `defi A constraint command will always return exactly one record, if successful. Note that also `DROP CONSTRAINT` will return a record. + ===== Name This field contains the name of the constraint, either user- or system-defined. + ===== Definition This field contains the constraint definition, which is the contents of the constraint creation command following (and including) the `FOR` clause. + ===== Details The contents of this field are left unspecified, to be used for implementation-specific messages and/or details. @@ -187,6 +202,7 @@ myConstraint | FOR (n:NODE) | n/a | REQUIRE NODE KEY n.prop1, n.prop2 | ---- + === Examples In this section we provide several examples of constraints that are possible to express in the specified syntax. @@ -321,11 +337,13 @@ FOR p = ()-[:R*]-() REQUIRE acyclic(p) ---- + === Interaction with existing features The main interaction between the constraints and the rest of the language occurs during updating statements. Existing constraints will cause some updating statements to fail, thereby fulfilling the main purpose of this feature. + === Alternatives Alternative syntaxes have been discussed: @@ -341,6 +359,7 @@ FOR (p:Person), (q:Person) REQUIRE p.email <> q.email AND p <> q ---- + == What others do In SQL, the following constraints exist (inspired by http://www.w3schools.com/sql/sql_constraints.asp): @@ -387,6 +406,7 @@ ALTER TABLE Person ADD CONSTRAINT uc_PersonID UNIQUE (P_Id,LastName) ---- + == Benefits to this proposal Constraints make Cypher's notion of schema more well-defined, allowing users to maintain graphs in a more regular, easier-to-manage form. @@ -394,6 +414,7 @@ Constraints make Cypher's notion of schema more well-defined, allowing users to Additionally, this specification is deliberately defining a constraint _language_ within which a great deal of possible concrete constraints are made possible. This allows different implementers of Cypher to independently choose how to limit the scope of supported constraint expressions that fit their model and targeted use cases, while retaining a common and consistent semantic and syntactic model. + == Caveats to this proposal Some constraints may prove challenging to enforce in a system seeking to implement the contents of this CIP, as these generally require scanning through large parts of the graph to locate conflicting entities. From 3e1acdaa8845f5838c20641c60feb7b9c24a126f Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Thu, 1 Jul 2021 12:21:08 +0200 Subject: [PATCH 32/42] Remove notion about creating multiple constraints --- .../CIP2016-12-14-Constraint-syntax.adoc | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index db45db857d..b74db95a1b 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -36,15 +36,13 @@ The constraint syntax is defined as follows: .Grammar definition for constraint syntax. [source, ebnf] ---- -constraint command = create-constraint | drop-constraint ; -add-constraint = "CREATE", constraint-keyword, constraint-declaration, { ",", constraint-declaration } ; -constraint-keyword = "CONSTRAINT" | "CONSTRAINTS" ; -constraint-declaration = [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; -constraint-name = symbolic-name ; -constraint-predicate = expression | unique | node-key ; -unique = "UNIQUE", property-expression ; -node-key = "NODE KEY", property-expression, { ",", property-expression } ; -drop-constraint = "DROP", "CONSTRAINT", constraint-name ; +constraint command = create-constraint | drop-constraint ; +add-constraint = "CREATE", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; +constraint-name = symbolic-name ; +constraint-predicate = expression | unique | node-key ; +unique = "UNIQUE", property-expression ; +node-key = "NODE KEY", property-expression, { ",", property-expression } ; +drop-constraint = "DROP", "CONSTRAINT", constraint-name ; ---- The `REQUIRE` clause works exactly like the `WHERE` clause in a standard Cypher query, with the addition of also supporting the special constraint operators `UNIQUE` and `NODE KEY`. @@ -74,12 +72,6 @@ DROP CONSTRAINT foo ---- -==== Plurality - -The syntax allows for multiple constraint creations to be requested simultaneously, by using the `CREATE CONSTRAINTS ...` syntax. -This is a syntactic shorthand for specifying each individual constraint separately and combining the return records (see <>) as a `UNION`. - - === Semantics The semantics for constraints follow these general rules: @@ -299,18 +291,6 @@ FOR (p:Person) REQUIRE size((p)-[:LOVES]->()) > 3 ---- -.Property value limitations and cardinality -[source, cypher] ----- -CREATE CONSTRAINTS -road_width -FOR ()-[r:ROAD]-() -REQUIRE 5 < r.width < 50, -spread_the_love -FOR (p:Person) -REQUIRE size((p)-[:LOVES]->()) > 3 ----- - .Endpoint requirements [source, cypher] ---- From 1f1be129cfa89ad4a9a685c2eeb025aaebfacd54 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 10:38:42 +0200 Subject: [PATCH 33/42] Update grammar definition - use same EBNF style as @hvub did in #493 - use links to connect to grammar constructs in openCypher grammar spec - modify constraint operators to be suffix-modeled, ala IS NOT NULL - introduce 'grouped-expression' concept --- .../CIP2016-12-14-Constraint-syntax.adoc | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index b74db95a1b..be6a9ea3c5 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -36,22 +36,48 @@ The constraint syntax is defined as follows: .Grammar definition for constraint syntax. [source, ebnf] ---- -constraint command = create-constraint | drop-constraint ; -add-constraint = "CREATE", "CONSTRAINT", [ constraint-name ], "FOR", pattern, "REQUIRE", constraint-predicate, { "REQUIRE", constraint-predicate } ; -constraint-name = symbolic-name ; -constraint-predicate = expression | unique | node-key ; -unique = "UNIQUE", property-expression ; -node-key = "NODE KEY", property-expression, { ",", property-expression } ; -drop-constraint = "DROP", "CONSTRAINT", constraint-name ; + ::= + + | ; + + ::= + "DROP", "CONSTRAINT", ; + + ::= + "CREATE", "CONSTRAINT", [ ], + "FOR", , + "REQUIRE", , + { "REQUIRE", } ; + + ::= + + | + | ; + + ::= + , "IS", "UNIQUE" ; + + ::= + , "IS", "NODE", "KEY" ; + + ::= + + | "(", , { ",", }, ")" ; ---- -The `REQUIRE` clause works exactly like the `WHERE` clause in a standard Cypher query, with the addition of also supporting the special constraint operators `UNIQUE` and `NODE KEY`. -This allows for very complex concrete constraint definitions (using custom predicates) within the specified syntax. +References to existing grammar parts: + +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=SymbolicName[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=Pattern[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=Expression[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=PropertyExpression[] + +The `REQUIRE` clause works exactly like the `WHERE` clause in a standard Cypher query, with the addition of also supporting the special constraint operators `IS UNIQUE`, `IS NODE KEY`, and the new `` expression. +This allows for complex concrete constraint definitions (using custom predicates) within the specified syntax. -For details on `UNIQUE` and `NODE KEY`, see the dedicated sections below: <>, <>. +For details on `IS UNIQUE`, `IS NODE KEY`, and ``, see the dedicated sections below: <>, <>, <>. The term 'constraint expression' is used in the following to describe the expressions that constitute the body of the constraint predicate. -In other words, the `expression` or `property-expression` grammar references in the above syntax. ==== Constraint names @@ -145,6 +171,11 @@ REQUIRE UNIQUE p.name REQUIRE exists(p.name) ---- +[[grouped-expression]] +==== Grouped expression + +// TODO + ==== Compositionality From 73fa12d38a586e3fccb1d70c371032ca12a849f7 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 10:42:12 +0200 Subject: [PATCH 34/42] Prefer element over entity --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index be6a9ea3c5..ef9eeaeb77 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -102,11 +102,11 @@ DROP CONSTRAINT foo The semantics for constraints follow these general rules: -1. The constraint pattern define the constraint _domain_, where all entities that would be returned by a `MATCH` clause with the same pattern constitute the domain, with one notable exception (see <>). +1. The constraint pattern define the constraint _domain_, where all elements that would be returned by a `MATCH` clause with the same pattern constitute the domain, with one notable exception (see <>). 2. The constraint expressions defined in the `REQUIRE` clauses of the constraint definition must all evaluate to `true`, at all times. -3. [[domain-exception]]Entities for which a constraint expression evaluate to `null` under Cypher's ternary logic are _excluded_ from the constraint domain, even if they fit within the constraint pattern. +3. [[domain-exception]]Elements for which a constraint expression evaluate to `null` under Cypher's ternary logic are _excluded_ from the constraint domain, even if they fit within the constraint pattern. 4. The constraint expression must be deterministic and free of side effects (such as graph mutations). @@ -133,7 +133,7 @@ Should a user wish to change a constraint definition, the constraint has to be d The new operator `UNIQUE` is only valid as part of a constraint predicate. It takes as argument a single property expression, and asserts that this property is unique across the domain of the constraint. -Following on rule <> above, entities for which the property is not defined (is `null`) are not part of the constraint domain. +Following on rule <> above, elements for which the property is not defined (is `null`) are not part of the constraint domain. .Example of a constraint definition using `UNIQUE`, over the domain of nodes labeled with `:Person`: [source, cypher] @@ -149,8 +149,8 @@ REQUIRE UNIQUE p.name The new operator `NODE KEY` is only valid as part of a constraint predicate. It takes as argument one or more property expressions, and asserts that the combination of the evaluated values of the expressions (forming a tuple) is unique across the constraint domain. -It further asserts that the property expressions all exist on the entities of the domain, and thus avoids applicability of rule <> above. -The domain of a node key constraint is thus exactly defined as all entities which fit the constraint pattern. +It further asserts that the property expressions all exist on the elements of the domain, and thus avoids applicability of rule <> above. +The domain of a node key constraint is thus exactly defined as all elements which fit the constraint pattern. .Example of a constraint definition using `NODE KEY`, over the domain of nodes labeled with `:Person`: [source, cypher] @@ -428,4 +428,4 @@ This allows different implementers of Cypher to independently choose how to limi == Caveats to this proposal -Some constraints may prove challenging to enforce in a system seeking to implement the contents of this CIP, as these generally require scanning through large parts of the graph to locate conflicting entities. +Some constraints may prove challenging to enforce in a system seeking to implement the contents of this CIP, as these generally require scanning through large parts of the graph to locate conflicting elements. From 7acef693d0b23ee759f7e8157a6186b38e0d0e78 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 10:55:44 +0200 Subject: [PATCH 35/42] Update uniqueness to use IS UNIQUE operator Extend definition to reference grouped expression --- .../CIP2016-12-14-Constraint-syntax.adoc | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index ef9eeaeb77..09f0e5ccd0 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -131,16 +131,17 @@ Should a user wish to change a constraint definition, the constraint has to be d [[uniqueness]] ==== Uniqueness -The new operator `UNIQUE` is only valid as part of a constraint predicate. -It takes as argument a single property expression, and asserts that this property is unique across the domain of the constraint. -Following on rule <> above, elements for which the property is not defined (is `null`) are not part of the constraint domain. +The new operator `IS UNIQUE` is only valid as part of a constraint predicate. +It takes as argument a <>, and asserts that it is unique across the domain of the constraint. +Following on rule <> above, elements for which the grouped expression is `null` are not part of the constraint domain. +In particular, in the case where the grouped expression is a single property expression, this means that the uniqueness constraint does not hinder the existence of multiple elements having a `null` value for the specified property. -.Example of a constraint definition using `UNIQUE`, over the domain of nodes labeled with `:Person`: +.Example of a constraint definition using `IS UNIQUE`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- CREATE CONSTRAINT only_one_person_per_name FOR (p:Person) -REQUIRE UNIQUE p.name +REQUIRE p.name IS UNIQUE ---- @@ -160,14 +161,14 @@ FOR (p:Person) REQUIRE NODE KEY p.name, p.email, p.address ---- -In the context of a single property, a semantically equivalent constraint is achieved by composing the use of the `UNIQUE` operator with `exists()` predicates, as exemplified by: +In the context of a single property, a semantically equivalent constraint is achieved by composing the use of the `IS UNIQUE` operator with `exists()` predicates, as exemplified by: .Example of a constraint definition equivalent to a `NODE KEY` on a single property `name`: [source, cypher] ---- CREATE CONSTRAINT person_details FOR (p:Person) -REQUIRE UNIQUE p.name +REQUIRE p.name IS UNIQUE REQUIRE exists(p.name) ---- @@ -252,7 +253,7 @@ Owing to the duplication of the `rgb` property, the following attempt at adding ---- CREATE CONSTRAINT only_one_color_per_rgb FOR (c:Color) -REQUIRE UNIQUE c.rgb +REQUIRE c.rgb IS UNIQUE ---- Now, consider the following query which retrieves the RGB value of a color with a given `name`: @@ -364,7 +365,7 @@ Alternative syntaxes have been discussed: * `ADD` instead of `CREATE` ** It is desirable for verb pairs for modifying operations to be consistent in the language, and recent discussions are (so far informally) suggesting `INSERT`/`DELETE` to be used for data modification, thus making `CREATE` and `DROP` available for schema modification such as constraints. -The use of an existing expression to express uniqueness -- instead of using the operator `UNIQUE` -- becomes unwieldy for multiple properties, as exemplified by the following: +The use of an existing expression to express uniqueness -- instead of using the operator `IS UNIQUE` -- becomes unwieldy for multiple properties, as exemplified by the following: ---- FOR (p:Person), (q:Person) REQUIRE p.email <> q.email AND p <> q @@ -383,7 +384,7 @@ In SQL, the following constraints exist (inspired by http://www.w3schools.com/sq * `DEFAULT` - Specifies a default value for a column. The `NOT NULL` SQL constraint is expressible using an `exists()` constraint predicate. -The `UNIQUE` SQL constraint is exactly as Cypher's `UNIQUE` constraint predicate. +The `UNIQUE` SQL constraint is exactly as Cypher's `IS UNIQUE` constraint predicate. The `PRIMARY KEY` SQL constraint is exactly as Cypher's `NODE KEY` constraint predicate. SQL constraints may be introduced at table creation time in a `CREATE TABLE` statement, or in an `ALTER TABLE` statement: From aa4a549f00344105d9ce55bd381e077a7340ac3c Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 11:08:06 +0200 Subject: [PATCH 36/42] Update node key to use IS NODE KEY operator - Update definition to reference grouped expression - Reformulate equivalence example to use IS NOT NULL and IS UNIQUE over a grouped expression --- .../CIP2016-12-14-Constraint-syntax.adoc | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 09f0e5ccd0..ea377453cc 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -148,30 +148,38 @@ REQUIRE p.name IS UNIQUE [[node-key]] ==== Node key -The new operator `NODE KEY` is only valid as part of a constraint predicate. -It takes as argument one or more property expressions, and asserts that the combination of the evaluated values of the expressions (forming a tuple) is unique across the constraint domain. -It further asserts that the property expressions all exist on the elements of the domain, and thus avoids applicability of rule <> above. +The new operator `IS NODE KEY` is only valid as part of a constraint predicate. +It takes as argument a <>, and asserts that two conditions hold: + +. [[not-null]]Each property expression within the grouped expression cannot be `null`. +. The grouped expression is unique across the domain of the constraint. + +By way of <> the node key constraint avoids applicability of rule <> above. The domain of a node key constraint is thus exactly defined as all elements which fit the constraint pattern. -.Example of a constraint definition using `NODE KEY`, over the domain of nodes labeled with `:Person`: +.Example of a constraint definition using `IS NODE KEY`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- CREATE CONSTRAINT person_details FOR (p:Person) -REQUIRE NODE KEY p.name, p.email, p.address +REQUIRE (p.name, p.email, p.address) IS NODE KEY ---- -In the context of a single property, a semantically equivalent constraint is achieved by composing the use of the `IS UNIQUE` operator with `exists()` predicates, as exemplified by: +The node key constraint can be equivalently expressed using a combination of the `IS UNIQUE` and `IS NOT NULL` operators. +The below example illustrates this. -.Example of a constraint definition equivalent to a `NODE KEY` on a single property `name`: +.Example of a constraint definition using `IS UNIQUE` and `IS NOT NULL`, over the domain of nodes labeled with `:Person`: [source, cypher] ---- CREATE CONSTRAINT person_details FOR (p:Person) -REQUIRE p.name IS UNIQUE -REQUIRE exists(p.name) +REQUIRE (p.name, p.email, p.address) IS UNIQUE +REQUIRE p.name IS NOT NULL +REQUIRE p.email IS NOT NULL +REQUIRE p.address IS NOT NULL ---- + [[grouped-expression]] ==== Grouped expression @@ -214,7 +222,7 @@ The contents of this field are left unspecified, to be used for implementation-s ---- CREATE CONSTRAINT myConstraint FOR (n:Node) -REQUIRE NODE KEY n.prop1, n.prop2 +REQUIRE (n.prop1, n.prop2) IS NODE KEY ---- A correct result record for it could be: @@ -223,7 +231,7 @@ A correct result record for it could be: name | definition | details ----------------------------------------------------------------------- myConstraint | FOR (n:NODE) | n/a - | REQUIRE NODE KEY n.prop1, n.prop2 | + | REQUIRE (n.prop1, n.prop2) IS NODE KEY | ---- @@ -283,7 +291,7 @@ If we instead want to make the _combination_ of the properties `name` and `rgb` ---- CREATE CONSTRAINT color_schema FOR (c:Color) -REQUIRE NODE KEY c.rgb, c.name +REQUIRE (c.rgb, c.name) IS NODE KEY ---- This constraint will make sure that all `:Color` nodes has a value for their `rgb` and `name` properties, and that the combination is unique across all the nodes. @@ -385,7 +393,7 @@ In SQL, the following constraints exist (inspired by http://www.w3schools.com/sq The `NOT NULL` SQL constraint is expressible using an `exists()` constraint predicate. The `UNIQUE` SQL constraint is exactly as Cypher's `IS UNIQUE` constraint predicate. -The `PRIMARY KEY` SQL constraint is exactly as Cypher's `NODE KEY` constraint predicate. +The `PRIMARY KEY` SQL constraint is exactly as Cypher's `IS NODE KEY` constraint predicate. SQL constraints may be introduced at table creation time in a `CREATE TABLE` statement, or in an `ALTER TABLE` statement: From 056f6e50869746df6bd1a5046f73e9567209bd25 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 11:22:18 +0200 Subject: [PATCH 37/42] Define a grouped expression and its type Move ahead of referencing sections to ease readability of CIP --- .../CIP2016-12-14-Constraint-syntax.adoc | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index ea377453cc..ac62eac367 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -128,6 +128,22 @@ Once a constraint has been added, it may not be amended. Should a user wish to change a constraint definition, the constraint has to be dropped and added anew with an updated structure. +[[grouped-expression]] +==== Grouped expression + +This CIP introduces the concept of a _grouped expression_, consisting of one or more property expressions. +A grouped expression expresses a new value type in Cypher: a tuple type. +This type exists only for the purposes of the `IS UNIQUE` and `IS NODE KEY` operators and this CIP does not further extend its applicability. + +The tuple type is composed of the types of the property expressions. +These rules apply: + +. When one of the contained property expressions is `null`, the tuple type is also `null`. +. When compared for equality to another tuple type, the comparison is equivalent to comparing the property expressions of the tuples respectively, in a conjunction. + +A wider definition is not necessary for this type to satisfy the requirements of the `IS UNIQUE` and `IS NODE KEY` operators. + + [[uniqueness]] ==== Uniqueness @@ -180,12 +196,6 @@ REQUIRE p.address IS NOT NULL ---- -[[grouped-expression]] -==== Grouped expression - -// TODO - - ==== Compositionality It is possible to define multiple `REQUIRE` clauses within the scope of the same constraint. From 09eb4b13ab4d8ba6adf01140732f917fba2eec75 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 13:57:33 +0200 Subject: [PATCH 38/42] Prefer IS NOT NULL over exists() --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index ac62eac367..7ec5b30824 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -279,7 +279,7 @@ Now, consider the following query which retrieves the RGB value of a color with [source, cypher] ---- MATCH (c:Color {name: $name}) -WHERE exists(c.rgb) +WHERE c.rgb IS NOT NULL RETURN c.rgb ---- @@ -290,7 +290,7 @@ It may, however, be eliminated by the introduction of a constraint asserting the ---- CREATE CONSTRAINT colors_must_have_rgb FOR (c:Color) -REQUIRE exists(c.rgb) +REQUIRE c.rgb IS NOT NULL ---- Any updating statement that would create a `:Color` node without specifying an `rgb` property for it would now fail. @@ -314,7 +314,7 @@ More complex constraint definitions are considered below: ---- CREATE CONSTRAINT person_properties FOR (p:Person) -REQUIRE exists(p.name) AND exists(p.email) +REQUIRE p.name IS NOT NULL AND p.email IS NOT NULL ---- .Using larger pattern From 2843a65bdb4aeced689416762b6e40dffb37a5b7 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 13:57:57 +0200 Subject: [PATCH 39/42] Stylistic improvements of examples --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 7ec5b30824..0ea037bf91 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -227,7 +227,10 @@ This field contains the constraint definition, which is the contents of the cons The contents of this field are left unspecified, to be used for implementation-specific messages and/or details. -.Example: consider the following constraint: + +===== Return record example + +.Consider the following constraint: [source, Cypher] ---- CREATE CONSTRAINT myConstraint @@ -386,7 +389,7 @@ Alternative syntaxes have been discussed: The use of an existing expression to express uniqueness -- instead of using the operator `IS UNIQUE` -- becomes unwieldy for multiple properties, as exemplified by the following: ---- FOR (p:Person), (q:Person) -REQUIRE p.email <> q.email AND p <> q +REQUIRE p.email <> q.email AND p.name <> q.name AND p <> q ---- @@ -414,7 +417,8 @@ CREATE TABLE Person ( P_Id int NOT NULL UNIQUE, LastName varchar(255) NOT NULL, - FirstName varchar(255)) + FirstName varchar(255) +) ---- .Creating a `Person` table in MySQL: From 03bca9289d26dde9bdaa1891f5c8e3196c97458c Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 13:59:04 +0200 Subject: [PATCH 40/42] Add analysis of discarded alternatives --- cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 0ea037bf91..990d8a2ca3 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -385,6 +385,9 @@ Alternative syntaxes have been discussed: * `ASSERT`, `ENFORCE`, `IMPLIES` instead of `REQUIRE` * `ADD` instead of `CREATE` ** It is desirable for verb pairs for modifying operations to be consistent in the language, and recent discussions are (so far informally) suggesting `INSERT`/`DELETE` to be used for data modification, thus making `CREATE` and `DROP` available for schema modification such as constraints. +* Using a prefix model for uniqueness and node keys, alike `REQUIRE UNIQUE (n.a, n.b)` +** This was discarded in favour of the suffix model due to similarity with already existing `IS NOT NULL`. + Prefix operators are uncommon in Cypher. The use of an existing expression to express uniqueness -- instead of using the operator `IS UNIQUE` -- becomes unwieldy for multiple properties, as exemplified by the following: ---- From 26f22bb6b59167aa2dcc335640f81d4c48d0d966 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 14:26:44 +0200 Subject: [PATCH 41/42] Update openCypher grammar description - Including links to these from the CIP - Also update examples for the parser tests --- .../CIP2016-12-14-Constraint-syntax.adoc | 10 +++++ grammar/basic-grammar.xml | 7 +-- grammar/commands.xml | 44 +++++++++---------- tools/grammar/src/test/resources/cypher.txt | 21 ++++++--- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc index 990d8a2ca3..4bfc6ffa60 100644 --- a/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc +++ b/cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc @@ -72,6 +72,16 @@ References to existing grammar parts: * https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=Expression[] * https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=PropertyExpression[] +References to new grammar parts: + +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=Command[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=CreateConstraint[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=DropConstraint[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=ConstraintPredicate[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=Unique[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=NodeKey[] +* https://raw.githack.com/openCypher/openCypher/master/tools/grammar-production-links/grammarLink.html?p=GroupedPropertyExpression[] + The `REQUIRE` clause works exactly like the `WHERE` clause in a standard Cypher query, with the addition of also supporting the special constraint operators `IS UNIQUE`, `IS NODE KEY`, and the new `` expression. This allows for complex concrete constraint definitions (using custom predicates) within the specified syntax. diff --git a/grammar/basic-grammar.xml b/grammar/basic-grammar.xml index 433e9144af..ce31ea1c94 100644 --- a/grammar/basic-grammar.xml +++ b/grammar/basic-grammar.xml @@ -149,7 +149,7 @@ - + : &WS; @@ -714,6 +714,9 @@ OF ADD DROP + NODE + KEY + UNIQUE @@ -728,8 +731,6 @@ ANY NONE SINGLE - NODE - KEY diff --git a/grammar/commands.xml b/grammar/commands.xml index f64969ff2b..19891361be 100644 --- a/grammar/commands.xml +++ b/grammar/commands.xml @@ -68,41 +68,41 @@ - - CREATE &SP; CONSTRAINT &SP; &SP; - FOR &SP; &SP; - REQUIRE &SP; - - - DROP &SP; CONSTRAINT &SP; + DROP &SP; CONSTRAINT &SP; - - - - - - - ( &var; &label; ) - ( ) - [ &var; ] - ( ) - + + CREATE &SP; CONSTRAINT &SP; &SP; + FOR &SP; &SP; + REQUIRE &SP; - + - - + + - - UNIQUE &SP; + + &SP; IS &SP; UNIQUE - NODE &SP; KEY &SP; &WS; , &WS; + &SP; IS &SP; NODE &SP; KEY + + + + + + + ( &WS; + &WS; , &WS; + &WS; ) + + diff --git a/tools/grammar/src/test/resources/cypher.txt b/tools/grammar/src/test/resources/cypher.txt index 18d25f2560..fe08f8c259 100644 --- a/tools/grammar/src/test/resources/cypher.txt +++ b/tools/grammar/src/test/resources/cypher.txt @@ -314,21 +314,30 @@ RETURN count(label) AS numLabels§ CALL db.labels() YIELD x WHERE label CONTAINS 'User' AND foo + bar = foo RETURN count(label) AS numLabels§ CREATE CONSTRAINT foo FOR (p:Person) -REQUIRE UNIQUE p.name§ +REQUIRE p.name IS UNIQUE§ +CREATE CONSTRAINT foo +FOR (p:Person) +REQUIRE (p.name) IS UNIQUE§ +CREATE CONSTRAINT foo +FOR (p:Person) +REQUIRE (p.name, p.email) IS UNIQUE§ CREATE CONSTRAINT baz FOR (p:Person) -REQUIRE exists(p.name)§ +REQUIRE p.name IS NOT NULL§ CREATE CONSTRAINT cru FOR ()-[r:REL]-() -REQUIRE exists(r.property)§ +REQUIRE r.property > 5§ DROP CONSTRAINT foo_bar_baz§ CREATE CONSTRAINT nodeKey FOR (n:Node) -REQUIRE NODE KEY n.prop§ +REQUIRE n.prop IS NODE KEY§ +CREATE CONSTRAINT nodeKey +FOR (n:Node) +REQUIRE (n.prop) IS NODE KEY§ CREATE CONSTRAINT nodeKey FOR (n:Node) -REQUIRE NODE KEY n.p1, n.p2, n.p3§ +REQUIRE (n.p1, n.p2, n.p3) IS NODE KEY§ CREATE CONSTRAINT nodeKey FOR (n:Node) -REQUIRE NODE KEY n.p1 ,n.p2, n.p3§ +REQUIRE ( n.p1 ,n.p2, n.p3 ) IS NODE KEY§ DROP CONSTRAINT foo§ From 334fe9551e71e7030d53501a41da3a12c79b0395 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Fri, 2 Jul 2021 17:32:08 +0200 Subject: [PATCH 42/42] Specify NODE and KEY as symbolic names, not reserved words --- grammar/basic-grammar.xml | 4 ++-- tools/grammar/src/test/resources/cypher.txt | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/grammar/basic-grammar.xml b/grammar/basic-grammar.xml index ce31ea1c94..e46d6269a9 100644 --- a/grammar/basic-grammar.xml +++ b/grammar/basic-grammar.xml @@ -714,8 +714,6 @@ OF ADD DROP - NODE - KEY UNIQUE @@ -731,6 +729,8 @@ ANY NONE SINGLE + NODE + KEY diff --git a/tools/grammar/src/test/resources/cypher.txt b/tools/grammar/src/test/resources/cypher.txt index fe08f8c259..065ba20cc3 100644 --- a/tools/grammar/src/test/resources/cypher.txt +++ b/tools/grammar/src/test/resources/cypher.txt @@ -340,4 +340,5 @@ REQUIRE (n.p1, n.p2, n.p3) IS NODE KEY§ CREATE CONSTRAINT nodeKey FOR (n:Node) REQUIRE ( n.p1 ,n.p2, n.p3 ) IS NODE KEY§ -DROP CONSTRAINT foo§ +DROP CONSTRAINT node§ +DROP CONSTRAINT key§