# Lesson 3.4: Fetching schema types

## [](#_variablizing_types)Variablizing types

So far, we’ve seen that the output of Fetch queries includes a `type` field for every retrieved data instance and their attributes. However, there are also types involved in the query that are not returned. Consider this polymorphic query from [Lesson 3.2](../3.2-fetching-polymorphic-data/index.md), where we asked for books that a user had interacted with in some way on the web store.

```typeql
match
$user isa user, has id "u0008";
$book isa book;
action-execution (executor: $user, action: $action);
$rel links ($book, $action);
fetch {
  "isbn": [$book.isbn],
  "title": $book.title
};
```

```json
{
  "title": "Electron Backscatter Diffraction in Materials Science",
  "isbn": [
    "9780387881355",
    "0387881352"
  ]
}
{
  "isbn": [
    "9780060929794",
    "0060929790"
  ],
  "title": "One Hundred Years of Solitude"
}
{
  "isbn": [
    "9780008627843",
    "0008627843"
  ],
  "title": "The Hobbit"
}
{
  "isbn": [
    "9780500026557",
    "0500026556"
  ],
  "title": "Hokusai's Fuji"
}
{
  "isbn": [
    "9780008627843",
    "0008627843"
  ],
  "title": "The Hobbit"
}
{
  "title": "Classical Mythology",
  "isbn": [
    "9780195153446",
    "0195153448"
  ]
}
```

While the results list the books, they do not tell us anything about the nature of the user’s interaction with them. We can easily retrieve this information by adding a couple of additional lines to the query.

```typeql
match
$user isa user, has id "u0008";
$book isa book;
action-execution (executor: $user, action: $action);
$rel links ($book, $action);
$action isa! $action-type;   # new line
fetch {
  "isbn": [ $book.isbn ],
  "title": $book.title,
  "action-type": $action-type
};
```

If we run this new query, we now also retrieve the type of `$action`!

```json
{
  "title": "Electron Backscatter Diffraction in Materials Science",
  "isbn": [
    "9780387881355",
    "0387881352"
  ],
  "action-type": {
    "kind": "entity",
    "label": "order"
  }
}
{
  "isbn": [
    "9780060929794",
    "0060929790"
  ],
  "title": "One Hundred Years of Solitude",
  "action-type": {
    "kind": "entity",
    "label": "order"
  }
}
{
  "title": "The Hobbit",
  "action-type": {
    "kind": "entity",
    "label": "order"
  },
  "isbn": [
    "9780008627843",
    "0008627843"
  ]
}
{
  "action-type": {
    "kind": "entity",
    "label": "review"
  },
  "title": "Hokusai's Fuji",
  "isbn": [
    "9780500026557",
    "0500026556"
  ]
}
{
  "isbn": [
    "9780008627843",
    "0008627843"
  ],
  "action-type": {
    "kind": "entity",
    "label": "review"
  },
  "title": "The Hobbit"
}
{
  "action-type": {
    "kind": "entity",
    "label": "review"
  },
  "isbn": [
    "9780195153446",
    "0195153448"
  ],
  "title": "Classical Mythology"
}
```

We have been able to retrieve this information by introducing a new `$action-type` variable in an additional statement.

```typeql
$action isa! $action-type;
```

Here we’ve used the `isa!` keyword which, like the `isa` keyword, is used to specify the type of a variable. However, while the `isa` keyword simultaneously matches a type and all of its subtypes, the `isa!` keyword matches a type _exactly_, without using inheritance polymorphism. We will see more examples of this throughout this lesson and later in the course.

