CREATE SCHEMA
Defines a new node type (schema) with named, typed fields.
CREATE SCHEMA SchemaName (
field1: TYPE,
field2: TYPE,
...
);
CREATE SCHEMA User (
name: STRING,
age: INT64
);
CREATE SCHEMA Company (
name: STRING,
revenue: FLOAT64
);
CREATE EDGE SCHEMA WORKS_AT (
since: INT64,
role: STRING
);
CREATE NODE
Creates a new node of a given schema. Optionally returns the auto-assigned ID.
CREATE NODE SchemaName ( field = value, ... ) [RETURN id];
CREATE NODE User (name = "Alice", age = 30) RETURN id;
CREATE NODE User (name = "Bob", age = 25);
CREATE NODE Company (name = "Google", revenue = 300.5);
id automatically (each schema has its own counter starting from 0). Use RETURN id to see the assigned ID.
CREATE EDGE
Creates a directed edge between two nodes. Nodes are referenced by Schema(id) or property lookup.
CREATE [UNIQUE] EDGE edge_type
FROM Schema(id)
TO Schema(id);
// By ID
CREATE EDGE friend FROM User(0) TO User(1);
// By property lookup
CREATE EDGE works_at
FROM (User{name = "Alice"})
TO (Company{name = "Google"});
// Unique edge (prevents duplicates)
CREATE UNIQUE EDGE manages
FROM User(0) TO User(1);
CREATE EDGE ... WITH (...) property assignment is not yet wired in the shell. Create the edge first, then set properties with UPDATE MATCH ... SET e.field = ....
MATCH
The primary query statement. Uses Cypher-inspired pattern syntax to traverse the graph.
MATCH <pattern> [, <pattern> ...]
[WHERE <condition>]
[SELECT <fields>];
Node Patterns
(alias:SchemaName) // bind alias to schema
(alias) // alias only (schema inferred)
Edge Patterns
// Outgoing edge
-[:EDGE_TYPE JOIN]->
-[e:EDGE_TYPE JOIN]-> // bind edge alias
// Incoming edge
<-[:EDGE_TYPE JOIN]-
Full Examples
MATCH (u:User)-[:friend INNER]->(f:User)
WHERE u.age > 25
SELECT u.name, f.name;
MATCH (u:User)-[:friend INNER]->(f:User)
-[:works_at INNER]->(c:Company)
WHERE c.name = "Google"
SELECT u.name, f.name, c.name;
MATCH (u:User)-[:friend LEFT]->(f:User)
SELECT u.name, f.name;
// Result: all users, f.name = NULL if no friends
MATCH (u:User)-[:friend INNER]->(f:User),
(f)-[:works_at INNER]->(c:Company)
SELECT u.name, c.name;
DELETE
Removes nodes or edges from the graph.
// Delete a specific node by ID
DELETE User(123);
// Delete nodes matching a pattern
DELETE (u:User) WHERE u.age < 18;
// Delete edges by type + direction
DELETE EDGE friend FROM User(0) TO User(1);
// Delete all outgoing edges of a type from a node
DELETE EDGE friend FROM User(0);
// Delete all incoming edges of a type to a node
DELETE EDGE works_at TO Company(0);
UPDATE
Modifies field values on existing nodes. Supports three forms: by ID (direct), by pattern (single schema + optional WHERE), and by MATCH (traversals / joins + optional WHERE).
Form 1 — Update by ID
Targets a single node using Schema(id). Field names are bare (no alias prefix).
UPDATE Schema(id) SET field = value [, field = value ...] ;
// Update a single field
UPDATE User(0) SET age = 31;
// Update multiple fields at once (creates one version)
UPDATE User(0) SET name = "Alice B.", age = 31;
Form 2 — Update by Pattern
Uses a node pattern (alias:Schema) with an optional WHERE clause to match nodes. Field names must be alias-qualified (alias.field).
UPDATE (alias:Schema) SET alias.field = value [, ...]
[WHERE alias.field op value] ;
// Update all users named Alice
UPDATE (u:User) SET u.age = 31
WHERE u.name = "Alice";
// Update with compound condition
UPDATE (u:User) SET u.name = "Senior"
WHERE u.age > 30 AND u.age < 50;
// Update all nodes of a schema (no WHERE)
UPDATE (u:User) SET u.age = 0;
Form 3 — Update by MATCH (traversals / joins)
Uses a full MATCH pattern with traversals to find nodes across multiple schemas, then applies SET assignments. Field names must be alias-qualified. Multiple schemas can be updated in a single statement, including edge aliases.
UPDATE MATCH (a:Schema1)-[e:EDGE_TYPE]->(b:Schema2)
SET a.field = value [, b.field = value ...] [, e.field = value ...]
[WHERE alias.field op value] ;
// Update both user and company for a traversal
UPDATE MATCH (u:User)-[:WORKS_AT]->(c:Company)
SET u.employed = true, c.size = 1
WHERE c.name = "Acme Corp";
// Update only one side of the relationship
UPDATE MATCH (u:User)-[:WORKS_AT]->(c:Company)
SET u.status = "employed"
WHERE c.name = "Google";
// Same-schema traversal (e.g. friends)
UPDATE MATCH (a:User)-[:FRIEND]->(b:User)
SET a.has_friend = true, b.has_friend = true;
// Update edge properties by alias
UPDATE MATCH (u:User)-[e:WORKS_AT]->(c:Company)
SET e.since = 2025, e.role = "engineer"
WHERE u.name = "Alice";
SET clause, TundraDB creates one version for the entire batch — not one per field.
SET age = 31 is only valid in the by-ID form. In pattern and MATCH forms you must write SET u.age = 31.
COMMIT
Persists the current database state to disk (Parquet files + JSON metadata).
COMMIT;
COMMIT, data lives in-memory only. Call COMMIT before shutting down to persist.
SHOW
Inspect edge data and available edge types.
// List all edge types
SHOW EDGE TYPES;
// Show edges of a specific type
SHOW EDGES friend;
WHERE Clause
Filters results using boolean expressions. Supports AND, OR, and parenthesized grouping. Works for node aliases and edge aliases (for example, e.since).
WHERE alias.field operator value
[AND|OR alias.field operator value]
WHERE u.age >= 25 AND u.age < 40
WHERE c.name = "Google" OR c.name = "Apple"
WHERE e.since >= 2025
WHERE (u.age > 30 AND u.name = "Alice") OR u.age < 20
SELECT Clause
Choose which fields to include in the result table. Supports aliasing with AS, edge field selection, and selecting a full edge alias.
SELECT alias.field [AS alias_name], ...
SELECT e; // full edge alias expansion
MATCH (u:User)-[:works_at INNER]->(c:Company)
SELECT u.name AS employee, c.name AS company, u.age;
MATCH (u:User)-[e:WORKS_AT]->(c:Company)
SELECT u.name, e.since, e;
SET Clause
Specifies field assignments in an UPDATE statement. Comma-separated list of field = value pairs.
SET field = value [, field = value ...]
| Update Form | Field Name Format | Example |
|---|---|---|
| By ID | Bare name | SET name = "Alice", age = 31 |
| By Pattern | Alias-qualified | SET u.name = "Alice", u.age = 31 |
SET clause are applied atomically — one version is created per node, regardless of how many fields are changed.
JOIN Types
Specified inside the edge pattern -[:EDGE_TYPE JOIN]->. Controls how unmatched nodes are handled.
| Join | Sources | Targets | Description |
|---|---|---|---|
INNER |
Only matched | Only matched | Default. Both sides must have a connection. |
LEFT |
All kept | Only matched | Keep all source nodes; target = NULL if no edge. |
RIGHT |
Only matched | All kept | Keep all target nodes; source = NULL if no edge. |
FULL |
All kept | All kept | Keep both sides. NULLs for unmatched on either side. |
// INNER: only Alice (has a friend)
MATCH (u:User)-[:friend INNER]->(f:User);
// LEFT: all users, f=NULL for users without friends
MATCH (u:User)-[:friend LEFT]->(f:User);
// FULL: all users on both sides, NULLs where no match
MATCH (u:User)-[:friend FULL]->(f:User);
unmatched = all_target_ids - matched_source_ids to avoid duplicates. For cross-schema joins, it uses all_target_ids - matched_target_ids.
Data Types
| TundraQL | Internal | Arrow | Description |
|---|---|---|---|
STRING | ValueType::STRING | utf8 | Variable-length UTF-8 string |
INT64 | ValueType::INT64 | int64 | 64-bit signed integer |
FLOAT64 | ValueType::DOUBLE | float64 | 64-bit floating point |
| — | ValueType::INT32 | int32 | 32-bit signed integer (C++ API only) |
| — | ValueType::FLOAT | float32 | 32-bit floating point (C++ API only) |
| — | ValueType::BOOL | boolean | Boolean (C++ API only) |
| — | FIXED_STRING16/32/64 | utf8 | Fixed-size strings for memory optimization |
StringArena — short strings (≤16/32/64 bytes) are stored in fixed-size pools for cache efficiency. Longer strings go into a variable-length pool.
Operators
| Operator | Meaning | Example |
|---|---|---|
= | Equal | u.name = "Alice" |
!= | Not equal | u.age != 30 |
> | Greater than | u.age > 25 |
>= | Greater or equal | u.age >= 25 |
< | Less than | u.age < 40 |
<= | Less or equal | u.age <= 40 |
AND | Logical and | u.age > 20 AND u.age < 40 |
OR | Logical or | u.name = "Alice" OR u.name = "Bob" |
Pattern Syntax Reference
Complete pattern grammar used in MATCH and DELETE statements.
pattern := nodePattern ( edgePattern nodePattern )*
nodePattern := '(' alias [ ':' Schema ] ')'
edgePattern := '-[' [ ':' type ] [ join ] ']->' // outgoing
| '<-[' [ ':' type ] [ join ] ']-' // incoming
join := INNER | LEFT | RIGHT | FULL
value := STRING_LITERAL | INTEGER | FLOAT
// 1. Define schemas
CREATE SCHEMA User (name: STRING, age: INT64);
CREATE SCHEMA Company (name: STRING, size: INT64);
// 2. Create nodes
CREATE NODE User (name = "Alice", age = 30) RETURN id;
CREATE NODE User (name = "Bob", age = 25) RETURN id;
CREATE NODE Company (name = "Google", size = 3000); // → Company id: 0
// 3. Create edges (IDs are per-schema: User has 0,1 — Company has 0)
CREATE EDGE friend FROM User(0) TO User(1);
CREATE EDGE works_at FROM User(1) TO Company(0);
// 4. Query: Alice's friends who work at Google
MATCH (u:User)-[:friend INNER]->(f:User)
-[:works_at INNER]->(c:Company)
WHERE u.name = "Alice" AND c.name = "Google"
SELECT u.name AS user, f.name AS friend, c.name AS company;
// 5. Update: Alice turned 31
UPDATE User(0) SET age = 31;
// 6. Bulk update: set all users older than 30 to "Senior"
UPDATE (u:User) SET u.name = "Senior" WHERE u.age > 30;
// 7. Update with MATCH (traversal) — set employed flag for Google employees
UPDATE MATCH (u:User)-[:works_at]->(c:Company)
SET u.name = "Employed"
WHERE c.name = "Google";
// 8. Persist
COMMIT;