在本演示教程中,我们将展示如何应用差异和修补操作来监控TerminuDB模式、TerminuSB文档、JSON模式和其他文档数据库(如MongoDB)中的更改。
关于JSON、差异和补丁的一点背景知识
Git分布式管理源代码策略中的一个基本工具是diff和补丁的概念。这些基本操作使git成为可能。Diff用于构造可应用于对象的面片,以使最终状态对某些值有意义。
但是结构化数据呢?对于需要差异和修补操作的结构化数据,是否会出现类似情况?当然有。
在应用程序中,当两个或多个用户更新同一对象时,例如在线商店,通常通过锁定对象来实现这种管理操作。这意味着只有一个人能赢。锁是一个巨大的痛苦来源,这不仅是因为你无法实现完全合理的并发操作,还因为你可能会遇到过时的锁,并不得不决定何时释放它们。
当多人处理数据集时,往往会发生冲突。如果没有足够的工作流程和冲突度量,经常会有人的更改被压扁,因此,数据可能开始变得不准确。从长远来看,这会导致报告、客户服务和商业智能方面的各种问题。这就是diff和patch的用武之地,用户可以在每次向数据库提交更改时看到前后状态。在这里,任何冲突都可以标记出来,人工审查可以监督这些变化,以确保长期数据的准确性。更好的数据,更好的决策。
在TerminusDB Python中使用Diff和Patch
先决条件
您需要安装TerminusDB Python客户端,请查看此处。
确保Docker容器在本地主机上运行。
在这个脚本中,我们演示了diff如何返回一个补丁对象,通过该对象,您可以应用补丁来修改一个对象,我们为TerminusDB模式、TerminusDB文档和JSON模式展示了这一点。
在USDB中,文档和模式以JSON-LD格式表示。使用diff和patch,我们可以轻松地比较任何文档和模式,以查看发生了什么变化。
让我们将文档视为Python对象:
class Person(DocumentTemplate):
name: str
age: int
jane = Person(name="Jane", age=18)
janine = Person(name="Janine", age=18)
您可以直接应用差异来获得面片对象:
result_patch = client.diff(jane, janine)
pprint(result_patch.content)
使用patch对象(这里是result_patch),您可以查看其内容,也可以将其应用于对象,然后返回after对象。
after_patch = client.patch(jane, result_patch)
pprint(after_patch)
assert after_patch == janine._obj_to_dict()
如您所见,after_patch对象(文档)与janine相同。您可以使用replace_document将此文档放回数据库,以提交此更改。
Diff和patch还可用于JSON-LD文档:
jane = { "@id" : "Person/Jane", "@type" : "Person", "name" : "Jane"}
janine = { "@id" : "Person/Jane", "@type" : "Person", "name" : "Janine"}
result_patch = client.diff(jane, janine)
pprint(result_patch.content)
它也不限于JSON-LD,它可以使用模式:
class Company(DocumentTemplate):
name: str
director: Person
schema1 = WOQLSchema()
schema1.add_obj("Person", Person)
schema2 = WOQLSchema() schema2.add_obj("Person", Person)
schema2.add_obj("Company", Company)
result_patch = client.diff(schema1, schema2)
pprint(result_patch.content)
请注意,diff和patch将适用于大多数JSON格式。
另一个应用程序示例是比较2个JSON模式:
schema1 = {
"type": "object",
"properties": {
"name": { "type": "string" },
"birthday": { "type": "string", "format": "date" },
"address": { "type": "string" }, } }
schema2 = {
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"birthday": { "type": "string", "format": "date" },
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"country": { "type" : "string" }
}
}
}
}
result_patch = client.diff(schema1, schema2)
pprint(result_patch.content)
请参阅此处的完整脚本
在MongoDB中使用Diff和Patch
在这个脚本中,我们演示了如何在MongoDB工作流中使用diff和patch。脚本的第一部分是关于如何使用Pymongo的MongoDB教程,在第二部分中,我们演示了在将修补程序应用到MongoDB集合之前检查更改的额外步骤。
正如我们在上一节中发现的,diff和patch可以应用于任何JSON格式。由于MongoBD也使用JSON格式来描述其数据,我们可以使用diff和patch来做类似的事情。
这里我们以Pymongo教程为例:
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)
假设我们想要更改item_ 1:
new_item_1 = {
"_id" : "U1IT00001",
"item_name" : "Blender",
"max_discount" : "50%",
"batch_number" : "RR450020FRG",
"price" : 450,
"category" : "kitchen appliance"
}
我们可以将新旧项目1与差异和补丁进行比较:
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)
同样,我们可以在MongoDB进行更改之前查看:
collection_name.update_one(patch.before, {"$set": patch.update})
这是另一个更复杂的例子:
expiry_date = '2021-07-15T00:00:00.000'
expiry = dt.datetime.fromisoformat(expiry_date)
new_item_3 = {
"item_name" : "Bread",
"quantity" : 5,
"ingredients" :
"all-purpose flour",
"expiry_date" : expiry
}
item_3 = collection_name.find_one({"item_name" : "Bread"})
item_id = item_3.pop('_id') # We want to pop it out and optionally we can add it back
patch = tbd_endpoint.diff(item_3, new_item_3)
pprint(patch.content)
# Add _id back, though it still works without
before = patch.before
before['_id'] = item_id
collection_name.update_one(before, {"$set": patch.update})
在MongoDB JavaScript中使用Diff和Patch
与上一节一样,可以使用diff和patch来比较文档和模式,以查看使用JavaScript客户端所做的更改。
在这个脚本中,我们将演示它。
我们创建了一个名为patchMongo的函数:
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]
}
}
我们创建了一个对象,可以放回去更新MongoDB中的数据:
let patchPromise = client.getDiff(jane,janine,{});
patchPromise.then( patch => {
let [q,s] = mongoPatch(patch)
console.log([q,s]);
const res = db.inventory.updateOne(q, { $set : s});
console.log(res);
if (res.modifiedCount == 1){
console.log("yay!")
}else{
console.log("boo!")
}
console.log(patch);
});
原文标题:Compare JSON Documents and Apply Patches With TerminusDB and MongoDB
原文作者:Oliver Smith
原文链接:https://dzone.com/articles/compare-json-documents-and-apply-patches-with-term




