Omnigraph

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

TypeValues
StringUTF-8 text
Booltrue or false
I32, I64Signed integers
U32, U64Unsigned integers
F32, F64Floating-point numbers
DateCalendar date
DateTimeTimestamp with millisecond precision
Vector(N)N 32-bit floating-point values
BlobManaged 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.

ConstraintApplies toMeaning
@key(p, ...)node or edgeThe 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 edgeNo two entities may share the property tuple. Edge constraints may include @src and @dst.
@index(p, ...)node or edgeDeclares index intent. Indexes affect performance, not correctness.
@range(p, min..max)nodeRestricts a numeric property; either bound may be omitted.
@check(p, "regex")nodeRequires a String property to match the expression.
@card(min..max)edgeRestricts 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 @key derives its id from the complete typed key tuple. Renaming a key property with @rename_from does not change existing ids.
  • A node without a key receives a generated id unless input supplies one.
  • An edge with @key derives 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, __src and __dst on 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.omni

Supported 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.

CodeMeaning
OG-DS-102Drop a node type that contains entities.
OG-DS-103Drop an edge type that contains entities.
OG-DS-104Drop a populated property.
OG-MF-103Add a required property to a populated type.
OG-MF-106Change a property's type, including enum narrowing or renaming.

omnigraph schema plan includes these codes in human and JSON output.

On this page