Constraints and indexes
This article describes how you can use the @container directive in GraphQL to add constraints and enhance the searchability of the data in a data type.
Indexes and constraints apply to a container, and all the involved properties must belong to the same container.
Constraints
When you build a data model, you can use ! to add a requiredness constraint. To specify more complex requirements for how data should conform to the data model, you can add multiple constraints on each data type.
Uniqueness
For uniqueness constraints, you must give it a unique (with the space) identifier, and a list of the combination of fields that needs to be unique.
The uniqueness is enforced by an underlying index. The index can boost the performance of queries that need to filter or sort on the involved properties. Therefore, the order of the properties matter.
Currently, we don't support relation fields (direct or many-to-many) for uniqueness constraints.
The example below has a my-unique-constraint
constraint that checks name
and description
for uniqueness in combination.
type Facility
@container(
constraints: [
{
constraintType: UNIQUENESS
identifier: "my-unique-constraint"
fields: ["name", "description"]
}
]
) {
name: String
description: String
lang: Int
lat: Int
}
Views and containers
You can only specify constraints and indexes on new (non-imported) fields of a data type when using @container. See also the Limitations section.
Indexes
You can add custom indexes to containers to speed up queries by enabling fast lookups, filtering, and sorting. Indexes come at a cost, and aren't added to properties by default. For example, indexes increase storage usage, and make write operations slower, and don't guarantee that queries execute faster.
By default, you can efficiently filter all instances with the space
and externalId
fields.
To improve query execution, you may want to specify an explicit ordering of fields to index. In these cases, you can use the indexes
of the @container directive.
Like constraints, all indexes must have an identifier that's unique within a space. Additionally, it needs a specified type (currently, we only support BTREE
) and the set of properties. All the properties mentioned must be in the same container.
The field ordering defines what combinations of filters you can use, and what kinds of sorts the index supports.
If you have an index on (type, priority)
, queries that filter on type = ...
or type = ... AND priority >= ...
can use the index. Queries that sort on type
or (type, priority)
can use the index as a pre-sorted way of accessing the data.
However, a query that sorts or filters just on priority
won't be able to use an index on (type, priority)
.
The example below adds a my-index
index on the properties type
, priority
, in that order.
type WorkItem
@container(
indexes: [
{
identifier: "my-index"
indexType: BTREE
fields: ["type", "priority"]
}
]
) {
type: String
priority: Int
...
}
Combining constraints and indexes
You can combine indexes and constraints on a @container, as in this example:
type Facility
@container(
constraints: [
{
constraintType: UNIQUENESS
identifier: "my-unique-constraint"
fields: ["name", "description"]
}
{
constraintType: UNIQUENESS
identifier: "my-unique-constraint-2"
fields: ["lat", "lng"]
}
]
indexes: [
{
identifier: "my-index"
indexType: BTREE
fields: ["name", "tag", "description"]
}
]
) {
name: String
description: String
lng: Int
lat: Int
tag: String
}
Views and containers
You can only specify constraints and indexes on new (non-imported) fields of a data type when using @container.
Limitations
Since containers physically stores the data, you can only specify constraints and indexes on fields within the data type that are stored within the same container.
If the data type extends from another view (via @view or inheritance), you can't use the fields that belong in containers from the imported views to build constraints or indexes.
You can only specify constraints and indexes on new (non-imported) fields of a data type when using @container.