Search
ctrl/
Ask AI
Light
Dark
System

Constraints

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.

Copy
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.

exclusive

Enforce uniqueness (disallow duplicate values)

expression

Custom constraint expression (followed by keyword on)

one_of

A list of allowable values

max_value

Maximum value numerically/lexicographically

max_ex_value

Maximum value numerically/lexicographically (exclusive range)

min_value

Minimum value numerically/lexicographically

min_ex_value

Minimum value numerically/lexicographically (exclusive range)

max_len_value

Maximum length (str only)

min_len_value

Minimum length (str only)

regexp

Regex constraint (str only)

The max_len_value constraint below uses the built-in len() function, which returns the length of a string.

Copy
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.

Copy
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>).

Copy
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.

Copy
# 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.

Copy
type User {
  required username: str;
  required 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.

Copy
type User {
  username: str;
}

type BlogPost {
  title: str;
  author: User;

  constraint exclusive on ((.title, .author));
}

Constraints on object types can be made partial, so that they don’t apply when some condition holds.

Copy
type User {
  required username: str;
  deleted: bool;

  # Usernames must be unique unless marked deleted
  constraint exclusive on (.username) except (.deleted);
}

Custom scalar types can be constrained.

Copy
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.

Copy
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.

Copy
type User {
  required name: str {
    constraint exclusive;
  }
}
type Administrator extending User;
type Moderator extending User;
Copy
db> 
... 
... 
insert Administrator {
  name := 'Jan'
};
{default::Administrator {id: 7aeaa146-f5a5-11ed-a598-53ddff476532}}
Copy
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
Copy
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.

Copy
type User {
  required name: str {
    delegated constraint exclusive;
  }
}
type Administrator extending User;
type Moderator extending User;
Copy
db> 
... 
... 
insert Administrator {
  name := 'Jan'
};
{default::Administrator {id: 7aeaa146-f5a5-11ed-a598-53ddff476532}}
Copy
db> 
... 
... 
insert User {
  name := 'Jan'
};
{default::User {id: a6e3fdaf-c44b-4080-b39f-6a07496de66b}}
Copy
db> 
... 
... 
insert Moderator {
  name := 'Jan'
};
{default::Moderator {id: d3012a3f-0f16-40a8-8884-7203f393b63d}}
Copy
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.