Lesson 5.2: Defining type hierarchies
Defining entity type hierarchies
To define type hierarchies, we use the same Define queries as when defining individual types. The following query shows how we can define a hierarchy of book types.
define
book sub entity;
paperback sub book;
hardback sub book;
ebook sub book;
Run and commit
To make one type a subtype of another, we simply replace the root type with the intended supertype in the sub
statement that defines that type. When defined in a type hierarchy, subtypes will inherit all the owned attributes and played roles of their supertypes. Consider the following Define query where we assign a number of ownerships and roles to book
.
define
book owns title,
owns page-count,
owns genre,
owns price,
plays publishing:published,
plays order-line:item,
plays rating:rated,
plays promotion-inclusion:item;
title sub attribute, value string;
page-count sub attribute, value long;
genre sub attribute, value string;
Run and commit
paperback
, hardback
, and ebook
will inherit all of these capabilities from book
without having to explicitly declare this to be the case. This will even apply to new subtypes of book
defined in the future! If we want capabilities that only apply to certain subtypes, then we can declare them directly on those types only.
define
paperback owns stock;
hardback owns stock;
stock sub attribute, value long;
Run and commit
This allows paperbacks and hardbacks to have stock levels, but not ebooks.
Write a query to define an entity type company
with two subtypes publisher
and courier
, and define the following capabilities:
-
All companies can own attributes of the existing type
name
. -
Publishers can also play the existing role
publishing:publisher
. -
Couriers can also play the existing role
delivery:deliverer
.
Sample solution
define
company sub entity,
owns name;
publisher sub company,
plays publishing:publisher;
courier sub company,
plays delivery:deliverer;
Run and commit
Defining abstract types
Abstract types cannot be directly instantiated, much like abstract classes in OOP. An abstract type can only be instantiated via its subtypes. To make a type abstract, we use an abstract
statement. The following Define query makes the existing types book
and company
abstract.
define
book abstract;
company abstract;
Run and commit
Now book
can only be instantiated through paperback
, hardback
, or ebook
, and company
can only be instantiated through publisher
or courier
.
Write a query to define an abstract entity type place
with three subtypes, country
, state
, and city
, and define the following capabilities:
-
All places can own attributes of the existing type
name
. -
All places can play the existing role
locating:location
. -
All places can play the existing role
locating:located
.
Sample solution
define
place sub entity,
abstract,
owns name,
plays locating:location,
plays locating:located;
country sub place;
state sub place;
city sub place;
Run and commit
Defining relation type hierarchies
Relation type hierarchies are defined in the same way as entity type hierarchies. When defined in a hierarchy, subtypes of a relation type inherit all its roles. The following Define query defines a relation type contribution
and three subtypes.
define
contribution sub relation,
relates work,
relates contributor;
authoring sub contribution;
editing sub contribution;
illustrating sub contribution;
Run and commit
Here, authoring
, editing
, and illustrating
inherit the roles work
and contributor
from contribution
. This would allow instantiation of the following relation types:
-
contribution
with rolescontribution:work
andcontribution:contributor
-
authoring
with rolescontribution:work
andcontribution:contributor
-
editing
with rolescontribution:work
andcontribution:contributor
-
illustrating
with rolescontribution:work
andcontribution:contributor
We could also define new roles on a specific subtype using the relates
keyword on that subtype, in the same way we can define capabilities on specific subtypes using the owns
and plays
keywords on them. A new role can be made to override an existing role of a parent relation using the as
keyword. In the following query, the roles author
, editor
, and illustrator
override the inherited contributor
role.
define
authoring relates author as contributor;
editing relates editor as contributor;
illustrating relates illustrator as contributor;
Run and commit
This would instead allow instantiation of the following relation types:
-
contribution
with rolescontribution:work
andcontribution:contributor
-
authoring
with rolescontribution:work
andauthoring:author
-
editing
with rolescontribution:work
andediting:editor
-
illustrating
with rolescontribution:work
andillustrating:illustrator
Write a query to define the following new roleplayers:
-
The existing type
book
playingcontribution:work
. -
The existing type
contributor
playingcontribution:contributor
and its three subtypes.
Sample solution
define
book plays contribution:work;
contributor plays contribution:contributor;
contributor plays authoring:author;
contributor plays editing:editor;
contributor plays illustrating:illustrator;
Run and commit
With this schema, it is no longer possible to instantiate authoring
, editing
, and illustrating
with the contribution:contributor
role. However, it is still possible to query them via that role, as the overriding roles authoring:author
, editing:editor
, and illustrating:illustrator
are considered to be its subtypes. The match
clause of the following Fetch query would match instances of contributor
, authoring
, editing
, and illustrating
, and contributor names would be returned for all contributor roles.
match
(work: $book, contributor: $contributor) isa contribution;
fetch
$book: title;
$contributor: name;
Modify the above Fetch query to retrieve only author names instead of all contributor names.
Sample solution
match
(work: $book, author: $contributor) isa authoring;
fetch
$book: title;
$contributor: name;
Now instead modify it to retrieve only names of contributors who have made miscellaneous contributions (i.e. those in direct instances of contribution
and not its subtypes).
Sample solution
match
(work: $book, contributor: $contributor) isa! contribution;
fetch
$book: title;
$contributor: name;
Notice we have used the isa!
keyword.
Defining attribute type hierarchies
In order to define subtypes of a given attribute type, the supertype must be abstract. This is to prevent potential ambiguities in the interpretation of queries. In the following Define query, we define an attribute type isbn
with two subtypes.
define
isbn sub attribute,
abstract,
value string;
isbn-13 sub isbn;
isbn-10 sub isbn;
Run and commit
Value types of attribute types are inherited by their subtypes. In this case, isbn-13
and isbn-10
inherit the string
value type.
In TypeDB 2.x, all attribute types in a hierarchy must have the same value type, as specified in the definition of the supertype. In TypeDB 3.0, it will be possible to create abstract attribute types that do not have a value type, and to assign different value types to their subtypes. To learn more about this and other powerful new features, see the TypeDB 3.0 roadmap. |