Skip to content

type_bridge.migration.operations

operations

Migration operations for TypeDB schema changes.

Operations define atomic schema changes that can be applied to a TypeDB database. Each operation can generate forward TypeQL and optionally rollback TypeQL.

Example

from type_bridge.migration import operations as ops

operations = [ ops.AddAttribute(Phone), ops.AddOwnership(Person, Phone, optional=True), ops.RunTypeQL( forward="match $p isa person; insert $p has phone 'unknown';", reverse="match $p isa person, has phone 'unknown'; delete $p has phone 'unknown';", ), ]

Operation

Bases: ABC

Base class for migration operations.

Operations must implement: - to_typeql(): Generate forward migration TypeQL - to_rollback_typeql(): Generate rollback TypeQL (or None if irreversible)

reversible property

reversible

Whether this operation can be rolled back.

Returns:

Type Description
bool

True if rollback TypeQL is available

to_typeql abstractmethod

to_typeql()

Generate TypeQL for forward migration.

Returns:

Type Description
str

TypeQL string to execute

Source code in type_bridge/migration/operations.py
@abstractmethod
def to_typeql(self) -> str:
    """Generate TypeQL for forward migration.

    Returns:
        TypeQL string to execute
    """
    pass

to_rollback_typeql abstractmethod

to_rollback_typeql()

Generate TypeQL for rollback.

Returns:

Type Description
str | None

TypeQL string to execute, or None if operation is irreversible

Source code in type_bridge/migration/operations.py
@abstractmethod
def to_rollback_typeql(self) -> str | None:
    """Generate TypeQL for rollback.

    Returns:
        TypeQL string to execute, or None if operation is irreversible
    """
    pass

to_typeql_steps

to_typeql_steps()

Forward TypeQL, one query per step.

TypeDB executes one define/redefine/undefine block per query, so operations that mix verbs (annotation changes) override this to return several steps. The default wraps to_typeql().

Source code in type_bridge/migration/operations.py
def to_typeql_steps(self) -> list[str]:
    """Forward TypeQL, one query per step.

    TypeDB executes one define/redefine/undefine block per query, so
    operations that mix verbs (annotation changes) override this to
    return several steps. The default wraps ``to_typeql()``.
    """
    typeql = self.to_typeql()
    return [typeql] if typeql.strip() else []

to_rollback_typeql_steps

to_rollback_typeql_steps()

Rollback TypeQL, one query per step, or None if irreversible.

Source code in type_bridge/migration/operations.py
def to_rollback_typeql_steps(self) -> list[str] | None:
    """Rollback TypeQL, one query per step, or None if irreversible."""
    typeql = self.to_rollback_typeql()
    if typeql is None:
        return None
    return [typeql] if typeql.strip() else []

ModifyTypeAnnotations dataclass

ModifyTypeAnnotations(subject, old_doc=None, new_doc=None, old_meta=dict(), new_meta=dict())

Bases: Operation

Modify @doc/@meta annotations on an entity, relation, or attribute type.

TypeDB 3.12+. Annotation-only schema changes are metadata-safe: they never touch instance data.

Example

ops.ModifyTypeAnnotations( Person, old_doc=None, new_doc="A person known to the system.", )

ModifyRoleAnnotations dataclass

ModifyRoleAnnotations(relation, role_name, old_doc=None, new_doc=None, old_meta=dict(), new_meta=dict())

Bases: Operation

Modify @doc/@meta annotations on a relation role (TypeDB 3.12+).

Example

ops.ModifyRoleAnnotations( Employment, "employee", new_doc="The employed party.", )

AddAttribute dataclass

AddAttribute(attribute)

Bases: Operation

Add a new attribute type.

Example

ops.AddAttribute(Phone) # Creates: define attribute phone, value string;

RemoveAttribute dataclass

RemoveAttribute(attribute)

Bases: Operation

Remove an attribute type.

WARNING: This is a BREAKING change. Ensure all attribute instances and ownerships are removed first.

AddEntity dataclass

AddEntity(entity)

Bases: Operation

Add a new entity type.

Example

ops.AddEntity(Person)

RemoveEntity dataclass

RemoveEntity(entity)

Bases: Operation

Remove an entity type.

WARNING: This is a BREAKING change. Ensure all entity instances are deleted first.

AddOwnership dataclass

AddOwnership(owner, attribute, optional=False, key=False, unique=False, card_min=None, card_max=None)

Bases: Operation

Add attribute ownership to an entity or relation.

Example

ops.AddOwnership(Person, Phone, optional=True)

Creates: define person owns phone @card(0..1);

ops.AddOwnership(Person, Email, key=True)

Creates: define person owns email @key;

RemoveOwnership dataclass

RemoveOwnership(owner, attribute)

Bases: Operation

Remove attribute ownership from an entity or relation.

WARNING: This may orphan attribute data. Ensure attribute values are removed from instances first.

ModifyOwnership dataclass

ModifyOwnership(owner, attribute, old_annotations, new_annotations)

Bases: Operation

Modify ownership annotations (cardinality, key, unique).

TypeDB can only redefine parameterized annotations (@card); parameterless ones (@key, @unique, @distinct) must be added with define and removed with undefine. The operation decomposes the transition into per-annotation schema steps (one query each) via to_typeql_steps(); both the direct-execution path and the planner-backed path run those steps in order.

