Advent of TypeQL: Day 5

Happy Advent!
Welcome to our Advent of TypeQL. Each day, we’ll post a small series of TypeQL challenges. There will be 14 days from December 11 to December 24, inclusive, so stay tuned!
It’ll be easiest to work through the days consecutively, since they are designed to incrementally build up an intuition of TypeQL. However, each day will also have a shortcut setup to just get a database for that day’s activity, allowing you to hop in without going through all the previous days challenges if you want.
You’ll want to use TypeDB Studio or TypeDB Console running against a TypeDB Cloud or local instance of TypeDB CE.
Background
Santa was preparing to release his Christmas plans. Finally done, he had just torn down his development and staging environments in preparation for a production launch of his grand Christmas plans…
Unfortunately at that precise moment, all of his Christmas plans, supposedly safely stashed in his git repository, got deleted by a pesky engineer with an overly permissive Github token and a mistyped command. Oh dear!

Santa contacted support and was able to recover some of his plans. However, they are incomplete. We’re going to help Santa get his plans back on track for Christmas, one day at a time!
Setup
If you’ve done Day 4, you can continue without setting anything up – just open up Studio or Console and get going!
If you’re new here, you’ll first want to spin up a TypeDB instance and connect with Studio or Console, and create a new database.
Then, you can get Santa’s recovered database schema by copying the linked schema text into a schema transaction’s query interface in Studio or Console, and then commit (note: by default, Studio auto-commits each query when set to “auto” mode).
Get the initial dataset plus subsequent days’ changes as a data file. Then load it by doing the same (follow link, copy text, paste into Studio or Console), but this time use a write transaction – and make sure you have committed.
At this point, you should have a database ready to go!
Day 5
We’ll continue from where we left off yesterday, ensuring the right elves are assigned to create presents for each continent.
Summary
Our overall goal is to create production relations, which connect elf, present-blueprint, and have an attribute quantity-required.
Yesterday, we learned how to write a query to compute the number of presents required of each blueprint type per continent – enough for 1 present for each child.
Here’s the query we ended up composing:
match
$blueprint isa present-blueprint;
reduce $count = count;
match
$continent isa continent;
$country isa country;
location-contains ($continent, $country);
demographics ($country, $stats);
$stats isa country-statistics, has population $pop, has proportion-under-12 $under-12;
let $kids = round($pop * $under-12);
reduce $total-kids = sum($kids) groupby $continent, $count;
match
let $each-present-count = round($total-kids / $count);
This produces, for each continent, the number of presents we need to produce of each present blueprint.
Function abstraction
Long query pipelines can become unwieldly. One way to simplify them is to abstract them into functions.
A function contains any read-only query, followed by a return statement:
fun <name>(($var: type,)*) -> { return type }:
query-clauses
return { ($var, )* };
Let’s write a temporary function that executes just the first part of the query above: counting present-blueprints. You can create a temporary function for use within 1 query with the with keyword.
You can invoke functions returning multiple answers with a let ... in construction. The let ... in statement is used instead of let ... = because the function returns a stream of answers (as indicated by -> {} in the signature).
Answer
with fun blueprint_count() -> { integer }:
match
$blueprint isa present-blueprint;
reduce $count = count;
return { $count };
match
let $c in blueprint_count();
However, we can easily change the function to return a single answer, and therefore invoke it with let ... =, the more typical expression assignment syntax.
fun <name>(($var: type,)*) -> return type:
query-clauses
return first ($var, )*;
Since we’re only interested in a single count, let’s update the blueprint count function with this construct.
Answer
with fun blueprint_count() -> integer:
match
$blueprint isa present-blueprint;
reduce $count = count;
return first $count;
match
let $c = blueprint_count();
Alright – now let’s rewrite our full query pipeline, but replace the first two clauses with this function!
Answer
with fun blueprint_count() -> integer:
match
$blueprint isa present-blueprint;
reduce $count = count;
return first $count;
match
$continent isa continent;
$country isa country;
location-contains ($continent, $country);
demographics ($country, $stats);
$stats isa country-statistics, has population $pop, has proportion-under-12 $under-12;
let $kids = round($pop * $under-12);
reduce $total-kids = sum($kids) groupby $continent;
match
let $count = blueprint_count();
let $each-present-count = round($total-kids / $count);
Now, let’s also convert the middle part of the query into a function, and invoke it in the final match clause. Aim for this signature for your extracted function:
fun continent_kids() -> { continent, integer }
This function returns pairs of continents and kids on that continent, and can be invoked with let $var1, $var2 in continent_kids();
Answer
with fun blueprint_count() -> integer:
match
$blueprint isa present-blueprint;
reduce $count = count;
return first $count;
with fun continent_kids() -> { continent, integer }:
match
$continent isa continent;
$country isa country;
location-contains ($continent, $country);
demographics ($country, $stats);
$stats isa country-statistics, has population $pop, has proportion-under-12 $under-12;
let $kids = round($pop * $under-12);
reduce $total-kids = sum($kids) groupby $continent;
return { $continent, $total-kids };
match
let $continent, $total-kids in continent_kids();
let $count = blueprint_count();
let $each-present-count = round($total-kids / $count);
We can use functions like this to modularize and simplify really complex queries. Functions can even be define-ed and committed in the schema to be used across multiple transactions and make them reusable.
Creating productions
Now that we have a bunch of modular functions, let’s write a query that:
- matches a continent and the number of each type of present to make in that continent
- matches the elf who lives in that continent
- matches each present blueprint
- inserts the
productionrelation between the blueprint, the elf, and the quantity required to be produced
First, let’s do 1 & 2, by extending our query from before with matching for the elf that lives-in a continent.
Answer
with fun blueprint_count() -> integer:
match
$blueprint isa present-blueprint;
reduce $count = count;
return first $count;
with fun continent_kids() -> { continent, integer }:
match
$continent isa continent;
$country isa country;
location-contains ($continent, $country);
demographics ($country, $stats);
$stats isa country-statistics, has population $pop, has proportion-under-12 $under-12;
let $kids = round($pop * $under-12);
reduce $total-kids = sum($kids) groupby $continent;
return { $continent, $total-kids };
match
let $continent, $total-kids in continent_kids();
let $count = blueprint_count();
let $each-present-count = round($total-kids / $count);
$elf isa elf;
lives-in ($continent, $elf);
To do 3 & 4: we just add to the match statement to look up each blueprint, then append an insert clause.
with fun blueprint_count() -> integer:
match
$blueprint isa present-blueprint;
reduce $count = count;
return first $count;
with fun continent_kids() -> { continent, integer }:
match
$continent isa continent;
$country isa country;
location-contains ($continent, $country);
demographics ($country, $stats);
$stats isa country-statistics, has population $pop, has proportion-under-12 $under-12;
let $kids = round($pop * $under-12);
reduce $total-kids = sum($kids) groupby $continent;
return { $continent, $total-kids };
match
let $continent, $total-kids in continent_kids();
let $count = blueprint_count();
let $each-present-count = round($total-kids / $count);
$elf isa elf;
lives-in ($continent, $elf);
$blueprint isa present-blueprint;
insert
$production isa production (builder: $elf, blueprint: $blueprint), has quantity-required == $each-present-count;
Fantastic! This produces a whole lot of production relations at once: it matches each blueprint, and each continent, computes the number of each type of present for that continent, and then wires up the relation.
Santa’s plans are starting to take shape!
See you soon!
We’ve made excellent progress rebuilding Santa’s data – great job! We’ll continue our work tomorrow – Day 6. Now available here.

If you encounter any issues, want to chat, or anything else – feel free to post in our Discord or feel free to email me directly.
