Skip to content

graflo.architecture.backend.fold

Upsert semantics over append-only chunks: records folded by identity.

A file backend directory is written append-only, so one entity upserted twice is stored twice. Reads fold those records into one per entity, in write order, the way a database applies the same upserts: a later record's fields replace earlier ones, except merge: union list fields, which take the union of every record's members in first-seen order, nulls dropped. The folded entity sits where its first record was.

A record whose identity is incomplete is not an upsert target and is kept as written.

Functions:

fold_edges(records, source_identity, target_identity, key_fields=(), union=())

One [source, target, properties] record per edge of records.

An edge is keyed by its endpoints' identities and the values of key_fields among its properties (the relation is the collection's own).

Source code in graflo/architecture/backend/fold.py
def fold_edges(
    records: Iterable[list[Any]],
    source_identity: Sequence[str],
    target_identity: Sequence[str],
    key_fields: Sequence[str] = (),
    union: Collection[str] = (),
) -> list[list[Any]]:
    """One ``[source, target, properties]`` record per edge of *records*.

    An edge is keyed by its endpoints' identities and the values of
    *key_fields* among its properties (the relation is the collection's own).
    """
    out: list[list[Any]] = []
    position: dict[tuple[Any, ...], int] = {}
    for record in records:
        if not isinstance(record, list) or len(record) < 2:
            out.append(record)
            continue
        source, target = record[0], record[1]
        properties = (
            record[2] if len(record) > 2 and isinstance(record[2], dict) else {}
        )
        source_key = _key(source, source_identity)
        target_key = _key(target, target_identity)
        if source_key is None or target_key is None:
            out.append(record)
            continue
        extra = tuple(str(properties.get(field)) for field in key_fields)
        key = (source_key, target_key, extra)
        at = position.get(key)
        if at is None:
            position[key] = len(out)
            out.append([dict(source), dict(target), _first(properties, union)])
        else:
            folded = out[at]
            folded[0].update(source)
            folded[1].update(target)
            merge_record(folded[2], properties, union)
    return out

fold_vertices(records, identity, union=())

One document per entity of records, keyed by the identity fields.

Source code in graflo/architecture/backend/fold.py
def fold_vertices(
    records: Iterable[dict[str, Any]],
    identity: Sequence[str],
    union: Collection[str] = (),
) -> list[dict[str, Any]]:
    """One document per entity of *records*, keyed by the *identity* fields."""
    out: list[dict[str, Any]] = []
    position: dict[tuple[Any, ...], int] = {}
    for record in records:
        key = _key(record, identity)
        if key is None:
            out.append(record)
            continue
        at = position.get(key)
        if at is None:
            position[key] = len(out)
            out.append(_first(record, union))
        else:
            merge_record(out[at], record, union)
    return out

merge_record(into, record, union)

Apply record to into as an upsert: replace, and union union fields.

Source code in graflo/architecture/backend/fold.py
def merge_record(
    into: dict[str, Any], record: dict[str, Any], union: Collection[str]
) -> None:
    """Apply *record* to *into* as an upsert: replace, and union *union* fields."""
    for field, value in record.items():
        if field in union:
            into[field] = union_members(into.get(field), value)
        else:
            into[field] = value