AshR2RML.Mapping.Changeset (AshR2RML v26.8.29)

Copy Markdown View Source

Structured add/update/replace/remove diff between two RDF.Graph snapshots of a rendered mapping (e.g. two successive R2RML Turtle renders of the same AshR2RML.Mapping.Resource, or two successive AshR2RML.OBDA.InMemory.materialize/3 snapshots).

Ported from Gno's Gno.Changeset action-graph algebra (~/gno/lib/gno/changeset/{changeset,action,validation}.ex), stripped of everything that assumes a live, queryable triple store: Gno's changeset is ultimately applied to and diffed against a running SPARQL store via Gno.EffectiveChangeset.Query's SPARQL-CONSTRUCT algorithm. AshR2RML never has such a store -- Ontop is virtual OBDA over Postgres, not a materialized graph -- so this module keeps only the store-independent half: the action vocabulary (add/update/replace/remove), its mutual-exclusion validation rules, and inversion, all as pure RDF.Graph transformations.

Action semantics

  • :add -- statements present in the new snapshot but absent from the old
  • :remove -- statements present in the old snapshot but absent from the new
  • :update -- statements whose subject+predicate persist across both snapshots but whose object changed (a predicate-level replacement)
  • :replace -- statements for a subject that exists in both snapshots but whose entire description changed (a subject-level replacement); mutually exclusive with :update for the same subject, matching Gno's validation rule that a subject cannot be both partially updated and wholly replaced in the same changeset

Use diff/3 to compute a changeset between two snapshots (defaults to a pure add/remove diff -- the :update/:replace classification requires the caller to say which predicates constitute an entity's identity vs. its mutable state, which AshR2RML's own AshR2RML.Mapping.Resource IR already knows and can supply via subject_identity_predicates).

Summary

Functions

The changeset action field names, in a fixed, deterministic order.

Computes a pure add/remove changeset between two RDF.Graph snapshots: statements only in new_graph become :add; statements only in old_graph become :remove. Statements in both graphs are left out of the changeset entirely (unchanged). This is the store-independent replacement for Gno's EffectiveChangeset.Query SPARQL-CONSTRUCT diff -- both inputs are already in-memory graphs, so no store round-trip is needed.

True if the changeset has no statements in any action (a no-op diff).

The graph of statements this changeset would insert if applied: :add, :update, and :replace all contribute new statements; :remove does not.

Inverts a changeset: what would undo its effect if applied on top of the result. :add becomes :remove and vice versa; :update and :replace fold into :remove (the caller must supply the prior state's statements to restore them -- inversion alone cannot recover data an update/replace overwrote, matching Gno's own documented limitation for these two actions).

Merges every action graph of changeset into a single graph of all statements it touches, regardless of action (add/update/replace/remove combined) -- the union Gno calls merged_graph/1.

Builds a changeset from keyword/map options; each of :add, :update, :replace, :remove is optional and defaults to nil (no statements for that action). Returns {:error, %Refusal{}} if validation fails.

The graph of statements this changeset would remove if applied.

Validates a changeset's mutual-exclusion invariants (ported from Gno.Changeset.Validation)

Types

t()

@type t() :: %AshR2RML.Mapping.Changeset{
  add: RDF.Graph.t() | nil,
  remove: RDF.Graph.t() | nil,
  replace: RDF.Graph.t() | nil,
  update: RDF.Graph.t() | nil
}

Functions

action_fields()

@spec action_fields() :: [atom()]

The changeset action field names, in a fixed, deterministic order.

diff(old_graph, new_graph)

@spec diff(RDF.Graph.t(), RDF.Graph.t()) ::
  {:ok, t()} | {:error, AshR2RML.Refusal.t()}

Computes a pure add/remove changeset between two RDF.Graph snapshots: statements only in new_graph become :add; statements only in old_graph become :remove. Statements in both graphs are left out of the changeset entirely (unchanged). This is the store-independent replacement for Gno's EffectiveChangeset.Query SPARQL-CONSTRUCT diff -- both inputs are already in-memory graphs, so no store round-trip is needed.

empty?(changeset)

@spec empty?(t()) :: boolean()

True if the changeset has no statements in any action (a no-op diff).

inserts(changeset)

@spec inserts(t()) :: RDF.Graph.t()

The graph of statements this changeset would insert if applied: :add, :update, and :replace all contribute new statements; :remove does not.

invert(changeset)

@spec invert(t()) :: t()

Inverts a changeset: what would undo its effect if applied on top of the result. :add becomes :remove and vice versa; :update and :replace fold into :remove (the caller must supply the prior state's statements to restore them -- inversion alone cannot recover data an update/replace overwrote, matching Gno's own documented limitation for these two actions).

merged_graph(changeset)

@spec merged_graph(t()) :: RDF.Graph.t()

Merges every action graph of changeset into a single graph of all statements it touches, regardless of action (add/update/replace/remove combined) -- the union Gno calls merged_graph/1.

new(opts \\ [])

@spec new(keyword() | map()) :: {:ok, t()} | {:error, AshR2RML.Refusal.t()}

Builds a changeset from keyword/map options; each of :add, :update, :replace, :remove is optional and defaults to nil (no statements for that action). Returns {:error, %Refusal{}} if validation fails.

removals(changeset)

@spec removals(t()) :: RDF.Graph.t()

The graph of statements this changeset would remove if applied.

validate(changeset)

@spec validate(t()) :: {:ok, t()} | {:error, AshR2RML.Refusal.t()}

Validates a changeset's mutual-exclusion invariants (ported from Gno.Changeset.Validation):

  • the same triple must not appear in both :add and :remove
  • the same subject must not be described in both :update and :replace

Returns {:ok, changeset} on success, {:error, %Refusal{}} naming the conflicting subject/triple otherwise.