TypeDB Fundamentals Lecture Series: from Nov 16 to Jan 9

Define types

See the Fundamentals page for more information about schema and types.

See the full table of contents for this page

To use a new TypeDB database, we need to define its schema first. We can do it with a Define query.

To define a schema, a schema session must be opened, and a write transaction started. The changes must be committed, or they will NOT be permanent.

There is no limitation in the order of types to define. We can define schema types in any order if the schema is valid. TypeDB Clients validate our schema definition requests before sending them to a TypeDB server. We will not be able to commit changes if the resulting schema isn’t valid.

Define queries are idempotent.

Running the same define query a second time shall not produce any changes to the database schema.

Running a modified version of an already executed schema definition query can add concepts to the schema but mostly can’t modify existing ones. TypeQL schema statements do NOT replace existing type definitions but add missing parts.

Two notable exceptions are:

  • rules (defining a new rule with an existing label/name will replace the existing rule completely), and

  • annotations, like the @key keyword (which we can remove by redefining attribute ownership without the annotation).

Define query

A define query adds new or modifies existing types and/or rules in the database schema.

A define query consists of a single define clause.

TypeQL define clause must begin with the define keyword.

We can run multiple statements as a single query. The define keyword must only be included once per query at the beginning.

This page only covers how to define types. For defining rules, see the Define rules page.

Syntax

Define queries are written in TypeQL with the following syntax:

define <pattern>

The pattern used in the define query is limited to the DDL part of TypeQL as it declares only types (and rules), but not instances of data.

Type definition syntax

This is the generic syntax of the define query for a type definition:

define

<label> sub <supertype labell>
[(, abstract)]
[, value <value-type>]
(, relates <role label>)
[(, relates <role label>)...]
[(, owns <attribute type label> [@annotation])...]
[(, plays <relation type label>:<role>)...];

The line with the value keyword is relevant only for attribute types definition.

The lines with the relates keyword are relevant only for relation types definition.

See the specific syntax for entity types, relation types, or attribute types below.

Example

define

credential sub attribute, value string;
full-name sub attribute, value string;
id sub attribute,
    abstract,
    value string;
email sub id, value string;

subject sub entity,
    abstract,
    owns credential,
    plays group-membership:group-member;

user sub subject,
    abstract;

person sub user,
    owns full-name,
    owns email;

membership sub relation,
    abstract,
    relates parent,
    relates member;

group-membership sub membership,
    relates group as parent,
    relates group-member as member;

The above example can be run in TypeDB Studio. See the Quickstart guide for full descriptions of the following steps:

  1. Make sure a TypeDB server is up.

  2. Start TypeDB Studio.

  3. Connect TypeDB Studio to the server.

  4. Create or select a new database.

  5. Open a Project folder.

  6. Copy and paste the query code above into a new tab of the Text editor.

  7. Ensure the Session type switch is set to "Schema".

  8. Ensure the Transaction type switch is set to "Write".

  9. Click the green "play" button to start the transaction.

  10. Click the "checkmark" button to commit the changes.

After a successful commit, all the types can be seen in the Type Browser panel in the bottom left part of the Studio window.

The following query can be run in a new tab in Schema / Read mode to see the schema visualized as a graph.

match $s sub thing;
get;

As the result of the above query TypeDB Studio should display a graph visualization of the schema defined earlier.

Schema visualization example

The thing internal type will be deprecated in one of the upcoming versions and deleted in TypeDB version 3.0.

Consider using entity, attribute, or relation built-in types instead.

For example:

Data retrieval example
match
    $s isa $t;
    {$t type entity;} or {$t type relation;} or {$t type attribute;};

In the following sections, we can find more detailed information on different schema definition queries and possibilities.

Define entity types

Overview

Entity types are defined independently of other types, but may subtype other entity types.

Optionally, entity types can:

  • Be abstract.

  • Own attribute types.

  • Play roles defined in relation types.

Syntax

Entity types are defined in TypeQL with the following pattern:

