Skip to content
Closed
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7157bf3
Add the constraints CIP
Mats-SX Dec 14, 2016
6448277
Add property existence constraint syntax
Mats-SX Dec 14, 2016
6b667b0
Introduce the concept of domain
Mats-SX Dec 15, 2016
bbb46f4
Add example for existence constraint
Mats-SX Dec 15, 2016
bf6e05c
Add SQL examples
Mats-SX Dec 15, 2016
a7521d2
Update standardisation scope
Mats-SX Dec 15, 2016
27cd6e6
Add grammar rules for new constraint syntax
Mats-SX Dec 15, 2016
08f8eb7
Edited the textual contents of the CIP
Feb 15, 2017
0d1ae5e
More textual edits to the Constraints CIP
Feb 16, 2017
824a2c7
Amended w3 reference to denote alterations in 'quoted' text
Feb 17, 2017
4103056
Rework CIP
Mats-SX Mar 1, 2017
fcaad28
Remove Motivation section
Mats-SX Mar 1, 2017
35c4730
Move Mutability and Name sections
Mats-SX Mar 1, 2017
9d7aaf4
Add cross-links
Mats-SX Mar 1, 2017
ec56ba9
Add example for CIR-2017-172
Mats-SX Mar 1, 2017
c6a6d38
Support arbitrary patterns
Mats-SX Mar 1, 2017
d355dcc
Introduce PRIMARY KEY constraint predicate
Mats-SX Mar 3, 2017
9438af1
Rename constraint operator to NODE KEY
Mats-SX Mar 7, 2017
f31f09f
Use ADD for constraint creation
Mats-SX Mar 7, 2017
a2dc74d
Add specification for the return record
Mats-SX Mar 7, 2017
52f3a5e
Add tests verifying NODE KEY works in grammar
Mats-SX May 4, 2017
8eca441
Reformatted title
Jan 17, 2018
53b0445
Use CREATE instead of ADD
Mats-SX Jul 19, 2019
0ec02df
Make textual clarifications
Mats-SX Jul 19, 2019
4ef7b32
Update grammar to use CREATE
Mats-SX Jul 22, 2019
dd6fbcd
Add Neo4j index extension CIP
Mats-SX Mar 3, 2017
41a0c27
Reformatted title
Jan 17, 2018
d92de3e
Update CIP with latest developments
Mats-SX Jul 26, 2019
f02ad09
Add detail on relationship to contraints CIP
Mats-SX Jul 26, 2019
2cebf70
Add result record specification
Mats-SX Jul 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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`
  • Loading branch information
Mats-SX committed Jul 26, 2019
commit d355dcca588bddb6e99600eaba0bbcc1f5be78a9
79 changes: 53 additions & 26 deletions cip/1.accepted/CIP2016-12-14-Constraint-syntax.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<uniqueness>> for details).
For details on `UNIQUE` and `PRIMARY KEY`, see the dedicated sections below: <<uniqueness>>, <<primary-key>>.

==== 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

Expand Down Expand Up @@ -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 <<domain-exception,3.>> 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 <<domain-exception, 3.>> 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 <<domain-exception,3.>>) 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

Expand Down Expand Up @@ -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`:
Expand All @@ -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:

Expand Down Expand Up @@ -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:

Expand Down