typeql
npx skills add https://github.com/caliluke/skills --skill typeql
Agent 安装分布
Skill 文档
TypeQL Language Reference for TypeDB 3.8+
This skill provides comprehensive guidance for writing TypeQL queries against TypeDB databases.
Note (3.8+): Trailing commas are now allowed in all comma-separated contexts (variable lists, statements, reductions) for easier query composition.
Note (3.8+): Unicode identifiers are now supported. Type names, attribute names, and variable names can use any Unicode XID_START character followed by XID_CONTINUE characters (e.g.,
åå,prénom,гоÑод).
Quick Reference
Transaction Types
| Type | Use For | Commit |
|---|---|---|
schema |
Define/undefine types, functions | Yes |
write |
Insert, update, delete data | Yes |
read |
Match, fetch, select queries | No (use close) |
Query Structure
[with ...] -- Inline function preamble
[define|undefine|redefine] -- Schema operations
[match] -- Pattern matching
[insert|put|update|delete] -- Data mutations
[select|sort|offset|limit] -- Stream operators
[require|distinct] -- Filtering operators
[reduce ... groupby] -- Aggregations
[fetch] -- JSON output
; -- EVERY query MUST end with a semicolon
1. Schema Definition
Schema definitions create types and constraints. Run in schema transactions.
Root Types (TypeDB 3.x)
In TypeDB 3.x, thing is no longer a valid root. The three roots are:
entityrelationattribute
Attribute Types
define
# Basic value types
attribute name, value string;
attribute age, value integer;
attribute salary, value double;
attribute is_active, value boolean;
attribute created_at, value datetime;
# With constraints
attribute email, value string @regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
attribute priority, value integer @range(1..5);
attribute status, value string @values("pending", "active", "completed");
Entity Types
define
# Simple entity
entity person;
# Entity with attributes
entity person,
owns name,
owns email @key, # Unique identifier
owns age @card(0..1); # Optional (0 or 1)
# Abstract entity (cannot be instantiated)
entity artifact @abstract,
owns name,
owns created_at;
# Entity inheritance
entity user sub artifact,
owns email @key;
Relation Types
define
# Basic relation with roles
relation employment,
relates employer,
relates employee;
# Entities playing roles
entity company,
plays employment:employer;
entity person,
plays employment:employee;
# Relation with cardinality on roles
relation manages,
relates manager @card(1), # Exactly one manager
relates report @card(1..); # One or more reports
# Relation owning attributes
relation employment,
relates employer,
relates employee,
owns start_date,
owns end_date @card(0..1);
# Self-referential relation
relation friendship,
relates friend; # Same role played twice
entity person,
plays friendship:friend;
Role Overriding (Inheritance)
define
# Base relation
relation relationship,
relates partner;
# Specialized relation with role override
relation marriage sub relationship,
relates spouse as partner; # 'spouse' overrides 'partner'
entity person,
plays marriage:spouse;
Type Aliases
define
# Create an alias for an existing type
attribute username alias name; # username is an alias for name
Annotations Reference
| Annotation | Usage | Description |
|---|---|---|
@key |
owns name @key |
Unique identifier for type |
@unique |
owns email @unique |
Unique but not primary key |
@card(n) |
owns age @card(0..1) |
Cardinality constraint |
@regex(...) |
value string @regex(...) |
Pattern validation |
@range(...) |
value integer @range(1..100) |
Value range |
@values(...) |
value string @values("a","b") |
Enum-like constraint |
@abstract |
entity @abstract |
Cannot be instantiated |
@cascade |
owns ref @cascade |
Cascade delete when owner deleted |
@independent |
owns tag @independent |
Attribute exists independently of owner |
@distinct |
owns item @distinct |
Each value can only be owned once |
@subkey(...) |
owns code @subkey(region) |
Composite key with another attribute |
2. Schema Modification
Undefine (Remove Schema Elements)
undefine
# Remove entire type
person;
# Remove capability from type
owns email from person;
plays employment:employee from person;
relates employee from employment;
# Remove annotation from type
@abstract from artifact;
# Remove annotation from capability
@key from person owns email;
# Remove role specialization
as spouse from marriage relates spouse;
# Remove function
fun get_active_users;
# Remove struct
struct address;
Redefine (Modify Existing Schema)
redefine
# Change annotation on type
entity person @abstract;
# Change annotation on capability
person owns age @card(1..1); # Make required
# Redefine function (replaces entirely)
fun count_users() -> integer:
match $u isa user, has status "active";
return count;
3. Data Operations (CRUD)
Insert
# Insert entity with attributes
insert
$p isa person,
has name "Alice",
has email "alice@example.com";
# Insert using anonymous variable (when reference not needed)
insert
$_ isa person, has name "Bob";
# Insert relation (match entities first)
match
$p isa person, has email "alice@example.com";
$c isa company, has name "Acme Inc";
insert
(employer: $c, employee: $p) isa employment,
has start_date 2024-01-15;
Put (Upsert)
# Put creates if not exists, matches if exists
# Useful for idempotent operations
match
$c isa company, has name "Acme Inc";
put
$p isa person, has email "alice@example.com", has name "Alice";
insert
(employer: $c, employee: $p) isa employment;
# Put finds existing person by email (key) or creates new one
# Then insert creates the relation
Update
# Update single-cardinality attribute (implicit replacement)
match
$p isa person, has email "alice@example.com";
update
$p has email "alice.smith@example.com";
# Update multi-cardinality: delete old, insert new
match
$p isa person, has name "Alice";
$p has nickname $old;
delete
$p has $old;
insert
$p has nickname "Ally";
Delete
# Delete entity
match
$p isa person, has email "alice@example.com";
delete
$p;
# Delete attribute from entity
match
$p isa person, has email "alice@example.com", has nickname $n;
delete
$p has $n;
# Alternative: delete attribute using 'of'
match
$p isa person, has email "alice@example.com", has nickname $n;
delete
has $n of $p;
# Delete relation
match
$p isa person, has email "alice@example.com";
$c isa company, has name "Acme Inc";
$e (employer: $c, employee: $p) isa employment;
delete
$e;
# Delete role player from relation (keeps relation)
match
$rel (member: $old_member, group: $g) isa membership;
$old_member has email "alice@example.com";
delete
links ($old_member) of $rel;
# Optional delete (try) - no error if not found
match
$p isa person, has email "alice@example.com";
delete
try {
$p has nickname $n;
};
4. Querying Data
Match + Fetch (Primary Query Pattern)
# Fetch specific attributes
match
$p isa person, has name $n, has email $e;
fetch {
"name": $n,
"email": $e
}
# Fetch all attributes of entity
match
$p isa person, has email "alice@example.com";
fetch {
"person": { $p.* }
}
# Fetch with attribute projection
match
$p isa person;
fetch {
"name": $p.name,
"email": $p.email
}
# Fetch multi-valued attributes as list
match
$p isa person, has email "alice@example.com";
fetch {
"name": $p.name,
"all_nicknames": [ $p.nickname ]
}
# Nested fetch for related data
match
$c isa company, has name "Acme Inc";
fetch {
"company": $c.name,
"employees": [
match
(employer: $c, employee: $p) isa employment;
fetch {
"name": $p.name
}
]
}
# Fetch with inline function
match
$c isa company;
fetch {
"company": $c.name,
"employee_count": (
match
(employer: $c, employee: $e) isa employment;
reduce $count = count;
)
}
Filtering Patterns
# Filter by type
match $x isa person;
# Filter by exact type (not subtypes)
match $x isa! person;
# Filter by attribute value
match $p isa person, has age > 30;
# Filter by relation
match
$c isa company, has name "Acme Inc";
(employer: $c, employee: $p) isa employment;
# Comparison operators: ==, !=, <, <=, >, >=, like, contains
match
$p isa person, has name $n;
$n like "^A.*"; # Regex match
match
$p isa person, has bio $b;
$b contains "engineer"; # Substring match
Identity Check (is)
# Check if two variables refer to the same concept
match
$p1 isa person;
$p2 isa person;
(friend: $p1, friend: $p2) isa friendship;
not { $p1 is $p2; }; # Exclude self-friendship
fetch { "person": $p1.name }
Conjunction (Grouping with AND)
# Explicit grouping with curly braces
match
$p isa person;
{
$p has age > 18;
$p has status "active";
}; # Both conditions must be true
Disjunction (OR)
match
$p isa person, has email $e;
{
$p has name "Alice";
} or {
$p has name "Bob";
};
fetch { "email": $e }
Negation (NOT)
# Find persons not employed by Acme
match
$p isa person;
not {
$c isa company, has name "Acme Inc";
(employer: $c, employee: $p) isa employment;
};
fetch { "unemployed": $p.name }
Optional Patterns (TRY)
# Match with optional pattern
match
$p isa person, has name $n;
try {
$p has email $e;
};
fetch {
"name": $n,
"email": $e # May be null if no email
}
5. Stream Operators
Order must be: sort, offset, limit
# Sort results
match
$p isa person, has name $n;
sort $n asc;
# Sort descending
match
$p isa person, has age $a;
sort $a desc;
# Pagination
match
$p isa person, has name $n;
sort $n asc;
offset 10;
limit 10;
# Select specific variables (like SQL SELECT)
match
$p isa person, has name $n, has email $e;
select $n, $e;
# Distinct results
match
$p isa person, has name $n;
distinct;
# Require variables to be bound (filter nulls from try)
match
$p isa person, has name $n;
try { $p has email $e; };
require $e; # Only return rows where email exists
6. Aggregations
# Count
match
$p isa person;
reduce $count = count;
# Count specific variable
match
$p isa person, has email $e;
reduce $count = count($e);
# Multiple aggregations
match
$p isa person, has salary $s;
reduce
$max = max($s),
$min = min($s),
$avg = mean($s);
# Group by
match
$c isa company;
(employer: $c, employee: $e) isa employment;
reduce
$count = count groupby $c;
# Multiple groupby variables
match
$c isa company, has industry $ind;
(employer: $c, employee: $e) isa employment;
$e has department $dept;
reduce
$count = count groupby $ind, $dept;
Available Reducers
| Reducer | Usage | Description |
|---|---|---|
count |
count or count($var) |
Count results |
sum($var) |
sum($salary) |
Sum numeric values |
min($var) |
min($age) |
Minimum value |
max($var) |
max($age) |
Maximum value |
mean($var) |
mean($score) |
Average |
median($var) |
median($score) |
Median |
std($var) |
std($score) |
Standard deviation |
list($var) |
list($name) |
Collect into list |
7. Expressions and Computed Values
Arithmetic Operators
match
$p isa product, has price $price, has quantity $qty;
let $subtotal = $price * $qty; # Multiplication
let $with_tax = $subtotal * 1.1; # More multiplication
let $discount = $subtotal / 10; # Division
let $final = $subtotal - $discount; # Subtraction
let $total = $final + 5.00; # Addition (shipping)
let $squared = $price ^ 2; # Power/exponent
let $remainder = $qty % 3; # Modulo
fetch {
"product": $p.name,
"total": $total
}
Assignment and Literals
match
$p isa product, has price $price;
let $vat_rate = 0.2; # Assign literal
let $price_with_vat = $price * (1 + $vat_rate);
fetch {
"price_with_vat": $price_with_vat
}
Built-in Functions
match
$p isa product, has price $price, has name $name;
# NOTE: ceil, floor, round only work on double/decimal, not integers
let $rounded = round($price); # Round to nearest integer
let $ceiling = ceil($price); # Round up
let $floored = floor($price); # Round down
let $absolute = abs($price - 100); # Absolute value
let $name_len = len($name); # String length (note: len, not length)
let $higher = max($price, 10.0); # Maximum of two values
let $lower = min($price, 100.0); # Minimum of two values
fetch { "stats": { $rounded, $ceiling, $floored } }
# String concatenation
match
$u isa user, has first_name $fn, has last_name $ln;
let $full = concat($fn, " ", $ln);
fetch { "full_name": $full }
# Get IID of a concept (3.8+)
match
$p isa person, has email "alice@example.com";
fetch {
"iid": iid($p) # Get internal identifier
}
# Get type label (3.8+) - NOTE: label() works on TYPE variables, not instances!
# Must bind the exact type first using isa! and a type variable
match
$p isa! $t, has email "alice@example.com";
$t sub person; # Bind $t to exact type of $p
fetch {
"iid": iid($p),
"type": label($t) # label() on TYPE variable $t
}
8. List Operations
List Literals
match
$p isa person, has name $name;
let $tags = ["active", "verified", "premium"]; # List literal
fetch { "name": $name, "tags": $tags }
List Indexing
match
$p isa person, has score $scores; # Multi-valued attribute
let $first = $scores[0]; # First element (0-indexed)
let $second = $scores[1]; # Second element
fetch { "first_score": $first }
List Slicing
match
$p isa person, has score $scores;
let $top_three = $scores[0..3]; # Elements 0, 1, 2
let $rest = $scores[3..10]; # Elements 3 through 9
fetch { "top_scores": $top_three }
9. Structs
Struct Definition
define
# Define a struct type
struct address:
street value string,
city value string,
zip value string?, # Optional field (nullable)
country value string;
Using Structs
# Insert with struct value
insert
$p isa person,
has name "Alice",
has home_address { street: "123 Main St", city: "Boston", zip: "02101", country: "USA" };
# Match struct fields
match
$p isa person, has home_address $addr;
$addr isa address { city: "Boston" };
fetch { "person": $p.name }
Struct Destructuring
# Destructure struct in let
match
$p isa person, has home_address $addr;
let { city: $city, zip: $zip } = $addr;
fetch {
"person": $p.name,
"city": $city
}
10. Functions
Define reusable query logic in schema.
Function Definition
define
# Return stream of values
fun get_active_users() -> { string }:
match
$u isa user, has status == "active", has email $e;
return { $e };
# Return single value with aggregation
fun count_users() -> integer:
match
$u isa user;
return count;
# Function with parameters
fun get_user_projects($user_email: string) -> { string }:
match
$u isa user, has email == $user_email;
(member: $u, project: $p) isa membership;
$p has name $name;
return { $name };
# Return multiple values in stream
fun get_user_details($email: string) -> { string, string }:
match
$u isa user, has email == $email, has name $n, has role $r;
return { $n, $r };
# Return first match only
fun get_oldest_user() -> string:
match
$u isa user, has name $n, has age $a;
sort $a desc;
return first $n;
# Return last match only
fun get_youngest_user() -> string:
match
$u isa user, has name $n, has age $a;
sort $a desc;
return last $n;
# Return boolean (existence check)
fun user_exists($email: string) -> boolean:
match
$u isa user, has email == $email;
return check; # Returns true if match found, false otherwise
Using Functions
# Call function in match with 'let ... in'
match
let $emails in get_active_users();
fetch { "active_emails": $emails }
# Call function with parameter
match
let $projects in get_user_projects("alice@example.com");
fetch { "projects": $projects }
# Call function returning single value
match
let $count in count_users();
fetch { "total_users": $count }
# Use function in expression
match
$u isa user;
let $exists in user_exists($u.email);
fetch { "user": $u.name, "verified": $exists }
Inline Functions with WITH
# Define function inline for single query
with
fun active_in_dept($dept: string) -> { user }:
match
$u isa user, has department == $dept, has status "active";
return { $u };
match
let $user in active_in_dept("Engineering");
fetch { "engineer": $user.name }
11. IID (Internal Identifier) Operations
# Match by IID (for direct lookups)
match
$entity iid 0x1f0005000000000000012f;
fetch { "data": { $entity.* } }
# Get IID using iid() function (3.8+)
match
$p isa person, has email "alice@example.com";
fetch {
"person": $p.name,
"iid": iid($p)
}
# Get IID and exact type label together (3.8+)
# NOTE: label() requires a TYPE variable, use isa! to bind it
match
$p isa! $t, has email "alice@example.com";
$t sub person;
fetch {
"iid": iid($p),
"type": label($t)
}
12. Rules (Inference)
define
# Transitive relation
rule transitive_manages:
when {
(manager: $a, report: $b) isa manages;
(manager: $b, report: $c) isa manages;
} then {
(manager: $a, report: $c) isa manages;
};
# Derived relation
rule colleague_rule:
when {
(employer: $c, employee: $p1) isa employment;
(employer: $c, employee: $p2) isa employment;
not { $p1 is $p2; };
} then {
(colleague: $p1, colleague: $p2) isa colleague;
};
# Rule with disjunction
rule privileged_access:
when {
$u isa user;
{ $u has role "admin"; } or { $u has role "superuser"; };
} then {
(accessor: $u) isa privileged;
};
13. Common Patterns
Upsert (Insert if not exists)
# Use 'put' for upsert behavior
match
$c isa company, has name "Acme Inc";
put
$p isa person, has email "new@example.com", has name "New Person";
insert
(employer: $c, employee: $p) isa employment;
Polymorphic Queries
# Query abstract supertype to get all subtypes
match
$artifact isa artifact, has name $n; # Gets all subtypes
fetch { "name": $n }
# Query exact type only (not subtypes)
match
$artifact isa! artifact, has name $n; # Only direct instances
fetch { "name": $n }
Graph Traversal
# Find all connected nodes (1-hop)
match
$center isa entity, has id == "target-id";
$rel links ($center), links ($neighbor);
not { $neighbor is $center; };
fetch {
"center": $center.id,
"neighbor": $neighbor.id
}
Existence Check
# Check if pattern exists
match
$u isa user, has email "alice@example.com";
not { (member: $u) isa team_membership; };
# Returns results only if user exists but has no team
14. Critical Pitfalls
Match Clauses are Simultaneous (AND)
All statements in match must be satisfied together. Not sequential.
# WRONG assumption: "get all books, then find promotions"
# ACTUAL: Only returns books that ARE in promotions
match
$b isa book;
(book: $b, promo: $p) isa promotion;
Unconstrained Variables = Cross Join
# PROBLEM: Returns every book x every promotion (Cartesian product)
match
$b isa book;
$p isa promotion; # Not linked to $b!
# FIX: Link variables through relations
match
$b isa book;
(book: $b, promotion: $p) isa book_promotion;
Symmetric Relations Return Duplicates
# PROBLEM: Returns (A,B) and (B,A)
match
(friend: $p1, friend: $p2) isa friendship;
# FIX: Add asymmetric constraint
match
$p1 has email $e1;
$p2 has email $e2;
(friend: $p1, friend: $p2) isa friendship;
$e1 < $e2; # Each pair only once
Cardinality Validated at Commit
Insert violations don’t fail immediately – only on commit.
Sorting Loads All Results
sort requires loading all matching data into memory before pagination.
Variable Scoping in OR/NOT
Variables inside or blocks are not guaranteed bound outside:
# INVALID: $company might not be bound
match
$p isa person;
{
(employee: $p, employer: $company) isa employment;
} or {
$p has status "freelancer";
};
not { $company has name "BadCorp"; }; # $company may be unbound!
# VALID: Bind variable in parent scope first
match
$p isa person;
(employee: $p, employer: $company) isa employment;
not { $company has name "BadCorp"; };
Variable Reuse in OR Branches
# INVALID: $x means different things in each branch
match {
(person: $p, document: $x) isa editing;
} or {
(person: $p, message: $x) isa messaging;
};
# VALID: Use different variable names
match {
(person: $p, document: $doc) isa editing;
} or {
(person: $p, message: $msg) isa messaging;
};
15. CLI Notes
Command Execution
# Server commands: NO semicolon
typedb console --command "database list"
# TypeQL in transactions: WITH semicolons
typedb console --command "transaction mydb schema" --command "define entity person;"
Script Files (.tqls)
# Console commands without semicolons
transaction mydb schema
define
entity person;
commit
Transaction Lifecycle
schema,writetransactions: usecommitreadtransactions: useclose
16. Value Types Reference
| TypeQL Type | Description | Example Literal |
|---|---|---|
string |
UTF-8 text | "hello" |
boolean |
true/false | true, false |
integer |
64-bit signed int | 42, -17 |
double |
Double-precision float | 3.14159 |
decimal |
Precise decimal | 99.99dec |
date |
Date only | 2024-01-15 |
datetime |
Date and time | 2024-01-15T10:30:00 |
datetime-tz |
With timezone | 2024-01-15T10:30:00 America/New_York |
duration |
Time duration | P1Y2M3D, PT4H30M |
Duration Format (ISO 8601)
P[n]Y[n]M[n]DT[n]H[n]M[n]S
P1Y2M3D = 1 year, 2 months, 3 days
PT4H30M = 4 hours, 30 minutes
P1W = 1 week
PT1H30M45S = 1 hour, 30 minutes, 45 seconds
17. Debugging Queries
Test Match Before Write
# Before running delete:
match
$u isa user, has status "inactive";
fetch { "will_delete": $u.email }
# Then execute:
match
$u isa user, has status "inactive";
delete $u;
Check Schema
# In read transaction - list entity types
match
$type sub entity;
fetch { "entity_types": $type }
# List relation types
match
$rel sub relation;
fetch { "relation_types": $rel }
# List attribute types
match
$attr sub attribute;
fetch { "attribute_types": $attr }
Verify Cardinality
# Count instances per type
match
$type sub entity;
$instance isa! $type;
reduce $count = count groupby $type;
18. Complete Operator Reference
Comparison Operators
| Operator | Description | Example |
|---|---|---|
== |
Equal | $age == 30 |
!= |
Not equal | $status != "done" |
< |
Less than | $age < 18 |
<= |
Less than or equal | $age <= 65 |
> |
Greater than | $salary > 50000 |
>= |
Greater than or equal | $score >= 90 |
like |
Regex match | $name like "^A.*" |
contains |
Substring match | $bio contains "dev" |
Math Operators
| Operator | Description | Example |
|---|---|---|
+ |
Addition | $a + $b |
- |
Subtraction | $a - $b |
* |
Multiplication | $a * $b |
/ |
Division | $a / $b |
% |
Modulo | $a % $b |
^ |
Power | $a ^ 2 |
Expression Functions
| Function | Description | Example |
|---|---|---|
abs($x) |
Absolute value | abs($n - 10) |
ceil($x) |
Round up (double/decimal only) | ceil($price) |
floor($x) |
Round down (double/decimal only) | floor($price) |
round($x) |
Round nearest (double/decimal) | round($price) |
len($s) |
String length | len($name) |
max($a,$b) |
Maximum of two values | max($x, 0) |
min($a,$b) |
Minimum of two values | min($x, 100) |
iid($var) |
Get internal identifier (3.8+) | iid($p) |
label($t) |
Get type label (TYPE var only!) | label($t) |
concat(â¦) |
Concatenate strings | concat($a," ",$b) |
Pattern Keywords
| Keyword | Description |
|---|---|
isa |
Type check (includes subtypes) |
isa! |
Exact type check (excludes subtypes) |
has |
Attribute ownership |
links |
Role player in relation |
is |
Identity comparison |
or |
Disjunction |
not |
Negation |
try |
Optional pattern |