<label> sub (entity | <entity type label>) [(, abstract)]
[(, owns <attribute type label> [@annotation])...]
[(, plays <relation type label>:<role>)...];

Examples

Basic

The following define query creates a new entity type, object, by subtyping the entity built-in type.

define

object sub entity;

Abstract

Optionally, types can be defined as abstract. An abstract type can’t be instantiated and must be subtyped in order to create entities. It exists only so other types can inherit ownerships over attribute types it owns and the roles it plays. We can use abstract types in queries, expecting to retrieve instances of its concrete subtypes.

define

object sub entity,
    abstract;

Owns an attribute

To define a new entity type that owns one or more attribute types, use the owns keyword followed by the label of the attribute type. The attribute types are appended to the entity type definition with commas. Note, attribute types must be defined before or concurrently (in the same query) with its owner(s). We can add owners later, but we can’t own nonexistent attribute types.

define

object-type sub attribute, value string;

object sub entity,
    abstract,
    owns object-type;
Cardinality

By default, an entity can have multiple attributes of the same type: zero, one, or many.

By having multiple attributes of the same type we’re effectively creating a multivalued attribute (as if having an attribute type instantiated with multiple values).

Use the @key keyword to limit the cardinality to exactly one and add uniqueness constraint. Hence, the instance of the type with key ownership will have exactly one (no more and no less) key attribute instance.

See the example in the Key attribute section.

Plays a role

To add roles that entities of a specific entity type can play, use the plays keyword.

define

access sub relation,
    relates object;

object sub entity,
    abstract,
    plays access:object;

Subtypes another entity

All types that are subtyping entity built-in type directly or through other subtypes are called entity types. Instances of entity types are called entities. A similar terminology is applied to attributes and relations.

An entity type can subtype another entity type by using the same sub keyword, but replacing the entity keyword after it with a label of another entity type to subtype.

define

path sub attribute, value string;

object sub entity,
    abstract,
    owns object-type,
    plays access:object;
resource sub object,
    abstract;
file sub resource,
    owns path;

In the above example, the resource and file entity types are subtypes of the object, which itself is a subtype of the entity built-in type. They inherit the object-type attribute type ownership from it as well as its access:object role. However, while the resource subtype is abstract, the file subtype is not. Hence, we can create file entities, but not resource entities.

Further, the path attribute type will only be owned by the file entity type and any other entity types which subtype it or directly define ownership.

Overrides inherited attribute ownership

To override an inherited ownership use owns keyword with the new attribute type label, followed by the as keyword and the inherited attribute type label. For example:

define file sub resource, owns file-type as object-type;

The new attribute type that overrides inherited type is defined in the schema as subtype of the inherited attribute type. Hence, the inherited attribute type is abstract and has the same value type as the new attribute type. The example above in a schema would look like this:

define

path sub attribute, value string;

object-type sub attribute, abstract, value string;
file-type sub object-type, value string;

object sub entity,
    abstract,
    owns object-type;
resource sub object,
    abstract;
file sub resource,
    owns path,
    owns file-type as object-type;

In the above example, the file entity type owns the path and file-type attribute types, with the file-type attribute type overriding the inherited object-type attribute type.

Define relation types

Overview

Relation types are defined independently of other types but may subtype other relation types. They can be abstract. Their definition can include ownership of attribute types, roles other types play within them, and roles they can play in other relation types:

  • Owned attribute types are added with the owns keyword followed by the attribute type label.

  • Its own roles are added with the relates keyword followed by the role label. At least one role must be defined for any relation.

  • Roles it can play in other relations are added with the plays keyword followed by the relation type label and role.

Syntax

Relation types are defined in TypeQL with the following pattern:

<label> sub (relation | <relation type label>) [(, abstract)]
[(, owns <attribute type label> [@annotation])...]
(, relates <role label>)
[(, relates <role label>)...]
[(, plays <relation type label>:<role>)...];

Examples

Basic

