New ACM paper, free-tier cloud, and open-source license

Lesson 11.1: Retrieving objects

The Get query

In previous lessons, the query results we retrieved were in JSON format. But TypeDB also includes functionality for retrieving stateful objects, which we can continue to operate on within the scope of a transaction, allowing us to read or write associated data. In order to retrieve stateful objects, we must use a Get query issued using a TypeDB driver. The following code snippet shows an example of such a query.

with TypeDB.core_driver(ADDRESS) as driver:
    with driver.session(DATABASE, SessionType.DATA) as session:
        with session.transaction(TransactionType.READ) as transaction:
            results: Iterator[ConceptMap] = transaction.query.get("""
                match
                $user isa user;
                $execution ($user, $action) isa action-execution,
                    has timestamp $timestamp;
                $action isa $action-type;
                get;
            """)

            for result in results:
                user: Entity = result.get("user").as_entity()
                action: Entity = result.get("action").as_entity()
                execution: Relation = result.get("execution").as_relation()
                timestamp: Attribute = result.get("timestamp").as_attribute()
                action_type: EntityType = result.get("action-type").as_entity_type()

We briefly encountered Get queries in Lesson 8.3, where we used them for aggregation subqueries. We also use them for stateful object retrieval. A Get query consists of a match clause and a get clause, and returns one result for each pattern matched in the data. The get clause is used to filter those results, which we will explore further shortly. In this query, the clause is empty, indicating that the results should be returned unfiltered.

We saw in Lesson 6.4 that Fetch queries return dictionary iterators, however Get queries return concept map iterators. A concept map is a collection of stateful objects that were matched by the variables in the query. In the above example, we have variables corresponding to both data instances and types, so objects of both kinds will be returned.

To retrieve the objects from the concept map, we can use its get method, which takes the variable name as an argument, and returns the object corresponding to the variable name for the respective result. In the above example, we have retrieved the objects corresponding to the $user, $action, $execution, $timestamp, and $action-type variables. Because the get method cannot distinguish the type of the variable, we must cast the object to the correct type using one of the following methods, as we do above:

  • as_entity

  • as_relation

  • as_attribute

  • as_entity_type

  • as_relation_type

  • as_attribute_type

With the above query, we have retrieved two entity objects, one relation object, one attribute object, and one entity type object. We will see how we can operate on these objects in Lesson 11.2.

Exercise

Write a function that yields the books in a specified genre as stateful objects. The function should have the following signature.

get_books_in_genre(transaction: TypeDBTransaction, genre: str) -> Iterator[Entity]

You may find it useful to refer to the bookstore’s schema.

Schema
define

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

hardback sub book,
    owns stock;

paperback sub book,
    owns stock;

ebook sub book;

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

company sub entity,
    abstract,
    owns name;

publisher sub company,
    plays publishing:publisher;

courier sub company,
    plays delivery:deliverer;

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

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

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

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

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

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

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

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

city sub place;

state sub place;

country sub place;

contribution sub relation,
    relates contributor,
    relates work;

authoring sub contribution,
    relates author as contributor;

editing sub contribution,
    relates editor as contributor;

illustrating sub contribution,
    relates illustrator as contributor;

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

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

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

rating sub relation,
    relates review,
    relates rated;

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

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

locating sub relation,
    relates located,
    relates location;

recommendation sub relation,
    relates recommended,
    relates recipient;

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

