This section assumes a basic understanding of EdgeQL.
Constraints give users fine-grained control over which data is considered valid. They can be defined on properties, links, object types, and custom scalars.
Below is a simple property constraint.
type User {
required username: str {
constraint exclusive;
}
}
This example uses a built-in constraint, exclusive
. Refer to the table
below for a complete list; click the name of a given constraint for the full
documentation.
Enforce uniqueness among all instances of the containing type | |
Custom constraint expression | |
A list of allowable values | |
Maximum value numerically/lexicographically | |
Maximum value numerically/lexicographically (exclusive range) | |
Maximum length (strings only) | |
Minimum value numerically/lexicographically | |
Minimum value numerically/lexicographically (exclusive range) | |
Minimum length (strings only) | |
Regex constraint (strings only) |
The max_len_value
constraint below uses the built-in len()
function, which returns the length of a string.
type User {
required username: str {
# usernames must be unique
constraint exclusive;
# max length (built-in)
constraint max_len_value(25);
};
}
The expression
constraint is used to define custom constraint logic. Inside
custom constraints, the keyword __subject__
can used to reference the
value being constrained.
type User {
required username: str {
# max length (as custom constraint)
constraint expression on (len(__subject__) <= 25);
};
}
Constraints can be defined on object types. This is useful when the constraint logic must reference multiple links or properties.
Inside an object type declaration, you can omit __subject__
and simply
refer to properties with the leading dot notation
(e.g. .<name>
).
type ConstrainedVector {
required x: float64;
required y: float64;
constraint expression on (
.x ^ 2 + .y ^ 2 <= 25
);
}
Note that the constraint expression cannot contain arbitrary EdgeQL! Due to
how constraints are implemented, you can only reference single
(non-multi)
properties and links defined on the object type.
# Not valid!
type User {
required username: str;
multi friends: User;
# ❌ constraints cannot contain paths with more than one hop
constraint expression on ('bob' in .friends.username);
}
Constraints can be defined on computed properties.
type User {
required username: str;
required property clean_username := str_trim(str_lower(.username));
constraint exclusive on (.clean_username);
}
To define a composite constraint, create an exclusive
constraint on a
tuple of properties or links.
type User {
username: str;
}
type BlogPost {
title: str;
author: User;
constraint exclusive on ((.title, .author));
}
When defining a constraint on a link, __subject__
refers to the link
itself. This is commonly used add constraints to link properties.
type User {
name: str;
multi friends: User {
single strength: float64;
constraint expression on (
__subject__@strength >= 0
);
}
}
Custom scalar types can be constrained.
scalar type username extending str {
constraint regexp(r'^[A-Za-z0-9_]{4,20}$');
}
Note: you can’t use exclusive
constraints on custom scalar
types, as the concept of exclusivity is only defined in the context of a given
object type.
Use expression
constraints to declare custom constraints
using arbitrary EdgeQL expressions. The example below uses the built-in
str_trim()
function.
scalar type title extending str {
constraint expression on (
__subject__ = str_trim(__subject__)
);
}
If you define a constraint on a type and then extend that type, the constraint will not be applied individually to each extending type. Instead, it will apply globally across all the types that inherited the constraint.
type User {
required name: str {
constraint exclusive;
}
}
type Administrator extending User;
type Moderator extending User;
db> ... ...
insert Administrator {
name := 'Jan'
};
{default::Administrator {id: 7aeaa146-f5a5-11ed-a598-53ddff476532}}
db> ... ...
insert Moderator {
name := 'Jan'
};
edgedb error: ConstraintViolationError: name violates exclusivity constraint Detail: value of property 'name' of object type 'default::Moderator' violates exclusivity constraint
db> ... ...
insert User {
name := 'Jan'
};
edgedb error: ConstraintViolationError: name violates exclusivity constraint Detail: value of property 'name' of object type 'default::User' violates exclusivity constraint
As this example demonstrates, this means if an object of one of the extending types has a value for a property that is exclusive, an object of a different extending type cannot have the same value.
If that’s not what you want, you can instead delegate the constraint to the
inheriting types by prepending the delegated
keyword to the constraint.
The constraint would then be applied just as if it were declared individually
on each of the inheriting types.
type User {
required name: str {
delegated constraint exclusive;
}
}
type Administrator extending User;
type Moderator extending User;
db> ... ...
insert Administrator {
name := 'Jan'
};
{default::Administrator {id: 7aeaa146-f5a5-11ed-a598-53ddff476532}}
db> ... ...
insert User {
name := 'Jan'
};
{default::User {id: a6e3fdaf-c44b-4080-b39f-6a07496de66b}}
db> ... ...
insert Moderator {
name := 'Jan'
};
{default::Moderator {id: d3012a3f-0f16-40a8-8884-7203f393b63d}}
db> ... ...
insert Moderator {
name := 'Jan'
};
edgedb error: ConstraintViolationError: name violates exclusivity constraint Detail: value of property 'name' of object type 'default::Moderator' violates exclusivity constraint
With the addition of delegated
to the constraints, the inserts were
successful for each of the types. In this case, we did not hit a constraint
violation until we tried to insert a second Moderator
object with the same
name as the one we had just inserted.