Officially out now: The TypeDB 3.0 Roadmap

Lesson 3.1: Fetching simple data

The Fetch query

To begin with, we’ll use the following Fetch query to retrieve all data relating to paperback book entities in the database.

match
$book isa paperback;
fetch
$book: attribute;

studio run Run

This is equivalent to the following SQL query.

SELECT *
FROM paperback;

This TypeQL query comprises two clauses: a match clause and a fetch clause.

The match clause is equivalent to a selection in SQL. It identifies what entity types (tables) we wish to retrieve data from. It can also specify further constraints that the retrieved data must meet, as we’ll see shortly. The fetch clause is equivalent to a projection in SQL. It specifies what attributes (columns) we would like to retrieve for the entities (rows) identified. In this case, we’ve chosen to retrieve all attributes using the attribute keyword.

Using a data session and read transaction, run studio run this query. You should see the following result.

{
    "book": {
        "attribute": [
            { "value": 1, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 240, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9780393045215", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 21.600000381469727, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "history", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "nonfiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "0393045218", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "The Mummies of Urumchi", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 12, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 820, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9780195153446", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 34.97999954223633, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "history", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "nonfiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "0195153448", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "Classical Mythology", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 16, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 281, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9780446310789", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 21.639999389648438, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "fiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "historical fiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "0446310786", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "To Kill a Mockingbird", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 8, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 260, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9798691153570", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 11.989999771118164, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "business", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "nonfiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "Business Secrets of The Pharoahs", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 11, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 416, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9780500026557", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 24.469999313354492, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "art", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "nonfiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "0500026556", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "Hokusai's Fuji", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 15, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 295, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9780553212150", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 17.989999771118164, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "fiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "historical fiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "055321215X", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "Pride and Prejudice", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 15, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 199, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9781489962287", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 47.16999816894531, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "nonfiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "technology", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "148996228X", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "Interpretation of Electron Diffraction Patterns", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 4, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 160, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9781859840665", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 14.520000457763672, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "biography", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "nonfiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "1859840663", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "The Motorcycle Diaries: A Journey Around South America", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 18, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 352, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9780500291221", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 12.050000190734863, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "history", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "nonfiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "0500291225", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "Great Discoveries in Medicine", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 4, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 458, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9780060929794", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 6.119999885559082, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "fiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "historical fiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "0060929790", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "One Hundred Years of Solitude", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "attribute": [
            { "value": 9, "value_type": "long", "type": { "label": "stock", "root": "attribute" } },
            { "value": 215, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } },
            { "value": "9780671461492", "value_type": "string", "type": { "label": "isbn-13", "root": "attribute" } },
            { "value": 91.47000122070312, "value_type": "double", "type": { "label": "price", "root": "attribute" } },
            { "value": "fiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "science fiction", "value_type": "string", "type": { "label": "genre", "root": "attribute" } },
            { "value": "0671461494", "value_type": "string", "type": { "label": "isbn-10", "root": "attribute" } },
            { "value": "The Hitchhiker's Guide to the Galaxy", "value_type": "string", "type": { "label": "title", "root": "attribute" } }
        ],
        "type": { "label": "paperback", "root": "entity" }
    }
}

All Fetch queries return results in JSON format.

In the match clause of the query, we declared a single variable: $book. In TypeQL, variables are declared using a $ prefix. We also specified the type of $book to be paperback using the isa keyword. We can see that each JSON object returned represents an instance of paperback and contains a list of all that book’s attributes.

Exercise

Write a query to retrieve all the attributes of user entities. It should be equivalent to the following SQL query.

SELECT *
FROM users;

N.b. in the SQL query we need to use users rather than user as the latter is a reserved keyword in SQL. In general, singular nouns are preferred for entity type names.

Sample solution
match
$user isa user;
fetch
$user: attribute;

studio run Run

Projections

Looking at the list of each book’s attributes in the previous result, we can see that we’ve retrieved attributes of six types: isbn-13, isbn-10, title, genre, page-count, price, and stock. By modifying the fetch clause, we can choose to retrieve only specific attributes.

match
$book isa paperback;
fetch
$book: title, page-count;

studio run Run

If we run this query, we see the following result.

{
    "book": {
        "page-count": [ { "value": 352, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "Great Discoveries in Medicine", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 416, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "Hokusai's Fuji", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 199, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "Interpretation of Electron Diffraction Patterns", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 295, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "Pride and Prejudice", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 281, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "To Kill a Mockingbird", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 260, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "Business Secrets of The Pharoahs", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 240, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "The Mummies of Urumchi", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 820, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "Classical Mythology", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 458, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "One Hundred Years of Solitude", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 215, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "The Hitchhiker's Guide to the Galaxy", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 160, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "The Motorcycle Diaries: A Journey Around South America", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}

This time, only the titles and page counts of each book have been retrieved. Now this TypeQL query is equivalent to the following SQL query.

SELECT title, page_count
FROM paperback;
Exercise

Write a query to instead retrieve the isbn-13, price, and stock attributes of paperbacks.

Sample solution
match
$book isa paperback;
fetch
$book: isbn-13, price, stock;

studio run Run

Selections

In the next query, we’ll add a constraint to the match clause, specifying that we want the details for a specific book with ISBN-13 "9780446310789".

match
$book isa paperback, has isbn-13 "9780446310789";
fetch
$book: title, page-count;

studio run Run

{
    "book": {
        "page-count": [ { "value": 281, "value_type": "long", "type": { "label": "page-count", "root": "attribute" } } ],
        "title": [ { "value": "To Kill a Mockingbird", "value_type": "string", "type": { "label": "title", "root": "attribute" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}

We can see from the result that we now only retrieve the data for the specific book we’re interested in. To do so we’ve used the has keyword, which is used to specify the value of an entity’s attribute, in this case the $book entity. In SQL, this query would be expressed in the following way.

SELECT title, page_count
FROM paperback
WHERE isbn_13 = '9780446310789';

Because TypeQL is composable, we could alternatively construct this query in the following equivalent way.

match
$book isa paperback;
$book has isbn-13 "9780446310789";
fetch
$book: title, page-count;

studio run Run

Try running these two queries. You should get the same results.

In the first version, we used a single composite statement in the match clause, whereas in the second version, we instead used two simple statements. If simple statements concern the same variable (in this case $book), we can always concatenate them using commas to form a composite statement, and vice versa.

Exercise

Write a query to retrieve the page-count and price attributes of the paperback with title "Great Discoveries in Medicine". Write the query once using a composite statement, and again using simple statements.

Sample solution

With a composite statement:

match
$book isa paperback, has title "Great Discoveries in Medicine";
fetch
$book: page-count, price;

studio run Run

With simple statements:

match
$book isa paperback;
$book has title "Great Discoveries in Medicine";
fetch
$book: page-count, price;

studio run Run

Entities and relations

There are two types of data objects in TypeDB: entities and relations. Entity types like book are used to represent application classes, while relation types are used to represent references between them. Relations must be instantiated with reference to one or more roleplayers, which play defined roles.

In order to represent a relation in TypeQL, we use tuple syntax of the following form.

$line (order: $order, item: $book) isa order-line;

This statement signifies that:

  • $line is a relation of type order-line.

  • $order plays the role of order in $line.

  • $book plays the role of item in $line.

Here we are using order-line relations to represent the references that order entities make to book entities. In the following Fetch query, we retrieve the IDs of orders that include To Kill a Mockingbird and the quantity ordered.

match
$book isa paperback, has isbn-13 "9780446310789";
$line (order: $order, item: $book) isa order-line;
fetch
$order: id;
$line: quantity;

studio run Run

{
    "line": {
        "quantity": [ { "value": 1, "value_type": "long", "type": { "label": "quantity", "root": "attribute" } } ],
        "type": { "label": "order-line", "root": "relation" }
    },
    "order": {
        "id": [ { "value": "o0016", "value_type": "string", "type": { "label": "id", "root": "attribute" } } ],
        "type": { "label": "order", "root": "entity" }
    }
}
{
    "line": {
        "quantity": [ { "value": 1, "value_type": "long", "type": { "label": "quantity", "root": "attribute" } } ],
        "type": { "label": "order-line", "root": "relation" }
    },
    "order": {
        "id": [ { "value": "o0032", "value_type": "string", "type": { "label": "id", "root": "attribute" } } ],
        "type": { "label": "order", "root": "entity" }
    }
}
{
    "line": {
        "quantity": [ { "value": 2, "value_type": "long", "type": { "label": "quantity", "root": "attribute" } } ],
        "type": { "label": "order-line", "root": "relation" }
    },
    "order": {
        "id": [ { "value": "o0036", "value_type": "string", "type": { "label": "id", "root": "attribute" } } ],
        "type": { "label": "order", "root": "entity" }
    }
}

This is equivalent to the following SQL query.

SELECT orders.id, order_line.quantity
FROM orders
INNER JOIN order_line ON order_line.order_id = orders.id
INNER JOIN paperback ON paperback.isbn_13 = order_line.item_id
WHERE paperback.isbn_13 = '9780446310789';

In a relational database, the relation type order-line would be represented by an associative table with foreign keys to the tables representing the entity types order and paperback1. As a general rule, associative tables in relational databases can be mapped onto relation types in TypeDB.

A key difference here is that the TypeQL query uses roles to connect the order-line relation and its roleplayers $order and $book, whereas the SQL query connects different rows based on literal value equalities. Simply sharing a variable between multiple statements is sufficient to describe the connections between data instances in TypeQL, without having to identify attribute values to join on (like the order ID and book ISBN).

Exercise

Modify the above query to also retrieve the status attribute of the order and the price attribute of the book.

Sample solution
match
$book isa paperback, has isbn-13 "9780446310789";
$line (order: $order, item: $book) isa order-line;
fetch
$order: id, status;
$line: quantity;
$book: price;

studio run Run

Ternary relations

In the previous query, $line was a binary relation between the two roleplayers $order and $book. However, the tuple syntax of relations is extremely flexible and allows us to use a tuple with a different number of elements to represent a relation with a different number of roleplayers. In the next query, we extend the previous query by also retrieving the name of the courier that is delivering the order and the street address of the order’s destination.

match
$book isa paperback, has isbn-13 "9780446310789";
$line (order: $order, item: $book) isa order-line;
(deliverer: $courier, delivered: $order, destination: $address) isa delivery;
fetch
$order: id;
$line: quantity;
$courier: name;
$address: street;

studio run Run

{
    "address": {
        "street": [ { "value": "464 Pilgrim Lane", "value_type": "string", "type": { "label": "street", "root": "attribute" } } ],
        "type": { "label": "address", "root": "entity" }
    },
    "courier": {
        "name": [ { "value": "FedEx", "value_type": "string", "type": { "label": "name", "root": "attribute" } } ],
        "type": { "label": "courier", "root": "entity" }
    },
    "line": {
        "quantity": [ { "value": 1, "value_type": "long", "type": { "label": "quantity", "root": "attribute" } } ],
        "type": { "label": "order-line", "root": "relation" }
    },
    "order": {
        "id": [ { "value": "o0016", "value_type": "string", "type": { "label": "id", "root": "attribute" } } ],
        "type": { "label": "order", "root": "entity" }
    }
}
{
    "address": {
        "street": [ { "value": "984 Williams Street", "value_type": "string", "type": { "label": "street", "root": "attribute" } } ],
        "type": { "label": "address", "root": "entity" }
    },
    "courier": {
        "name": [ { "value": "FedEx", "value_type": "string", "type": { "label": "name", "root": "attribute" } } ],
        "type": { "label": "courier", "root": "entity" }
    },
    "line": {
        "quantity": [ { "value": 1, "value_type": "long", "type": { "label": "quantity", "root": "attribute" } } ],
        "type": { "label": "order-line", "root": "relation" }
    },
    "order": {
        "id": [ { "value": "o0032", "value_type": "string", "type": { "label": "id", "root": "attribute" } } ],
        "type": { "label": "order", "root": "entity" }
    }
}
{
    "address": {
        "street": [ { "value": "20 Ridge Lane", "value_type": "string", "type": { "label": "street", "root": "attribute" } } ],
        "type": { "label": "address", "root": "entity" }
    },
    "courier": {
        "name": [ { "value": "DHL", "value_type": "string", "type": { "label": "name", "root": "attribute" } } ],
        "type": { "label": "courier", "root": "entity" }
    },
    "line": {
        "quantity": [ { "value": 2, "value_type": "long", "type": { "label": "quantity", "root": "attribute" } } ],
        "type": { "label": "order-line", "root": "relation" }
    },
    "order": {
        "id": [ { "value": "o0036", "value_type": "string", "type": { "label": "id", "root": "attribute" } } ],
        "type": { "label": "order", "root": "entity" }
    }
}

Here the delivery is a ternary relation between three roleplayers: $courier, $order, and $address. Higher order relations are used to represent rich references between multiple classes.

We have also not given the delivery a variable name. Compare this to the order line, which has the variable name $line. In this case, the delivery relation is represented by an anonymous variable. In many cases where we do not need to refer to a relation anywhere else in the query, we can omit a variable name. As we need to refer to the order line in the fetch clause to retrieve the associated quantity, we must give it the variable name $line, but this is not the case for the delivery.

Exercise

In a relational database, a ternary relation would be represented by an associative table between three foreign key columns. Write a SQL query that is equivalent to the above TypeQL query.

Sample solution
SELECT orders.id, order_line.quantity, courier.name, address.street
FROM orders
INNER JOIN order_line ON order_line.order_id = orders.id
INNER JOIN paperback ON paperback.isbn_13 = order_line.item_id
INNER JOIN delivery ON delivery.delivered_id = orders.id
INNER JOIN courier ON courier.id = delivery.courier_id
INNER JOIN address ON address.id = delivery.address_id
WHERE paperback.isbn_13 = '9780446310789';

In the same way that we can use a tuple with two or three elements respectively to represent a binary or ternary relation, we can likewise use a tuple with n elements for an n-ary relation!

(role-1: $a, role-2: $b, role-3: $c, role-4: $d, ...) isa n-ary-relation;

This way, we can represent relations with any number of roleplayers.

Footnotes

  1. ^ The seasoned SQL engineer will notice that, if paperbacks are not the only item that can be ordered, then we could not use a foreign key to reference them. We’d need a proper strategy for modeling the polymorphism in the model, likely one of Martin Fowler’s inheritance design patterns. If we go with the class-table inheritance pattern, then the foreign key would instead be to a product table. Conveniently, using this pattern means the isbn_13 column of the paperback table would reference the id column of the product table, so the SQL query shown would remain the same. As a polymorphic database, TypeDB is not affected by these architectural challenges! We’ll see how polymorphism is modeled in TypeDB in Lesson 5.