JSON Diff and Patch Operations

How to perform JSON diff & patch operations with a TerminusDB client or with MongoDB

Requirements

Install a client

Install a JavaScript or Python TerminusDB client.

Use the client with TerminusDB

Install and run the docker container on localhost

Diff & Patch with Client

TerminusDB represents objects such as documents and schemas in JSON-LD format. Use JSON Diff and Patch to easily compare these objects to obtain differences between them.
The Diff function returns a Patch object containing differences between the objects compared. Use Patch to implement manual or programmatic actions to resolve differences. Actions include:
  • Retain the original object.
  • Change to the new (or latest) version of the object.
  • Create a new version of the object.
The functionality above is demonstrated in three simple steps:

Define documents

For simplicity, this demonstration represents JSON documents as client objects. Two 'document' objects are defined - jane and janine.
JavaScript
Python
const jane = {
"@id": "Person/Jane",
"@type": "Person",
age: 18,
name: "Jane",
};
const janine = {
"@id": "Person/Jane",
"@type": "Person",
age: 18,
name: "Janine",
};
class Person(DocumentTemplate):
name: str
age: int
jane = Person(name="Jane", age=18)
janine = Person(name="Janine", age=18)

Compare documents

Apply the diff function to jane and janine to populate the patch objectresult_patch. View the contents of result_patch to see any differences.
JavaScript
Python
const result_patch = await client.getDiff(jane, janine);
console.log(result_patch);
result_patch = client.diff(jane, janine)
pprint(result_patch.content)

Implement actions

After patch
Use the patch function to apply result_patch to the document jane. The object after_patch contains a copy of jane after the patch is applied.
Use after_patch to suit your requirements. The following example compares the modified jane document to the original janine document.
JavaScript
Python
const after_patch = await client.patch(jane, result_patch);
console.log(after_patch);
console.log(JSON.stringify(after_patch) === JSON.stringify(janine));
after_patch = client.patch(jane, result_patch)
pprint(after_patch)
assert after_patch == janine._obj_to_dict()

Replace/update document

If there are differences between the two documents, you may wish to replace one document with the other. Use replace document functionality for this.

Diff & Patch with MongoDB

An example in four simple steps demonstrating the use JSON Diff and Patch with another JSON-compliant database - MongoDB:

Insert items into a MongoDB database

Create a new MongoDB database and insert three items - item_1 to item_3 into a collection object.
JavaScript
Python
const { MongoClient } = require("mongodb");
// Connection URL
const url = "mongodb://localhost:27017";
const client = new MongoClient(url);
// Database Name
const dbName = "user_shopping_list";
async function main() {
// Use connect method to connect to the server
await client.connect();
console.log("Connected successfully to server");
// Create the database for our example (we will use the same database throughout the tutorial
const db = client.db(dbName);
const collection = db.collection("user_1_items");
let item_1 = {
_id: "U1IT00001",
item_name: "Blender",
max_discount: "10%",
batch_number: "RR450020FRG",
price: 340,
category: "kitchen appliance",
};
const item_2 = {
_id: "U1IT00002",
item_name: "Egg",
category: "food",
quantity: 12,
price: 36,
item_description: "brown country eggs",
};
const insertResult = await collection.insertMany([item_1, item_2]);
console.log("Inserted documents =>", insertResult);
const item_3 = {
item_name: "Bread",
quantity: 2,
ingredients: "all-purpose flour",
expiry_date: "2021-07-13 00:00:00",
};
await collection.insertOne(item_3);
return "done";
}
main()
.then(console.log)
.catch(console.error)
.finally(() => client.close());
client = MongoClient(os.environ["MONGO_CONNECTION_STRING"])
# Create the database for our example (we will use the same database throughout the tutorial
connection = client['user_shopping_list']
collection_name = connection["user_1_items"]
item_1 = {
"_id" : "U1IT00001",
"item_name" : "Blender",
"max_discount" : "10%",
"batch_number" : "RR450020FRG",
"price" : 340,
"category" : "kitchen appliance"
}
item_2 = {
"_id" : "U1IT00002",
"item_name" : "Egg",
"category" : "food",
"quantity" : 12,
"price" : 36,
"item_description" : "brown country eggs"
}
collection_name.insert_many([item_1,item_2])
expiry_date = '2021-07-13T00:00:00.000'
expiry = dt.datetime.fromisoformat(expiry_date)
item_3 = {
"item_name" : "Bread",
"quantity" : 2,
"ingredients" : "all-purpose flour",
"expiry_date" : expiry
}
collection_name.insert_one(item_3)

Modify an item

The 'document' new_item_1 represents a modified instance of item_1 with changes to properties max_discount and price.
JavaScript
Python
const new_item_1 = {
_id: "U1IT00001",
item_name: "Blender",
max_discount: "50%",
batch_number: "RR450020FRG",
price: 450,
category: "kitchen appliance",
};
new_item_1 = {
"_id" : "U1IT00001",
"item_ame" : "Blender",
"max_discount" : "50%",
"batch_number" : "RR450020FRG",
"price" : 450,
"category" : "kitchen appliance"
}

Compare modified and original items

Retrieve the original item in the collection and compare it to the modified item new_item_1.
JavaScript
Python
const tbd_endpoint = new TerminusDBClient.WOQLClient("http://127.0.0.1:6363/", {
user: "admin",
organization: "admin",
key: "root",
});
// Find the item back from database in case someone already changed it
item_1 = collection.findOne({ item_name: "Blender" });
const patch = tbd_endpoint.getDiff(item_1, new_item_1);
console.log(patch);
tbd_endpoint = WOQLClient("http://localhost:6363/")
# Find the item back from database in case someone already changed it
item_1 = collection_name.find_one({"item_name" : "Blender"})
patch = tbd_endpoint.diff(item_1, new_item_1)
pprint(patch.content)

Update the original item

Update the original item in the MongoDB database. If required, review the differences prior to the update.
JavaScript
Python
Define a function patchMongo to get a patch object:
const mongoPatch = function(patch){
let query = {};
let set = {};
if('object' === typeof patch){
for(var key in patch){
const entry = patch[key];
if( entry['@op'] == 'SwapValue'){
query[key] = entry['@before'];
set[key] = entry['@after'];
}else if(key === '_id'){
query[key] = ObjectId(entry);
}else{
let [sub_query,sub_set] = mongoPatch(entry);
query[key] = sub_query;
if(! sub_set === null){
set[key] = sub_set;
}
}
}
return [query,set]
}else{
return [patch,null]
}
}
Apply the patch:
let [q,s] = mongoPatch(patch);
console.log([q,s]);
await collection.updateOne(q, {"$set": s});
collection_name.update_one(patch.before, {"$set": patch.update})

Further Reading

JSON Diff and Patch client functions

JavaScript client diff and patch.
Python client diff and patch.

Demonstration script

The full script for this demonstration (on GitHub.)