Constraints enforce extra validation on node and edge properties in the graph. Any attempt to insert or update data that violates these rules will result in an error.
Ultipa supports the following constraint types:
| Constraint Type | Description | Composite of Properties |
|---|---|---|
NOT NULL | Ensures a property never contains null values. | Not supported |
UNIQUE | Ensures a property contains no duplicate values. | Supported |
KEY | Combines NOT NULL and UNIQUE, marking a property as the identifying key of a node type. Available on node types only. | Supported |
CHECK | Ensures a property satisfies a custom predicate on every row, for example CHECK (price >= 0). Declared inline only. | Not supported |
When a constraint type supports a composite of properties, a row violates the constraint only when all listed properties match an existing row.
Show constraints in the current graph:
GQLSHOW CONSTRAINTS SHOW NODE CONSTRAINTS SHOW EDGE CONSTRAINTS
Filter by the label set a constraint targets with ON:
GQL-- Only node constraints whose target label set is {Person} SHOW CONSTRAINTS ON (:Person) -- Only node constraints whose target label set is {A, B} SHOW CONSTRAINTS ON (:A&B) -- Only edge constraints whose target label set is {KNOWS} SHOW CONSTRAINTS ON ()-[:KNOWS]->()
To inspect a single constraint by name:
GQLDESCRIBE CONSTRAINT myConstraint -- DESC is a shorthand for DESCRIBE DESC CONSTRAINT myConstraint
Each constraint provides the following metadata:
| Field | Description |
|---|---|
name | The user-supplied or auto-generated name.. |
type | node, edge, or wildcard. |
matchers | A list of <labels>.<properties> descriptors, one per OR alternative the constraint targets.
|
constraint_type | NOT NULL, UNIQUE, KEY, or CHECK (<predicate>). |
Creating a constraint on a non-empty graph scans existing data to verify compliance, and may take time on large graphs. The creation fails if any existing row violates the constraint.
Constraints can be created two ways:
Syntax<create constraint statement> ::= "CREATE" { "CONSTRAINT" [ "IF NOT EXISTS" ] | "OR REPLACE CONSTRAINT" } [ <constraint name> ] "FOR" <constraint scope> "REQUIRE" <constraint requirement> <constraint scope> ::= <node constraint scope> | <edge constraint scope> <node constraint scope> ::= "(" <node variable declaration> <label set> ")" <edge constraint scope> ::= "()-[" <edge variable declaration> <label set> "]->()" <label set> ::= ":" <label conjunction> [ { "|" <label conjunction> }... ] | ":%" <label conjunction> ::= <label name> [ { "&" <label name> }... ] <constraint requirement> ::= <property references> "IS" <constraint type> <property references> ::= <property reference> | "(" <property reference> [ { "," <property reference> }... ] ")" <property reference> ::= <node/edge variable> "." <property name> <constraint type> ::= "NOT NULL" | "UNIQUE" | "KEY"
Details
<labels>_<properties>_<type>, e.g. User_email_not_null, Person_firstName_lastName_unique. The IF NOT EXISTS and OR REPLACE variants still require an explicit name as they identify the constraint by name.:A): applies to nodes/edges with that label.:A&B): applies to nodes/edges whose contains every scope label. {A, B} and {A, B, C} both satisfy; {A} alone does not.:A|B): applies to nodes/edges that have any of the alternatives. A, B, or both.:A&B|C): & binds tighter than |, so this parses as (A&B) | C.:%): applies to every node or edge in the graph.CREATE CONSTRAINT statement creates NOT NULL, UNIQUE, and KEY constraints. CHECK constraints cannot be created this way; they are declared inline only (see Inline in a Type Definition).GQL-- NOT NULL constraint on User nodes' name CREATE CONSTRAINT nn_user_name FOR (n:User) REQUIRE n.name IS NOT NULL -- UNIQUE constraint on KNOWS edges' eid CREATE CONSTRAINT FOR ()-[e:KNOWS]->() REQUIRE e.eid IS UNIQUE -- Composite UNIQUE constraint User nodes' firstName and lastName CREATE CONSTRAINT FOR (n:User) REQUIRE (n.firstName, n.lastName) IS UNIQUE -- KEY constraint on User nodes' uid CREATE CONSTRAINT user_key FOR (n:User) REQUIRE n.uid IS KEY -- Composite KEY constraint on Account nodes' tenantId and externalId CREATE CONSTRAINT account_key FOR (n:Account) REQUIRE (n.tenantId, n.externalId) IS KEY -- Wildcard NOT NULL: every node must have a non-null createdAt CREATE CONSTRAINT FOR (n:%) REQUIRE n.createdAt IS NOT NULL -- Wildcard UNIQUE: every edge's eid must be unique CREATE CONSTRAINT FOR ()-[r:%]->() REQUIRE r.eid IS UNIQUE -- Conjunction: email is unique only on nodes that carry BOTH User and Employee labels CREATE CONSTRAINT FOR (n:User&Employee) REQUIRE n.email IS UNIQUE -- Disjunction: email is unique on every User node and every Actor node CREATE CONSTRAINT FOR (n:User|Actor) REQUIRE n.email IS UNIQUE -- Mixed: parses as (Employee&Manager) | (Contractor&Lead) -- Enforces on nodes that are EITHER {Employee, Manager} OR {Contractor, Lead} CREATE CONSTRAINT FOR (n:Employee&Manager|Contractor&Lead) REQUIRE n.badgeId IS UNIQUE
You can use the IF NOT EXISTS clause to prevent errors when attempting to create a constraint that already exists. It allows the statement to be safely executed.
GQLCREATE CONSTRAINT IF NOT EXISTS KNOWS_eid_unique FOR ()-[e:KNOWS]->() REQUIRE e.eid IS UNIQUE
You can use OR REPLACE to drop an existing constraint with the same name and create a new one in its place:
GQLCREATE OR REPLACE CONSTRAINT KNOWS_eid_unique FOR ()-[e:KNOWS]->() REQUIRE e.eid IS UNIQUE
Inline declaration attaches constraint type keywords directly to a property in a node or edge type definition, alongside its data type. The constraint takes effect as soon as the type is created. Inline declarations cover NOT NULL, UNIQUE, KEY, and CHECK.
Inline declarations are limited to single-property constraints. For a composite constraint, use the CREATE CONSTRAINT statement instead.
Inline constraints get the same auto-generated name as a nameless CREATE CONSTRAINT FOR …. For example, NODE User ({uid STRING KEY}) registers a constraint named User_uid_key. A property declared NOT NULL UNIQUE registers two separate constraints: <Label>_<prop>_not_null and <Label>_<prop>_unique.
When applied to the same property, constraint type keywords can be written in either order. For example, NOT NULL UNIQUE and UNIQUE NOT NULL are equivalent.
You can declare inline constraints in any of the following:
GQL-- In a CREATE GRAPH body CREATE GRAPH myGraph { NODE User ({uid STRING KEY, name STRING NOT NULL UNIQUE, age UINT32}), EDGE KNOWS ()-[{createdOn TIMESTAMP NOT NULL, eid STRING}]->() } -- In a CREATE GRAPH TYPE body CREATE GRAPH TYPE gType { NODE User ({uid STRING KEY, name STRING NOT NULL UNIQUE, age UINT32}), EDGE KNOWS ()-[{createdOn TIMESTAMP NOT NULL, eid STRING}]->() } -- In CREATE NODE (add a node type to a closed graph) CREATE NODE User ({uid STRING KEY, name STRING NOT NULL UNIQUE, age UINT32}) -- In CREATE EDGE (add an edge type to a closed graph) CREATE EDGE KNOWS (User)-[{createdOn TIMESTAMP NOT NULL, eid STRING}]->(User)
A CHECK constraint attaches a custom predicate to a property, enforcing a per-row invariant. A write is accepted only when the predicate holds for the row. Unlike NOT NULL, UNIQUE, and KEY, a CHECK cannot be created through the CREATE CONSTRAINT statement; it is inline only.
Declare a CHECK alongside the property's data type:
GQLCREATE GRAPH inventory { NODE Product ({ name STRING KEY, price FLOAT CHECK (price >= 0), stock INT32 NOT NULL CHECK (stock >= 0) }), EDGE Rated ()-[{ grade INT32 CHECK (grade >= 0 AND grade <= 5) }]->() }
The same form works in CREATE GRAPH TYPE, CREATE NODE, and CREATE EDGE.
Referencing other properties
A CHECK predicate may reference any property of the same node or edge, so cross-property invariants are supported:
GQLCREATE NODE Product ({ cost FLOAT, price FLOAT CHECK (price > cost) })
Evaluation semantics
A CHECK follows SQL semantics: a write is rejected only when the predicate evaluates to a definite false. It passes when the predicate is true or unknown, and a predicate is unknown whenever a property it references is absent or null. To also require that the value is present, pair CHECK with NOT NULL:
GQLCREATE NODE Product ({ stock INT32 NOT NULL CHECK (stock >= 0) })
The predicate must be deterministic: no subqueries and no side-effecting functions.
Drop a constraint by its name:
GQLDROP CONSTRAINT nn_user_name
The IF EXISTS clause is used to prevent errors when attempting to delete a constraint that does not exist. It allows the statement to be safely executed.
GQLDROP CONSTRAINT IF EXISTS nn_user_name