graflo.architecture.evolution¶
Manifest evolution: apply high-level schema + ingestion transforms to :class:~graflo.architecture.contract.manifest.GraphManifest.
Use :func:~graflo.migrate.io.manifest_hash to compare contract identity before and after.
Modules:
| Name | Description |
|---|---|
alignment |
Derived identity: lower a merged class's identity branches to fundamental ops. |
apply |
Apply manifest evolution operations to a copy of a :class: |
autogenerate |
Derive a contract change set from two manifests. |
canonical |
Canonical vocabulary maps, and the shared pieces of merge resolution. |
canonicalize |
Canonical form: the payload a content hash is taken over. |
codec |
(De)serialization for contract operations. |
commit |
|
db_profile |
Update :class: |
equivalence |
The resolved shape of a merge's groups. |
hashing |
Content hashes over manifest blocks. |
history |
The commit DAG, and commits on disk. |
identity |
Identity-plane evolution ops. |
ingestion |
Ingestion-side evolution ops: mutations whose primary effect is the pipeline. |
inverse |
Inverses for the subset of contract operations that have one. |
inverse_edges |
Realize declared inverses as explicit logical edges ( |
inverse_plan |
Planning what to do about declared inverses: realize, repair, switch, withdraw. |
merge |
Binary merge of two :class: |
merge3 |
Three-way merge over manifest change sets, and tracked re-merges. |
merge_commit |
Recording a merge as a commit. |
merge_core |
Pure merge helpers for logical vertices and edges. |
merge_types |
Merged property types: declared on a merge, refused when they disagree undeclared. |
naming_graph |
One naming graph over the original names of both sides of a merge. |
ops |
Typed manifest evolution operations. |
physical |
Typing and physical-profile evolution ops. |
preview |
What a merge would do, and every way it could refuse — without refusing. |
project |
Pure planning for :class: |
rewrite |
Structured rewrite of vertex names in pipeline dicts and related resource fields. |
sanitize |
Physical naming for a target database flavor. |
semantics |
Applying grounding changes to a manifest. |
state_core |
|
structure |
Structure-plane evolution ops: introducing and retargeting graph entities. |
version |
Semantic version bump helpers for manifest evolution. |
Attributes¶
INGESTION_REWRITING_OPS = frozenset({'add_inverse_edges', 'add_resource_transforms', 'add_resources', 'canonicalize', 'remove_resources', 'replace_resources', 'ensure_extracted_fields', 'merge_edges', 'merge_vertices', 'project_manifest', 'remove_edge_properties', 'remove_edges', 'remove_vertex_properties', 'remove_vertices', 'rename_edge_properties', 'rename_relations', 'rename_resources', 'rename_vertex_properties', 'rename_vertices', 'replace_identity', 'retarget_edges', 'sanitize', 'set_inverse_emission'})
module-attribute
¶
IdentityBranchDecl = RawBranch | DerivedBranch | LocalKeyBranch
module-attribute
¶
IdentityTarget = Annotated[NaturalIdentityTarget | HashIdentityTarget | FunnelIdentityTarget | AssignedIdentityTarget | BlankIdentityTarget, PydanticField(discriminator='mode')]
module-attribute
¶
ManifestOp = Annotated[RemoveVerticesOp | AddResourceTransformsOp | EnsureExtractedFieldsOp | AddResourcesOp | RemoveResourcesOp | ReplaceResourcesOp | AddVerticesOp | AddEdgesOp | RetargetEdgesOp | AddSecondaryIdentitiesOp | RemoveSecondaryIdentitiesOp | ReplaceEdgeIdentitiesOp | ChangeFieldTypesOp | AddVertexIndexesOp | RemoveVertexIndexesOp | AddEdgeIndexesOp | RemoveEdgeIndexesOp | SetEdgeDirectedOp | SetBindingsOp | SetDbProfileOp | SetVertexSemanticsOp | SetVertexDescriptionsOp | SetEdgeSemanticsOp | SetFieldSemanticsOp | MergeVerticesOp | CanonicalizeOp | RenameVertexPropertiesOp | RemoveVertexPropertiesOp | AddVertexPropertiesOp | RenameVerticesOp | RenameRelationsOp | RenameResourcesOp | RemoveEdgesOp | MergeEdgesOp | RenameEdgePropertiesOp | RemoveEdgePropertiesOp | AddEdgePropertiesOp | DeclareEdgeInversesOp | RetractEdgeInversesOp | AddInverseEdgesOp | SetNativeInversesOp | SetInverseEmissionOp | ProjectManifestOp | ReplaceIdentityOp | SanitizeOp | MergeManifestsOp, PydanticField(discriminator='op')]
module-attribute
¶
__all__ = ['CANON_VERSION', 'COMMIT_KINDS', 'INGESTION_REWRITING_OPS', 'IRREVERSIBLE', 'LIST_ORDER', 'AddEdgeIndexesOp', 'AddEdgePropertiesOp', 'AddEdgesOp', 'AddInverseEdgesOp', 'AddResourceTransformsOp', 'AddResourcesOp', 'AddSecondaryIdentitiesOp', 'AddVertexIndexesOp', 'AddVertexPropertiesOp', 'AddVerticesOp', 'AlignmentConflictError', 'AssignedIdentityTarget', 'BlankIdentityTarget', 'CanonicalMap', 'CanonicalizeOp', 'ChangeFieldTypesOp', 'Cluster', 'ClusterIndex', 'Commit', 'CommitError', 'Completion', 'ConflictResolution', 'DanglingEntry', 'DeclareEdgeInversesOp', 'DeclaredMaps', 'DerivationSpec', 'DerivedBranch', 'EdgeFieldSemanticsTarget', 'EdgeIdentitiesEntry', 'EdgeIndexEntry', 'EdgeRetargetEntry', 'EdgeSelector', 'EnsureExtractedFields', 'EnsureExtractedFieldsOp', 'FieldSemanticsTarget', 'FieldTypeSpec', 'FileCommitStore', 'FunnelIdentityTarget', 'HashIdentityTarget', 'History', 'IdentityBranchDecl', 'IdentityPlan', 'IdentityReplacement', 'IdentityTarget', 'InversePlan', 'ListOrder', 'LocalKeyBranch', 'LocalKeySource', 'ManifestOp', 'MergeCanonicalConflictError', 'MergeConflict', 'MergeEdgesOp', 'MergeError', 'MergeFieldTypes', 'MergeIdentityError', 'MergeIncompleteError', 'MergeManifestsOp', 'MergeNameConflictError', 'MergeNamingError', 'MergeNamingIncompleteError', 'MergeRecipe', 'MergeRecipeRef', 'MergeRenames', 'MergeResult', 'MergeVerticesOp', 'NamingFinding', 'NamingGraph', 'NamingResult', 'NaturalIdentityTarget', 'ProjectManifestOp', 'PropertyEquivalence', 'RelationCluster', 'RelationEquivalence', 'RemoveEdgeIndexesOp', 'RemoveEdgePropertiesOp', 'RemoveEdgesOp', 'RemoveResourcesOp', 'RemoveSecondaryIdentitiesOp', 'RemoveVertexIndexesOp', 'RemoveVertexPropertiesOp', 'RemoveVerticesOp', 'RenameEdgePropertiesOp', 'RenameHints', 'RenameRelationsOp', 'RenameResourcesOp', 'RenameVertexPropertiesOp', 'RenameVerticesOp', 'ReplaceEdgeIdentitiesOp', 'ReplaceIdentityOp', 'ReplaceResourcesOp', 'RetargetEdgesOp', 'RetractEdgeInversesOp', 'RevisionOp', 'SanitizeOp', 'SetEdgeDirectedOp', 'SetEdgeSemanticsOp', 'SetFieldSemanticsOp', 'SetInverseEmissionOp', 'SetNativeInversesOp', 'SetVertexDescriptionsOp', 'SetVertexSemanticsOp', 'SideMaps', 'SideRenames', 'UnclassifiedListField', 'VertexEquivalence', 'apply_add_edge_indexes', 'apply_add_edge_properties', 'apply_add_edges', 'apply_add_inverse_edges', 'apply_add_resource_transforms', 'apply_add_resources', 'apply_add_secondary_identities', 'apply_add_vertex_indexes', 'apply_add_vertex_properties', 'apply_add_vertices', 'apply_canonicalize', 'apply_change_field_types', 'apply_declare_edge_inverses', 'apply_evolution', 'apply_merge_edges', 'apply_merge_vertices', 'apply_project_manifest', 'apply_remove_edge_ids', 'apply_remove_edge_indexes', 'apply_remove_edge_properties', 'apply_remove_edges', 'apply_remove_resources', 'apply_remove_secondary_identities', 'apply_remove_vertex_indexes', 'apply_remove_vertex_properties', 'apply_remove_vertices', 'apply_rename_edge_properties', 'apply_rename_relations', 'apply_rename_resources', 'apply_rename_vertex_properties', 'apply_rename_vertices', 'apply_replace_edge_identities', 'apply_replace_identity', 'apply_replace_resources', 'apply_retarget_edges', 'apply_retract_edge_inverses', 'apply_sanitize', 'apply_set_edge_directed', 'apply_set_edge_semantics', 'apply_set_field_semantics', 'apply_set_inverse_emission', 'apply_set_native_inverses', 'apply_set_vertex_descriptions', 'apply_set_vertex_semantics', 'build_commit', 'build_merge_commit', 'build_merge_recipe', 'build_multi_parent_commit', 'build_naming', 'build_recipe', 'build_revert_commit', 'build_root_commit', 'canonical_map_to_ops', 'canonical_near_collisions', 'canonical_payload', 'canonicalize_ops', 'checkout', 'checkout_parent', 'compose_canonical_maps', 'compute_commit_id', 'compute_root_commit_id', 'dangling_entries', 'describe_slot', 'diff_manifests', 'diff_manifests_verified', 'find_commit_by_tree', 'find_merge_base', 'fold_declared_maps', 'full_hash', 'graph_hash', 'identity_to_ops', 'ingestion_hash', 'invert_op', 'invert_ops', 'irreversible_reason', 'is_reversible', 'manifest_hash', 'merge_manifests', 'merge_three_way', 'naming_table', 'op_from_dict', 'op_slots', 'op_to_dict', 'ops_from_dicts', 'ops_from_yaml', 'ops_reaching_ingestion', 'ops_to_dicts', 'ops_to_yaml_str', 'plan_declare_symmetric', 'plan_realize_inverses', 'plan_repair_inverses', 'plan_switch_realization', 'plan_withdraw_realization', 're_merge', 'rehash_trees', 'schema_hash', 'stable_hash', 'subject', 'suggest_merge_op', 'take_left', 'take_right', 'trim_canonical_map', 'validate_and_complete_canonical_map', 'validate_identity', 'verify_history']
module-attribute
¶
Classes¶
AddEdgeIndexesOp
¶
Bases: ConfigBaseModel
Author secondary indexes on edge physical specs.
Source code in graflo/architecture/evolution/ops.py
AddEdgePropertiesOp
¶
Bases: ConfigBaseModel
Add edge properties for each relation in schema/profile defaults.
An entry may be a bare name (an untyped property) or a full
:class:~graflo.architecture.schema.vertex.Field, as on
:class:AddVertexPropertiesOp, so a typed or grounded edge property is
one replayable step rather than an add followed by a type change.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
additions = PydanticField(..., description='Per-relation edge property additions: ``{relation_name: [field_name | Field, ...]}``.', min_length=1)
class-attribute
instance-attribute
¶
op = 'add_edge_properties'
class-attribute
instance-attribute
¶
Methods:¶
field_names(relation)
¶
The names added to relation, whichever shape they were written in.
Source code in graflo/architecture/evolution/ops.py
AddEdgesOp
¶
Bases: ConfigBaseModel
Introduce new logical edge relations between existing vertex types.
Source code in graflo/architecture/evolution/ops.py
AddInverseEdgesOp
¶
Bases: ConfigBaseModel
Realize declared inverse pairs as explicit logical edges (portable to every backend).
Edges are derived from the relation map: for each directed edge (S, T, r)
whose relation has a declared pair inv, adds the logical edge
(T, S, inv) unless it exists and its physical spec, and sets
emit_inverse on the edge steps that write r, so the same rows write
both. No step is generated: the inverse is mirrored at assembly, after the
relation is resolved, which covers every way a step can name its relation.
A resource that already writes the inverse with a step of its own is left
alone. The pair must be declared first (:class:DeclareEdgeInversesOp);
this op never declares. A symmetric relation has no inverse edge to add --
its edges are undirected. Refused for a relation whose inverse is native
(:class:SetNativeInversesOp), since both would store the same fact.
Withdraw with :class:RemoveEdgesOp on the inverse edges: removing an edge
clears the emit_inverse flags that fed it.
Source code in graflo/architecture/evolution/ops.py
AddResourceTransformsOp
¶
Bases: ConfigBaseModel
Append transform steps to named resources' pipelines.
The first op whose primary effect is ingestion: graph_schema is
untouched. Steps land at the root level of each pipeline unless at
names a deeper one; at whichever level they land, actor type-priority
sorting (transform runs before vertex extraction at the same level) makes
the position safe.
The level is load-bearing rather than cosmetic. An actor reads its
transform buffer at its own LocationIndex with no ancestor fallback,
and a descend subtree runs before its own level's transforms, so a
step appended at the root is invisible to a vertex produced under a
descend — and a transform whose declared inputs are missing skips
silently by default. Target the level that produces the vertex.
Steps may reference a registry transform via call.use (resolved
against the manifest's existing ingestion_model.transforms union the
op's own transforms) or carry a fully inline call
(module + foo + params), which cannot collide by name.
Source code in graflo/architecture/evolution/ops.py
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 | |
Attributes¶
additions = PydanticField(..., description='Per-resource transform steps to append: ``{resource_name: [step_dict, ...]}``.', min_length=1)
class-attribute
instance-attribute
¶
at = PydanticField(default_factory=dict, description='Per-resource pipeline level to append into: ``{resource_name: [step_index, ...]}``. Each index must address a ``descend`` step, descending one level per element; an omitted or empty path means the root level.')
class-attribute
instance-attribute
¶
op = 'add_resource_transforms'
class-attribute
instance-attribute
¶
transforms = PydanticField(default_factory=list, description='Named transforms to register in ``ingestion_model.transforms`` for steps that reference them via ``call.use``. A name already registered with a different body is an error at apply time.')
class-attribute
instance-attribute
¶
AddResourcesOp
¶
Bases: ConfigBaseModel
Introduce ingestion resources, in the shape the ingestion block accepts.
The unary way to grow ingestion_model: without it a change set could
only ever rename or narrow the resources it started with, and the differ
had to report an added resource as inexpressible.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
op = 'add_resources'
class-attribute
instance-attribute
¶
resources = PydanticField(..., description='Full resource definitions.', min_length=1)
class-attribute
instance-attribute
¶
transforms = PydanticField(default_factory=list, description='Named transforms to register in ``ingestion_model.transforms`` for steps of the new resources that reference them via ``call.use``. Unioned by name exactly as ``add_resource_transforms`` does: an identical body already registered dedupes, a different one is an error at apply time.')
class-attribute
instance-attribute
¶
AddSecondaryIdentitiesOp
¶
Bases: ConfigBaseModel
Declare alternate lookup keys on existing vertices.
Secondary identities are lookup-only: upserts keep using the primary identity.
Each declared field-set automatically gains a non-unique index at
:meth:Schema.finish_init, so this op is how an edge-only source gains a way to
reference endpoints by a business key without touching the primary identity.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
additions = PydanticField(..., description='Per-vertex secondary identities to declare: ``{vertex_name: [{name, fields}, ...]}``. A bare field list is accepted for each entry and auto-named.', min_length=1)
class-attribute
instance-attribute
¶
op = 'add_secondary_identities'
class-attribute
instance-attribute
¶
AddVertexIndexesOp
¶
Bases: ConfigBaseModel
Author secondary indexes on vertices in the database profile.
Source code in graflo/architecture/evolution/ops.py
AddVertexPropertiesOp
¶
Bases: ConfigBaseModel
Add vertex properties to existing logical vertex types.
An entry may be a bare name or a full :class:~graflo.architecture.schema.vertex.Field.
Bare names were the original shape and still mean what they meant -- an
untyped property -- but they cannot express a property that arrives with a
type and a grounding, which is what any op stream that adds a measured or
temporal property has to say in one replayable step.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
additions = PydanticField(..., description='Per-vertex property additions: ``{vertex_name: [field_name | Field, ...]}``.', min_length=1)
class-attribute
instance-attribute
¶
op = 'add_vertex_properties'
class-attribute
instance-attribute
¶
Methods:¶
field_names(vertex)
¶
The names added to vertex, whichever shape they were written in.
AddVerticesOp
¶
Bases: ConfigBaseModel
Introduce new logical vertex types.
The unary counterpart to what :class:MergeManifestsOp can only do binarily.
A replayable change set that cannot introduce a type could only ever describe a
shrinking graph, which is why this exists alongside remove_vertices.
Source code in graflo/architecture/evolution/ops.py
AssignedIdentityTarget
¶
Bases: ConfigBaseModel
Target an assigned identity: an intentional UUID primary key.
Source code in graflo/architecture/evolution/ops.py
BlankIdentityTarget
¶
Bases: ConfigBaseModel
Target a blank identity: an auto-generated placeholder ID.
Source code in graflo/architecture/evolution/ops.py
CanonicalizeOp
¶
Bases: ConfigBaseModel
Relabel classes, attributes and relations by one vocabulary map, in one step.
The map is a partial function on names — identity where unmapped — applied
simultaneously over the original schema, so a chain ({X: Z, Z: Q}) and
a swap resolve without an intermediate state, and the fibers of the map
are exactly the groups that merge. A target that already exists and does
not move must be declared a member of its own group with a self entry
(Company: Company); otherwise the op refuses rather than merging into
it silently. properties is keyed by the source class name and is
applied before the class relabel.
This is the single lowering of a
:class:~graflo.architecture.evolution.canonical.CanonicalMap, and the
per-side step of
:func:~graflo.architecture.evolution.merge.merge_manifests.
Source code in graflo/architecture/evolution/ops.py
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | |
Attributes¶
allow_merges = PydanticField(default=False, description='Accept a group of more than one class or relation collapsing onto one target. A merge fuses entities and can create self-relations, so it is acknowledged here rather than inferred from the map.')
class-attribute
instance-attribute
¶
allow_observation_fusion = PydanticField(default=False, description='Accept a merge whose sources are produced in one accumulator slot (the same pipeline level and the same ``role``, or both bare), fusing those observations into one node.')
class-attribute
instance-attribute
¶
allow_self_relations = PydanticField(default=False, description='Accept a merge whose sources are connected by an edge that becomes a self-relation once both endpoints land on the same class.')
class-attribute
instance-attribute
¶
merges
property
¶
Whether any class or relation group has more than one member.
op = 'canonicalize'
class-attribute
instance-attribute
¶
properties = PydanticField(default_factory=dict, description='Per-source-class attribute map: ``{source_class: {source_attr: canonical_attr}}``.')
class-attribute
instance-attribute
¶
relation_groups
property
¶
{target: [members]} over relations, self entries included.
relations = PydanticField(default_factory=dict, description='Relation map: ``{source_relation: canonical_relation}``.')
class-attribute
instance-attribute
¶
vertex_groups
property
¶
{target: [members]} over vertices, self entries included.
vertices = PydanticField(default_factory=dict, description='Class map: ``{source_class: canonical_class}``.')
class-attribute
instance-attribute
¶
ChangeFieldTypesOp
¶
Bases: ConfigBaseModel
Set the logical type of existing vertex or edge properties.
Makes the differ's CHANGE_VERTEX_FIELD_TYPE / CHANGE_EDGE_FIELD_TYPE
authorable. Targets are validated against the profile's db_flavor so an
unsupported type fails here rather than at define time.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
edges = PydanticField(default_factory=dict, description='``{relation_name: {field_name: {type, item_type}}}``.')
class-attribute
instance-attribute
¶
op = 'change_field_types'
class-attribute
instance-attribute
¶
vertices = PydanticField(default_factory=dict, description='``{vertex_name: {field_name: {type, item_type}}}``.')
class-attribute
instance-attribute
¶
DeclareEdgeInversesOp
¶
Bases: ConfigBaseModel
Declare inverse pairs and symmetric relations in edge_config.
Purely logical: it records how relation names read one fact from its two
endpoints and creates nothing. inverses pairs two distinct names; a pair
is unordered, so {a: b}, {b: a} and {a: b, b: a} declare the same
thing. symmetric names relations that are their own inverse. Together
they must give every relation at most one inverse (no a: b with
b: c), in the op and against what is already declared.
Realize a pair with :class:AddInverseEdgesOp (explicit, portable inverse
edges) or :class:SetNativeInversesOp (TigerGraph maintains the pair) --
never both for one relation. A symmetric relation is realized by its edges
being undirected (:class:SetEdgeDirectedOp), which the schema requires.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
inverses = PydanticField(default_factory=dict, description='Pairs to declare: ``{relation: inverse_relation}``, either order.')
class-attribute
instance-attribute
¶
op = 'declare_edge_inverses'
class-attribute
instance-attribute
¶
symmetric = PydanticField(default_factory=list, description='Relations to declare as their own inverse.')
class-attribute
instance-attribute
¶
DerivationSpec
¶
Bases: ConfigBaseModel
How one resource derives a canonical attribute from its raw doc fields.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
foo = PydanticField(default='normalized_key', description='Function name; called as ``foo(*values, **params)``. The default trims and casefolds one value.')
class-attribute
instance-attribute
¶
input = PydanticField(..., min_length=1, description='RAW source-doc field names fed to the function, in order. Documents keep their original keys after property renames, so canonical property names are usually wrong here.')
class-attribute
instance-attribute
¶
module = PydanticField(default='graflo.util.transform', description='Module holding the derivation function.')
class-attribute
instance-attribute
¶
params = PydanticField(default_factory=dict, description='Keyword parameters for the function.')
class-attribute
instance-attribute
¶
when = PydanticField(default=None, description='Run the step only for documents whose RAW field holds one of the listed values (``{field, in}``). A merge derives this guard from how the resource produces the class; one set here replaces it. Not allowed on a spec keyed by member: the member decides.')
class-attribute
instance-attribute
¶
DerivedBranch
¶
Bases: ConfigBaseModel
A funnel branch over an attribute each source derives from its own columns.
name is the canonical attribute the branch keys on: the digest's
input, not where the digest is stored (that is
:attr:VertexEquivalence.digest_field). sources is
keyed by resource, because derivation inputs are that resource's raw
column names. An entry is either
- one :class:
DerivationSpec— the resource derives the attribute the same way for every record of the class it produces; or - a dict keyed by member class — the resource produces several members and which one a record is decides the derivation. A member is keyed by its own name on its side or by its canonical one.
The merge reads how the resource produces the class on its side and guards
each step with when: a member-keyed spec on the discriminator values
that route onto its member, a single spec behind a vertex_router on
the values that route onto the class, and nothing for a plain vertex
step. An explicit :attr:DerivationSpec.when replaces the derived guard.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
name = PydanticField(..., description='Derived attribute this branch digests; its funnel branch id. Not where the key is stored: see `VertexEquivalence.digest_field`.')
class-attribute
instance-attribute
¶
sources = PydanticField(..., min_length=1, description='Per-resource derivation: ``{resource: spec}``, or ``{resource: {member_class: spec}}`` when the member a document is must decide the derivation.')
class-attribute
instance-attribute
¶
Methods:¶
members_for(resource)
¶
Member classes keying resource's specs, or None if unkeyed.
specs_for(resource)
¶
Derivations resource contributes to this branch, in order.
Source code in graflo/architecture/evolution/ops.py
EdgeFieldSemanticsTarget
¶
Bases: ConfigBaseModel
One property of one edge triple, and the grounding to put on it.
Edge properties carry the same FieldSemantics as vertex properties --
a since on an edge has a unit as much as a temperature on a vertex
does -- but until this target existed no op could reach them.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
field = PydanticField(..., description='Property name on that edge.')
class-attribute
instance-attribute
¶
relation = PydanticField(default=None, description='Relation name; ``None`` matches the edge with no relation set.')
class-attribute
instance-attribute
¶
semantics = PydanticField(default=None, description='Grounding for the property; ``None`` clears it.')
class-attribute
instance-attribute
¶
source = PydanticField(..., description='Source vertex type name.')
class-attribute
instance-attribute
¶
target = PydanticField(..., description='Target vertex type name.')
class-attribute
instance-attribute
¶
Methods:¶
edge_id()
¶
EdgeIdentitiesEntry
¶
Bases: ConfigBaseModel
New uniqueness keys for one edge triple.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
identities = PydanticField(..., description='Replacement uniqueness keys. Each key lists fields that, together with the resolved endpoints, must be unique; the ``source`` / ``target`` tokens stand for the endpoints themselves. An empty list clears them.')
class-attribute
instance-attribute
¶
relation = PydanticField(default=None, description='Relation name; ``None`` matches the edge with no relation set.')
class-attribute
instance-attribute
¶
source = PydanticField(..., description='Source vertex type name.')
class-attribute
instance-attribute
¶
target = PydanticField(..., description='Target vertex type name.')
class-attribute
instance-attribute
¶
Methods:¶
EdgeIndexEntry
¶
Bases: ConfigBaseModel
Indexes for one edge physical spec.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
fields = PydanticField(default_factory=list, description='Field lists identifying indexes to remove from this spec.')
class-attribute
instance-attribute
¶
indexes = PydanticField(default_factory=list, description='Indexes to add to this spec.')
class-attribute
instance-attribute
¶
purpose = PydanticField(default=None, description='Physical variant purpose; ``None`` addresses the base spec.')
class-attribute
instance-attribute
¶
relation = PydanticField(default=None, description='Relation name; ``None`` matches the edge with no relation set.')
class-attribute
instance-attribute
¶
source = PydanticField(..., description='Source vertex type name.')
class-attribute
instance-attribute
¶
target = PydanticField(..., description='Target vertex type name.')
class-attribute
instance-attribute
¶
Methods:¶
EdgeRetargetEntry
¶
Bases: ConfigBaseModel
Repoint one edge triple at a different source and/or target vertex type.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
new_source = PydanticField(default=None, description='Replacement source vertex type; omit to keep the current one.')
class-attribute
instance-attribute
¶
new_target = PydanticField(default=None, description='Replacement target vertex type; omit to keep the current one.')
class-attribute
instance-attribute
¶
relation = PydanticField(default=None, description='Relation name; ``None`` matches the edge with no relation set.')
class-attribute
instance-attribute
¶
source = PydanticField(..., description='Current source vertex type name.')
class-attribute
instance-attribute
¶
target = PydanticField(..., description='Current target vertex type name.')
class-attribute
instance-attribute
¶
Methods:¶
edge_id()
¶
EdgeSelector
¶
Bases: ConfigBaseModel
Schema edge triple selector matching :data:~graflo.architecture.graph_types.EdgeId.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
relation = PydanticField(default=None, description='Relation name; ``None`` matches edges with no relation set.')
class-attribute
instance-attribute
¶
source = PydanticField(..., description='Source vertex type name.')
class-attribute
instance-attribute
¶
target = PydanticField(..., description='Target vertex type name.')
class-attribute
instance-attribute
¶
Methods:¶
EnsureExtractedFields
¶
Bases: ConfigBaseModel
Fields that must survive extraction for one vertex type at one level.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
at = PydanticField(default_factory=list, description='Pipeline level holding the producing step, as ``descend`` step indices. Empty means the root level.')
class-attribute
instance-attribute
¶
fields = PydanticField(..., min_length=1, description='Property names that must reach the extracted vertex document.')
class-attribute
instance-attribute
¶
vertex = PydanticField(..., description='The vertex type whose extraction must keep ``fields``.')
class-attribute
instance-attribute
¶
EnsureExtractedFieldsOp
¶
Bases: ConfigBaseModel
Widen a producing step's projection so named fields are not dropped.
Needed because a vertex_router delivers differently from a vertex
step. The router builds its child VertexActor at
lindex.extend((role, 0)), where the transform buffer is empty, so
derived fields reach the child only through the merged observation — that
is, through passthrough or from. A plain vertex step instead reads
the buffer directly, which bypasses keep_fields and
extraction_scope entirely.
So on a router, extraction_scope: mapped_only or a keep_fields list
that does not name the fields drops them silently. This op restores them:
keep_fields gains the names, and under mapped_only the per-type
vertex_from_map entry gains identity mappings — seeded from the
router-level from when the entry does not exist yet, since creating it
otherwise replaces the author's projection rather than extending it.
A plain vertex step, or a router that restricts nothing, is a no-op:
the fields already survive.
Source code in graflo/architecture/evolution/ops.py
FieldSemanticsTarget
¶
Bases: ConfigBaseModel
One property of one vertex, and the grounding to put on it.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
field = PydanticField(..., description='Property name on that vertex.')
class-attribute
instance-attribute
¶
semantics = PydanticField(default=None, description='Grounding for the property; ``None`` clears it.')
class-attribute
instance-attribute
¶
vertex = PydanticField(..., description='Vertex type name.')
class-attribute
instance-attribute
¶
Methods:¶
FieldTypeSpec
¶
Bases: ConfigBaseModel
Target logical type for one property.
Source code in graflo/architecture/evolution/ops.py
FunnelIdentityTarget
¶
Bases: ConfigBaseModel
Target an identity funnel: fallback branches digested into digest_field.
The general form of :class:HashIdentityTarget — a flat hash key is a funnel
with one branch. Both resolve to identity mode hash.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
digest_field = PydanticField(default='id', min_length=1, description="Property the digest is stored in; the vertex's identity field.")
class-attribute
instance-attribute
¶
funnel = PydanticField(..., description='Ordered fallback branches; the first complete one wins.')
class-attribute
instance-attribute
¶
mode = 'funnel'
class-attribute
instance-attribute
¶
HashIdentityTarget
¶
Bases: ConfigBaseModel
Target a hash identity: a deterministic synthetic id digested from fields.
Source code in graflo/architecture/evolution/ops.py
IdentityReplacement
¶
Bases: ConfigBaseModel
New identity policy for one vertex, plus what becomes of the old one.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
endpoints = PydanticField(default='follow_new', description='How edge steps that match this vertex on its primary identity behave afterwards. ``follow_new`` (default) leaves them on the primary, so they match the new identity. ``pin_to_retired`` rewrites them to select the demoted secondary identity, preserving the previous matching behaviour for sources that only carry the old key. Requires ``retire: demote``.')
class-attribute
instance-attribute
¶
retire = PydanticField(default='demote', description='What happens to the old identity field-set. ``demote`` turns it into a secondary identity (lookup index follows automatically), ``keep`` leaves the fields as plain properties, ``drop`` removes them. Demotion is downgraded to ``keep`` when the old identity was synthetic (hash / assigned / blank) or already equals the new one.')
class-attribute
instance-attribute
¶
retire_as = PydanticField(default=None, description='Name for the demoted secondary identity. Defaults to ``retired_identity``. Only meaningful with ``retire: demote``.')
class-attribute
instance-attribute
¶
to = PydanticField(..., description='The identity policy this vertex should have after the op.')
class-attribute
instance-attribute
¶
LocalKeyBranch
¶
Bases: ConfigBaseModel
The last funnel branch: each source's own key behind a namespace tag.
A record that completes no earlier branch keys on this one, so it is still
written — as its own vertex, not joined with records of another source.
local_key maps each resource to the column carrying its own key, or,
like :attr:DerivedBranch.sources, to one such source per member class.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
local_key = PydanticField(..., min_length=1, description='Per-resource local-key wiring: ``{resource: source}``, or ``{resource: {member_class: source}}`` when the member decides.')
class-attribute
instance-attribute
¶
name = PydanticField(default='local_key', description='Canonical fallback property name on the class.')
class-attribute
instance-attribute
¶
sep = PydanticField(default=':', description='Separator between tag and key.')
class-attribute
instance-attribute
¶
sources
property
¶
The per-resource wiring, under the name :class:DerivedBranch uses.
Methods:¶
members_for(resource)
¶
Member classes keying resource's sources, or None if unkeyed.
sources_for(resource)
¶
Local-key sources resource contributes, in order.
Source code in graflo/architecture/evolution/ops.py
LocalKeySource
¶
Bases: ConfigBaseModel
Where one resource's side-local key comes from, and its namespace tag.
The tag is what keeps records of different sources apart once they fail to
fuse: f2 from one source and f2 from another are different
entities, and a:f2 / b:f2 say so. It is required so that opting
out is a statement, not an omission: tag=None (stored as "", the
neutral element, so it survives serialization) keeps the raw value as the
local key with no separator — the author's claim that the values are
already unique across every source of the class (UUIDs, IRIs, ids the
source itself prefixes).
Source code in graflo/architecture/evolution/ops.py
Attributes¶
field = PydanticField(..., description='RAW doc field carrying the side-local key.')
class-attribute
instance-attribute
¶
tag = PydanticField(..., description='Namespace tag: tag \'a\' turns \'f2\' into \'a:f2\'. ``None`` or ``""`` keeps the raw value, no separator — only for values already unique across every source of the class.')
class-attribute
instance-attribute
¶
when = PydanticField(default=None, description='Same as :attr:`DerivationSpec.when`: an explicit guard replacing the one the merge derives. Not allowed on a source keyed by member.')
class-attribute
instance-attribute
¶
MergeEdgesOp
¶
Bases: ConfigBaseModel
Merge source relation names into a canonical relation name.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
into = PydanticField(..., description='Canonical relation name that receives all source relations.')
class-attribute
instance-attribute
¶
op = 'merge_edges'
class-attribute
instance-attribute
¶
sources = PydanticField(..., description='Relation names to merge away. Must not include ``into``.', min_length=1)
class-attribute
instance-attribute
¶
MergeFieldTypes
¶
Bases: ConfigBaseModel
The type of a merged property, declared on the merge by its merged names.
Keyed by the class or relation name and the property name the merged manifest carries. The merge retypes every member, on either side, that carries the property (under whatever spelling it is renamed from) before folding it, so members that disagree on a type merge into the declared one. Members that do not carry the property are left alone.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
edges = PydanticField(default_factory=dict, description='``{merged_relation: {property: {type, item_type}}}``.')
class-attribute
instance-attribute
¶
vertices = PydanticField(default_factory=dict, description='``{merged_vertex: {merged_property: {type, item_type}}}``.')
class-attribute
instance-attribute
¶
MergeManifestsOp
¶
Bases: ConfigBaseModel
Merge two full GraphManifests using explicit equivalence maps.
Binary only — apply via :func:~graflo.architecture.evolution.merge.merge_manifests.
Unary :func:~graflo.architecture.evolution.apply.apply_evolution rejects this op.
Every name in the op is a name the input manifests declare. The
vocabulary (canonical_maps), the equivalences and renames are
resolved together, in one pass over those names, so nothing has to be
written in an intermediate vocabulary. A group is everything an
equivalence links, including every class a vocabulary merges with one of
its members; its merged name is into, else the vocabulary's name, else
the one spelling its members share. Empty equivalences yield a disjoint
union, subject to name_conflict.
A vertex equivalence's identity with a derived or local_key branch
is applied to the merged union before return (canonical attributes →
resource derivations → priority funnel), then the members' own keys are
demoted to secondary identities.
Source code in graflo/architecture/evolution/ops.py
2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 | |
Attributes¶
allow_dangling_entries = PydanticField(default=False, description='Accept canonical map entries that name nothing on the side they are scoped to, dropping and logging each one instead of refusing with the list. Set it on a map itself to say the map is broader than this merge; set it here to say so for both maps at once.')
class-attribute
instance-attribute
¶
canonical_maps = PydanticField(default_factory=dict, description="The vocabulary per side. ``left`` / ``right`` apply to that manifest's own names, ``both`` to either. A vocabulary is the default name of a class; an equivalence's ``into`` overrides it for the whole group.")
class-attribute
instance-attribute
¶
field_types = PydanticField(default=None, description='Merged property types, keyed by merged names: every member that carries the property is retyped before the fold, so members that disagree on a type merge into this one.')
class-attribute
instance-attribute
¶
name = PydanticField(default=None, description="Label for the merged manifest and its schema. Unset, the two sides' names are folded into ``left+right``.")
class-attribute
instance-attribute
¶
name_conflict = PydanticField(default='error', description='How to handle a name both sides arrive at that no equivalence covers (vertices, relations, resources, connectors). ``error`` refuses and names the equivalences to declare; ``prefix_right`` keeps them apart under ``r_`` names; ``union_right`` unions vertices and relations of exactly the same name, each pair becoming a synthesized 1-1 equivalence, so identity and property reconciliation apply as to a declared one (resources and connectors are addresses, not concepts, so it behaves as ``error`` for them). Two spellings of one concept (``OrderLine`` / ``order_line``) are never unioned: ``error`` and ``union_right`` refuse them, ``prefix_right`` keeps them apart.')
class-attribute
instance-attribute
¶
op = 'merge_manifests'
class-attribute
instance-attribute
¶
relation_equivalences = PydanticField(default_factory=list, description='Relation equivalences across the two input manifests.')
class-attribute
instance-attribute
¶
renames = PydanticField(default_factory=MergeRenames, description='Per-side renames of classes, relations, attributes and resources that no equivalence groups.')
class-attribute
instance-attribute
¶
router_scope = PydanticField(default='side', description="What a ``vertex_router`` may route a discriminator value missing from its ``type_map`` to, after the merge. ``side`` closes each router over its own side's classes: merge writes every class of that side into the table, under its merged name, and sets ``type_map_only``, so a value the side never modeled is skipped as it was before the merge. ``union`` leaves routers open: such a value can name any class of the merged schema, the other side's included -- for sources that share type names and ids.")
class-attribute
instance-attribute
¶
target_namespace = PydanticField(default=None, description="Database / graph / space the merged schema deploys into. Supersedes both sides' ``db_profile.target_namespace`` (so it also resolves a disagreement between them) and is validated against the merged ``db_flavor``. Unset, the namespace is derived from the schema name when deployed.")
class-attribute
instance-attribute
¶
vertex_equivalences = PydanticField(default_factory=list, description='Vertex equivalences across the two input manifests.')
class-attribute
instance-attribute
¶
MergeRenames
¶
Bases: ConfigBaseModel
Per-side renames of a merge, in each side's own names.
Source code in graflo/architecture/evolution/ops.py
MergeVerticesOp
¶
Bases: ConfigBaseModel
Merge source vertices into a single logical name (schema, edges, ingestion).
Source code in graflo/architecture/evolution/ops.py
Attributes¶
allow_observation_fusion = PydanticField(default=False, validation_alias=AliasChoices('allow_observation_fusion', 'allow_row_fusion'), description='Accept resource pipelines whose sources land in one accumulator slot — the same level and the same ``role``, or both bare. Those steps then write to one slot, fusing into one node what a single source document emitted as two vertex observations. Same-level steps with distinct ``role``s never share a slot and need no acknowledgement. Rejected unless set. ``allow_row_fusion`` is accepted as a legacy alias.')
class-attribute
instance-attribute
¶
allow_self_relations = PydanticField(default=False, description='Accept edges whose endpoints both land on ``into``. A self-relation makes both endpoints share one accumulator slot, so assembly merges observations that were previously separate nodes. Rejected unless set.')
class-attribute
instance-attribute
¶
into = PydanticField(..., description='Resulting vertex type name. If it already exists, source vertices are merged into it. If it does not exist, a new vertex is built from all sources.')
class-attribute
instance-attribute
¶
op = 'merge_vertices'
class-attribute
instance-attribute
¶
sources = PydanticField(..., description='Vertex type names to merge away. Must not include ``into``. Each name must exist in the schema before the merge.', min_length=1)
class-attribute
instance-attribute
¶
NaturalIdentityTarget
¶
Bases: ConfigBaseModel
Target a natural key: the named properties identify the vertex directly.
Source code in graflo/architecture/evolution/ops.py
ProjectManifestOp
¶
Bases: ConfigBaseModel
Project a manifest to a vertex/edge subgraph with consistent cascade.
Keeps only the requested logical vertices and edges (and optionally resources).
All schema, db_profile, ingestion, and bindings references to removed
entities are pruned. Inverse edges are not kept by default; list them in
keep_edges, or set keep_inverse_edges to keep the declared mirror of
every edge that is kept.
With connectivity="induced_prune" (v1 default), when keep_vertices is
set, vertex types from that list with no incident surviving edge are dropped.
depth turns keep_vertices from a literal list into seeds for a
neighbourhood walk. One rule covers every combination: let E be
keep_edges when given and every declared edge otherwise; the survivors are
the vertex types within depth hops of a seed along E under
direction, and then E restricted to surviving endpoints. So the result
is the induced subgraph on the hop ball — an edge between two neighbours
survives even though no walk needed it — and keep_edges bounds the walk
rather than being overridden by it. Pruning is unchanged: a seed left with no
surviving edge is still dropped, whatever the depth.
Edge.by (the third vertex type on an EdgeType.INDIRECT edge) is not part
of schema adjacency, so a walk never pulls it in — the same blind spot the flat
selection already has.
Source code in graflo/architecture/evolution/ops.py
1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 | |
Attributes¶
connectivity = PydanticField(default='induced_prune', description='How to interpret ``keep_vertices`` relative to surviving edges.')
class-attribute
instance-attribute
¶
depth = PydanticField(default=0, ge=0, description='Hops to expand ``keep_vertices`` by before the induced slice. ``0`` (default) keeps the literal list.')
class-attribute
instance-attribute
¶
direction = PydanticField(default=EdgeDirection.ANY, description='Orientation followed when expanding by ``depth``. Edges declared ``directed: false`` are followed both ways regardless. Ignored when ``depth`` is 0.')
class-attribute
instance-attribute
¶
keep_edges = PydanticField(default=None, description='Edge triples ``(source, target, relation)`` to retain.')
class-attribute
instance-attribute
¶
keep_inverse_edges = PydanticField(default=False, description='With ``keep_edges``: also keep the declared mirror ``(T, S, inv)`` of each kept ``(S, T, r)``, so a materialized pair survives as a pair. Without ``keep_edges`` every edge between surviving vertices is kept already.')
class-attribute
instance-attribute
¶
keep_resources = PydanticField(default=None, description='Optional ingestion resource names to retain after graph slice.')
class-attribute
instance-attribute
¶
keep_vertices = PydanticField(default=None, description='Vertex type names to retain (after induced connectivity pruning).')
class-attribute
instance-attribute
¶
op = 'project_manifest'
class-attribute
instance-attribute
¶
partial_resources = PydanticField(default='trim', description='A resource whose pipeline the projection shortens: ``trim`` keeps what survives and logs a warning naming each one; ``drop`` removes it with the bindings that served it, keeping only resources the projection leaves whole.')
class-attribute
instance-attribute
¶
strict = PydanticField(default=True, description='When True, unknown vertex/edge selectors raise ``ValueError``.')
class-attribute
instance-attribute
¶
PropertyEquivalence
¶
Bases: ConfigBaseModel
Align a property from the left and/or right member(s) onto a canonical name.
At least one of left / right must be set. A bare string applies to
every member declared on that side of the owning
:class:VertexEquivalence; a {member: field} dict maps per member,
for when members are not aligned under the same source field name.
Exact-name matches do not need a :class:PropertyEquivalence: after
boundary rename, merge_vertex_models unions fields by spelling, so a
property present under the same name on every member fuses for free.
Declare an equivalence only to rename or to pick a different into; the
merged key is declared on the :class:VertexEquivalence, the merged type
in :attr:MergeManifestsOp.field_types.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
into = PydanticField(..., description='Canonical property name on the merged vertex.')
class-attribute
instance-attribute
¶
left = PydanticField(default=None, description='Field name on the left member(s): a bare string applies to every left member of the owning equivalence, a ``{member: field}`` dict maps per member.')
class-attribute
instance-attribute
¶
right = PydanticField(default=None, description='Same shape as ``left``, for the right member(s).')
class-attribute
instance-attribute
¶
RelationEquivalence
¶
Bases: ConfigBaseModel
Collapse one or more left relations and one or more right relations onto one name.
Shares the left / right n-ary shape and the naming rules of
:class:VertexEquivalence.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
into = PydanticField(default=None, description='Merged relation name, never translated by a vocabulary. Omitted, the name comes from the vocabulary, or from the one spelling every member shares.')
class-attribute
instance-attribute
¶
left = PydanticField(..., description='One or more left relation names.')
class-attribute
instance-attribute
¶
left_members
property
¶
right = PydanticField(..., description='One or more right relation names.')
class-attribute
instance-attribute
¶
right_members
property
¶
Methods:¶
RemoveEdgeIndexesOp
¶
Bases: ConfigBaseModel
Withdraw authored indexes from edge physical specs, addressed by field list.
Source code in graflo/architecture/evolution/ops.py
RemoveEdgePropertiesOp
¶
Bases: ConfigBaseModel
Remove edge properties for each relation across schema/profile/ingestion.
Source code in graflo/architecture/evolution/ops.py
RemoveEdgesOp
¶
Bases: ConfigBaseModel
Remove logical edges from schema, profile, and ingestion selectors.
Two addressing forms, combinable in one op. relations removes a relation
on every endpoint pair it occurs on. edges removes exactly the named
triples, which is the only way to remove one pair of several sharing a
relation, or an edge with no relation set at all.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
edges = PydanticField(default_factory=list, description='Edge triples ``(source, target, relation)`` to remove.')
class-attribute
instance-attribute
¶
op = 'remove_edges'
class-attribute
instance-attribute
¶
relations = PydanticField(default_factory=list, description='Relation names to remove from edge definitions and references.')
class-attribute
instance-attribute
¶
RemoveResourcesOp
¶
Bases: ConfigBaseModel
Remove ingestion resources and the bindings that wired them.
Source code in graflo/architecture/evolution/ops.py
RemoveSecondaryIdentitiesOp
¶
Bases: ConfigBaseModel
Withdraw alternate lookup keys, dropping their derived indexes.
Rejected when a surviving edge step still selects the removed field-set — that step would have no way to resolve its endpoint.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
op = 'remove_secondary_identities'
class-attribute
instance-attribute
¶
removals = PydanticField(..., description='Per-vertex secondary identities to withdraw, addressed by name or by field list: ``{vertex_name: [name | [field, ...], ...]}``.', min_length=1)
class-attribute
instance-attribute
¶
RemoveVertexIndexesOp
¶
Bases: ConfigBaseModel
Withdraw authored vertex indexes, addressed by field list.
Indexes derived from secondary_identities are not removable here — they would
be re-registered by the next finish_init. Use
:class:RemoveSecondaryIdentitiesOp for those.
Source code in graflo/architecture/evolution/ops.py
RemoveVertexPropertiesOp
¶
Bases: ConfigBaseModel
Remove vertex properties and propagate pruning to ingestion/db profile references.
Source code in graflo/architecture/evolution/ops.py
RemoveVerticesOp
¶
Bases: ConfigBaseModel
Remove logical vertices and cascade: edges, ingestion resources, bindings.
Source code in graflo/architecture/evolution/ops.py
RenameEdgePropertiesOp
¶
Bases: ConfigBaseModel
Rename edge properties for each relation across schema/profile/ingestion.
Source code in graflo/architecture/evolution/ops.py
RenameRelationsOp
¶
Bases: ConfigBaseModel
Rename logical edge relation names across schema and ingestion.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
op = 'rename_relations'
class-attribute
instance-attribute
¶
renames = PydanticField(..., validation_alias=AliasChoices('renames', 'relations'), description='Relation rename map: ``{old_relation: new_relation}``. Must be injective. ``relations`` is accepted as a legacy alias.', min_length=1)
class-attribute
instance-attribute
¶
RenameResourcesOp
¶
Bases: ConfigBaseModel
Rename ingestion resource names and bindings references.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
op = 'rename_resources'
class-attribute
instance-attribute
¶
renames = PydanticField(..., validation_alias=AliasChoices('renames', 'resources'), description='Ingestion resource rename map: ``{old_resource: new_resource}``. Must be injective. ``resources`` is accepted as a legacy alias.', min_length=1)
class-attribute
instance-attribute
¶
RenameVertexPropertiesOp
¶
Bases: ConfigBaseModel
Rename vertex properties (and identity references) and propagate to ingestion.
renames maps each vertex name to a per-vertex {old_field: new_field} map.
Schema-side: rewrites Field.name, vertex.identity, and any DB profile
structures that reference field names (vertex_indexes, edge_specs.indexes).
Ingestion-side: rewrites VertexActor.from so the doc still uses the OLD field
name (injecting {new_field: old_field} when missing), rewrites
TransformActor.rename values that target a renamed vertex field, and updates
Resource.extra_weights / edge.vertex_weights (:class:~graflo.architecture.graph_types.Weight
fields, map, and filter keys that address vertex observation columns).
Source code in graflo/architecture/evolution/ops.py
RenameVerticesOp
¶
Bases: ConfigBaseModel
Rename logical vertex names across schema, ingestion, and bindings.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
op = 'rename_vertices'
class-attribute
instance-attribute
¶
renames = PydanticField(..., validation_alias=AliasChoices('renames', 'vertices'), description='Vertex rename map: ``{old_vertex: new_vertex}``. Must be injective. ``vertices`` is accepted as a legacy alias.', min_length=1)
class-attribute
instance-attribute
¶
ReplaceEdgeIdentitiesOp
¶
Bases: ConfigBaseModel
Replace the uniqueness keys of logical edges.
The edge-side counterpart of :class:ReplaceIdentityOp. There is no retire policy:
edge identities have no lookup plane to demote into. Non-endpoint tokens are merged
into edge properties by Edge.finish_init, as with authored identities.
Source code in graflo/architecture/evolution/ops.py
ReplaceIdentityOp
¶
Bases: ConfigBaseModel
Replace the identity policy of one or more vertices.
Covers both a change of identity fields and a change of identity mode
(natural / hash / assigned / blank), because the cascade is the
same in either case: the field-set that upserts changes, and everything that
referenced the old one must be repointed or retired.
Not covered: blank vertices cannot retire by demotion (they cannot declare
secondary identities at all).
Source code in graflo/architecture/evolution/ops.py
Attributes¶
op = 'replace_identity'
class-attribute
instance-attribute
¶
replacements = PydanticField(..., validation_alias=AliasChoices('replacements', 'vertices'), description='Per-vertex identity replacement: ``{vertex_name: replacement}``. ``vertices`` is accepted as a legacy alias.', min_length=1)
class-attribute
instance-attribute
¶
ReplaceResourcesOp
¶
Bases: ConfigBaseModel
Replace the definitions of existing resources, matched by name.
Each resource keeps its position in ingestion_model.resources and its
bindings; only its definition changes. This is how an edited pipeline is
expressed, since a pipeline is an ordered program no finer op can patch.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
op = 'replace_resources'
class-attribute
instance-attribute
¶
resources = PydanticField(..., description='Full new definitions; each name must already exist.', min_length=1)
class-attribute
instance-attribute
¶
transforms = PydanticField(default_factory=list, description='Named transforms the new definitions reference via ``call.use``, registered as ``add_resources`` registers them.')
class-attribute
instance-attribute
¶
RetargetEdgesOp
¶
Bases: ConfigBaseModel
Change which vertex types an edge connects, preserving everything else.
Remove-plus-add would lose the edge's properties, uniqueness keys, directed
flag, and its db_profile physical spec. Retargeting rewrites the EdgeId
everywhere it is keyed instead: edge config, physical specs, and pipeline edge steps.
Source code in graflo/architecture/evolution/ops.py
RetractEdgeInversesOp
¶
Bases: ConfigBaseModel
Withdraw declared inverses, addressed by relation name.
A paired relation retracts its whole pair (either side names it); a
symmetric relation retracts its own declaration. Refused while a native
inverse still realizes a pair: that would leave the database maintaining a
type the schema no longer names. Explicit inverse edges are ordinary edges
and survive a retraction; so does directed: false.
Source code in graflo/architecture/evolution/ops.py
SanitizeOp
¶
Bases: ConfigBaseModel
Record the physical names a target flavor needs in DatabaseProfile.
Vertex storage names, relation names and vertex/edge property names that the flavor cannot store (reserved word, invalid character, forbidden prefix) get a stored name in the profile, deduplicated within each database namespace. The logical schema and the ingestion model are left untouched: a backend naming constraint is a physical fact, so it never renames a logical property. Idempotent.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
db_flavor = PydanticField(..., description='Target database flavor whose reserved words/constraints drive the sanitization.')
class-attribute
instance-attribute
¶
op = 'sanitize'
class-attribute
instance-attribute
¶
reserved_words = PydanticField(default=None, description="Optional override for the flavor's reserved words. When unset, ``graflo.db.util.load_reserved_words(db_flavor)`` is used.")
class-attribute
instance-attribute
¶
SetEdgeDirectedOp
¶
Bases: ConfigBaseModel
Set the directed flag on logical edges.
Small, but load-bearing for replay: directed decides what
:class:AddInverseEdgesOp is allowed to duplicate, so an un-authorable flag makes
inverse-edge change sets non-replayable.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
directed = PydanticField(..., description='Value applied to every selected edge.')
class-attribute
instance-attribute
¶
edges = PydanticField(..., description='Edge triples whose ``directed`` flag changes.', min_length=1)
class-attribute
instance-attribute
¶
op = 'set_edge_directed'
class-attribute
instance-attribute
¶
SetEdgeSemanticsOp
¶
Bases: ConfigBaseModel
Ground edge relations in an external vocabulary.
The vertex op's counterpart. Relations carry as much meaning as types --
wasDerivedFrom and dependsOn are not interchangeable -- and a
conformance profile that asks whether types are grounded has to be able to
ask it of edges too.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
edges = PydanticField(..., description='Edge triples whose grounding changes.', min_length=1)
class-attribute
instance-attribute
¶
op = 'set_edge_semantics'
class-attribute
instance-attribute
¶
semantics = PydanticField(default=None, description='Grounding applied to every selected edge; ``None`` clears it. Not ``FieldSemantics``: a unit on an edge is meaningless, and the model split is what makes ``unit:`` here a validation error.')
class-attribute
instance-attribute
¶
SetFieldSemanticsOp
¶
Bases: ConfigBaseModel
Ground vertex and edge properties, including their unit of measure.
Takes :class:~graflo.architecture.schema.semantics.FieldSemantics rather
than :class:~graflo.architecture.schema.semantics.Semantics, which is the
entire reason this is a third op rather than a mode of the vertex one: only
a property may carry unit, and the two models are kept apart so that
unit: on a type is a validation error rather than a silent no-op.
A target names either a vertex property (vertex + field) or an
edge property (source / target / relation + field).
Source code in graflo/architecture/evolution/ops.py
SetInverseEmissionOp
¶
Bases: ConfigBaseModel
Set or clear emit_inverse on edge steps, addressed by position.
The ingestion half of a materialized inverse, as a primitive: which steps
mirror the edges they write into the declared inverse. A step is addressed
by resource and :class:EdgeStepRef (at / step / link), so the
op says exactly which steps change and nothing is inferred at replay time.
Enabling is refused where the flag could never write anything -- a step naming exactly one edge whose relation has no declared pair, is symmetric, or has no declared inverse edge. A step whose relation comes from the data is accepted; it mirrors per document what has a materialized inverse.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
enabled = PydanticField(default=True, description='``False`` clears the flag.')
class-attribute
instance-attribute
¶
op = 'set_inverse_emission'
class-attribute
instance-attribute
¶
steps = PydanticField(..., description='Edge steps whose flag changes: ``{resource: [step ref, ...]}``.', min_length=1)
class-attribute
instance-attribute
¶
SetNativeInversesOp
¶
Bases: ConfigBaseModel
Have the database maintain declared inverses (TigerGraph WITH REVERSE_EDGE).
Physical, not logical: adds relations to, or removes them from,
db_profile.native_inverses. Keyed by relation, as TigerGraph is: the
reverse type belongs to the edge type, which spans every (S, T) pair of
the relation. The reverse type is named by the declared pair, so the pair
must be declared (:class:DeclareEdgeInversesOp). Refused for symmetric
relations, where explicit inverse edges exist, on undirected edges, for a
relation stored under several physical names, and on non-TigerGraph
profiles.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
enabled = PydanticField(default=True, description='``False`` withdraws the native inverse.')
class-attribute
instance-attribute
¶
op = 'set_native_inverses'
class-attribute
instance-attribute
¶
relations = PydanticField(..., description='Paired relations whose declared inverse the database maintains.', min_length=1)
class-attribute
instance-attribute
¶
SetVertexDescriptionsOp
¶
Bases: ConfigBaseModel
Set or clear the human-readable description of existing vertex types.
A description was authorable only when a type was first written, so a change to one -- a merge folding two types together and joining what each side said about it, or a correction to what an inferred type means -- had no operation and could only be diffed as inexpressible. Like grounding, a description is never consulted at execution time: this op cannot change how anything ingests or stores.
Source code in graflo/architecture/evolution/ops.py
SetVertexSemanticsOp
¶
Bases: ConfigBaseModel
Ground vertex types in an external vocabulary.
Semantics were authorable only when a type was first written: no operation
could attach an iri to a type that already existed, so a manifest that
arrived ungrounded — an inferred one, or anything predating the block — could
never be grounded through the op system, only rewritten by hand.
Grounding is purely additive and never consulted at execution time, so this op cannot change how anything ingests or stores. What it changes is whether a reader who did not author the schema can tell what a type denotes.
Source code in graflo/architecture/evolution/ops.py
SideRenames
¶
Bases: ConfigBaseModel
Renames applied to one side's names that no equivalence groups.
Applied simultaneously with the side's groups and vocabulary, so a chain
({Asset: WorkOrder, WorkOrder: Ticket}) and a swap resolve without an
intermediate name, and any name either side uses or vacates may be a
target. Every source must exist on the side. A class or relation that an
equivalence groups is named by that equivalence's into, not here.
Source code in graflo/architecture/evolution/ops.py
Attributes¶
properties = PydanticField(default_factory=dict, description="Attribute renames keyed by the class's own name: ``{class: {old: new}}``.")
class-attribute
instance-attribute
¶
relations = PydanticField(default_factory=dict, description='Relation renames: ``{old: new}``.')
class-attribute
instance-attribute
¶
resources = PydanticField(default_factory=dict, description='Resource renames, applied before the union: ``{old: new}``.')
class-attribute
instance-attribute
¶
vertices = PydanticField(default_factory=dict, description='Class renames: ``{old: new}``.')
class-attribute
instance-attribute
¶
Methods:¶
VertexEquivalence
¶
Bases: ConfigBaseModel
Collapse one or more left classes and one or more right classes into one.
GraFlo applies this map deterministically; it does not infer semantic
matches. left / right accept a bare class name (a 1-1 equivalence)
or a list (an n-ary cluster): {Company, Shop} ~ {Org, Branch} ->
Company. A member may also be spelled by the canonical name a vocabulary
gives it, which stands for every class the vocabulary sends there.
The group is everything the equivalence links: its members, and every class a vocabulary merges with one of them. Per-member maps (property equivalences, member-keyed identity sources) may name any class of the group by its own name.
Properties with the same spelling on every member after alignment fuse by
exact name without an entry in properties — list only renames.
identity is the merged key, as ordered funnel branches in canonical
names. A branch is a property the members carry (a name, or a composite
[a, b]), a :class:DerivedBranch each source computes from its own
columns, or a :class:LocalKeyBranch (the tagged fallback, last). One
property branch keys the class on that natural key; anything else keys it
on a funnel, where a record keys on its first complete branch. A funnel's
digest is stored in digest_field (id by default).
derive declares attributes each source computes, without making each
one a branch: a name or composite branch in identity keys on them, so
[host_key, group_key] fires only when both are derived and the digest
joins them. A DerivedBranch {name, sources} is the same as
derive: {name: sources} with the branch name.
Source code in graflo/architecture/evolution/ops.py
2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 | |
Attributes¶
allow = PydanticField(default_factory=list, description='Consequences of this merge it accepts. `self_relations`: an edge between two members becomes an edge from the class to itself. `observation_fusion`: two members produced in one accumulator slot (same pipeline level and `role`, or both bare) fuse into one node.')
class-attribute
instance-attribute
¶
derive = PydanticField(default=None, description='Attributes each source derives from its own columns, keyed by attribute name; each entry has the shape of `DerivedBranch.sources`. Identity branches key on them by name: a composite ``[a, b]`` of derived attributes fires only when every part is derived. Requires `identity`.')
class-attribute
instance-attribute
¶
derive_at = PydanticField(default_factory=dict, description='Per-resource pipeline level the derived and local-key branches derive at, as ``descend`` step indices. Omitted resources resolve to the single level producing the class (for member-keyed sources: the level producing the member on its side); supply a path only when a resource produces it at more than one level.')
class-attribute
instance-attribute
¶
digest_field = PydanticField(default='id', min_length=1, description="Property the merged funnel's digest is stored in -- the class's identity field. Only for a funnel `identity`; it must not be a branch field. Set it when a member carries a real `id`.")
class-attribute
instance-attribute
¶
has_derivation
property
¶
Whether the key needs pipeline steps: derive, a derived or local-key branch.
identity = PydanticField(default=None, description='The merged key, as ordered funnel branches in canonical names: a property the members carry, a composite ``[a, b]``, a derived branch ``{name, sources}``, or ``{local_key: ...}`` (last). One property branch is a natural key; anything else is a funnel. When unset, identity is carried through only if every member agrees; disagreement raises `MergeIdentityError`.')
class-attribute
instance-attribute
¶
into = PydanticField(default=None, description="Merged vertex type name: any name, including one either side uses or vacates. Never translated by a vocabulary; when it differs from the vocabulary's name for the group it wins. Omitted, the name comes from the vocabulary, or from the one spelling every member shares.")
class-attribute
instance-attribute
¶
is_natural_key
property
¶
Whether identity is one property branch, kept as a natural key.
left = PydanticField(..., description='One or more left-manifest vertex type names.')
class-attribute
instance-attribute
¶
left_members
property
¶
properties = PydanticField(default_factory=list, description='Property alignment map applied before the vertex merge.')
class-attribute
instance-attribute
¶
retire = PydanticField(default='demote', description="What becomes of each member's pre-merge key once `identity` re-keys the merged class. `demote` keeps each as a lookup-only secondary identity on `into`, and points edge steps of resources that only reference a member at it; `keep` leaves the fields as plain properties. Unused while the merged class keeps its members' shared key.")
class-attribute
instance-attribute
¶
right = PydanticField(..., description='One or more right-manifest vertex type names.')
class-attribute
instance-attribute
¶
right_members
property
¶
Methods:¶
derivations()
¶
Every derived attribute: the derive entries, then derived branches.
derive_attributes()
¶
The derive entries, each as the derivation of one attribute.
Source code in graflo/architecture/evolution/ops.py
derived_branches()
¶
The derived branches of identity, in priority order.
local_key_branch()
¶
The local_key branch of identity, if declared.
members(side)
¶
property_maps(side)
¶
{member: {old_field: into_field}} for side, bare strings expanded.
Source code in graflo/architecture/evolution/ops.py
raw_branches()
¶
The property branches of identity, each as its field tuple.
Source code in graflo/architecture/evolution/ops.py
Functions:¶
__dir__()
¶
__getattr__(name)
¶
Source code in graflo/architecture/evolution/__init__.py
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | |
ops_reaching_ingestion(ops)
¶
Names of ops whose effect extends into ingestion_model, in order.