Previous `isa` statements we’ve used have used a variable as the [subject](https://en.wikipedia.org/wiki/Subject%E2%80%93verb%E2%80%93object_word_order) of the statement and a **type label** as the [object](https://en.wikipedia.org/wiki/Subject%E2%80%93verb%E2%80%93object_word_order), but in this `isa!` statement we’ve also used a variable, `$action-type`, as the object. The `isa` and `isa!` keywords must always take a data instance as the subject and a type as the object, which means that the variable `$action-type` represents a type rather than a data instance! Here we’ve made use of **type variablization**, one of TypeQL’s most powerful features.

Exercise

Modify the above query to also retrieve the type of the relation between `$book` and `$action`.

Sample solution

```typeql
match
$user isa user, has id "u0008";
$book isa book;
action-execution (executor: $user, action: $action);
$rel isa! $relation-type, links ($book, $action);
$action isa! $action-type;
fetch {
  "isbn": [$book.isbn],
  "title": $book.title,
  "action-type": $action-type,
  "relation-type": $relation-type,
};
```

## [](#_querying_type_hierarchies)Querying type hierarchies

In addition to using type variablization to retrieve the types of data instances returned in our queries, we can also query the schema directly, without having to involve data instances at all. For example, the following query retrieves the subtypes of `book`.

```typeql
match $book_type sub book;
fetch { "book-type" : $book_type };
```

```json
{ "book-type": { "kind": "entity", "label": "book" } }
{ "book-type": { "kind": "entity", "label": "hardback" } }
{ "book-type": { "kind": "entity", "label": "paperback" } }
{ "book-type": { "kind": "entity", "label": "ebook" } }
```

These are indeed the subtypes of `book`. We also retrieve the type `book` itself because it is trivially its own subtype. For this query, we’ve used the `sub` keyword, which matches subtypes of a given supertype. Unlike the `isa` keyword, both the subject and the object of `sub` statements must be **types**. In this case, we’ve variablized the subject of the `sub` statement to query subtypes, but we could also variablize the object to query supertypes. In the following example, we query the supertypes of `publisher`.

```typeql
match publisher sub $supertype;
fetch { "supertype" : $supertype };
```

```json
{ "supertype": { "kind": "entity", "label": "company" } }
{ "supertype": { "kind": "entity", "label": "publisher" } }
```

If we would like to retrieve only _direct_ subtypes or supertypes of a type, we can instead use the `sub!` keyword, similar to the `isa!` keyword!

```typeql
match publisher sub! $supertype;
fetch { "supertype" : $supertype };
```

```json
{ "supertype": { "kind": "entity", "label": "company" } }
```

Exercise

Write a query to retrieve the direct supertype of `city`.

Sample solution

```typeql
match
city sub! $supertype;
fetch {
  "supertype": $supertype,
};
```

Now write a query to determine the other types that share that direct supertype.

Hint

The query should retrieve the direct subtypes of `place`.

Sample solution

```typeql
match $place_type sub! place;
fetch { "place-type" : $place_type };
```

## [](#_querying_ownership_interfaces)Querying ownership interfaces

In addition to querying the type hierarchies defined in the schema, we can also query the defined **ownership** and **role** interfaces. In the following query, we retrieve the list of attribute types that the entity type `promotion` owns.

```typeql
match promotion owns $attribute;
fetch { "attribute-type": $attribute };
```

```json
{ "attribute-type": { "kind": "attribute", "label": "code", "valueType": "string" } }
{ "attribute-type": { "kind": "attribute", "label": "name", "valueType": "string" } }
{ "attribute-type": { "kind": "attribute", "label": "start-timestamp", "valueType": "datetime" } }
{ "attribute-type": { "kind": "attribute", "label": "end-timestamp", "valueType": "datetime" } }
```

To query ownerships, we use the `owns` keyword. As with the `sub` keyword, we can invert the intent of the statement by variablizing the object instead of the subject. We do this in the next query to retrieve the list of types that own the attribute type `isbn-13`.

```typeql
match $type owns isbn-13;
fetch { "type" : $type };
```

```json
{ "type": { "kind": "entity", "label": "book" } }
{ "type": { "kind": "entity", "label": "hardback" } }
{ "type": { "kind": "entity", "label": "paperback" } }
{ "type": { "kind": "entity", "label": "ebook" } }
```

Exercise

Write a query to retrieve the list of types that own `name`.

Sample solution

```typeql
#!test[schema]
#{{
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

define

entity book @abstract,
    owns isbn @card(0..2),
    owns isbn-13 @key,
    owns isbn-10 @unique,
    owns title,
    owns page-count,
    owns genre @card(0..),
    owns price,
    plays contribution:work,
    plays publishing:published,
    plays promotion-inclusion:item,
    plays order-line:item,
    plays rating:rated,
    plays recommendation:recommended;

entity hardback sub book,
    owns stock;

entity paperback sub book,
    owns stock;

entity ebook sub book;

entity contributor,
    owns name,
    plays contribution:contributor,
    plays authoring:author,
    plays editing:editor,
    plays illustrating:illustrator;

entity company @abstract,
    owns name;

entity publisher sub company,
    plays publishing:publisher;

entity courier sub company,
    plays delivery:deliverer;

entity publication,
    owns year,
    plays publishing:publication,
    plays locating:located;

entity user,
    owns id @key,
    owns name,
    owns birth-date,
    plays action-execution:executor,
    plays locating:located,
    plays recommendation:recipient;

entity order,
    owns id @key,
    owns status,
    plays order-line:order,
    plays action-execution:action,
    plays delivery:delivered;

entity promotion,
    owns code @key,
    owns name,
    owns start-timestamp,
    owns end-timestamp,
    plays promotion-inclusion:promotion;

entity review,
    owns id @key,
    owns score,
    owns verified,
    plays rating:review,
    plays action-execution:action;

entity login,
    owns success,
    plays action-execution:action;

entity address,
    owns street,
    plays delivery:destination,
    plays locating:located;

entity place @abstract,
    owns name,
    plays locating:located,
    plays locating:location;

entity city sub place;

entity state sub place;

entity country sub place;

relation contribution,
    relates contributor,
    relates work;

relation authoring sub contribution,
    relates author as contributor;

relation editing sub contribution,
    relates editor as contributor;

relation illustrating sub contribution,
    relates illustrator as contributor;

relation publishing,
    relates publisher,
    relates published,
    relates publication;

relation promotion-inclusion,
    relates promotion,
    relates item,
    owns discount;

relation order-line,
    relates order,
    relates item,
    owns quantity,
    owns price;

relation rating,
    relates review,
    relates rated;

relation action-execution,
    relates action,
    relates executor,
    owns timestamp;

relation delivery,
    relates deliverer,
    relates delivered,
    relates destination;

relation locating,
    relates located,
    relates location;

relation recommendation,
    relates recommended,
    relates recipient;

attribute isbn @abstract, value string;
attribute isbn-13 sub isbn;
attribute isbn-10 sub isbn;
attribute title, value string;
attribute page-count, value integer;
attribute genre, value string;
attribute stock, value integer;
attribute price, value double;
attribute discount, value double;
attribute id, value string;
attribute code, value string;
attribute name, value string;
attribute birth-date, value datetime;
attribute street, value string;
attribute year, value integer;
attribute quantity, value integer;
attribute score, value integer;
attribute verified, value boolean;
attribute timestamp, value datetime;
attribute start-timestamp, value datetime;
attribute end-timestamp, value datetime;
attribute status, value string @regex("^(paid|dispatched|delivered|returned|canceled)$");
attribute success, value boolean;

# TODO: Change to check
fun is_review_verified_by_purchase($review: review) -> { order }:
  match
    ($review, $product) isa rating;
    ($order, $product) isa order-line;
    ($user, $review) isa action-execution, has timestamp $review-time;
    ($user, $order) isa action-execution, has timestamp $order-time;
    $review-time > $order-time;
  return { $order };

fun book_recommendations_for($user: user) -> {book}:
  match
    $new-book isa book;
    {
        let $new-book in book_recommendations_by_author($user);
    } or {
        let $new-book in book_recommendations_by_genre($user);
    };
  return { $new-book };

fun book_recommendations_by_genre($user: user) -> { book }:
match
    $user isa user;
    $liked-book isa book;
    {
        ($user, $order-for-liked) isa action-execution;
        ($order-for-liked, $liked-book) isa order-line;
    } or {
        ($user, $review-for-liked) isa action-execution;
        ($review-for-liked, $liked-book) isa rating;
        $review-for-liked has score >= 7;
    };
    $new-book isa book;
    not { {
        ($user, $order-for-new) isa action-execution;
        ($order-for-new, $new-book) isa order-line;
    } or {
        ($user, $review-for-new) isa action-execution;
        ($review-for-new, $new-book) isa rating;
    }; };
    $liked-book has genre $shared-genre;
    $new-book has genre $shared-genre;
    not { {
        $shared-genre == "fiction";
    } or {
        $shared-genre == "nonfiction";
    }; };
  return { $new-book };

fun book_recommendations_by_author($user: user) -> { book }:
  match
    $user isa user;
    $liked-book isa book;
    {
        ($user, $order-for-liked) isa action-execution;
        ($order-for-liked, $liked-book) isa order-line;
    } or {
        ($user, $review-for-liked) isa action-execution;
        ($review-for-liked, $liked-book) isa rating;
        $review-for-liked has score >= 7;
    };
    $new-book isa book;
    not { {
        ($user, $order-for-new) isa action-execution;
        ($order-for-new, $new-book) isa order-line;
    } or {
        ($user, $review-for-new) isa action-execution;
        ($review-for-new, $new-book) isa rating;
    }; };
    ($liked-book, $shared-author) isa authoring;
    ($new-book, $shared-author) isa authoring;
  return { $new-book };

fun order_line_best_price($line: order-line) -> { double }:
  match
    ($order) isa action-execution, has timestamp $order-time;
    $line isa order-line, links ($order, $item);
    $item has price $retail-price;
    let $time_value = $order-time;
    let $best-discount = best_discount_for_item($item, $time_value);
    let $discounted-price = round(100 * $retail-price * (1 - $best-discount)) / 100;
    $line has quantity $quantity;
    let $line-total = $quantity * $discounted-price;
  return { $line-total };

fun best_discount_for_item($item: book, $order-time: datetime) -> double:
  match
    {
        $inclusion isa promotion-inclusion,
            links ($promotion, $item),
            has discount $discount-attr;
        $promotion has start-timestamp <= $order-time,
            has end-timestamp >= $order-time;
        let $discount = $discount-attr;
    } or {
        let $discount = 0.0; # default
    };
return max($discount);

fun transitive_places($place: place) -> { place }:
  match
    {
      locating (located: $place, location: $parent);
    } or {
      locating (located: $place, location: $middle);
      let $parent in transitive_places($middle);
    };
  return { $parent };
#}}
#!test[read]
match
$type owns name;
fetch {
  "owner": $type,
};
```

## [](#_querying_role_interfaces)Querying role interfaces

Ownerships of attribute types are queried using a single keyword `owns`, but roles in relations must be queried using two different keywords: `relates` and `plays`. We will see how they work in the next few examples. To begin with, we will use the `relates` keyword to retrieve the list of roles in the `locating` relation.

```typeql
match locating relates $role;
fetch { "role" : $role };
```

```json
{ "role": { "kind": "relation:role", "label": "locating:located" } }
{ "role": { "kind": "relation:role", "label": "locating:location" } }
```

When not part of a relation tuple, roles are described by **role labels**, comprising two terms specified by a `:` delimiter. The first term is the name of the relation that the role is part of, and the second is the name of the role in the scope of the relation. This is because multiple relations are permitted to use the same role names. So, for instance, the label `locating:located` represents the `located` role of the `locating` relation type.

To query for types that play a role using the `plays` keyword, we must use the same notation.

```typeql
match $type plays locating:located;
fetch { "type" : $type };
```

```json
{ "type": { "kind": "entity", "label": "publication" } }
{ "type": { "kind": "entity", "label": "user" } }
{ "type": { "kind": "entity", "label": "address" } }
{ "type": { "kind": "entity", "label": "place" } }
{ "type": { "kind": "entity", "label": "city" } }
{ "type": { "kind": "entity", "label": "state" } }
{ "type": { "kind": "entity", "label": "country" } }
```

Here we have queried the types that play the `located` role in `locating`. As with the `sub` and `owns` keywords, we can also invert the intent of `plays` statements[1](#_footnotes). In the next query, we do so in order to retrieve the list of roles that `book` plays.

```typeql
match book plays $role;
fetch { "role" : $role };
```

```json
{ "role": { "kind": "relation:role", "label": "contribution:work" } }
{ "role": { "kind": "relation:role", "label": "publishing:published" } }
{ "role": { "kind": "relation:role", "label": "promotion-inclusion:item" } }
{ "role": { "kind": "relation:role", "label": "order-line:item" } }
{ "role": { "kind": "relation:role", "label": "rating:rated" } }
{ "role": { "kind": "relation:role", "label": "recommendation:recommended" } }
```

Exercise

Write a query to retrieve the list of roles in the `delivery` relation.

Sample solution

```typeql
match
delivery relates $role;
fetch {
  "role": $role,
};
```

Now modify the query to also retrieve the types that play those roles.

Sample solution

```typeql
match
delivery relates $role;
$type plays $role;
fetch {
  "role": $role,
  "player": $type,
};
```

## [](#_footnotes)Footnotes

1.  [^](#_footnote_1) The astute reader will notice that we have used this "inversion" technique for `sub`, `owns`, and `plays` statements, but not for `relates` statements. TypeQL is flexible enough that we could certainly do so if we wanted, but it is not particularly useful to and there are certain practical considerations that make the syntax tricky to use correctly. While learning the basics of TypeQL, it is best to avoid `relates` statements of this kind.
    

[Using functions](../3.3-using-functions/index.md) [Query validation](../3.5-query-validation/index.md)

[Edit on GitHub](https://github.com/typedb/typedb-docs/edit/3.x-development/academy/modules/ROOT/pages/3-reading-data/3.4-fetching-schema-types.adoc) Edit this page on GitHub.