Fetch query
A Fetch query retrieves values from a TypeDB database and returns them as JSON objects. For a practical guide on how to send a Fetch query to TypeDB, see the Fetch query page of the TypeDB Manual.
Behavior
A Fetch query retrieves concepts from a database and projects values and types to a JSON object.
A match
clause is used to match data by a pattern, while a fetch
clause is used to format the returned JSON.
The output of a Fetch query is a lazy stream/iterator of JSON objects.
The number of query results can be less than the number of solutions of its match
clause due to set semantics
if not all query variables are projected into results.
You can use sorting and pagination with a Fetch query.
Fetch queries can use rule-based inference.
Match clause
A match
clause in a Fetch query matches existing concepts in a database to project their values
or values of attributes they own.
You can use a declarative and composable TypeQL pattern in a match
clause and TypeDB will find data that matches
specified pattern.
A match
clause is mandatory in a Fetch query.
For more information on patterns and statements used in a match
clause, see the
Patterns and Statements sections.
Fetch clause
A fetch
clause is used in a Fetch query to specify the format of the returned JSON objects and
projection of values (and types) from matched database concepts to the JSON.
In a fetch
clause you specify top level projections to JSON, separating them with a semicolon.
Projected variables and attribute types are used as keys in the resulted JSON,
but you can customize that with output customization.
There are three types of projections that you can use in a Fetch query:
-
Direct projection — use a concept variable with either a type object or attribute.
-
Ownership projection — use a concept variable, colon, and attribute types that can be owned by an object in the variable.
-
Subquery — use another Fetch query or aggregated Get query.
A fetch
clause is executed exactly once for every result matched by the preceding match
clause of the same query.
But it can return fewer results,
than matched by the match
clause if not all variables are projected.
Fetch variables
Types and values can be projected directly into a JSON.
Fetch a value
For this example, use a database with the IAM schema and sample data loaded.
To fetch a value, you can either fetch a concept variable with an attribute, or fetch a value variable.
For example, let’s match all files that own a path
attribute and fetch all path
attributes:
match
$f isa file, has path $p;
fetch
$p;
{ "p": { "value": "psukg.java", "type": { "label": "path", "root": "attribute", "value_type": "string" } } }
{ "p": { "value": "budget_2021-08-01.xlsx", "type": { "label": "path", "root": "attribute", "value_type": "string" } } }
{ "p": { "value": "README.md", "type": { "label": "path", "root": "attribute", "value_type": "string" } } }
Fetch a type
For this example, use a database with the IAM schema and sample data loaded.
To fetch a type from a database, use a concept variable that is matched to a type by an
isa or sub statement.
Make sure to use an exclamation mark (isa!
/sub!
) to get only the exact type, and no supertypes via type inference.
match
$x has attribute "iopvu.java";
$x isa! $type;
fetch
$type;
The above query matches any instance of data ($x
) that has name
of iopvu.java
and any size-kb
($s
).
The variable $type
is assigned to be equal to the type of $x
.
Finally, we bound the value variable ?size
to some value, based on the value of the $s
.
{ "type": { "label": "file", "root": "entity" } }
Fetch owned attributes
For this example, use a database with the IAM schema and sample data loaded.
Entities and relations can be used in a fetch
clause only by extracting values of attributes they own.
You can project all attributes owned by a data instance, filtered by an attribute type.
To do that, use the concept variable, matched in a preceding match
clause,
in a fetch
clause followed by a colon and attribute type label.
You can use multiple types by listing them with a comma as a separator.
Let’s see how to fetch all attributes owned by every person
entity of type full-name
and email
.
First, we match every person entity, then we use the concept variable we matched them with in a fetch
clause
and specify attribute types to fetch:
match
$p isa person;
fetch
$p: full-name, email;
The above query example should produce a set of JSON objects with a similar format: the p
as top-level key,
with full-name
, email
, and type
keys inside.
{
"p": {
"email": [ { "value": "kevin.morrison@typedb.com", "type": { "label": "email", "root": "attribute", "value_type": "string" } } ],
"full-name": [ { "value": "Kevin Morrison", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } } ],
"type": { "label": "person", "root": "entity" }
}
}
Fetching owned attributes by their type returns a list of owned attributes. This list can contain any number of attributes. Even if there are no attributes of selected type, the empty list is returned for the respective key. |
To fetch all attributes owned by a data instance, use the See exampleFetching all attributes
The above query example matches all files (even those that do not have any attributes) and then fetches all attributes for every file. Output example (partial)
|
Fetch with custom labels
For this example, use a database with the IAM schema and sample data loaded.
A Fetch query supports output customization (or relabeling) of the top level keys in the resulting JSON.
Subqueries always use labels set in the query for their output.
To customize a key for a direct projection or ownership projection,
use the as
keyword after the concept variable in a fetch
clause.
By surrounding a new label in double quotes, you can use whitespaces in it.
For example, if we want to fetch all attributes of entities matched by the variable $f
,
relabeling f
to file
:
match
$f isa file;
fetch
$f as file: attribute;
{
"file": {
"attribute": [
{ "value": 758, "type": { "label": "size-kb", "root": "attribute", "value_type": "long" } },
{ "value": "budget_2022-05-01.xlsx", "type": { "label": "path", "root": "attribute", "value_type": "string" } }
],
"type": { "label": "file", "root": "entity" }
}
}
{
"file": {
"attribute": [ { "value": "LICENSE", "type": { "label": "path", "root": "attribute", "value_type": "string" } } ],
"type": { "label": "file", "root": "entity" }
}
}
Ownership projection customization
For this example, use a database with the IAM schema and sample data loaded.
You can also customize (relabel) keys for attribute types used in ownership projection
by using the same keyword as
after the attribute type.
For example, if we want to fetch all attributes of entities matched by the variable $f
,
relabeling the f
key to file
, and the attribute
key to the all attributes
string:
match
$f isa file;
fetch
$f as file: attribute as "all attributes";
The above query is equal to the previous one, but it uses the all attribute string
for the key of attributes.
{
"file": {
"all attributes": [
{ "value": 758, "type": { "label": "size-kb", "root": "attribute", "value_type": "long" } },
{ "value": "budget_2022-05-01.xlsx", "type": { "label": "path", "root": "attribute", "value_type": "string" } }
],
"type": { "label": "file", "root": "entity" }
}
}
{
"file": {
"all attributes": [ { "value": "LICENSE", "type": { "label": "path", "root": "attribute", "value_type": "string" } } ],
"type": { "label": "file", "root": "entity" }
}
}
Fetch with subqueries
For this example, use a database with the IAM schema and sample data loaded.
A Fetch query can include a subquery in a fetch
clause.
A subquery is another Fetch query that returns a JSON for the key or a
Get query with
aggregation that returns a value.
A subquery consists of label, colon, and a TypeQL query string surrounded by curly brackets.
The label determines the key that will be used to introduce results of the subquery into the JSON output.
For example, let’s match every file, and use a subquery to find its size in megabytes:
match
$f isa file;
fetch
$f as file: path as filename, size-kb;
file-size:{
match
$f has size-kb $sk;
?sm = round( $sk / 1024 );
fetch
?sm as size-mb;
};
Some files don’t have a size-kb
attribute.
Without a subquery, matching this attribute in the top level match
clause to calculate size in megabytes,
would result in skipping the files without size, as they would not match a pattern.
By adding a subquery, we can return all files.
If a file doesn’t have a size-kb
attribute, the subquery returns an empty list:
{
"file": {
"filename": [ { "value": "budget_2022-05-01.xlsx", "type": { "label": "path", "root": "attribute", "value_type": "string" } } ],
"size-kb": [ { "value": 758, "type": { "label": "size-kb", "root": "attribute", "value_type": "long" } } ],
"type": { "label": "file", "root": "entity" }
},
"file-size": [ { "size-mb": { "value": 1, "value_type": "long" } } ]
}
{
"file": {
"filename": [ { "value": "LICENSE", "type": { "label": "path", "root": "attribute", "value_type": "string" } } ],
"size-kb": [ ],
"type": { "label": "file", "root": "entity" }
},
"file-size": [ ]
}
A subquery shares variables with its parent query. At least one variable from a parent query must be used in a subquery. |
The number of nested subqueries is not limited, but using a big number of nested levels can break technical limitations of gRPC messaging.
Grouping results
For this example, use a database with the IAM schema and sample data loaded.
Subqueries can be used to group query results, reduce the size of a response, or simplify further processing.
For example, let’s find all files that a user has a permission to access with the modify_file
action.
To group the results, let’s iterate through users on the parent query and use subquery to find files:
match
$u isa user;
fetch
$u as user: attribute as "User attributes";
permited-files:{
match
$f isa file;
$va isa action, has name "modify_file";
$pa($f, $va) isa access;
$p($u, $pa) isa permission;
fetch
$f as file: attribute as "File attributes";
};
The above query matches all users at first.
Then it fetches all attributes for every user and runs the subquery for the subsection labeled as permitted-files
.
This subquery matches all files that participate in an access
relation with action named modify_file
that
play a role in a permission
relation with the user matched by the parent query.
Then it fetches all attributes for such files.
The resulting JSON objects have a predictable structure of keys, that was set by the fetch
clauses, using
relabeling:
-
user
-
User attributes
-
-
permitted-files
-
file
-
File attributes
-
-
{
"permited-files": [ ],
"user": {
"User attributes": [
{ "value": "masako.holley@typedb.com", "type": { "label": "email", "root": "attribute", "value_type": "string" } },
{ "value": "Masako Holley", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } }
],
"type": { "label": "person", "root": "entity" }
}
}
{
"permited-files": [
{
"file": {
"File attributes": [
{ "value": 70, "type": { "label": "size-kb", "root": "attribute", "value_type": "long" } },
{ "value": "lzfkn.java", "type": { "label": "path", "root": "attribute", "value_type": "string" } }
],
"type": { "label": "file", "root": "entity" }
}
},
...
{
"file": {
"File attributes": [
{ "value": 55, "type": { "label": "size-kb", "root": "attribute", "value_type": "long" } },
{ "value": "iopvu.java", "type": { "label": "path", "root": "attribute", "value_type": "string" } }
],
"type": { "label": "file", "root": "entity" }
}
}
],
"user": {
"User attributes": [
{ "value": "kevin.morrison@typedb.com", "type": { "label": "email", "root": "attribute", "value_type": "string" } },
{ "value": "Kevin Morrison", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } }
],
"type": { "label": "person", "root": "entity" }
}
}
{
"permited-files": [ ],
"user": {
"User attributes": [
{ "value": "pearle.goodman@typedb.com", "type": { "label": "email", "root": "attribute", "value_type": "string" } },
{ "value": "Pearle Goodman", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } }
],
"type": { "label": "person", "root": "entity" }
}
}
The order of key/value pairs in a JSON is not guaranteed. |
Aggregated Get subquery
To add an aggregated value for the Fetch output, use a Get query with an aggregation modifier.
The returned value is added to the resulted JSON with a subquery label as a key.
If there is an empty response (no answer) for the Get subquery, then the value in JSON will be null
.
match
$person isa person;
fetch
$person: full-name;
average-file-size: {
match
($person,$acc) isa permission;
$acc($file) isa access;
$file isa file, has size-kb $size;
get $size;
mean $size;
};
{
"average-file-size": null,
"person": {
"full-name": [ { "value": "Masako Holley", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } } ],
"type": { "label": "person", "root": "entity" }
}
}
{
"average-file-size": { "value": 444.85714285714283, "value_type": "double" },
"person": {
"full-name": [ { "value": "Kevin Morrison", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } } ],
"type": { "label": "person", "root": "entity" }
}
}
{
"average-file-size": { "value": 1231.5, "value_type": "double" },
"person": {
"full-name": [ { "value": "Pearle Goodman", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } } ],
"type": { "label": "person", "root": "entity" }
}
}
Number of results
For this example, use a database with the IAM schema and sample data loaded.
A Fetch query can return fewer results, than it was matched by its match
clause.
Due to set semantics, if fetch clause projects not every variable from the match
clause,
some results might lose their uniqueness and made redundant.
Let’s try to match all entities of the person
type that have an full-name
attribute,
and then project the full-name
attribute only:
match
$p isa person, has full-name $n;
fetch $n;
{ "n": { "value": "Masako Holley", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } } }
{ "n": { "value": "Kevin Morrison", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } } }
{ "n": { "value": "Pearle Goodman", "type": { "label": "full-name", "root": "attribute", "value_type": "string" } } }
The match
clause returns pairs of person
and its full-name
.
The fetch
clause then returns only the full-name
attributes projected into a JSON.
If multiple entities have the same attribute,
that is represented in a database as ownership of the very same single attribute.
Since we filter the results to have only the $n
variable, the results include such a name, but only once.
Regardless of how many entities $p
own it.
To prevent such deduplication, in this example,
you can extend the results to include the $p
variable into the projection, since every entity is unique.
The results then will include pairs of $p
and $n
.