rule review-verified-by-purchase:
    when {
        ($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;
    } then {
        $review has verified true;
    };

rule review-unverified:
    when {
        $review isa review;
        not { $review has verified true; };
    } then {
        $review has verified false;
    };

rule book-recommendation-by-genre:
    when {
        $user isa user;
        $liked-book isa book;
        {
            ($user, $order) isa action-execution;
            ($order, $liked-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $liked-book) isa rating;
            $review has score >= 7;
        };
        $new-book isa book;
        not { {
            ($user, $order) isa action-execution;
            ($order, $new-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $new-book) isa rating;
        }; };
        $liked-book has genre $shared-genre;
        $new-book has genre $shared-genre;
        not { {
            $shared-genre == "fiction";
        } or {
            $shared-genre == "nonfiction";
        }; };
    } then {
        (recommended: $new-book, recipient: $user) isa recommendation;
    };

rule book-recommendation-by-author:
    when {
        $user isa user;
        $liked-book isa book;
        {
            ($user, $order) isa action-execution;
            ($order, $liked-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $liked-book) isa rating;
            $review has score >= 7;
        };
        $new-book isa book;
        not { {
            ($user, $order) isa action-execution;
            ($order, $new-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $new-book) isa rating;
        }; };
        ($liked-book, $shared-author) isa authoring;
        ($new-book, $shared-author) isa authoring;
    } then {
        (recommended: $new-book, recipient: $user) isa recommendation;
    };

rule order-line-total-retail-price:
    when {
        ($order) isa action-execution, has timestamp $order-time;
        $line ($order, $item) isa order-line;
        not {
            ($promotion, $item) isa promotion-inclusion;
            $promotion has start-timestamp <= $order-time,
                has end-timestamp >= $order-time;
        };
        $item has price $retail-price;
        $line has quantity $quantity;
        ?line-total = $quantity * $retail-price;
    } then {
        $line has price ?line-total;
    };

rule order-line-total-discounted-price:
    when {
        ($order) isa action-execution, has timestamp $order-time;
        $line ($order, $item) isa order-line;
        ($best-promotion, $item) isa promotion-inclusion, has discount $best-discount;
        $best-promotion has start-timestamp <= $order-time,
            has end-timestamp >= $order-time;
        not {
            ($other-promotion, $item) isa promotion-inclusion, has discount > $best-discount;
            $other-promotion has start-timestamp <= $order-time,
                has end-timestamp >= $order-time;
        };
        $item has price $retail-price;
        ?discounted-price = round(100 * $retail-price * (1 - $best-discount)) / 100;
        $line has quantity $quantity;
        ?line-total = $quantity * ?discounted-price;
    } then {
        $line has price ?line-total;
    };

rule transitive-location:
    when {
        (location: $parent-place, located: $child-place) isa locating;
        (location: $child-place, located: $x) isa locating;
    } then {
        (location: $parent-place, located: $x) isa locating;
    };
Sample solution
def get_books_in_genre(transaction: TypeDBTransaction, genre: str) -> Iterator[Entity]:
    results = transaction.query.get(f"""
        match
        $book isa book, has genre "{genre}";
        get;
    """)

    for result in results:
        book = result.get("book").as_entity()

        yield book

Filtering results

In Lesson 8.3, we saw how a Get query will return one result per unique combination of variables matched by the pattern in the match clause. As a result, if a data instance is matched multiple times for a particular variable, it will be returned in more than one result. In the following example, we use a function to retrieve orders that include multiple copies of the same book.

def get_orders_with_multiple_copies(transaction: TypeDBTransaction) -> Iterator[Entity]:
    results = transaction.query.get("""
        match
        $order isa order;
        $book isa book;
        ($order, $book) isa order-line, has quantity > 1;
        get;
    """)

    for result in results:
        order = result.get("order").as_entity()

        yield order

If an order contains multiple copies of more than one book, then it will be matched for the $order variable once for each of those books. Each pair of $order and $book will be unique in the result set, but $order and $book may individually not be unique. In order to return a unique list of orders, we must filter the results on the $order variable using the get clause.

def get_orders_with_multiple_copies(transaction: TypeDBTransaction) -> Iterator[Entity]:
    results = transaction.query.get("""
        match
        $order isa order;
        $book isa book;
        ($order, $book) isa order-line, has quantity > 1;
        get $order;
    """)

    for result in results:
        order = result.get("order").as_entity()

        yield order

As a general rule, the get clause should be used to filter out any variables not retrieved from the concept map. This will ensure that the objects retrieved from the concept map form a unique set of results. In some niche cases, it may instead be desirable to return duplicate results.

Exercise

Write a function that yields the books that are currently discounted as stateful objects. Ensure that each such book is only yielded once, regardless of the number of promotions it is in. The function should have the following signature.

get_discounted_books(transaction: TypeDBTransaction) -> Iterator[Entity]

You may find it useful to refer to the bookstore’s schema.

Schema
define

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

hardback sub book,
    owns stock;

paperback sub book,
    owns stock;

ebook sub book;

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

company sub entity,
    abstract,
    owns name;

publisher sub company,
    plays publishing:publisher;

courier sub company,
    plays delivery:deliverer;

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

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

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

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

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

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

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

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

city sub place;

state sub place;

country sub place;

contribution sub relation,
    relates contributor,
    relates work;

authoring sub contribution,
    relates author as contributor;

editing sub contribution,
    relates editor as contributor;

illustrating sub contribution,
    relates illustrator as contributor;

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

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

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

rating sub relation,
    relates review,
    relates rated;

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

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

locating sub relation,
    relates located,
    relates location;

recommendation sub relation,
    relates recommended,
    relates recipient;

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

rule review-verified-by-purchase:
    when {
        ($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;
    } then {
        $review has verified true;
    };

rule review-unverified:
    when {
        $review isa review;
        not { $review has verified true; };
    } then {
        $review has verified false;
    };

rule book-recommendation-by-genre:
    when {
        $user isa user;
        $liked-book isa book;
        {
            ($user, $order) isa action-execution;
            ($order, $liked-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $liked-book) isa rating;
            $review has score >= 7;
        };
        $new-book isa book;
        not { {
            ($user, $order) isa action-execution;
            ($order, $new-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $new-book) isa rating;
        }; };
        $liked-book has genre $shared-genre;
        $new-book has genre $shared-genre;
        not { {
            $shared-genre == "fiction";
        } or {
            $shared-genre == "nonfiction";
        }; };
    } then {
        (recommended: $new-book, recipient: $user) isa recommendation;
    };

rule book-recommendation-by-author:
    when {
        $user isa user;
        $liked-book isa book;
        {
            ($user, $order) isa action-execution;
            ($order, $liked-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $liked-book) isa rating;
            $review has score >= 7;
        };
        $new-book isa book;
        not { {
            ($user, $order) isa action-execution;
            ($order, $new-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $new-book) isa rating;
        }; };
        ($liked-book, $shared-author) isa authoring;
        ($new-book, $shared-author) isa authoring;
    } then {
        (recommended: $new-book, recipient: $user) isa recommendation;
    };

rule order-line-total-retail-price:
    when {
        ($order) isa action-execution, has timestamp $order-time;
        $line ($order, $item) isa order-line;
        not {
            ($promotion, $item) isa promotion-inclusion;
            $promotion has start-timestamp <= $order-time,
                has end-timestamp >= $order-time;
        };
        $item has price $retail-price;
        $line has quantity $quantity;
        ?line-total = $quantity * $retail-price;
    } then {
        $line has price ?line-total;
    };

rule order-line-total-discounted-price:
    when {
        ($order) isa action-execution, has timestamp $order-time;
        $line ($order, $item) isa order-line;
        ($best-promotion, $item) isa promotion-inclusion, has discount $best-discount;
        $best-promotion has start-timestamp <= $order-time,
            has end-timestamp >= $order-time;
        not {
            ($other-promotion, $item) isa promotion-inclusion, has discount > $best-discount;
            $other-promotion has start-timestamp <= $order-time,
                has end-timestamp >= $order-time;
        };
        $item has price $retail-price;
        ?discounted-price = round(100 * $retail-price * (1 - $best-discount)) / 100;
        $line has quantity $quantity;
        ?line-total = $quantity * ?discounted-price;
    } then {
        $line has price ?line-total;
    };

rule transitive-location:
    when {
        (location: $parent-place, located: $child-place) isa locating;
        (location: $child-place, located: $x) isa locating;
    } then {
        (location: $parent-place, located: $x) isa locating;
    };
Sample solution
def get_discounted_books(transaction: TypeDBTransaction) -> Iterator[Entity]:
    current_timestamp = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")

    results = transaction.query.get(f"""
        match
        $book isa book;
        $promotion isa promotion,
            has start-timestamp <= {current_timestamp},
            has end-timestamp >= {current_timestamp};
        ($book, $promotion) isa promotion-inclusion;
        get $book;
    """)

    for result in results:
        book = result.get("book").as_entity()

        yield book

Retrieving objects programmatically

In addition to retrieving stateful objects via Get queries, we can also retrieve them programmatically via API calls. Type objects can be retrieved through the concepts property of a transaction object, as in the following example.

with TypeDB.core_driver(ADDRESS) as driver:
    with driver.session(DATABASE, SessionType.DATA) as session:
        with session.transaction(TransactionType.READ) as transaction:
            book_type: EntityType = transaction.concepts.get_entity_type("book").resolve()
            contribution_type: RelationType = transaction.concepts.get_relation_type("contribution").resolve()
            page_count_type: AttributeType = transaction.concepts.get_attribute_type("page-count").resolve()

The methods get_entity_type, get_relation_type, and get_attribute_type are used to retrieve entity, relation, and attribute types respectively, and take the type label as an argument. These methods return promises, and so must be resolved using their resolve methods in order to obtain the responses.

Once a type has been retrieved as a stateful object, we can retrieve all instances of the type as stateful objects using the type’s get_instances method. These methods require a transaction as an argument.

with TypeDB.core_driver(ADDRESS) as driver:
    with driver.session(DATABASE, SessionType.DATA) as session:
        with session.transaction(TransactionType.READ) as transaction:
            book_type: EntityType
            contribution_type: RelationType
            page_count_type: AttributeType

            books: Iterator[Entity] = book_type.get_instances(transaction)
            contributions: Iterator[Relation] = contribution_type.get_instances(transaction)
            page_counts: Iterator[Attribute] = page_count_type.get_instances(transaction)

Most API methods that retrieve information from the server return either iterators or promises. An iterator can be resolved by iterating over it or casting it to a sequence (such as a list), while a promise can be resolved by calling its resolve method. The return types of all Python driver methods are documented in the API reference. TypeDB drivers in other languages have equivalent functionality.

Sometimes, retrieving data instances this way can be more convenient than issuing a query, but most non-trivial constraints on the data instances to be returned can only be expressed by using a query. More importantly, we can also operate on the type object to insert new instances, as we will see in Lesson 11.2, and to perform programmatic schema migrations, which will be the topic of Lesson 17 (coming soon).

Exercise

Write a function that yields all users in the database as stateful objects. Write the function once by executing a TypeQL query, and again without using any TypeQL. The function should have the following signature.

get_users(transaction: TypeDBTransaction) -> Iterator[Entity]

You may find it useful to refer to the bookstore’s schema.

Schema
define

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

hardback sub book,
    owns stock;

paperback sub book,
    owns stock;

ebook sub book;

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

company sub entity,
    abstract,
    owns name;

publisher sub company,
    plays publishing:publisher;

courier sub company,
    plays delivery:deliverer;

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

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

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

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

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

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

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

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

city sub place;

state sub place;

country sub place;

contribution sub relation,
    relates contributor,
    relates work;

authoring sub contribution,
    relates author as contributor;

editing sub contribution,
    relates editor as contributor;

illustrating sub contribution,
    relates illustrator as contributor;

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

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

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

rating sub relation,
    relates review,
    relates rated;

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

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

locating sub relation,
    relates located,
    relates location;

recommendation sub relation,
    relates recommended,
    relates recipient;

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

rule review-verified-by-purchase:
    when {
        ($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;
    } then {
        $review has verified true;
    };

rule review-unverified:
    when {
        $review isa review;
        not { $review has verified true; };
    } then {
        $review has verified false;
    };

rule book-recommendation-by-genre:
    when {
        $user isa user;
        $liked-book isa book;
        {
            ($user, $order) isa action-execution;
            ($order, $liked-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $liked-book) isa rating;
            $review has score >= 7;
        };
        $new-book isa book;
        not { {
            ($user, $order) isa action-execution;
            ($order, $new-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $new-book) isa rating;
        }; };
        $liked-book has genre $shared-genre;
        $new-book has genre $shared-genre;
        not { {
            $shared-genre == "fiction";
        } or {
            $shared-genre == "nonfiction";
        }; };
    } then {
        (recommended: $new-book, recipient: $user) isa recommendation;
    };

rule book-recommendation-by-author:
    when {
        $user isa user;
        $liked-book isa book;
        {
            ($user, $order) isa action-execution;
            ($order, $liked-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $liked-book) isa rating;
            $review has score >= 7;
        };
        $new-book isa book;
        not { {
            ($user, $order) isa action-execution;
            ($order, $new-book) isa order-line;
        } or {
            ($user, $review) isa action-execution;
            ($review, $new-book) isa rating;
        }; };
        ($liked-book, $shared-author) isa authoring;
        ($new-book, $shared-author) isa authoring;
    } then {
        (recommended: $new-book, recipient: $user) isa recommendation;
    };

rule order-line-total-retail-price:
    when {
        ($order) isa action-execution, has timestamp $order-time;
        $line ($order, $item) isa order-line;
        not {
            ($promotion, $item) isa promotion-inclusion;
            $promotion has start-timestamp <= $order-time,
                has end-timestamp >= $order-time;
        };
        $item has price $retail-price;
        $line has quantity $quantity;
        ?line-total = $quantity * $retail-price;
    } then {
        $line has price ?line-total;
    };

rule order-line-total-discounted-price:
    when {
        ($order) isa action-execution, has timestamp $order-time;
        $line ($order, $item) isa order-line;
        ($best-promotion, $item) isa promotion-inclusion, has discount $best-discount;
        $best-promotion has start-timestamp <= $order-time,
            has end-timestamp >= $order-time;
        not {
            ($other-promotion, $item) isa promotion-inclusion, has discount > $best-discount;
            $other-promotion has start-timestamp <= $order-time,
                has end-timestamp >= $order-time;
        };
        $item has price $retail-price;
        ?discounted-price = round(100 * $retail-price * (1 - $best-discount)) / 100;
        $line has quantity $quantity;
        ?line-total = $quantity * ?discounted-price;
    } then {
        $line has price ?line-total;
    };

rule transitive-location:
    when {
        (location: $parent-place, located: $child-place) isa locating;
        (location: $child-place, located: $x) isa locating;
    } then {
        (location: $parent-place, located: $x) isa locating;
    };
Sample solution

With TypeQL:

def get_users(transaction: TypeDBTransaction) -> Iterator[Entity]:
    results = transaction.query.get("""
        match
        $user isa user;
        get;
    """)

    for result in results:
        user = result.get("user").as_entity()

        yield user

Without TypeQL:

def get_users(transaction: TypeDBTransaction) -> Iterator[Entity]:
    user_type = transaction.concepts.get_entity_type("user").resolve()
    users = user_type.get_instances(transaction)
    return users

Retrieving associated objects

Once we have retrieved a stateful object, we can use its methods to retrieve associated objects. For example, to retrieve the attributes that an entity or relation object owns, we can use its get_has method, and to retrieve the relations that it plays as role in, we can use its get_relations method. Both of these methods return the results as stateful objects. In the following code snippet, we retrieve the attributes of a book and the relations it is in.

with TypeDB.core_driver(ADDRESS) as driver:
    with driver.session(DATABASE, SessionType.DATA) as session:
        with session.transaction(TransactionType.READ) as transaction:
            book: Entity
            attributes: Iterator[Attribute] = book.get_has(transaction)
            relations: Iterator[Relation] = book.get_relations(transaction)

In addition to these two methods, there are a number of others for retrieving associated objects. The most useful methods are summarised in the following table.

Method Return type Usage

Entity.get_has, Relation.get_has

Iterator[Attribute]

Retrieves the attributes of an entity or relation. Attribute types may be optionally specified.

Entity.get_relations, Relation.get_relations

Iterator[Relation]

Retrieves the relations in which an entity or relation plays a role. Roles may be optionally specified.

Attribute.get_owners

Iterator[Thing]

Retrieves the owners of an attribute. Owner types may be optionally specified.

Relation.get_players_by_role_type

Iterator[Thing]

Retrieves the roleplayers in a relation. Roles may be optionally specified.

Because the owners of an attribute or the roleplayers in a relation can be both entities and relations, the objects returned by get_owners and get_players are of the generic Thing class. They can be cast to the appropriate type using the following helper function or similar.

def cast_thing(thing: Thing) -> Entity | Relation:
    if thing.is_entity():
        return thing.as_entity()
    if thing.is_relation():
        return thing.as_relation()

The Thing class is deprecated and will be removed in TypeDB 3.0.

The methods in the above table can also take types or roles as optional arguments on which to filter the data instances returned, as in the following example. This time, we only retrieve the contributions a book plays the work role in, rather than all relations.

with TypeDB.core_driver(ADDRESS) as driver:
    with driver.session(DATABASE, SessionType.DATA) as session:
        with session.transaction(TransactionType.READ) as transaction:
            book: Entity
            work_role: RoleType
            contributions = book.get_relations(transaction, work_role)
Exercise

Write a function that yields the entities that are related to a given entity by a relation of any type. The function should have the following signature.

get_related_entities(transaction: TypeDBTransaction, entity: Entity) -> Iterator[Entity]
Hint

For each roleplayer yielded, you will need to ensure that it is an entity, and that it is not the original entity.

Sample solution
def get_related_entities(transaction: TypeDBTransaction, entity: Entity) -> Iterator[Entity]:
    for relation in entity.get_relations(transaction):
        for player in relation.get_players_by_role_type(transaction):
            if not player.is_entity():
                continue
            elif player == entity:
                continue
            else:
                yield player.as_entity()

Provide Feedback