Officially out now: The TypeDB 3.0 Roadmap

Lesson 8.4: Sorting and pagination

Sorting

The results of any query can be sorted by appending the sort modifier to the query. As we learned in Lesson 8.3, modifiers are appended to queries to modify the results returned in some manner. The following query retrieves the list of every book’s title and page count, and sorts by page count in ascending order.

match
$book isa book, has page-count $count;
fetch
$book: title, page-count;
sort $count;
{
    "book": {
        "page-count": [ { "value": 79, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Iron Giant", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 160, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Motorcycle Diaries: A Journey Around South America", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 196, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Physical Principles of Electron Microscopy: An Introduction to TEM, SEM, and AEM", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 199, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Interpretation of Electron Diffraction Patterns", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 215, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Hitchhiker's Guide to the Galaxy", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 240, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Mummies of Urumchi", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 260, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Business Secrets of The Pharoahs", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 281, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "To Kill a Mockingbird", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 295, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Pride and Prejudice", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 296, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Under the Black Flag: The Romance and the Reality of Life Among the Pirates", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "hardback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 310, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Hobbit", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 352, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Great Discoveries in Medicine", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 374, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Pet Sematary", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 416, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Hokusai's Fuji", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 425, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Electron Backscatter Diffraction in Materials Science", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "hardback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 450, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Fundamentals of Data Engineering", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 458, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "One Hundred Years of Solitude", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 624, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Dune", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 656, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Odyssey", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 820, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Classical Mythology", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 1451, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Complete Calvin and Hobbes", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "hardback", "root": "entity" }
    }
}

The sort modifier takes a variable as an argument, denoting the variable to sort on. As with the aggregation modifiers, the variable can be either a concept variable representing an attribute, or a value variable. In this case, we’ve used the concept variable page-count. The results are sorted in ascending order. In order to sort in descending order, we can add append the keyword desc to the modifier as follows.

match
$book isa book, has page-count $count;
fetch
$book: title, page-count;
sort $count desc;
{
    "book": {
        "page-count": [ { "value": 1451, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Complete Calvin and Hobbes", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "hardback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 820, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Classical Mythology", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 656, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Odyssey", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 624, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Dune", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 458, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "One Hundred Years of Solitude", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 450, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Fundamentals of Data Engineering", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 425, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Electron Backscatter Diffraction in Materials Science", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "hardback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 416, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Hokusai's Fuji", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 374, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Pet Sematary", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 352, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Great Discoveries in Medicine", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 310, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Hobbit", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 296, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Under the Black Flag: The Romance and the Reality of Life Among the Pirates", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "hardback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 295, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Pride and Prejudice", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 281, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "To Kill a Mockingbird", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 260, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Business Secrets of The Pharoahs", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 240, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Mummies of Urumchi", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 215, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Hitchhiker's Guide to the Galaxy", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 199, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Interpretation of Electron Diffraction Patterns", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 196, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Physical Principles of Electron Microscopy: An Introduction to TEM, SEM, and AEM", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 160, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Motorcycle Diaries: A Journey Around South America", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 79, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Iron Giant", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}

Though sorting is always in ascending order by default, we can specify this explicitly using the keyword asc in the same way as desc.

The ordering of strings is determined according to the Java string specification:

  • If two strings have the same length and the characters at each index are the same, then they are equal.

  • If they have different characters at one or more index positions, then the lesser string is the one with the lower Unicode value at the first of those indices.

  • If they are different lengths but there is no index at which they differ, then the lesser string is the shorter one.

Exercise

Write a Fetch query to retrieve a list of book titles along with the names of any contributors to those books. Ensure that the results are sorted first by book title and then contributor name, both in ascending alphabetical order. You can assume that each book has exactly one title and that each contributor has exactly one name.

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;
    };
Hint

You will need to use a subquery to retrieve the contributors, and use sort modifiers on both the parent query and the subquery.

Sample solution
match
$book isa book, has title $title;
fetch
$title;
contributors: {
    match
    $contributor isa contributor, has name $name;
    ($book, $contributor) isa contribution;
    fetch
    $name;
    sort $name;
};
sort $title;

Pagination

We can paginate the sorted results of any query by appending the limit and offset modifiers. In the following example, we add a limit modifier to the previous query to retrieve only the first three books with the lowest page counts.

match
$book isa book, has page-count $count;
fetch
$book: title, page-count;
sort $count;
limit 3;
{
    "book": {
        "page-count": [ { "value": 79, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Iron Giant", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 160, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Motorcycle Diaries: A Journey Around South America", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 196, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Physical Principles of Electron Microscopy: An Introduction to TEM, SEM, and AEM", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "ebook", "root": "entity" }
    }
}

In the next example, we also add an offset modifier to retrieve the three books with the next lowest page counts.

match
$book isa book, has page-count $count;
fetch
$book: title, page-count;
sort $count;
offset 3;
limit 3;
{
    "book": {
        "page-count": [ { "value": 199, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "Interpretation of Electron Diffraction Patterns", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 215, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Hitchhiker's Guide to the Galaxy", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}
{
    "book": {
        "page-count": [ { "value": 240, "type": { "label": "page-count", "root": "attribute", "value_type": "long" } } ],
        "title": [ { "value": "The Mummies of Urumchi", "type": { "label": "title", "root": "attribute", "value_type": "string" } } ],
        "type": { "label": "paperback", "root": "entity" }
    }
}

Compare these results to those of the previous non-paginated query to see the difference.

The limit and offset modifiers both take a literal integer as an argument, indicating the number of results to retrieve and skip respectively. When both are used, the offset modifier comes before the limit modifier. These modifiers can also be used in a query without first sorting, though in which case the selection of results received will appear fairly random and may change when the query is re-run. Appending only a limit modifier to a query with many results can be a quick way to view a small random selection for debugging purposes.

The sort, limit, and offset modifiers can all be used individually or in any combination. When two or more are used together, those present must appear in the following order:

  1. sort

  2. offset

  3. limit

Exercise

Write a Fetch query to retrieve the titles of the five most recently published books in the database.

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
$book isa book;
$publication isa publication, has year $year;
($book, $publication) isa publishing;
fetch
$book: title;
sort $year desc;
limit 5;