Transaction Syntax
Introduction
Transactions modify the data stored in Fluree. Transaction data is encoded as a JSON object and can be a JSON object or an array of objects.
Transaction objects come in two types, insert
and delete
, where insert
and delete
can be combined in the space of one transaction to perform surgical updates against data state.
Fluree treats transaction objects as JSON-LD, and any object that is not a delete
object will be inserted as RDF triples in the database. Therefore, any valid JSON-LD object is a valid insert
object.
You can, however, transact vanilla JSON that doesn't use JSON-LD conventions (such as @id
or @type
):
{ "ledger": "notable-people", "insert": { "name": "Shonda Rhimes" }}
JSON-LD keywords can, however, be used and will be interpretd according to their JSON-LD meaning:
{ "@context": { "ex": "http://example.org/", "schema": "http://schema.org/" }, "ledger": "notable-people", "insert": { "@id": "ex:shonda-rhimes", "schema:name": "Shonda Rhimes" }}
Arbitrarily nested JSON-LD is also supported:
{ "@context": { "@base": "http://schema.org/", "@vocab": "http://schema.org/" }, "ledger": "media", "insert": { "@id": "https://www.wikidata.org/wiki/Q836821", "@type": ["Movie"], "name": "The Hitchhiker's Guide to the Galaxy", "disambiguatingDescription": "2005 British-American comic science fiction film directed by Garth Jennings", "titleEIDR": "10.5240/B752-5B47-DBBE-E5D4-5A3F-N", "isBasedOn": { "@id": "https://www.wikidata.org/wiki/Q3107329", "@type": "Book", "name": "The Hitchhiker's Guide to the Galaxy", "isbn": "0-330-25864-8", "author": { "@id": "https://www.wikidata.org/wiki/Q42", "@type": "Person", "name": "Douglas Adams" }, "isbn": "0-330-25864-8", "name": "The Hitchhiker's Guide to the Galaxy" }, "name": "The Hitchhiker's Guide to the Galaxy", "titleEIDR": "10.5240/B752-5B47-DBBE-E5D4-5A3F-N" }}
See the W3C standard for JSON-LD for more information on structuring JSON-LD objects.
Sample Data & Ledger
You can recreate and test each of the sample transactions in this page using the json objects below to create the "media" ledger and transact in your sample data.
Create Test Ledger "media"
{ "@context": { "schema": "http://schema.org/", "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "insert": { "@id": "wiki:Q836821", "@type": [ "schema:Movie" ], "schema:name": "The Hitchhiker's Guide to the Galaxy", "schema:disambiguatingDescription": "2005 British-American comic science fiction film directed by Garth Jennings", "schema:titleEIDR": "10.5240/B752-5B47-DBBE-E5D4-5A3F-N", "schema:isBasedOn": { "@id": "wiki:Q3107329", "@type": "schema:Book", "schema:name": "The Hitchhiker's Guide to the Galaxy", "schema:isbn": "0-330-25864-8", "schema:author": { "@id": "wiki:Q42", "@type": "schema:Person", "schema:name": "Douglas Adams" } }, "ex:boxOfficeGross": "$5,128,935.00", "ex:starring": "Andrew Johnson" }}
Transaction Object: Insert
Insert a Single Record
In this example we are inserting a new record to our "media" ledger for the book "The Phantom Tollbooth"
{ "@context": { "schema": "http://schema.org/", "wiki": "https://www.wikidata.org/wiki/" }, "ledger": "media", "insert": [ { "@id": "wiki:The_Phantom_Tollbooth", "@type": "schema:Book" } ]}
Insert Multiple Records
In this example we will insert two records, one for the book Hyperion, and the othe for the animated series AEon Flux.
{ "@context": { "schema": "http://schema.org/", "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "insert": [ { "@id": "wiki:Hyperion_(Simmons_novel)", "@type": "schema:Book" }, { "@id": "wiki:%C3%86on_Flux", "@type": "ex:animatedSeries", "schema:name": "AEon Flux", "schema:description": "Such a weird awesome show" } ]}
Transaction object: delete
To delete a subject, the transaction object must include the "delete"
key. We will also often use the where
key to bind existing data to logic variables, and then use them to qualify the exact existing data to be retracted. For more on the where
clause, see our Reference Doc on FlureeQL Syntax.
Deleting All Facts on a Subject
Delete all facts pertaining to the TV Show "AEon Flux"
:
{ "@context": { "wiki": "https://www.wikidata.org/wiki/" }, "ledger": "media", "where": { "@id": "wiki:%C3%86on_Flux", "?p": "?o" }, "delete": { "@id": "wiki:%C3%86on_Flux", "?p": "?o" }}
Deleting Values on a Single Predicate for a Single Subject
If a subject has a name of "The Hitchhiker's Guide to the Galaxy"
, delete the value(s) of the schema:name
predicate for that subject:
{ "@context": { "schema": "http://schema.org/" }, "ledger": "media", "where": { "@id": "?s", "schema:name": "?name" }, "values": ["?name", ["The Hitchhiker's Guide to the Galaxy"]], "delete": { "@id": "?s", "schema:name": "?name" }}
Deleting All Subjects with a Particular Property
Delete all subjects that contain the property schema:description
{ "@context": { "schema": "http://schema.org/" }, "ledger": "media", "where": { "@id": "?s", "schema:description": "?description", "?p": "?o" }, "delete": { "@id": "?s", "?p": "?o" }}
Deleting All Subjects with a Particular Value on a Particular Property
Delete all subjects with the @type
property "schema":"Movie"
{ "@context": { "schema": "http://schema.org/" }, "ledger": "media", "where": { "@id": "?s", "@type": "schema:Movie", "?p": "?o" }, "delete": { "@id": "?s", "?p": "?o" }}
Update Transactions
Unlike insert
and delete
, 'update' is not a transaction object in Fluree. However it is still possible perform updates to existing data, often using a combination of where
, delete
, and insert
, or even the schema of the data itself, to build the logic necessary for each transaction.
Update Existing Value
To replace an existing value, first delete
the existing value, and then insert
your desired data.
The below transaction is somewhat brittle. It will ONLY delete the existing value of "ex:boxOfficeGross"
if the value in the delete statement matches the current data state. If the delete
value contains a typo such as "$5,128,935"
, instead of replacing the "ex:boxOfficeGross"
value with "$26,232,138.00"
, "ex:boxOfficeGross"
will include both the new and existing values. See below for how to more dynamically and safely update existing data.
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "delete": { "@id": "wiki:Q836821", "ex:boxOfficeGross": "$5,128,935.00" }, "insert": { "@id": "wiki:Q836821", "ex:boxOfficeGross": "$26,232,138.00" }}
Test Query
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "from": "media", "select": { "wiki:Q836821": ["ex:boxOfficeGross"] }}
Update New OR Existing Value
If you don't know the specific value you are replacing in your data, or if you don't know if a subject/predicate exists to begin with, or if you want to minimize the risk of human error, you can write a value-agnostic transaction.
First, use where
to search for any existing value, then delete
to get rid of that value, and then insert
our new value. Here is an example:
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "where": { "@id": "wiki:Q836821", "ex:starring": "?star" }, "delete": { "@id": "wiki:Q836821", "ex:starring": "?star" }, "insert": { "@id": "wiki:Q836821", "ex:starring": "Martin Freeman" }}
Test Query
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "from": "media", "select": { "wiki:Q836821": ["ex:starring"] }}
Update Nested Data
To upsert new nested data you can include the full JSON-LD upsert in your transaction like so:
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "where": { "@id": "?s", "name": "The Hitchhiker's Guide to the Galaxy" }, "insert": { "@id": "?s", "ex:starring": "Martin Freeman", "ex:alsoAppearsIn": { "@id": "wiki:Sherlock_(TV_series)", "name": "Sherlock", "@type": ["TV Series"], "ex:starring": "Martin Freeman", "ex:alsoAppearsIn": { "@id": "wiki:Q836821" } } }}
However, there is a risk in the above transaction that no entities get bound to ?s
. When inserting nested data, if any parent nodes are dependent on a binding from the "where"
clause (in this case "?s"
), and if ?s
fails to resolve to any existing entities, then nested / children data won't be transacted if the parent data isn't transacted first.
To avoid this you can transact nested data as siblings, as demonstrated in the following example:
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "where": { "@id": "?s", "name": "The Hitchhiker's Guide to the Galaxy" }, "insert": [ { "@id": "?s", "ex:starring": "Martin Freeman", "ex:alsoAppearsIn": { "@id": "wiki:Sherlock_(TV_series)" } }, { "@id": "wiki:Sherlock_(TV_series)", "name": "Sherlock", "@type": ["TV Series"], "ex:starring": "Martin Freeman", "ex:alsoAppearsIn": { "@id": "wiki:Q836821" } } ]}
By inserting our data as siblings, we can ensure that even if the data dependent on ?s
fails to be inserted, the data that is not dependent on ?s
will still be inserted.
Test Query
{ "@context": { "ex": "http://example.org/" }, "from": "media", "select": { "?s": [ "ex:starring", "ex:alsoAppearsIn" ] }, "where": { "@id": "?s", "ex:starring": "?star" }}
Update Multi-Cardinality Data
In order to entirely retract-and-replace a set of values on a multi-cardinality property, you can do the following:
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "where": { "@id": "wiki:Q836821", "ex:starring": "?anyValues" }, "delete": { "@id": "wiki:Q836821", "ex:starring": "?anyValues" }, "insert": { "@id": "wiki:Q836821", "ex:starring": [ "Martin Freeman", "Sam Rockwell", "Mos Def", "Zooey Deschanel", "Bill Nighy", "Alan Rickman", "Anna Chancellor", "John Malkovich" ] }}
Test Query
{ "@context": { "ex": "http://example.org/" }, "from": "media", "select": { "?s": [ "ex:starring" ] }, "where": { "@id": "?s", "ex:starring": "?star" }}
Append to Array
If you only want to append new values to an array in your transaction, you can use the insert
term like this:
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "insert": { "@id": "wiki:Q836821", "ex:starring": ["Bill Nye"] }}
Test Query
{ "@context": { "ex": "http://example.org/", "wiki": "https://www.wikidata.org/wiki/" }, "from": "media", "select": { "wiki:Q836821": ["ex:starring"] }}
Only Update a Specific Array
You can also use a where
clause to only upsert to a set of values if those values contain a value or values of interest:
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "where": { "@id": "wiki:Q836821", "ex:starring": "?stars" }, "values": ["?stars", ["Martin Freeman"]], "delete": { "@id": "wiki:Q836821", "ex:starring": "?stars" }, "insert": { "@id": "wiki:Q836821", "ex:starring": [ "Martin Freeman", "Sam Rockwell", "Mos Def", "Zooey Deschanel", "Bill Nighy", "Alan Rickman", "Anna Chancellor", "John Malkovich" ] }}
Test Query
{ "@context": { "ex": "http://example.org/", "wiki": "https://www.wikidata.org/wiki/" }, "from": "media", "select": { "wiki:Q836821": ["ex:starring"] }}
Update Ordered Lists
Syntactically, updating ordered lists is exactly the same as unordered lists. As of right now, Fluree does not enable upserting data to a specific location within an ordered list. Instead users must delete the existing list and replace it with desired data, in the desired order.
Here is an example of how this transaction could look:
{ "@context": { "wiki": "https://www.wikidata.org/wiki/", "ex": "http://example.org/" }, "ledger": "media", "where": { "@id": "wiki:Q836821", "ex:starring": "?values" }, "delete": { "@id": "wiki:Q836821", "ex:starring": "?values" }, "insert": { "@id": "wiki:Q836821", "ex:starring": { "@container": "@list", "@value": ["Jean Paul Sartre", "Frederick Neitzche", "Simone De Beauvoir"] } }}
Test Query
{ "@context": { "ex": "http://example.org/", "wiki": "https://www.wikidata.org/wiki/" }, "from": "media", "select": { "wiki:Q836821": ["ex:starring"] }}