Officially out now: The TypeDB 3.0 Roadmap >>

Lesson 7.5: Value expressions

Value variables

Declaration

TypeQL allows the use of value expressions in queries through value variables. The following Fetch query shows how we might fetch the line totals for a particular order.

match
$order isa order, has id "o0014";
$book isa book, has price $retail-price;
($order, $book) isa order-line,
    has quantity $quantity;
?line-total = $quantity * $retail-price;
fetch
?line-total;

Value variables are declared using a ? prefix, like ?line-total in this example. They are distinct from concept variables, which are declared with a $ prefix as we saw in Lesson 3.1. Concept variables represent specific data instances or types within the databases, while value variables represent temporary values that exist only within the scope of a query.

Writing values

In addition to retrieving value variables in Fetch queries, we can also use them for data writes. In the following query, we modify the previous example to write the line total as an attribute to the order line instead of retrieving it.

match
$order isa order, has id "o0014";
$book isa book, has price $retail-price;
$line ($order, $book) isa order-line,
    has quantity $quantity;
?line-total = $quantity * $retail-price;
insert
$line has price ?line-total;

Unlike concept variables, value variables do not have schema types, only value types. If we use a write query to insert a value variable as an attribute, we must declare the attribute type, the same way we do when inserting a literal value as an attribute. If we changed the insert clause in the above query to the following, the query would not be valid.

...
insert
$line has ?line-total;

Assignment

Value variables are defined using the assignment operator =, as we have done above, and can hold values of any value type. The assigned value is described by a value expression, which can take any of the following forms:

  • A concept variable, representing an attribute’s value:

    ?value = $attribute;
  • A previously defined value variable:

    ?value = ?other-value;
  • A literal value:

    ?value = "literal";
  • Any combination of the above, using certain value operations:

    ?value = max($a + 100, ?c) / $d;

We will explore the different value operations, along with the value types they are valid for, in the remainder of this lesson.

In TypeDB 2.x, the only supported operations are on longs and doubles. Datetime, string, and boolean operations will be available in TypeDB 3.0. To learn more about this and other powerful new features, see the TypeDB 3.0 roadmap.

Arithmetic operations

TypeQL’s arithmetic operators and functions can be used to operate on longs and doubles. The arithmetic operators are summarised in the following table.

Operator Name Precedence

(...)

Parentheses

1

^

Exponentiation operator

2

*

Multiplication operator

3

/

Division operator

4

%

Modulo operator

5

+

Addition operator

6

-

Subtraction operator

7

When resolving an arithmetic expression, the order of operations is determined by their precedence. The arithmetic functions are summarised in the following table.

Function Name Usage

min(...)

Minimum function

Returns the minimum of comma-separated arguments.

max(...)

Maximum function

Returns the maximum of comma-separated arguments.

round(...)

Rounding function

Rounds the argument to the nearest integer.

floor(...)

Floor function

Rounds the argument to the nearest lesser integer.

ceil(...)

Ceiling function

Rounds the argument to the nearest greater integer.

abs(...)

Modulus function

Returns the absolute value of the argument.

Exercise

Write a Fetch query to retrieve the titles of books included in the Holiday Sale 2023, along with their retail and discounted prices. The entity representing this sale has the code "HOL23". Ensure that the discounted prices are rounded to the nearest cent (within the tolerance of double precision).

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
match
$sale isa promotion, has code "HOL23";
$included-book isa book,
    has title $title,
    has price $retail-price;
($sale, $included-book) isa promotion-inclusion,
    has discount $discount;
?discounted-price = round(100 * $retail-price * (1 - $discount)) / 100;
fetch
$title;
$retail-price;
?discounted-price;

Write an Update query to decrease the stock of Pride and Prejudice by one in a single query. The entity representing this book has the ISBN-13 "9780553212150". Ensure that the stock does not drop below zero.

Sample solution
match
$pride-prejudice isa book,
    has isbn "9780553212150",
    has stock $current-stock;
$current-stock > 0;
?new-stock = $current-stock - 1;
delete
$pride-prejudice has $current-stock;
insert
$pride-prejudice has stock ?new-stock;