The following statement creates an access relation that defines two roles:

  • object — played by instances of the object entity type or its subtypes (e.g.,file).

  • action — played by instances of the action entity type.

define

access sub relation,
    relates object,
    relates action;

Plays a role

In addition to defining its own roles played by other types, a relation type can play roles in other relation types.

define

access sub relation,
    relates object,
    relates action,
    plays permission:access,
    plays change-request:change;

In the above example, access relation type can play the access role in permission relation type and the change role in change-request relation type. Besides, an access relation type relates an object role (e.g., file) and a action role (e.g., read). Thus, a permission relation type relates the access (i.e., read + file) and a subject (e.g., person with full-name attribute Kevin Morrison).

Defines multiple roles

A relation can define multiple roles (from one to many).

define

change-request sub relation,
    relates change,
    relates requestee,
    relates requester;

Owns an attribute

A relation type can own zero, one, or many attribute types.

define

segregation-policy sub relation,
    relates action,
    plays segregation-violation:policy,
    owns name;

Abstract

Optionally, relation types can be defined as abstract, so they must be subtyped in order to create instances. An abstract relation type exists only so other relation types can inherit the attribute types it owns and the roles it defines and plays.

define

violation sub relation,
    abstract,
    owns name;

Subtypes another relation

A relation type can subtype another relation type by replacing the relation keyword with the label of another relation type. The subtype will inherit all owned attribute types and all roles related or played by the parent type.

define

membership sub relation,
    relates parent,
    relates member;

collection-membership sub membership;

In the example above, the collection-membership relation type inherits the parent and member roles defined in its parent type: membership.

Overrides a role

The labels of the inherited roles can be overridden to distinguish between the roles inherited by a relation subtype versus the roles defined by its parent type.

define

membership sub relation,
    relates parent,
    relates member;

collection-membership sub membership,
    relates collection as parent;

In the example above, the collection-membership relation type subtypes the membership relation type and overrides the inherited parent role as collection. The inherited member role is inherited as it is.

The two examples above can be run one after another. The second one will update the collection-membership type to override one of its inherited roles.

By overriding an inherited role, we implicitly prevent the sub-relation from relating the role that would otherwise be inherited.

Complex example

define

ownership sub relation,
    relates owned,
    relates owner;

group-ownership sub ownership,
    relates group as owned,
    owns ownership-type;

object-ownership sub ownership,
    relates object as owned,
    owns ownership-type;

access sub relation,
    relates object,
    relates action,
    plays change-request:change;

change-request sub relation,
    relates requester,
    relates requestee,
    relates change;

The example above defines one attribute type and five relation types:

  • ownership — subtypes the relation built-in type, and relates owned, and owner roles.

  • group-ownership — subtypes ownership relation type, relates group as owned, and owner (inherited).

  • object-ownership — subtypes ownership relation, relates object as owned, and owner (inherited).

  • access — subtypes the relation built-in type, relates object (e.g., file) and action (e.g., read), plays the role of change in change-request relation type.

  • change-request — subtypes the relation built-in type, relates requester, requestee and change.

Define attribute types

Overview

Attribute types are defined independently of other types, but may subtype a user-defined abstract attribute type. Any type can have an ownership over any attribute type.

Attributes owning an attributes feature will be deprecated in one of the upcoming versions and deleted in TypeDB version 3.0.

Attributes playing a role in a relation feature will be deprecated in one of the upcoming versions and deleted in TypeDB version 3.0.

Optionally, attribute types can:

  • Be abstract.

  • Own other attribute types (this will be deprecated).

  • Play roles in relations (this will be deprecated).

Syntax

Attribute types are defined in TypeQL with the following pattern:

<label> sub (attribute | <abstract attribute type label>) [(, abstract)], value <value type> [, regex "<regex-expression>"]

[(, owns <attribute type label> [@annotation])...]

[(, plays <relation type label>:<role>)...];

