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
entity book;
entity paperback, sub book;
entity hardback, sub book;
entity ebook, sub book;

→ 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
entity book owns title,
  owns page-count,
  owns genre,
  owns price,
  plays publishing:published,
  plays order-line:item,
  plays rating:rated,
  plays promotion-inclusion:item;
attribute title, value string;
attribute page-count, value integer;
attribute genre, value string;

→ 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
entity paperback, owns stock;
entity hardback, owns stock;
attribute stock, value integer;

→ Commit

This allows paperbacks and hardbacks to have stock levels, but not ebooks.

Exercise

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
entity company,
  owns name;
entity publisher,
  sub company,
  plays publishing:publisher;
entity courier,
  sub company,
  plays delivery:deliverer;

→ 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 annotation. The following Define query makes the existing types book and company abstract.

define
entity book @abstract;
entity company @abstract;

→ Commit

Now book can only be instantiated through paperback, hardback, or ebook, and company can only be instantiated through publisher or courier.

Exercise

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
entity place @abstract,
    owns name,
    plays locating:location,
    plays locating:located;
entity country, sub place;
entity state, sub place;
entity city, sub place;

→ 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
relation contribution,
    relates work,
    relates contributor;
relation authoring, sub contribution;
relation editing, sub contribution;
relation illustrating, sub contribution;

→ 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 roles contribution:work and contribution:contributor

  • authoring with roles contribution:work and contribution:contributor

  • editing with roles contribution:work and contribution:contributor

  • illustrating with roles contribution:work and contribution: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
relation authoring, relates author as contributor;
relation editing, relates editor as contributor;
relation illustrating, relates illustrator as contributor;

→ Commit

This would instead allow instantiation of the following relation types:

  • contribution with roles contribution:work and contribution:contributor

  • authoring with roles contribution:work and authoring:author

  • editing with roles contribution:work and editing:editor

  • illustrating with roles contribution:work and illustrating:illustrator

An overridden role defines a subtype of the role, and makes the role abstract in the sub-relation. In other words, you get a new role that can be used in place of the parent role, and you cannot use the parent role in the sub relation.

Exercise

Write a query to define the following new role players:

  • The existing type book playing contribution:work.

  • The existing type contributor playing contribution:contributor and its three subtypes.

Sample solution
define
entity book, plays contribution:work;
entity contributor, plays contribution:contributor;
entity contributor, plays authoring:author;
entity contributor, plays editing:editor;
entity contributor, plays illustrating:illustrator;

→ 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
contribution (work: $book, contributor: $contributor);
fetch {
  "title": $book.title,
  "name": $contributor.name
};
Exercise

Modify the above Fetch query to retrieve only author names instead of all contributor names.

Sample solution
match
authoring (work: $book, author: $contributor);
fetch {
  "title": $book.title,
  "name": $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
$rel isa! contribution, links (work: $book, contributor: $contributor);
fetch {
  "title": $book.title,
  "name": $contributor.name,
};

Notice we have used the isa! keyword.

Defining attribute type hierarchies

Attribute types can also be defined hierarchically.

define
attribute isbn value string;
attribute isbn-13 sub isbn;
attribute isbn-10 sub isbn;

→ Commit

Value types of attribute types are inherited by their subtypes. In this case, isbn-13 and isbn-10 inherit the string value type.

If you don’t want omit a value type in the super type, to allow different value types in sub types, you must make the attribute type abstract:

define
attribute id @abstract;
attribute email-id, sub id, value string;
attribute numerical-id, sub id, value integer;