How to write and read facts#

This guide shows you how to create an entity, write single- and multi-valued field facts onto it, and read those values back, either through an entity snapshot or directly by field. Use it once you have a schema and a FactGraph instance and want to populate and inspect data.

Prerequisites#

The examples below use this schema:

from factgraph.sdk import Entity, FactGraph, Field, Identity

class User(Entity):
    user_id: str = Identity()
    tenant_id: str = Identity()
    name: str = Field()
    tags: list[str] = Field()

fg = FactGraph.create(schema_classes=[User])

Steps#

  1. Create the entity and obtain a managed e_ref. fg.entities.create takes the entity class plus the full identity bundle as keyword arguments and returns the entity reference string:

    e_ref = fg.entities.create(User, user_id="alice", tenant_id="acme")

    create emits the Identity claims eagerly and atomically. It does not write any field values yet. A missing identity keyword raises SDKStoreError (“missing identity field: …”), and creating the same identity bundle twice raises EntityAlreadyExistsError (code ENTITY_ALREADY_EXISTS). See the errors reference.

  2. Write a single-valued field with fg.fields.set. Pass the Field descriptor (for example User.name), the e_ref, and the value. It returns the assertion id of the write:

    asrt_id = fg.fields.set(User.name, e_ref, "Alice")

    Calling set again on the same single field overwrites the visible value: the latest write wins.

  3. Append a multi-valued field with fg.fields.add. Each call adds one value and returns its assertion id:

    fg.fields.add(User.tags, e_ref, "red")
    fg.fields.add(User.tags, e_ref, "blue")

    Use set only for single-cardinality fields and add only for multi-cardinality fields. Calling set on a multi field (or add on a single field) raises CardinalityError (code FIELD_CARDINALITY_MISMATCH).

  4. To attach provenance to a write, pass a meta dictionary. Both set and add accept meta=None by default:

    fg.fields.add(User.tags, e_ref, "green", meta={"source": "import_job"})
  5. Read one field value back with fg.fields.get, passing the Field descriptor and e_ref. A single field returns its current value, or None when no active claim exists. A multi field returns a sorted tuple of its active values, or () when empty:

    fg.fields.get(User.name, e_ref)   # "Alice"
    fg.fields.get(User.tags, e_ref)   # ("blue", "red")
  6. To read a whole entity at once, call fg.entities.get with the entity class and its identity values. It returns an EntitySnapshot (or None if the entity is not visible). Field values are read off the snapshot as attributes:

    snap = fg.entities.get(User, user_id="alice", tenant_id="acme")
    snap.name    # "Alice"
    snap.tags    # ("blue", "red")

    The snapshot is read-only: assigning to an attribute raises FrozenSnapshotError. snap.identity returns the known identity kwargs, and snap.ref is the e_ref.

    To verify: snap.name equals the value you set, and snap.tags contains every value you added.

Variations#

  • Lazy materialization instead of explicit create. fg.entities.ref(User, user_id="alice", tenant_id="acme") returns a managed e_ref without writing to the ledger. The first fg.fields.set or fg.fields.add on that e_ref then materializes the Identity claims along with the field claim. Use create when you want the entity to exist before any field is written.
  • Entity-reference fields. When a field’s value type is another entity, first obtain a managed e_ref for the value entity (via ref or create), then pass that e_ref as the value to set or add. Reading the field back returns the value entity’s e_ref.
  • Writing to an unmanaged e_ref. set, add, and get resolve the e_ref through this FactGraph instance. Passing an e_ref that this instance did not produce raises SDKStoreError with code UNRESOLVABLE_E_REF; call fg.entities.ref(...) or fg.entities.create(...) first.

See also: Three-layer API reference for the full fg.entities / fg.fields surface, How to find and match entities, and Append-only ledger and provenance for why writes accumulate as assertions.

comments powered by Disqus