Primitives​
Gel has a robust type system consisting of primitive and object types. types. Primitive types are used to declare properties on object types, as query and function arguments, as as well as in other contexts.
Built-in scalar types​
Gel comes with a range of built-in scalar types, such as:
-
String:
str
-
Boolean:
bool
-
Various numeric types:
int16
,int32
,int64
,float32
,float64
,bigint
,decimal
-
JSON:
json
, -
UUID:
uuid
, -
Date/time:
datetime
,duration
cal::local_datetime
,cal::local_date
,cal::local_time
,cal::relative_duration
,cal::date_duration
Custom scalars​
You can extend built-in scalars with additional constraints or annotations.
Here's an example of a non-negative custom int64
variant:
scalar type posint64 extending int64 {
constraint min_value(0);
}
Enums​
Enum types are created by extending the abstract enum
type, e.g.:
scalar type Color extending enum<Red, Green, Blue>;
type Shirt {
color: Color;
}
which can be queries with:
select Shirt filter .color = Color.Red;
For a full reference on enum types, see the Enum docs.
Arrays​
Arrays store zero or more primitive values of the same type in an ordered list. Arrays cannot contain object types or other arrays, but can contain virtually any other type.
type Person {
str_array: array<str>;
json_array: array<json>;
tuple_array: array<tuple<float32, float32>>;
# INVALID: arrays of object types not allowed:
# friends: array<Person>
# INVALID: arrays cannot be nested:
# nested_array: array<array<str>>
# VALID: arrays can contain tuples with arrays in them
nested_array_via_tuple: array<tuple<array<str>>>
}
Array syntax in EdgeQL is very intuitive (indexing starts at 0
):
select [1, 2, 3];
select [1, 2, 3][1] = 2; # true
For a full reference on array types, see the Array docs.
Tuples​
Like arrays, tuples are ordered sequences of primitive data. Unlike arrays, each element of a tuple can have a distinct type. Tuple elements can be any type, including primitives, objects, arrays, and other tuples.
type Person {
unnamed_tuple: tuple<str, bool, int64>;
nested_tuple: tuple<tuple<str, tuple<bool, int64>>>;
tuple_of_arrays: tuple<array<str>, array<int64>>;
}
Optionally, you can assign a key to each element of the tuple. Tuples containing explicit keys are known as named tuples. You must assign keys to all elements (or none of them).
type BlogPost {
metadata: tuple<title: str, published: bool, upvotes: int64>;
}
Named and unnamed tuples are the same data structure under the hood. You can add, remove, and change keys in a tuple type after it's been declared. For details, see Tuples.
When you query an unnamed tuple using one of EdgeQL's client libraries, its value is converted to a list/array. When you fetch a named tuple, it is converted into an object/dictionary/hashmap depending on the language.
Ranges​
Ranges represent some interval of values. The intervals can be bound or unbound on either end. They can also be empty, containing no values. Only some scalar types have corresponding range types:
-
Numeric ranges:
range<int32>
,range<int64>
,range<float32>
,range<float64>
,range<decimal>
-
Date/time ranges:
range<datetime>
,range<cal::local_datetime>
,range<cal::local_date>
Example:
type DieRoll {
values: range<int64>;
}
For a full reference on ranges, functions and operators see the Range docs.
Sequences​
To represent an auto-incrementing integer property, declare a custom scalar
that extends the abstract sequence
type. Creating a sequence type
initializes a global int64
counter that auto-increments whenever a new
object is created. All properties that point to the same sequence type will
share the counter.
scalar type ticket_number extending sequence;
type Ticket {
number: ticket_number;
rendered_number := 'TICKET-\(.number)';
}
For a full reference on sequences, see the Sequence docs.
Declaring scalars​
This section describes the syntax to declare a custom scalar type in your schema.
Syntax​
[abstract] scalar type TypeName [extending supertype [, ...] ]
[ "{"
[ annotation-declarations ]
[ constraint-declarations ]
...
"}" ]
Description​
This declaration defines a new object type with the following options:
- abstract
-
If specified, the created scalar type will be abstract.
- TypeName
-
The name (optionally module-qualified) of the new scalar type.
- extending supertype
-
Optional clause specifying the supertype of the new type.
If supertype is an
enumerated type
declaration then an enumerated scalar type is defined.Use of
extending
creates a persistent type relationship between the new subtype and its supertype(s). Schema modifications to the supertype(s) propagate to the subtype.
The valid SDL sub-declarations are listed below:
- annotation-declarations
-
Set scalar type annotation to a given value.
- constraint-declarations
-
Define a concrete constraint for this scalar type.
DDL commands​
This section describes the low-level DDL commands for creating, altering, and dropping scalar types. You typically don't need to use these commands directly, but knowing about them is useful for reviewing migrations.
Create scalar​
Define a new scalar type.
[ with with-item [, ...] ]
create [abstract] scalar type name [ extending supertype ]
[ "{" subcommand; [...] "}" ] ;
where subcommand is one of
create annotation annotation-name := value
create constraint constraint-name ...
Description​
The command create scalar type
defines a new scalar type for use in the
current branch.
If name is qualified with a module name, then the type is created in that module, otherwise it is created in the current module. The type name must be distinct from that of any existing schema item in the module.
If the abstract
keyword is specified, the created type will be
abstract.
All non-abstract scalar types must have an underlying core
implementation. For user-defined scalar types this means that
create scalar type
must have another non-abstract scalar type
as its supertype.
The most common use of create scalar type
is to define a scalar
subtype with constraints.
Most sub-commands and options of this command are identical to the
SDL scalar type declaration. The
following subcommands are allowed in the create scalar type
block:
- create annotation annotation-name := value;
-
Set scalar type's annotation-name to value.
See
create annotation
for details. - create constraint constraint-name ...
-
Define a new constraint for this scalar type. See
create constraint
for details.
Alter scalar​
Alter the definition of a scalar type.
[ with with-item [, ...] ]
alter scalar type name
"{" subcommand; [...] "}" ;
where subcommand is one of
rename to newname
extending ...
create annotation annotation-name := value
alter annotation annotation-name := value
drop annotation annotation-name
create constraint constraint-name ...
alter constraint constraint-name ...
drop constraint constraint-name ...
Description​
The command alter scalar type
changes the definition of a scalar type.
name must be a name of an existing scalar type, optionally qualified
with a module name.
The following subcommands are allowed in the alter scalar type
block:
- rename to newname;
-
Change the name of the scalar type to newname.
- extending ...
-
Alter the supertype list. It works the same way as in
alter type
. - alter annotation annotation-name;
-
Alter scalar type annotation-name. See
alter annotation
for details. - drop annotation annotation-name
-
Remove scalar type's annotation-name from value. See
drop annotation
for details. - alter constraint constraint-name ...
-
Alter the definition of a constraint for this scalar type. See
alter constraint
for details. - drop constraint constraint-name
-
Remove a constraint from this scalar type. See
drop constraint
for details.
All the subcommands allowed in the create scalar type
block are also
valid subcommands for alter scalar type
block.
Drop scalar​
Remove a scalar type.
[ with with-item [, ...] ]
drop scalar type name ;