The following value types are supported:

  • long — a 64-bit signed integer

  • double — a double-precision floating point number, including a decimal point

  • string — enclosed in double " or single ' quotes

  • boolean — true or false

  • datetime — a date or date-time in the following formats:

    • yyyy-mm-dd

    • yyyy-mm-ddThh:mm

    • yyyy-mm-ddThh:mm:ss

    • yyyy-mm-ddThh:mm:ss.f

    • yyyy-mm-ddThh:mm:ss.ff

    • yyyy-mm-ddThh:mm:ss.fff

Examples

Basic

define

name sub attribute, value string;
email sub attribute, value string;
ownership-type sub attribute, value string;
review-date sub attribute, value datetime;
validity sub attribute, value boolean;

Subtypes another attribute type

An attribute type can subtype another attribute type if its abstract. This is useful when the possible values of an attribute type can be categorized, and applications can benefit from querying entities and relations not only by a value of an attribute but also by a label of attribute type.

An attribute type can only subtype an abstract attribute type. However, the subtype of an attribute type can itself be abstract. Further, an attribute subtype must have the same value type as its parent attribute type. Note, the value type of an attribute subtype can be omitted in its definition. It will be inherited from its parent attribute type.

define

id sub attribute, abstract, value string;
email sub id, value string;
name sub id, value string;
path sub id, value string;
number sub id, value string;

The above example creates an attribute type, id. However, because different entities can be identified by different information, the id type is subtyped by email, name, path, and number types — making it possible to query users by email, business units by name, files by path and records by number.

Unique attribute annotation

Use the @unique keyword to add uniqueness constraint to the ownership of an attribute. This prevents two instances of the same type from owning the same attribute instance (with the same value).

define

object-type sub attribute, value string;

object sub entity,
    abstract,
    owns object-type @unique;

The @unique annotation allows us to declare that an attribute instance may not be owned more than once by the owner type. But no cardinality restriction is generated from @unique annotation.

Unique annotation can be inherited, even using override of an ownership.

See example
define

organisation sub entity,
    abstract,
    owns organisation-id @unique;

organisation-id sub attribute, abstract, value string;

non-profit sub organisation,
    owns nonprofit-id as organisation-id;

nonprofit-id sub organisation-id, value string;

In this example non-profit owns nonprofit-id with unique constraint. It’s inherited from organizatrion-id.

Key attribute annotation

Use the @key keyword to set the owned attribute as key. That limits the cardinality to exactly one and adds uniqueness constraint. Hence, the instance of the type with attribute ownership with @key annotation will have exactly one (no more and no less) key attribute instance, and it will be unique for all instances of the same type.

define

object-type sub attribute, value string;

object sub entity,
    abstract,
    owns object-type @key;

An ownership can’t have both @unique and @key at the same time.

Regular expressions

The values of an attribute type can be restricted using Java regular expressions. For example, to constrain it to a set of options.

define

visibility sub attribute, value string, regex "^(public|private|closed)$";

The above example defines an attribute type: visibility. It is intended for user groups, and specifies a regex to restrict its values to public, private and closed.

Owns other attribute types

While it is more common for entity and relation types to own attributes, attribute types can also own (other) attribute types.

Attributes owning an attributes feature will be deprecated in one of the upcoming versions and deleted in TypeDB version 3.0.

define

symlink sub attribute, value string;

filepath sub attribute,
    value string,
    owns symlink;

The above example creates an attribute type filepath, intended for files. It is assumed there can be multiple copies of a file, each with its own filepath — and symlinks can be created that point to these filepaths. Thus, the filepath attribute type (and NOT the file entity type) owns the symlink attribute type.

Plays a role

While it is more common for the roles in relations to be played by entities or other relations, they can also be played by attributes.

Attributes playing a role in a relation feature will be deprecated in one of the upcoming versions and deleted in TypeDB version 3.0.

define

credential sub attribute,
    value string,
    plays change-request:change;

The above example creates the credential attribute type, and specifies it can play the role of change in the change-request relation type. While change-requests were intended to manage access changes, they can now be used to manage credential changes as well.

Provide Feedback