Constraints
LoraDB remains schema-free by default: labels, relationship types, and property keys appear when you write them. Constraints are optional catalog entries that add validation for selected labels or relationship types.
Constraints are checked in two places:
- when the constraint is created, existing matching data must already satisfy it;
- after creation, writes that would violate the constraint fail.
Create Constraints
Every constraint needs a name:
CREATE CONSTRAINT user_email
FOR (u:User)
REQUIRE u.email IS UNIQUE;
CREATE CONSTRAINT author_name
FOR (a:Author)
REQUIRE a.name IS NOT NULL;
CREATE CONSTRAINT actor_name
FOR (a:Actor)
REQUIRE (a.first, a.last) IS NODE KEY;
CREATE CONSTRAINT owns_id
FOR ()-[o:OWNS]-()
REQUIRE o.ownershipId IS RELATIONSHIP KEY;
CREATE CONSTRAINT movie_title
FOR (m:Movie)
REQUIRE m.title IS :: STRING;Use IF NOT EXISTS to make creation idempotent:
CREATE CONSTRAINT user_email IF NOT EXISTS
FOR (u:User)
REQUIRE u.email IS UNIQUE;Constraint names may also come from a string parameter:
CREATE CONSTRAINT $name
FOR (u:User)
REQUIRE u.email IS UNIQUE;Constraint Kinds
| Kind | Syntax | Applies to |
|---|---|---|
| Property uniqueness | REQUIRE n.email IS UNIQUE | Nodes or relationships; single or composite |
| Property existence | REQUIRE n.email IS NOT NULL | Nodes or relationships; single property |
| Node key | REQUIRE (n.a, n.b) IS NODE KEY | Nodes; existence plus uniqueness |
| Relationship key | REQUIRE r.id IS RELATIONSHIP KEY | Relationships; existence plus uniqueness |
| Property type | REQUIRE n.title IS :: STRING | Nodes or relationships; single property |
Composite uniqueness and key constraints must wrap the property list in parentheses:
CREATE CONSTRAINT order_line
FOR ()-[r:LINE_ITEM]-()
REQUIRE (r.orderId, r.lineNo) IS UNIQUE;A uniqueness or key constraint is backed by a RANGE index of the same
name. For a key that is only ever looked up by value ({key: $k},
MERGE), OPTIONS { indexProvider: 'hash' } keeps the hash index that
enforcement and lookups read and drops the sorted copy of each value:
about 110 bytes less per node for a 58-character string key. Range,
STARTS WITH and ORDER BY reads on the key still work, by scanning
the label.
CREATE CONSTRAINT record_key
FOR (r:Record)
REQUIRE r.key IS UNIQUE
OPTIONS { indexProvider: 'hash' };'range' (Neo4j's 'range-1.0' is accepted too) is the default. To
change the backing, drop the constraint and create it again.
Existence constraints are single-property only:
CREATE CONSTRAINT published_at
FOR (p:Post)
REQUIRE p.publishedAt IS NOT NULL;Existence is judged on the graph as a statement leaves it, so one statement may add a label and its required property in either order, or replace or remove a required property and set it again. A statement that ends without the property is rejected and changes nothing. Uniqueness and type constraints are checked at each write.
MATCH (d:Draft {id: $id})
SET d:Post, d.publishedAt = datetime(); // ok
MATCH (p:Post {id: $id})
SET p = {id: $id, title: $title},
p.publishedAt = datetime(); // ok
MATCH (p:Post {id: $id})
SET p = {id: $id} // rejected, nothing changesProperty Types
Property type constraints accept scalar types, lists with non-null elements, vectors, and closed unions:
CREATE CONSTRAINT movie_released
FOR (m:Movie)
REQUIRE m.released IS :: DATE;
CREATE CONSTRAINT article_tags
FOR (a:Article)
REQUIRE a.tags IS :: LIST<STRING NOT NULL>;
CREATE CONSTRAINT doc_embedding
FOR (d:Doc)
REQUIRE d.embedding IS :: VECTOR<FLOAT32>(1536);
CREATE CONSTRAINT tagline
FOR (m:Movie)
REQUIRE m.tagline IS :: STRING | LIST<STRING NOT NULL>;Supported scalar type names are BOOLEAN, STRING, INTEGER,
FLOAT, DATE, LOCAL TIME, ZONED TIME, LOCAL DATETIME,
ZONED DATETIME, DURATION, and POINT.
MAP, ANY, and nullable list element types such as LIST<STRING>
are rejected for property type constraints. Vector dimensions must be
in 1..=4096, and the coordinate type must match the stored vector
type.
Inspect Constraints
SHOW CONSTRAINTS;Rows contain:
| Column | Meaning |
|---|---|
name | Constraint name |
type | NODE_PROPERTY_UNIQUENESS, RELATIONSHIP_PROPERTY_UNIQUENESS, NODE_PROPERTY_EXISTENCE, RELATIONSHIP_PROPERTY_EXISTENCE, NODE_KEY, RELATIONSHIP_KEY, NODE_PROPERTY_TYPE, or RELATIONSHIP_PROPERTY_TYPE |
entityType | NODE or RELATIONSHIP |
labelsOrTypes | Label or relationship type scope |
properties | Constrained property keys |
ownedIndex | Backing index name for uniqueness and key constraints; null otherwise |
indexProvider | range or hash for uniqueness and key constraints; null otherwise |
propertyType | Type expression for property type constraints; null otherwise |
SHOW CONSTRAINTS accepts the same YIELD-anchored catalog pipeline
as SHOW INDEXES:
SHOW CONSTRAINTS
YIELD name, type
WHERE type = 'NODE_PROPERTY_UNIQUENESS'
RETURN name
ORDER BY name;Drop Constraints
DROP CONSTRAINT user_email;
DROP CONSTRAINT maybe_missing IF EXISTS;
DROP CONSTRAINT $name;Uniqueness and key constraints own a backing RANGE index with the
same name. Drop the constraint, not the backing index; direct
DROP INDEX on a constraint-owned index is rejected.
Write Enforcement
Constraints are enforced for matching writes:
CREATE CONSTRAINT user_email
FOR (u:User)
REQUIRE u.email IS UNIQUE;
CREATE (:User {email: 'ada@example.com'});
CREATE (:User {email: 'ada@example.com'}); // rejectedEnforcement covers node and relationship creation, property updates,
property replacement through SET n = {...}, property removal, and
adding a label that activates a node constraint.
Durability
Constraint catalog entries are included in snapshots and replayed from WAL records during recovery. Uniqueness and key constraints recreate their backing indexes as part of the catalog state.
Limitations
- Constraints are label/type scoped; there is no database-wide uniqueness constraint.
- Existence constraints are single-property only.
- Property type constraints do not support
MAP,ANY, or nullable list element types. - Constraint names are required.
- Dropping a backing index directly is rejected; use
DROP CONSTRAINT.
See Also
- Indexes - backing indexes and catalog inspection.
- CREATE - writes that may be checked by constraints.
- SET / REMOVE / DELETE - mutation clauses.
- Schema-free - optional schema controls.