Skip to content

type_bridge.models.entity

entity

Entity class for TypeDB entities.

Entity

Bases: TypeDBType

Base class for TypeDB entities with Pydantic validation.

Entities own attributes defined as Attribute subclasses. Use TypeFlags to configure type name and abstract status. Supertype is determined automatically from Python inheritance.

This class inherits from TypeDBType and Pydantic's BaseModel, providing: - Automatic validation of attribute values - JSON serialization/deserialization - Type checking and coercion - Field metadata via Pydantic's Field()

Example

class Name(String): pass

class Age(Integer): pass

class Person(Entity): flags = TypeFlags(name="person") name: Name = Flag(Key) age: Age

Abstract entity

class AbstractPerson(Entity): flags = TypeFlags(abstract=True) name: Name

Inheritance (Person sub abstract-person)

class ConcretePerson(AbstractPerson): age: Age

__init_subclass__

__init_subclass__()

Called when Entity subclass is created.

Source code in type_bridge/models/entity.py
def __init_subclass__(cls) -> None:
    """Called when Entity subclass is created."""
    super().__init_subclass__()
    logger.debug(f"Initializing Entity subclass: {cls.__name__}")

    from type_bridge.models.schema_scanner import SchemaScanner

    scanner = SchemaScanner(cls)
    cls._owned_attrs = scanner.scan_attributes(is_relation=False)

to_schema_definition classmethod

to_schema_definition()

Generate TypeQL schema definition for this entity.

Returns:

Type Description
str | None

TypeQL schema definition string, or None if this is a base class

Source code in type_bridge/models/entity.py
@classmethod
def to_schema_definition(cls) -> str | None:
    """Generate TypeQL schema definition for this entity.

    Returns:
        TypeQL schema definition string, or None if this is a base class
    """
    from type_bridge.typeql.annotations import format_type_annotations

    # Base classes don't appear in TypeDB schema
    if cls.is_base():
        return None

    type_name = cls.get_type_name()
    lines = []

    # Define entity type with supertype from Python inheritance
    # TypeDB 3.x syntax: entity name @abstract, sub parent,
    supertype = cls.get_supertype()
    type_annotations = format_type_annotations(abstract=cls.is_abstract())

    entity_def = f"entity {type_name}"
    if type_annotations:
        entity_def += " " + " ".join(type_annotations)
    if supertype:
        entity_def += f", sub {supertype}"

    lines.append(entity_def)

    # Add attribute ownerships using shared helper
    lines.extend(cls._build_owns_lines())

    # Join with commas, but end with semicolon (no comma before semicolon)
    return ",\n".join(lines) + ";"

to_ast

to_ast(var='$e')

Generate AST InsertClause for this instance.

Parameters:

Name Type Description Default
var str

Variable name to use

'$e'

Returns:

Type Description
InsertClause

InsertClause containing statements

Source code in type_bridge/models/entity.py
def to_ast(self, var: str = "$e") -> InsertClause:
    """Generate AST InsertClause for this instance.

    Args:
        var: Variable name to use

    Returns:
        InsertClause containing statements
    """
    from type_bridge.query.ast import InsertClause, IsaStatement, Statement

    type_name = self.get_type_name()
    statements: list[Statement] = [IsaStatement(variable=var, type_name=type_name)]

    # Add attribute statements using shared helper from TypeDBType
    statements.extend(self._build_attribute_statements(var))

    return InsertClause(statements=statements)

get_match_clause_info

get_match_clause_info(var_name='$e')

Get match clause info for this entity instance.

Prefers IID-based matching when available (most precise). Falls back to @key attribute matching.

Parameters:

Name Type Description Default
var_name str

Variable name to use in the match clause

'$e'

Returns:

Type Description
MatchClauseInfo

MatchClauseInfo with the match clause

Raises:

Type Description
ValueError

If entity has neither _iid nor key attributes

Source code in type_bridge/models/entity.py
def get_match_clause_info(self, var_name: str = "$e") -> MatchClauseInfo:
    """Get match clause info for this entity instance.

    Prefers IID-based matching when available (most precise).
    Falls back to @key attribute matching.

    Args:
        var_name: Variable name to use in the match clause

    Returns:
        MatchClauseInfo with the match clause

    Raises:
        ValueError: If entity has neither _iid nor key attributes
    """
    type_name = self.get_type_name()

    # Prefer IID-based matching when available
    entity_iid = getattr(self, "_iid", None)
    if entity_iid:
        main_clause = f"{var_name} isa {type_name}, iid {entity_iid}"
        return MatchClauseInfo(main_clause=main_clause, extra_clauses=[], var_name=var_name)

    # Fall back to key attribute matching
    key_attrs = {
        field_name: attr_info
        for field_name, attr_info in self.get_all_attributes().items()
        if attr_info.flags.is_key
    }

    if key_attrs:
        parts = [f"{var_name} isa {type_name}"]
        for field_name, attr_info in key_attrs.items():
            value = getattr(self, field_name, None)
            if value is None:
                from type_bridge.crud.exceptions import KeyAttributeError

                raise KeyAttributeError(
                    entity_type=self.__class__.__name__,
                    operation="identify",
                    field_name=field_name,
                )
            attr_name = attr_info.typ.get_attribute_name()
            parts.append(f"has {attr_name} {self._format_value(value)}")
        main_clause = ", ".join(parts)
        return MatchClauseInfo(main_clause=main_clause, extra_clauses=[], var_name=var_name)

    # Neither IID nor key attributes available
    raise ValueError(
        f"Entity '{self.__class__.__name__}' cannot be identified: "
        f"no _iid set and no @key attributes defined. Either fetch the entity from the "
        f"database first (to populate _iid) or add Flag(Key) to an attribute."
    )

