How to define a schema#
This guide shows you how to declare entity types with identity and fields in Python and build a FactGraph from them, which is the first step before you write or read any facts.
Prerequisites#
- The
factgraphpackage installed (v0.2.0). - The public symbols
Entity,Identity,Field, andFactGraph, all importable fromfactgraph.sdk.
Steps#
Declare an entity type by subclassing
Entity. Annotate each member with a Python type and assign either anIdentity()or aField()descriptor. Every entity must declare at least oneIdentity()member; the metaclass raisesSDKSchemaErrorwith the messageEntity '<name>' must declare at least one Identity fieldotherwise.from factgraph.sdk import Entity, Identity, Field, FactGraph class SchemaUser(Entity): user_id: str = Identity() name: str = Field() tags: list[str] = Field()Identity members form the entity’s stable, immutable identity bundle.
nameis a single-value field.tagsis a multi-value field because its annotation islist[str].Choose the cardinality of each
Field()through its annotation, not through a keyword argument. A scalar annotation (for examplestr,int,bool,bytes,float) produces a single-value field. Alist[T],set[T],frozenset[T], or variadictuple[T, ...]annotation produces a multi-value field.Optional,Union, theT | Noneform, plaindict, and non-variadictupleannotations are rejected withSDKSchemaError.To constrain a string value, pass
pattern=(a regular expression).pattern=is only supported for string-typed members; using it on a non-string member raisesSDKSchemaError.class SchemaTenantUser(Entity): user_id: str = Identity() tenant_id: str = Identity() name: str = Field()Identity()andField()accept onlypattern=andrepr=. Passing legacy keywords such asprimary_key,default,default_factory, orcardinalityraisesSDKSchemaError. Fields have no defaults: a missing single field reads asNoneand a missing multi field reads as().Declare more than one identity member when an entity’s identity is composite. All identity members together make up the complete bundle, and callers must later provide every one of them when they create the entity.
See reference/schema.md for the full cardinality rules, the type-domain mapping (including
Literal[...]enum fields and entity-reference fields), and theEntity.Metakeys (version,tags,repr).Optionally add explain-layer representation templates with
repr=on members and a nestedMetaclass on the entity. Member templates use the%CLS,%ENT, and%FLDplaceholders; theMeta.reprtemplate uses%CLSplus identity field names (for example%user_id). These templates are validated at class-definition time and compiled into the schema IR without changing the schema digest. Invalid placeholders raiseSDKSchemaError.class CEUser(Entity): class Meta: repr = "User %user_id" user_id: str = Identity() country: str = Field(repr="%ENT country %FLD")Build a
FactGraphby passing your entity classes toFactGraph.create(schema_classes=[...]). This compiles the declarations into the graph schema. Passpath=if you want a durable workspace you can later save and reload.fg = FactGraph.create(schema_classes=[SchemaUser, SchemaTenantUser])To verify: the entity types appear in
fg.schema_ir["entities"]and their compiled predicates appear in the separate top-levelfg.schema_ir["predicates"]list (entries identified bypred_id), and the graph is ready for entity and field writes.
Variations#
- To add a brand-new entity type to a live graph, call
fg.schema.register(NewEntityClass). Callingregisterfor an entity type that is already registered raisesSchemaConflictError. To add a non-identity field to an already-registered entity, callfg.schema.extend(UpdatedEntityClass). Both calls are additive-only: onextend, non-additive changes (removing a field, changing a field’s type or cardinality, or altering the identity bundle) raiseSchemaNonAdditiveError. See reference/schema.md. - To validate schema classes without building a graph, call
schema_preflight_from_classes([...])fromfactgraph.sdk.compile, which returns a preflight report dictionary. - To build a graph with no durable workspace, use
FactGraph.from_schema_classes([...])instead ofFactGraph.create(...).
See also: reference/schema.md, reference/factgraph.md, How to write and read facts, reference/persistence.md.