Patches

A mutation selects the entities that a patch changes. A direct patch supplies entity ids without a selection query. It changes many entities in one transaction. The native database merge patch call commits every change together or commits nothing.

The document follows JSON Merge Patch semantics in the selected document format. It does not use JSON Patch operations. Patches explains why Stardust records intended state in this form.

Start with an empty database. Submit this merge patch. The temporary IDs #_ada, #_bob and #_cy request new entities. Bob's mentor links to Ada within the same patch.

dust314B
#_ada {name    Ada
       role    engineer
       age     36
       active  true
       retired false}
#_bob {name   Bob
       role   engineer
       age    41
       mentor {#link #_ada}
       active false}
#_cy  {name Cy
       role designer
       age  29}
#_tx  {author tutorial
       reason create-people}

The commit result includes the transaction ID and an ids mapping from each temporary ID to its allocated entity ID. In this fresh database, these fields are:

Result / 82B
{transaction 4
 ids         {#_ada 1
              #_bob 2
              #_cy  3}}

Keep the IDs from your actual result. An existing database can allocate different numbers. The examples below use the returned IDs 1 for Ada, 2 for Bob and 3 for Cy. The special temporary ID #_tx attaches its author and reason metadata to transaction 4.

Merge patch keyed by id

A patch is a document whose keys are entity ids. Each key is the id of a data entity. Its value contains the fields to change.

dust89B
1 {active  false
   retired null
   role    engineer}
2 {active true
   note   reviewed}

This patch gives:

Result / 174B
{1 {name   Ada
    role   engineer
    age    36
    active false}
 2 {name   Bob
    role   engineer
    age    41
    mentor {#link 1}
    active true
    note   reviewed}}

The merge patch rules produce that result:

  • An omitted member stays as it is.
  • The patch removes a member set to null.
  • Each other value replaces the current value.

Stardust applies those rules to each entity that the patch names. The members of an entity object are its fields. That is why bob keeps role engineer, ada loses retired, and each other supplied field gets its new value. A transaction document uses the same decimal ID keys.

Creating with temporary names

A key that starts with #_ is a temporary name for an entity the patch creates. Stardust allocates an id for the entity when the patch commits. {#link #_name} refers to that id anywhere in the same patch, including in a new or existing entity. The temporary name applies only within the patch. After the commit, use the entity ID to address the entity. The engine rejects a repeated name, an undeclared temporary link target, and null on a temporary name.

dust135B
#_carol {name   Carol
         mentor {#link 1}}
#_dave  {name  Dave
         buddy {#link #_carol}}
1       {protege {#link #_carol}}

This commits two new entities and one change to Ada, all in one transaction. After the preceding examples, the commit returns these fields:

Result / 64B
{transaction 8
 ids         {#_carol 6
              #_dave  7}}

Use the returned IDs to refer to Carol and Dave in later calls.

#_tx is a special temporary ID for the transaction that commits the patch. Its object supplies metadata, such as author and reason, on that transaction. It does not create an ordinary data entity. {#link #_tx} links to the same transaction from elsewhere in the patch. This metadata accompanies data changes. Stardust rejects a patch that contains only #_tx.

One value at a time

A list or a map stored in a field is one Stardust value. The patch gives the complete intended value for that field. It does not give editing operations inside the value. An object inside an entity is a map in that field, never a second entity: a data entity has no children.

Stardust compares each field with the current facts before it writes. Stardust skips a field that already matches. It also skips null for a field that is absent. A patch with no effect commits no transaction.

Deleting

An entity id set to null deletes that entity.

dust7B
2 null

After this patch, bob has no facts:

Result / 8B
{2 null}

Entities elsewhere that link to a deleted entity survive, and their links keep the deleted target's stable numeric id.

Invalid patch forms

The keys of a patch are ids, so a field at the root is an error.

dust13B
missing true

Error

Rejected

An invalid id is an error, and the patch changes nothing:

dust28B
ffffffffffff {role visitor}

Error

Rejected

A #_name key creates a data entity. References to that temporary name resolve within the patch. The native commit result gives the resolved ids.

Stardust also rejects text that is not a document. No rejected patch changes a stored fact.

A schema-checked patch

The core patch operation can validate each staged entity against a schema before it commits. A deleted entity has no document to validate. The current public merge-patch call does not expose that schema scope. Schemas explains this validation boundary.

Reading the committed transaction

A commit result identifies its transaction. Reading that transaction document gives the patch for the commit, keyed by entity id. Each value holds asserted fields and null for retractions. Unchanged fields are absent.

The transaction entity appears under its own ID with stardust/transaction and stardust/committedAt. There are no entities or transaction wrappers. Definition and object changes include their structural facts and numeric references. The recorded patch stays the same after a later rename or deletion. Native documents and DUST contain the same changes.

Run it

database merge patch.