get_match_pattern

get_match_pattern(var_name='$e')

Get an AST EntityPattern for matching this entity instance.

Prefers IID-based matching when available (most precise). Falls back to @key attribute matching.

Parameters:

Name Type Description Default
var_name str

Variable name to use in the pattern

'$e'

Returns:

Type Description
EntityPattern

EntityPattern AST node

Raises:

Type Description
ValueError

If entity has neither _iid nor key attributes

Source code in type_bridge/models/entity.py
def get_match_pattern(self, var_name: str = "$e") -> EntityPattern:
    """Get an AST EntityPattern for matching this entity instance.

    Prefers IID-based matching when available (most precise).
    Falls back to @key attribute matching.

    Args:
        var_name: Variable name to use in the pattern

    Returns:
        EntityPattern AST node

    Raises:
        ValueError: If entity has neither _iid nor key attributes
    """
    from type_bridge.query.ast import EntityPattern

    type_name = self.get_type_name()
    constraints = self._build_identification_constraints()
    return EntityPattern(variable=var_name, type_name=type_name, constraints=constraints)

to_dict

to_dict(*, include=None, exclude=None, by_alias=False, exclude_unset=False)

Serialize the entity to a primitive dict.

Parameters:

Name Type Description Default
include set[str] | None

Optional set of field names to include.

None
exclude set[str] | None

Optional set of field names to exclude.

None
by_alias bool

When True, use attribute TypeQL names instead of Python field names.

False
exclude_unset bool

When True, omit fields that were never explicitly set.

False
Source code in type_bridge/models/entity.py
def to_dict(
    self,
    *,
    include: set[str] | None = None,
    exclude: set[str] | None = None,
    by_alias: bool = False,
    exclude_unset: bool = False,
) -> dict[str, Any]:
    """Serialize the entity to a primitive dict.

    Args:
        include: Optional set of field names to include.
        exclude: Optional set of field names to exclude.
        by_alias: When True, use attribute TypeQL names instead of Python field names.
        exclude_unset: When True, omit fields that were never explicitly set.
    """
    # Let Pydantic handle include/exclude/exclude_unset, then unwrap Attribute values.
    dumped = self.model_dump(
        include=include,
        exclude=exclude,
        by_alias=False,
        exclude_unset=exclude_unset,
    )

    attrs = self.get_all_attributes()
    result: dict[str, Any] = {}

    for field_name, raw_value in dumped.items():
        attr_info = attrs[field_name]
        key = attr_info.typ.get_attribute_name() if by_alias else field_name
        if by_alias and key in result and key != field_name:
            # Avoid collisions when multiple fields share the same attribute type
            key = field_name
        result[key] = self._unwrap_value(raw_value)

    return result

from_dict classmethod

from_dict(data, *, field_mapping=None, strict=True)

Construct an Entity from a plain dictionary.

Parameters:

Name Type Description Default
data dict[str, Any]

External data to hydrate the Entity.

required
field_mapping dict[str, str] | None

Optional mapping of external keys to internal field names.

None
strict bool

When True, raise on unknown fields; otherwise ignore them.

True
Source code in type_bridge/models/entity.py
@classmethod
def from_dict(
    cls,
    data: dict[str, Any],
    *,
    field_mapping: dict[str, str] | None = None,
    strict: bool = True,
) -> Self:
    """Construct an Entity from a plain dictionary.

    Args:
        data: External data to hydrate the Entity.
        field_mapping: Optional mapping of external keys to internal field names.
        strict: When True, raise on unknown fields; otherwise ignore them.
    """
    mapping = field_mapping or {}
    attrs = cls.get_all_attributes()
    alias_to_field = {info.typ.get_attribute_name(): name for name, info in attrs.items()}
    normalized: dict[str, Any] = {}

    for raw_key, raw_value in data.items():
        internal_key = mapping.get(raw_key, raw_key)
        if internal_key not in attrs and raw_key in alias_to_field:
            internal_key = alias_to_field[raw_key]

        if internal_key not in attrs:
            if strict:
                raise ValueError(f"Unknown field '{raw_key}' for {cls.__name__}")
            continue

        if raw_value is None:
            continue

        attr_info = attrs[internal_key]
        wrapped_value = cls._wrap_attribute_value(raw_value, attr_info)

        if wrapped_value is None:
            continue

        normalized[internal_key] = wrapped_value

    return cls(**normalized)

__repr__

__repr__()

Developer-friendly string representation of entity.

Source code in type_bridge/models/entity.py
def __repr__(self) -> str:
    """Developer-friendly string representation of entity."""
    field_strs = []
    for field_name in self._owned_attrs:
        value = getattr(self, field_name, None)
        if value is not None:
            field_strs.append(f"{field_name}={value!r}")
    return f"{self.__class__.__name__}({', '.join(field_strs)})"

__str__

__str__()

User-friendly string representation of entity.

Source code in type_bridge/models/entity.py
def __str__(self) -> str:
    """User-friendly string representation of entity."""
    # Extract key attributes first
    key_parts = []
    other_parts = []

    for field_name, attr_info in self._owned_attrs.items():
        value = getattr(self, field_name, None)
        if value is None:
            continue

        # Extract actual value from Attribute instance
        display_value = unwrap_attribute(value)

        # Format the field
        field_str = f"{field_name}={display_value}"

        # Separate key attributes
        if attr_info.flags.is_key:
            key_parts.append(field_str)
        else:
            other_parts.append(field_str)

    # Show key attributes first, then others
    all_parts = key_parts + other_parts

    if all_parts:
        return f"{self.get_type_name()}({', '.join(all_parts)})"
    else:
        return f"{self.get_type_name()}()"