Schema Language (`.pg`)
A schema declares the node and edge types a graph accepts. OmniGraph validates
A schema declares the node and edge types a graph accepts. OmniGraph validates
the same schema and constraints for mutation queries, loads, and branch merges.
Actor attribution on a commit does not create a graph node. Applications may
declare their own Actor, OmniActor, or other provenance types; these are
ordinary customer-owned types with no automatic rows or permission grants.
node Person {
email: String @key
display_name: String
age: I32?
@range(age, 0..150)
}
node Company {
slug: String @key
name: String
}
edge WorksAt: Person -> Company @card(0..) {
role: String?
@unique(@src, @dst)
}Comments use // ... or /* ... */.
Declarations
interface <Name> { <properties> }
node <Name> [implements <Interface>, ...] { <properties and constraints> }
edge <Name>: <FromNode> -> <ToNode> [@card(...)] { <properties and constraints> }Interfaces provide reusable properties. An edge's endpoint node types must
already be declared. Query traversals spell an edge with a lowercase first
letter (worksAt for WorksAt); lookup is otherwise case-insensitive.
Property types
| Type | Values |
|---|---|
String | UTF-8 text |
Bool | true or false |
I32, I64 | Signed integers |
U32, U64 | Unsigned integers |
F32, F64 | Floating-point numbers |
Date | Calendar date |
DateTime | Timestamp with millisecond precision |
Vector(N) | N 32-bit floating-point values |
Blob | Managed bytes or an external reference; see Blobs |
enum(a, b, ...) | One of the declared strings |
[T] | A list of scalar T values |
T? | A nullable value |
Property names starting with _ are reserved for system columns and are
rejected when a schema is admitted. The reserved namespace covers Lance's
virtual system columns (_rowid, _rowaddr, _rowoffset,
_row_created_at_version, _row_last_updated_at_version) and OmniGraph's
own implicit stored columns, spelled __id on nodes and edges and
__src/__dst on edges for newly created graphs.
Supported existing graphs without the system-columns feature keep the earlier spellings id, src,
and dst for their implicit columns. They reserve id on nodes and edges,
and src/dst on edges. On current graphs all three names are ordinary
property names. Query meta-fields use @id, @src, and @dst; projected
node objects use @id, and JSONL exports use a top-level id. The schema
endpoint (GET /schema) reports the physical spellings in system_columns.
_distance and _score are also reserved for new declarations: search-ordered
queries rank results by those columns. A graph whose schema already declared
either name before this reservation keeps opening; only new schemas are
refused.
On edge types the names from and to are also reserved: they are the
insert parameters that name the endpoints.
Constraints
Constraints can be written in the type body. @key, @unique, and @index
also have a single-property shorthand.
| Constraint | Applies to | Meaning |
|---|---|---|
@key(p, ...) | node or edge | The property tuple identifies the entity; its id is derived from it. Key properties must be non-null scalar values. An edge key must include both @src and @dst, and a key may be declared only when the type is created. |
@unique(p, ...) | node or edge | No two entities may share the property tuple. Edge constraints may include @src and @dst. |
@index(p, ...) | node or edge | Declares index intent. Indexes affect performance, not correctness. |
@range(p, min..max) | node | Restricts a numeric property; either bound may be omitted. |
@check(p, "regex") | node | Requires a String property to match the expression. |
@card(min..max) | edge | Restricts the number of edges; omit max for an unbounded range, as in @card(1..). The default is unbounded from zero. |
Blob, list, and vector properties cannot be keys. Blob properties also cannot
be unique or indexed.
Current automatic property indexes are created for single-property node declarations: orderable scalars and enums receive a scalar index, free-text Strings receive a full-text index, and vectors receive a vector index. Composite declarations and edge-property declarations are accepted as schema intent but do not currently create a property index. Edge endpoints are indexed independently for traversal. See Search.
IDs
Every node and edge has a String id. On the wire it rides at the top level of
the load or export envelope, under the fixed key id, beside type (nodes) or
edge; data holds user properties. Legacy-vintage graphs also accept
data.id as identity when the top-level id is absent; providing both is
refused. In queries the id is the meta-field
$p.@id and an edge's endpoints are $e.@src and $e.@dst
(system fields); a bare id in a query
is always a user property of that name.
- A node with
@keyderives its id from the complete typed key tuple. Renaming a key property with@rename_fromdoes not change existing ids. - A node without a key receives a generated id unless input supplies one.
- An edge with
@keyderives its id the same way, in the catalog's key order:@src, then@dst, then any scalar members. A composite id encodes as a JSON array of the member values, for example["Alice","Bob"]. - An edge without a key uses generated or supplied ids. Edges store their
endpoints as the graph's endpoint columns,
__srcand__dston graphs created by this binary.
For hand-authored load data, omit a keyed node's or keyed edge's top-level id
and let OmniGraph derive it; a supplied id on a keyed edge must equal the
derived id exactly. Export includes ids so a graph can be rebuilt without losing edge
references.
Annotations
@rename_from("OldName")on a node, edge, or property declares a rename during schema migration. Use the current name everywhere after applying it.@description("...")adds human-readable metadata to types and properties.@instruction("...")adds usage guidance to node and edge types.@embed("source_property", model="model-id")associates a vector with its source String property. The model is optional; see Embeddings.
Property-level @key, @unique, and @index are shorthands for their
single-property constraints. Unknown annotations are retained as metadata but
have no built-in behavior.
Schema changes
Always preview a direct schema change before applying it:
omnigraph schema plan --schema next.pg graph.omni
omnigraph schema apply --schema next.pg graph.omniSupported changes include adding types, adding nullable properties, renaming
nodes, edges, or properties with @rename_from, adding index declarations,
widening an enum with new values, updating descriptions or instructions, and
dropping node, edge, or property declarations.
None of these changes rewrites existing rows. Adding a nullable property,
renaming a property, or dropping a property changes only the table's metadata:
existing rows read the new property as null, no stored value or Blob is read,
and existing indexes keep covering the rows they covered. The time and memory
the apply takes therefore do not grow with the number of rows or the size of
their values. They still grow with the table's metadata: its columns, its
indexes and its fragments, of which a table written by many small commits has
more until omnigraph optimize compacts them.
Applying changed schema text, including comments or formatting, records the new source in one graph commit even when no table changes are needed. Applying the same accepted source again leaves the graph unchanged.
Changes such as adding a required property to existing entities, changing a property type (except enum widening), changing edge endpoints or cardinality, changing a node's implemented interfaces, and adding or removing most constraints are rejected. The plan reports the exact unsupported step before anything changes.
A drop removes the declaration from the current schema and reclaims no storage
at apply. Older commits still read the dropped data until omnigraph cleanup
stops retaining them. A dropped type's data is reclaimed by that cleanup. A
dropped property's values also stay in the table's current data files, beside
the properties that remain, until the next omnigraph optimize: every
optimize rewrites each fragment of a table that still stores a dropped
property's values, copying only the remaining properties. Optimize rewrites
main only, and cleanup deletes the old files only once nothing it retains
references them: a live branch keeps the commit it was created from and its
head, a tag keeps the snapshot it names, and the retention policy keeps older
commits. Merging a branch releases none of its files; only deleting the branch
does. To erase dropped data, run optimize after the drop, delete every branch
created from a commit before that optimize and every tag naming such a commit,
then run cleanup with a retention that excludes the commits before the
optimize, for example --keep 1; after that, the dropped data cannot be
recovered. Review the plan carefully, and treat that cleanup as the step that
cannot be undone. See optimize and
cleanup.
Cluster-managed graphs change schema through omnigraph cluster apply. Direct
schema apply and the server schema-apply endpoint refuse cluster-managed graphs.
Diagnostic codes
Migration rejections may include a stable OG-... code. Match automation on the
code rather than the message text.
| Code | Meaning |
|---|---|
OG-DS-102 | Drop a node type that contains entities. |
OG-DS-103 | Drop an edge type that contains entities. |
OG-DS-104 | Drop a populated property. |
OG-MF-103 | Add a required property to a populated type. |
OG-MF-106 | Change a property's type, including enum narrowing or renaming. |
omnigraph schema plan includes these codes in human and JSON output.