Relations
SurrealDB's graph model lives in relation tables: tables whose records are edges, each connecting an in record to an out record. Surform spells relations in the header, in RELATE's own order.
The arrow form
namespace acme;
database shop;
table user->wrote->post {
enforced;
at: datetime {
default: time::now();
};
};DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE TABLE OVERWRITE wrote TYPE RELATION IN `user` OUT `post` ENFORCED SCHEMALESS;
DEFINE FIELD OVERWRITE `at` ON wrote TYPE datetime DEFAULT time::now();
from -> edge -> to. The edge table's own fields (at above) nest inside, exactly like a normal table's. What the header adds:
- the sides become typed —
inmust be auserrecord,outapostrecord; enforced;adds the existence check: writing an edge whose endpoints don't exist is rejected. Without it, dangling endpoints are allowed.
Sides can be lists (with |, SurrealQL's separator) and either side can be omitted to leave it unconstrained:
namespace acme;
database shop;
table user | org->wrote->post | comment {
enforced;
};
table follower->watched->; // IN-only: out is unconstrained
table->tagged->post; // OUT-only: in is unconstrainedDEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE TABLE OVERWRITE wrote TYPE RELATION IN `user` | org OUT `post` | `comment` ENFORCED SCHEMALESS;
DEFINE TABLE OVERWRITE watched TYPE RELATION IN follower SCHEMALESS;
DEFINE TABLE OVERWRITE tagged TYPE RELATION OUT `post` SCHEMALESS;
The unconstrained relation
An edge with no named sides at all — the generic join:
namespace acme;
database shop;
table relation generic_edge;DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE TABLE OVERWRITE generic_edge TYPE RELATION SCHEMALESS;
table -> generic_edge ->; means the same thing; the relation word is the canonical spelling.
Lightweight edges (SurrealDB 3.3)
A lightweight relation stores no edge records at all — an edge is just the canonical pair [in, out]. RELATE becomes idempotent per pair, nothing beyond the endpoints can be mutated, and the endpoints must be named:
namespace acme;
database shop;
table person->likes->post lightweight;
table person->watched->post lightweight {
permissions: full;
inline edges: 8;
};DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE TABLE OVERWRITE likes TYPE RELATION IN person OUT `post` ENFORCED LIGHTWEIGHT SCHEMALESS;
DEFINE TABLE OVERWRITE watched TYPE RELATION IN person OUT `post` ENFORCED LIGHTWEIGHT SCHEMALESS
PERMISSIONS FULL
INLINE EDGES 8;
lightweight implies enforced — the engine always prints the pair together, and so does Surform. Because a lightweight edge has no records, the record-oriented clauses are rejected: drop;, changefeed, schemafull, and any nested field, index, or event:
namespace acme;
database shop;
table person->likes->post lightweight {
schemafull;
};SDL-E1044 (spec/table.md)
x clause does not apply to this table kind: a LIGHTWEIGHT relation cannot be declared SCHEMAFULL: its edges store no records
,-[4:27]
3 |
4 | table person->likes->post lightweight {
: ^^^^^|^^^^^
: `-- not this table kind's clause
5 | schemafull;
`----
help: each `table` kind defines its own clauses — check the header kind and the clause list in the linked spec
A lightweight table can grow into a full relation later (while it holds no edges) by dropping the word.
The in/out fields you never write
Every relation table carries hidden in and out fields — the endpoints. You never declare them; the engine installs them from the header, the reader folds them back, and rewriting them by hand is a lint (SDL-L0010 — a record-typed in/out field would redefine the endpoints behind the table's back).
Relate-ability for normal tables
RELATE requires a relation table — with one exception: a table declared any accepts edges too. If you have a schemaless table that must take RELATE today and become a real relation someday, say table any thing.
Where next
- Fields —
inline;payloads on edge fields, andreferenceon plain record fields. - The linter — the un-enforced relation warning (
SDL-L0006). - Indexes — indexing edge fields for fast traversals.