Example

ops.ModifyOwnership( Person, Phone, old_annotations="@card(0..1)", new_annotations="@card(1..1)" )

AddRelation dataclass

AddRelation(relation)

Bases: Operation

Add a new relation type with its roles.

Example

ops.AddRelation(Employment)

RemoveRelation dataclass

RemoveRelation(relation)

Bases: Operation

Remove a relation type.

WARNING: This is a BREAKING change. Ensure all relation instances are deleted first.

AddRole dataclass

AddRole(relation, role_name, player_types=list())

Bases: Operation

Add a new role to an existing relation.

Example

ops.AddRole(Employment, "manager", ["person"])

RemoveRole dataclass

RemoveRole(relation, role_name)

Bases: Operation

Remove a role from a relation.

WARNING: This is a BREAKING change. Ensure no relation instances have role players for this role.

AddRolePlayer dataclass

AddRolePlayer(relation, role_name, player_type)

Bases: Operation

Add a player type to an existing role.

Example

ops.AddRolePlayer(Employment, "employee", "contractor")

Allows Contractor entities to play the employee role

RemoveRolePlayer dataclass

RemoveRolePlayer(relation, role_name, player_type)

Bases: Operation

Remove a player type from a role.

WARNING: This is a BREAKING change. Ensure no relation instances have this player type in this role.

RunTypeQL dataclass

RunTypeQL(forward, reverse=None)

Bases: Operation

Execute arbitrary TypeQL for complex migrations.

Use this for: - Data migrations (updating existing data) - Complex schema changes not covered by other operations - Renaming attributes (requires data migration)

Example

ops.RunTypeQL( forward=""" match $p isa person; not { $p has phone $ph; }; insert $p has phone "unknown"; """, reverse=""" match $p isa person, has phone "unknown"; delete $p has phone "unknown"; """ )

RunPython dataclass

RunPython(code, reverse=None, description=None, resources=(), import_checks=())

Bases: Operation

Run Python ORM code during migration execution.

RunPython is for migrations that need the normal TypeBridge ORM surface rather than portable TypeQL, for example loading JSON/TOML data, creating many entities/relations, or querying existing data before writing derived values. The callable receives the migration executor's database connection, so existing code such as User.manager(db).filter(...).execute() works.

Example

def forwards(db): users = User.manager(db).filter(name__startswith="A").execute() ...

operations = [ops.RunPython(forwards)]

RenameAttribute dataclass

RenameAttribute(old_name, new_name, value_type)

Bases: Operation

Rename an attribute type — placeholder without an executable lowering.

A real rename is a staged multi-step change (define new attribute, plain ownerships, data backfill, annotation tightening, old-value cleanup, removal) that needs the full owner list from a schema. This single operation cannot carry that, so it has no executable TypeQL and the migration planner refuses to lower it.

Use author_migration(..., attribute_renames=[(old, new)]) to author the staged expansion from two schemas, or spell out the primitive operations (AddAttribute, AddOwnership, CopyAttribute, RunTypeQL, RemoveOwnership, RemoveAttribute) by hand.

CopyAttribute dataclass

CopyAttribute(owner, source, dest, filter=None)

Bases: Operation

Copy an attribute value from source to destination on all instances of the owner type.

This is a DML (data manipulation) backfill operation that copies attribute values from one attribute to another for every instance of the owning type. The forward operation uses an insert-if-absent pattern (idempotent — safe to re-run). The reverse deletes all destination attribute values added by this operation.

No transform function is supported in v1. Use RunTypeQL for value transforms.

Example

ops.CopyAttribute( owner=Person, source="legacy-name", dest="display-name", )

Backfills: match $x isa person, has legacy-name $v;

not { $x has display-name $d; };

insert $x has display-name == $v;

Note: dest must already be owned by owner via a prior schema op.

to_typeql

to_typeql()

Generate insert-if-absent backfill TypeQL.

Emits a match+insert that copies source values to dest for every owner instance that does not already have the destination attribute.

Source code in type_bridge/migration/operations.py
def to_typeql(self) -> str:
    """Generate insert-if-absent backfill TypeQL.

    Emits a match+insert that copies ``source`` values to ``dest`` for every
    owner instance that does not already have the destination attribute.
    """
    owner_name = _type_name(self.owner)
    filter_line = f"\n  {self.filter};" if self.filter else ""
    # `has <dest> == $v` assigns the *value* of the matched source attribute
    # to a new destination attribute. Writing `has <dest> $v` instead would
    # fail TypeDB type inference: `$v` is typed as the source attribute and
    # cannot simultaneously be a destination-attribute instance.
    return (
        f"match\n"
        f"  $x isa {owner_name}, has {self.source} $v;\n"
        f"  not {{ $x has {self.dest} $d; }};{filter_line}\n"
        f"insert\n"
        f"  $x has {self.dest} == $v;"
    )

to_rollback_typeql

to_rollback_typeql()

Generate the inverse delete that removes all dest values added by this op.

Source code in type_bridge/migration/operations.py
def to_rollback_typeql(self) -> str | None:
    """Generate the inverse delete that removes all dest values added by this op."""
    owner_name = _type_name(self.owner)
    return f"match $x isa {owner_name}, has {self.dest} $v;\ndelete $v of $x;"