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.
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.
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 000 | 54 52 4F 4E | magic TRON |
| 004 | 7C 72657469726564 | txt "retired" |
| 00C | 01 | bit false |
| 00D | 0F 0A 04000000 0C00 0000 | map leaf, size 10: @4 → @C |
| 017 | 3C 616765 | txt "age" |
| 01B | 02 24000000000000 00 | i64 36 |
| 024 | 0F 0A 17000000 1B00 0000 | map leaf, size 10: @17 → @1B |
| 02E | 4C 726F6C65 | txt "role" |
| 033 | 8C 656E67696E6565 72 | txt "engineer" |
| 03C | 0F 0A 2E000000 3300 0000 | map leaf, size 10: @2E → @33 |
| 046 | 6C 616374697665 | txt "active" |
| 04D | 09 | bit true |
| 04E | 0F 0A 46000000 4D00 0000 | map leaf, size 10: @46 → @4D |
| 058 | 07 0E 60000000 3C00 0000 4E000000 | map branch, size 14, depth 1, slots 5, 6: @3C → @4E |
| 066 | 4C 6E616D65 | txt "name" |
| 06B | 3C 416461 | txt "Ada" |
| 06F | 0F 0A 66000000 6B00 0000 | map leaf, size 10: @66 → @6B |
| 079 | 07 16 01910000 0D00 0000 24000000 5800 0000 6F000000 | map branch, size 22, depth 0 (root), slots 0, 8, 12, 15: @D → @24 → @58 → @6F |
| 08F | 5C 236C696E6B | txt "#link" |
| 095 | 5C 235F616461 | txt "#_ada" |
| 09B | 0F 0A 8F000000 9500 0000 | map leaf, size 10: @8F → @95 |
| 0A5 | 3C 616765 | txt "age" |
| 0A9 | 02 29000000000000 00 | i64 41 |
| 0B2 | 0F 0A A5000000 A900 0000 | map leaf, size 10: @A5 → @A9 |
| 0BC | 6C 6D656E746F72 | txt "mentor" |
| 0C3 | 0F 0A BC000000 9B00 0000 | map leaf, size 10: @BC → @9B |
| 0CD | 4C 726F6C65 | txt "role" |
| 0D2 | 8C 656E67696E6565 72 | txt "engineer" |
| 0DB | 0F 0A CD000000 D200 0000 | map leaf, size 10: @CD → @D2 |
| 0E5 | 6C 616374697665 | txt "active" |
| 0EC | 01 | bit false |
| 0ED | 0F 0A E5000000 EC00 0000 | map leaf, size 10: @E5 → @EC |
Show all 62 nodes
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 0F7 | 07 0E 60000000 DB00 0000 ED000000 | map branch, size 14, depth 1, slots 5, 6: @DB → @ED |
| 105 | 4C 6E616D65 | txt "name" |
| 10A | 3C 426F62 | txt "Bob" |
| 10E | 0F 0A 05010000 0A01 0000 | map leaf, size 10: @105 → @10A |
| 118 | 07 16 00990000 B200 0000 C3000000 F700 0000 0E010000 | map branch, size 22, depth 0 (root), slots 8, 11, 12, 15: @B2 → @C3 → @F7 → @10E |
| 12E | 3C 616765 | txt "age" |
| 132 | 02 1D000000000000 00 | i64 29 |
| 13B | 0F 0A 2E010000 3201 0000 | map leaf, size 10: @12E → @132 |
| 145 | 4C 726F6C65 | txt "role" |
| 14A | 8C 64657369676E65 72 | txt "designer" |
| 153 | 0F 0A 45010000 4A01 0000 | map leaf, size 10: @145 → @14A |
| 15D | 4C 6E616D65 | txt "name" |
| 162 | 2C 4379 | txt "Cy" |
| 165 | 0F 0A 5D010000 6201 0000 | map leaf, size 10: @15D → @162 |
| 16F | 07 12 00910000 3B01 0000 53010000 6501 0000 | map branch, size 18, depth 0 (root), slots 8, 12, 15: @13B → @153 → @165 |
| 181 | 6C 617574686F72 | txt "author" |
| 188 | 8C 7475746F726961 6C | txt "tutorial" |
| 191 | 0F 0A 81010000 8801 0000 | map leaf, size 10: @181 → @188 |
| 19B | 6C 726561736F6E | txt "reason" |
| 1A2 | DC 6372656174652D 70656F706C65 | txt "create-people" |
| 1B0 | 0F 0A 9B010000 A201 0000 | map leaf, size 10: @19B → @1A2 |
| 1BA | 07 0E 40080000 9101 0000 B0010000 | map branch, size 14, depth 0 (root), slots 6, 11: @191 → @1B0 |
| 1C8 | 4C 235F7478 | txt "#_tx" |
| 1CD | 0F 0A C8010000 BA01 0000 | map leaf, size 10: @1C8 → @1BA |
| 1D7 | 4C 235F6379 | txt "#_cy" |
| 1DC | 0F 0A D7010000 6F01 0000 | map leaf, size 10: @1D7 → @16F |
| 1E6 | 5C 235F626F62 | txt "#_bob" |
| 1EC | 0F 0A E6010000 1801 0000 | map leaf, size 10: @1E6 → @118 |
| 1F6 | 5C 235F616461 | txt "#_ada" |
| 1FC | 0F 0A F6010000 7900 0000 | map leaf, size 10: @1F6 → @79 |
| 206 | 07 16 88210000 CD01 0000 DC010000 EC01 0000 FC010000 | map branch, size 22, depth 0 (root), slots 3, 7, 8, 13: @1CD → @1DC → @1EC → @1FC |
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 21C | 06020000 00000000 | root → @206; previous: none |
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:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 4C 235F6379 | txt "#_cy" |
| 09 | 02 03000000000000 00 | i64 3 |
| 12 | 0F 0A 04000000 0900 0000 | map leaf, size 10: @4 → @9 |
| 1C | 5C 235F626F62 | txt "#_bob" |
| 22 | 02 02000000000000 00 | i64 2 |
| 2B | 0F 0A 1C000000 2200 0000 | map leaf, size 10: @1C → @22 |
| 35 | 5C 235F616461 | txt "#_ada" |
| 3B | 02 01000000000000 00 | i64 1 |
| 44 | 0F 0A 35000000 3B00 0000 | map leaf, size 10: @35 → @3B |
| 4E | 07 12 80210000 1200 0000 2B000000 4400 0000 | map branch, size 18, depth 0 (root), slots 7, 8, 13: @12 → @2B → @44 |
| 60 | 3C 696473 | txt "ids" |
| 64 | 0F 0A 60000000 4E00 0000 | map leaf, size 10: @60 → @4E |
| 6E | BC 7472616E736163 74696F6E | txt "transaction" |
| 7A | 02 04000000000000 00 | i64 4 |
| 83 | 0F 0A 6E000000 7A00 0000 | map leaf, size 10: @6E → @7A |
| 8D | 07 0E 01020000 6400 0000 83000000 | map branch, size 14, depth 0 (root), slots 0, 9: @64 → @83 |
| 9B | 8D000000 00000000 | root → @8D; previous: none |
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.
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 7C 72657469726564 | txt "retired" |
| 0C | 00 | nil |
| 0D | 0F 0A 04000000 0C00 0000 | map leaf, size 10: @4 → @C |
| 17 | 4C 726F6C65 | txt "role" |
| 1C | 8C 656E67696E6565 72 | txt "engineer" |
| 25 | 0F 0A 17000000 1C00 0000 | map leaf, size 10: @17 → @1C |
| 2F | 6C 616374697665 | txt "active" |
| 36 | 01 | bit false |
| 37 | 0F 0A 2F000000 3600 0000 | map leaf, size 10: @2F → @36 |
| 41 | 07 0E 60000000 2500 0000 37000000 | map branch, size 14, depth 1, slots 5, 6: @25 → @37 |
| 4F | 07 0E 01100000 0D00 0000 41000000 | map branch, size 14, depth 0 (root), slots 0, 12: @D → @41 |
| 5D | 6C 616374697665 | txt "active" |
| 64 | 09 | bit true |
| 65 | 0F 0A 5D000000 6400 0000 | map leaf, size 10: @5D → @64 |
| 6F | 4C 6E6F7465 | txt "note" |
| 74 | 8C 72657669657765 64 | txt "reviewed" |
| 7D | 0F 0A 6F000000 7400 0000 | map leaf, size 10: @6F → @74 |
| 87 | 07 0E C0000000 6500 0000 7D000000 | map branch, size 14, depth 1, slots 6, 7: @65 → @7D |
| 95 | 07 0A 00100000 8700 0000 | map branch, size 10, depth 0 (root), slots 12: @87 |
| 9F | 1C 31 | txt "1" |
| A1 | 0F 0A 9F000000 4F00 0000 | map leaf, size 10: @9F → @4F |
| AB | 1C 32 | txt "2" |
| AD | 0F 0A AB000000 9500 0000 | map leaf, size 10: @AB → @95 |
| B7 | 07 0E 14000000 A100 0000 AD000000 | map branch, size 14, depth 0 (root), slots 2, 4: @A1 → @AD |
| C5 | B7000000 00000000 | root → @B7; previous: none |
This patch gives:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 000 | 54 52 4F 4E | magic TRON |
| 004 | 3C 616765 | txt "age" |
| 008 | 02 24000000000000 00 | i64 36 |
| 011 | 0F 0A 04000000 0800 0000 | map leaf, size 10: @4 → @8 |
| 01B | 4C 726F6C65 | txt "role" |
| 020 | 8C 656E67696E6565 72 | txt "engineer" |
| 029 | 0F 0A 1B000000 2000 0000 | map leaf, size 10: @1B → @20 |
| 033 | 6C 616374697665 | txt "active" |
| 03A | 01 | bit false |
| 03B | 0F 0A 33000000 3A00 0000 | map leaf, size 10: @33 → @3A |
| 045 | 07 0E 60000000 2900 0000 3B000000 | map branch, size 14, depth 1, slots 5, 6: @29 → @3B |
| 053 | 4C 6E616D65 | txt "name" |
| 058 | 3C 416461 | txt "Ada" |
| 05C | 0F 0A 53000000 5800 0000 | map leaf, size 10: @53 → @58 |
| 066 | 07 12 00910000 1100 0000 45000000 5C00 0000 | map branch, size 18, depth 0 (root), slots 8, 12, 15: @11 → @45 → @5C |
| 078 | 5C 236C696E6B | txt "#link" |
| 07E | 02 01000000000000 00 | i64 1 |
| 087 | 0F 0A 78000000 7E00 0000 | map leaf, size 10: @78 → @7E |
| 091 | 3C 616765 | txt "age" |
| 095 | 02 29000000000000 00 | i64 41 |
| 09E | 0F 0A 91000000 9500 0000 | map leaf, size 10: @91 → @95 |
| 0A8 | 6C 6D656E746F72 | txt "mentor" |
| 0AF | 0F 0A A8000000 8700 0000 | map leaf, size 10: @A8 → @87 |
| 0B9 | 4C 726F6C65 | txt "role" |
| 0BE | 8C 656E67696E6565 72 | txt "engineer" |
| 0C7 | 0F 0A B9000000 BE00 0000 | map leaf, size 10: @B9 → @BE |
| 0D1 | 6C 616374697665 | txt "active" |
| 0D8 | 09 | bit true |
| 0D9 | 0F 0A D1000000 D800 0000 | map leaf, size 10: @D1 → @D8 |
| 0E3 | 4C 6E6F7465 | txt "note" |
| 0E8 | 8C 72657669657765 64 | txt "reviewed" |
| 0F1 | 0F 0A E3000000 E800 0000 | map leaf, size 10: @E3 → @E8 |
| 0FB | 07 12 E0000000 C700 0000 D9000000 F100 0000 | map branch, size 18, depth 1, slots 5, 6, 7: @C7 → @D9 → @F1 |
| 10D | 4C 6E616D65 | txt "name" |
| 112 | 3C 426F62 | txt "Bob" |
| 116 | 0F 0A 0D010000 1201 0000 | map leaf, size 10: @10D → @112 |
| 120 | 07 16 00990000 9E00 0000 AF000000 FB00 0000 16010000 | map branch, size 22, depth 0 (root), slots 8, 11, 12, 15: @9E → @AF → @FB → @116 |
| 136 | 1C 31 | txt "1" |
| 138 | 0F 0A 36010000 6600 0000 | map leaf, size 10: @136 → @66 |
| 142 | 1C 32 | txt "2" |
| 144 | 0F 0A 42010000 2001 0000 | map leaf, size 10: @142 → @120 |
| 14E | 07 0E 14000000 3801 0000 44010000 | map branch, size 14, depth 0 (root), slots 2, 4: @138 → @144 |
| 15C | 4E010000 00000000 | root → @14E; previous: none |
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.
Compare-and-set per entity
Put per-entity conditions in #_tx.stardust/checkAndSet. Each map key is a
decimal entity ID. Its value is the expected transaction.
For a separate example, submit only the initial patch in a fresh database.
It changes Ada and Bob in transaction 4. Then submit:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 3C 616765 | txt "age" |
| 08 | 02 25000000000000 00 | i64 37 |
| 11 | 0F 0A 04000000 0800 0000 | map leaf, size 10: @4 → @8 |
| 1B | 1C 31 | txt "1" |
| 1D | 02 04000000000000 00 | i64 4 |
| 26 | 0F 0A 1B000000 1D00 0000 | map leaf, size 10: @1B → @1D |
| 30 | 1C 32 | txt "2" |
| 32 | 02 04000000000000 00 | i64 4 |
| 3B | 0F 0A 30000000 3200 0000 | map leaf, size 10: @30 → @32 |
| 45 | 07 0E 14000000 2600 0000 3B000000 | map branch, size 14, depth 0 (root), slots 2, 4: @26 → @3B |
| 53 | 14 14 737461726475 73742F636865636B 416E64536574 | txt "stardust/checkAndSet" |
| 69 | 0F 0A 53000000 4500 0000 | map leaf, size 10: @53 → @45 |
| 73 | 1C 31 | txt "1" |
| 75 | 0F 0A 73000000 1100 0000 | map leaf, size 10: @73 → @11 |
| 7F | 4C 235F7478 | txt "#_tx" |
| 84 | 0F 0A 7F000000 6900 0000 | map leaf, size 10: @7F → @69 |
| 8E | 07 0E 0C000000 7500 0000 84000000 | map branch, size 14, depth 0 (root), slots 2, 3: @75 → @84 |
| 9C | 8E000000 00000000 | root → @8E; previous: none |
This patch changes Ada only if both entities still have revision 4. A
change to another entity does not invalidate these checks. A check can guard
an entity that the patch does not change.
Stardust checks every revision in the atomic write transaction. A mismatch
rejects the complete patch with conflict. The diagnostic identifies the
entity and gives the expected and actual revisions. No changes or temporary
IDs are committed. The engine consumes stardust/checkAndSet; it does not
store these conditions as transaction metadata.
A revision includes field removals and entity deletion. Changing a value and then restoring it does not restore the old revision. To retry after a conflict, read the current state and obtain its revision before computing a new patch.
Positive expectations require an exact last-touch transaction. Zero instead requires that the entity has no current facts, including after deletion:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 1C 31 | txt "1" |
| 06 | 02 00000000000000 00 | i64 0 |
| 0F | 0F 0A 04000000 0600 0000 | map leaf, size 10: @4 → @6 |
| 19 | 14 14 737461726475 73742F636865636B 416E64536574 | txt "stardust/checkAndSet" |
| 2F | 0F 0A 19000000 0F00 0000 | map leaf, size 10: @19 → @F |
| 39 | 4C 235F7478 | txt "#_tx" |
| 3E | 0F 0A 39000000 2F00 0000 | map leaf, size 10: @39 → @2F |
| 48 | 3E000000 00000000 | root → @3E; previous: none |
A request with only conditions checks them without creating a transaction.
It returns no_change on success. The same checks apply when supplied field
values already match. Other transaction metadata accompanies data changes.
The condition must be an object. Checked IDs must be decimal user entity IDs and appear only once. Expected transactions must be nonnegative integers. An empty condition map imposes no entity conditions. Temporary IDs, deletions and other transaction metadata work as in an ordinary patch.
Reading an entity for a conditional write
Read entity returns one document from one store snapshot. Read Bob:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 6C 656E74697479 | txt "entity" |
| 0B | 02 02000000000000 00 | i64 2 |
| 14 | 0F 0A 04000000 0B00 0000 | map leaf, size 10: @4 → @B |
| 1E | 14000000 00000000 | root → @14; previous: none |
The result is:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 000 | 54 52 4F 4E | magic TRON |
| 004 | 5C 236C696E6B | txt "#link" |
| 00A | 02 01000000000000 00 | i64 1 |
| 013 | 0F 0A 04000000 0A00 0000 | map leaf, size 10: @4 → @A |
| 01D | 3C 616765 | txt "age" |
| 021 | 02 29000000000000 00 | i64 41 |
| 02A | 0F 0A 1D000000 2100 0000 | map leaf, size 10: @1D → @21 |
| 034 | 6C 6D656E746F72 | txt "mentor" |
| 03B | 0F 0A 34000000 1300 0000 | map leaf, size 10: @34 → @13 |
| 045 | 4C 726F6C65 | txt "role" |
| 04A | 8C 656E67696E6565 72 | txt "engineer" |
| 053 | 0F 0A 45000000 4A00 0000 | map leaf, size 10: @45 → @4A |
| 05D | 6C 616374697665 | txt "active" |
| 064 | 09 | bit true |
| 065 | 0F 0A 5D000000 6400 0000 | map leaf, size 10: @5D → @64 |
| 06F | 4C 6E6F7465 | txt "note" |
| 074 | 8C 72657669657765 64 | txt "reviewed" |
| 07D | 0F 0A 6F000000 7400 0000 | map leaf, size 10: @6F → @74 |
| 087 | 07 12 E0000000 5300 0000 65000000 7D00 0000 | map branch, size 18, depth 1, slots 5, 6, 7: @53 → @65 → @7D |
| 099 | 4C 6E616D65 | txt "name" |
| 09E | 3C 426F62 | txt "Bob" |
| 0A2 | 0F 0A 99000000 9E00 0000 | map leaf, size 10: @99 → @9E |
| 0AC | 07 16 00990000 2A00 0000 3B000000 8700 0000 A2000000 | map branch, size 22, depth 0 (root), slots 8, 11, 12, 15: @2A → @3B → @87 → @A2 |
| 0C2 | 1C 32 | txt "2" |
| 0C4 | 02 05000000000000 00 | i64 5 |
| 0CD | 0F 0A C2000000 C400 0000 | map leaf, size 10: @C2 → @C4 |
| 0D7 | 9C 7472756E636174 6564 | txt "truncated" |
| 0E1 | 01 | bit false |
| 0E2 | 0F 0A D7000000 E100 0000 | map leaf, size 10: @D7 → @E1 |
| 0EC | 8C 646F63756D656E 74 | txt "document" |
| 0F5 | 0F 0A EC000000 AC00 0000 | map leaf, size 10: @EC → @AC |
| 0FF | 9C 7265766973696F 6E73 | txt "revisions" |
| 109 | 0F 0A FF000000 CD00 0000 | map leaf, size 10: @FF → @CD |
| 113 | 07 12 82200000 E200 0000 F5000000 0901 0000 | map branch, size 18, depth 0 (root), slots 1, 7, 13: @E2 → @F5 → @109 |
| 125 | 13010000 00000000 | root → @113; previous: none |
document holds the entity's current fields. revisions maps each entity
the result reflects to its last-touch transaction, so it serves directly as
the stardust/checkAndSet map of a patch computed from that document. The
returned document is independent of the database. Close it when finished.
Read results omit object fields whose values are empty objects or arrays,
including objects that become empty after nested fields are omitted. This
applies to entity data and metadata: links is absent when empty. Null, empty
strings, false, zero, and array positions stay intact.
document is absent if all its fields are omitted.
An entity with no current facts has a null document. Its revision is the
deletion's transaction after a deletion, and zero for a never-touched entity.
Thus, a positive condition can require an exact deletion revision, while zero
checks only the absence of current facts.
With max_depth 1, the read also expands Bob's mentor in place.
links preserves the original mentor reference, and revisions adds Ada:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 6C 656E74697479 | txt "entity" |
| 0B | 02 02000000000000 00 | i64 2 |
| 14 | 0F 0A 04000000 0B00 0000 | map leaf, size 10: @4 → @B |
| 1E | 9C 6D61785F646570 7468 | txt "max_depth" |
| 28 | 02 01000000000000 00 | i64 1 |
| 31 | 0F 0A 1E000000 2800 0000 | map leaf, size 10: @1E → @28 |
| 3B | 07 0E 10020000 1400 0000 31000000 | map branch, size 14, depth 0 (root), slots 4, 9: @14 → @31 |
| 49 | 3B000000 00000000 | root → @3B; previous: none |
The result is:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 000 | 54 52 4F 4E | magic TRON |
| 004 | 3C 616765 | txt "age" |
| 008 | 02 24000000000000 00 | i64 36 |
| 011 | 0F 0A 04000000 0800 0000 | map leaf, size 10: @4 → @8 |
| 01B | 4C 726F6C65 | txt "role" |
| 020 | 8C 656E67696E6565 72 | txt "engineer" |
| 029 | 0F 0A 1B000000 2000 0000 | map leaf, size 10: @1B → @20 |
| 033 | 6C 616374697665 | txt "active" |
| 03A | 01 | bit false |
| 03B | 0F 0A 33000000 3A00 0000 | map leaf, size 10: @33 → @3A |
| 045 | 07 0E 60000000 2900 0000 3B000000 | map branch, size 14, depth 1, slots 5, 6: @29 → @3B |
| 053 | 4C 6E616D65 | txt "name" |
| 058 | 3C 416461 | txt "Ada" |
| 05C | 0F 0A 53000000 5800 0000 | map leaf, size 10: @53 → @58 |
| 066 | 07 12 00910000 1100 0000 45000000 5C00 0000 | map branch, size 18, depth 0 (root), slots 8, 12, 15: @11 → @45 → @5C |
| 078 | 3C 616765 | txt "age" |
| 07C | 02 29000000000000 00 | i64 41 |
| 085 | 0F 0A 78000000 7C00 0000 | map leaf, size 10: @78 → @7C |
| 08F | 6C 6D656E746F72 | txt "mentor" |
| 096 | 0F 0A 8F000000 6600 0000 | map leaf, size 10: @8F → @66 |
| 0A0 | 4C 726F6C65 | txt "role" |
| 0A5 | 8C 656E67696E6565 72 | txt "engineer" |
| 0AE | 0F 0A A0000000 A500 0000 | map leaf, size 10: @A0 → @A5 |
| 0B8 | 6C 616374697665 | txt "active" |
| 0BF | 09 | bit true |
| 0C0 | 0F 0A B8000000 BF00 0000 | map leaf, size 10: @B8 → @BF |
| 0CA | 4C 6E6F7465 | txt "note" |
| 0CF | 8C 72657669657765 64 | txt "reviewed" |
| 0D8 | 0F 0A CA000000 CF00 0000 | map leaf, size 10: @CA → @CF |
| 0E2 | 07 12 E0000000 AE00 0000 C0000000 D800 0000 | map branch, size 18, depth 1, slots 5, 6, 7: @AE → @C0 → @D8 |
| 0F4 | 4C 6E616D65 | txt "name" |
| 0F9 | 3C 426F62 | txt "Bob" |
| 0FD | 0F 0A F4000000 F900 0000 | map leaf, size 10: @F4 → @F9 |
| 107 | 07 16 00990000 8500 0000 96000000 E200 0000 FD000000 | map branch, size 22, depth 0 (root), slots 8, 11, 12, 15: @85 → @96 → @E2 → @FD |
| 11D | 5C 236C696E6B | txt "#link" |
| 123 | 02 01000000000000 00 | i64 1 |
| 12C | 0F 0A 1D010000 2301 0000 | map leaf, size 10: @11D → @123 |
| 136 | 7C 2F6D656E746F72 | txt "/mentor" |
| 13E | 0F 0A 36010000 2C01 0000 | map leaf, size 10: @136 → @12C |
| 148 | 1C 31 | txt "1" |
| 14A | 02 05000000000000 00 | i64 5 |
| 153 | 0F 0A 48010000 4A01 0000 | map leaf, size 10: @148 → @14A |
| 15D | 1C 32 | txt "2" |
| 15F | 02 05000000000000 00 | i64 5 |
| 168 | 0F 0A 5D010000 5F01 0000 | map leaf, size 10: @15D → @15F |
| 172 | 07 0E 14000000 5301 0000 68010000 | map branch, size 14, depth 0 (root), slots 2, 4: @153 → @168 |
| 180 | 9C 7472756E636174 6564 | txt "truncated" |
| 18A | 01 | bit false |
| 18B | 0F 0A 80010000 8A01 0000 | map leaf, size 10: @180 → @18A |
| 195 | 8C 646F63756D656E 74 | txt "document" |
| 19E | 0F 0A 95010000 0701 0000 | map leaf, size 10: @195 → @107 |
| 1A8 | 5C 6C696E6B73 | txt "links" |
| 1AE | 0F 0A A8010000 3E01 0000 | map leaf, size 10: @1A8 → @13E |
| 1B8 | 07 0E 20100000 9E01 0000 AE010000 | map branch, size 14, depth 1, slots 5, 12: @19E → @1AE |
| 1C6 | 9C 7265766973696F 6E73 | txt "revisions" |
| 1D0 | 0F 0A C6010000 7201 0000 | map leaf, size 10: @1C6 → @172 |
| 1DA | 07 12 82200000 8B01 0000 B8010000 D001 0000 | map branch, size 18, depth 0 (root), slots 1, 7, 13: @18B → @1B8 → @1D0 |
| 1EC | DA010000 00000000 | root → @1DA; previous: none |
links maps JSON Pointer paths
within document to their original references. /employer/city
selects a nested field; /friends/0 selects an array element. In a field
name, ~ becomes ~0 and / becomes ~1, so /a~1b selects the field
a/b. References replaced by null or an omitted empty object keep their
entries in links.
max_depth counts link hops along each path, up to 32. A link to an entity
with no current fields becomes null, and the field stays; revisions still
records that entity. Links inside stardust/ fields stay references. An
entity reached on several paths, or on a cycle, is expanded again on each
path until max_depth. Every link one hop away is expanded before any two
hops away.
max_entities (default 1000) bounds the entity documents in the result, and
the result counts against the database memory budget. When either limit
refuses an expansion, that link and every later one stay {#link id}
references and truncated is true. The conditions in revisions then cover
exactly the entities in the document.
A link in a patch can name an ID that no entity has yet. An ID of 0, a
negative ID or an ID at or above 2^40 is refused with invalid_argument and
the code invalid_link, which names the key and field.
A no-change commit does not give a new entity revision. Do not infer the last touch from the newest remaining fact: a field removal can be more recent.
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.
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 000 | 54 52 4F 4E | magic TRON |
| 004 | 5C 236C696E6B | txt "#link" |
| 00A | 02 01000000000000 00 | i64 1 |
| 013 | 0F 0A 04000000 0A00 0000 | map leaf, size 10: @4 → @A |
| 01D | 6C 6D656E746F72 | txt "mentor" |
| 024 | 0F 0A 1D000000 1300 0000 | map leaf, size 10: @1D → @13 |
| 02E | 4C 6E616D65 | txt "name" |
| 033 | 5C 4361726F6C | txt "Carol" |
| 039 | 0F 0A 2E000000 3300 0000 | map leaf, size 10: @2E → @33 |
| 043 | 07 0E 00880000 2400 0000 39000000 | map branch, size 14, depth 0 (root), slots 11, 15: @24 → @39 |
| 051 | 5C 236C696E6B | txt "#link" |
| 057 | 7C 235F6361726F6C | txt "#_carol" |
| 05F | 0F 0A 51000000 5700 0000 | map leaf, size 10: @51 → @57 |
| 069 | 5C 6275646479 | txt "buddy" |
| 06F | 0F 0A 69000000 5F00 0000 | map leaf, size 10: @69 → @5F |
| 079 | 4C 6E616D65 | txt "name" |
| 07E | 4C 44617665 | txt "Dave" |
| 083 | 0F 0A 79000000 7E00 0000 | map leaf, size 10: @79 → @7E |
| 08D | 07 0E 04800000 6F00 0000 83000000 | map branch, size 14, depth 0 (root), slots 2, 15: @6F → @83 |
| 09B | 5C 236C696E6B | txt "#link" |
| 0A1 | 7C 235F6361726F6C | txt "#_carol" |
| 0A9 | 0F 0A 9B000000 A100 0000 | map leaf, size 10: @9B → @A1 |
| 0B3 | 7C 70726F74656765 | txt "protege" |
| 0BB | 0F 0A B3000000 A900 0000 | map leaf, size 10: @B3 → @A9 |
| 0C5 | 1C 31 | txt "1" |
| 0C7 | 0F 0A C5000000 BB00 0000 | map leaf, size 10: @C5 → @BB |
| 0D1 | 7C 235F6361726F6C | txt "#_carol" |
| 0D9 | 0F 0A D1000000 4300 0000 | map leaf, size 10: @D1 → @43 |
| 0E3 | 6C 235F64617665 | txt "#_dave" |
| 0EA | 0F 0A E3000000 8D00 0000 | map leaf, size 10: @E3 → @8D |
| 0F4 | 07 12 04180000 C700 0000 D9000000 EA00 0000 | map branch, size 18, depth 0 (root), slots 2, 11, 12: @C7 → @D9 → @EA |
| 106 | F4000000 00000000 | root → @F4; previous: none |
This commits two new entities and one change to Ada, all in one transaction. After the preceding examples, the commit returns these fields:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 7C 235F6361726F6C | txt "#_carol" |
| 0C | 02 06000000000000 00 | i64 6 |
| 15 | 0F 0A 04000000 0C00 0000 | map leaf, size 10: @4 → @C |
| 1F | 6C 235F64617665 | txt "#_dave" |
| 26 | 02 07000000000000 00 | i64 7 |
| 2F | 0F 0A 1F000000 2600 0000 | map leaf, size 10: @1F → @26 |
| 39 | 07 0E 00180000 1500 0000 2F000000 | map branch, size 14, depth 0 (root), slots 11, 12: @15 → @2F |
| 47 | 3C 696473 | txt "ids" |
| 4B | 0F 0A 47000000 3900 0000 | map leaf, size 10: @47 → @39 |
| 55 | BC 7472616E736163 74696F6E | txt "transaction" |
| 61 | 02 08000000000000 00 | i64 8 |
| 6A | 0F 0A 55000000 6100 0000 | map leaf, size 10: @55 → @61 |
| 74 | 07 0E 01020000 4B00 0000 6A000000 | map branch, size 14, depth 0 (root), slots 0, 9: @4B → @6A |
| 82 | 74000000 00000000 | root → @74; previous: none |
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.
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 1C 32 | txt "2" |
| 06 | 00 | nil |
| 07 | 0F 0A 04000000 0600 0000 | map leaf, size 10: @4 → @6 |
| 11 | 07000000 00000000 | root → @7; previous: none |
After this patch, bob has no facts:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 1C 32 | txt "2" |
| 06 | 00 | nil |
| 07 | 0F 0A 04000000 0600 0000 | map leaf, size 10: @4 → @6 |
| 11 | 07000000 00000000 | root → @7; previous: none |
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.
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 7C 6D697373696E67 | txt "missing" |
| 0C | 09 | bit true |
| 0D | 0F 0A 04000000 0C00 0000 | map leaf, size 10: @4 → @C |
| 17 | 0D000000 00000000 | root → @D; previous: none |
Error
Rejected
An invalid id is an error, and the patch changes nothing:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 4C 726F6C65 | txt "role" |
| 09 | 7C 76697369746F72 | txt "visitor" |
| 11 | 0F 0A 04000000 0900 0000 | map leaf, size 10: @4 → @9 |
| 1B | CC 66666666666666 6666666666 | txt "ffffffffffff" |
| 28 | 0F 0A 1B000000 1100 0000 | map leaf, size 10: @1B → @11 |
| 32 | 28000000 00000000 | root → @28; previous: none |
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.
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.