65.9K
CodeProject 正在变化。 阅读更多。
Home

教程:在 TerminusDB 和 MongoDB 中比较 JSON 文档并应用补丁

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2022年3月2日

CPOL

4分钟阅读

viewsIcon

5915

节省比较 JSON 文档的时间,并在您的应用程序中构建协作功能

关于 JSON 差异和补丁的背景介绍

Git 用于分布式源代码管理策略的一个基本工具是差异和补丁的概念。这些基础操作是 Git 得以实现的关键。Diff 用于构建一个可以应用于对象的补丁,使最终状态对于某个值来说是有意义的。

但是结构化数据呢?结构化数据中是否存在类似的情况,需要进行差异和补丁操作?当然有。

在应用程序中,当两个人或更多人正在更新同一个对象时,例如在线商店,这种类型的管理操作通常是通过锁定对象来实现的。这意味着只有一个可以成功。而锁定是痛苦的巨大来源,不仅因为您无法实现原本完全合理的并发操作,而且因为您有风险导致锁失效,并且不得不弄清楚何时释放它们。

当多人处理数据集时,经常会出现冲突。如果没有适当的工作流和冲突措施,很多时候一个人的更改会被覆盖,导致数据开始变得不准确。长远来看,这会导致报告、客户服务和商业智能方面出现各种问题。这时就需要引入差异和补丁,用户每次提交更改到数据库时都可以看到更改前和更改后的状态。在此,可以标记出任何冲突,并由人工审核这些更改,以确保长期的准确性。更好的数据,更好的决策。

在 TerminusDB Python 中使用差异和补丁

必备组件

您需要安装 TerminusDB Python 客户端,请在此处查看

确保您的 docker 容器正在 localhost 上运行

这个脚本中,我们演示了 diff 如何返回一个 Patch 对象,并使用该对象,您可以应用 patch 来修改对象,我们为此演示了 TerminusDB schema、TerminusDB documents 和 JSON schema。

在 TerminusDB 中,文档和 schema 以 JSON-LD 格式表示。通过差异和补丁,我们可以轻松比较任何文档和 schema,以查看哪些内容已更改。

让我们看一下 Python 对象形式的文档

class Person(DocumentTemplate):
    name: str
    age: intjane = Person(name="Jane", age=18)
janine = Person(name="Janine", age=18)

您可以直接应用 diff 来获取补丁对象

result_patch = client.diff(jane, janine)pprint(result_patch.content)

使用补丁对象(此处为 result_patch),您可以审查其内容,或者将其应用于对象,然后得到一个更改后的对象。

after_patch = client.patch(jane, result_patch)pprint(after_patch)
assert after_patch == janine._obj_to_dict()

正如您所见,after_patch 对象(文档)与 janine 相同。您可以使用 replace_document 将此文档重新存入数据库以提交此更改。

差异和补丁也适用于 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,还可以处理 schema

class Company(DocumentTemplate):
    name: str
    director: Personschema1 = 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)

请注意,差异和补丁适用于大多数 JSON 格式。

另一个应用示例是比较 2 个 JSON schema

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 中使用差异和补丁

这个脚本中,我们演示了如何在您的 MongoDB 工作流中使用差异和补丁。脚本的第一部分是关于如何使用 Pymongo 的 MongoDB 教程,第二部分我们演示了在将补丁应用到您的 MongoDB collection 之前进行审查更改的额外步骤。

正如我们在上一节中发现的,差异和补丁可以应用于任何 JSON 格式。由于 MongoDB 也使用 JSON 格式来描述其数据,因此我们可以使用差异和补丁来执行类似的操作。

这里我们以 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"
}

我们可以使用差异和补丁来比较旧的和新的 item 1

tbd_endpoint = WOQLClient("https://: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 wnat 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_idcollection_name.update_one(before, {"$set": patch.update})

请在此处查看完整的脚本

在 MongoDB JavaScript 中使用差异和补丁

与上一节一样,使用 JavaScript 客户端,差异和补丁可用于比较文档和 schema,以查看哪些内容已更改。

这个脚本中,我们将对此进行演示。

我们创建了一个名为 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);
});

请在此处查看完整的脚本


我们希望您发现本教程很有用。我们还在下方提供了一些额外的链接供您进一步阅读

历史

  • 2022年3月2日:初始版本
© . All rights reserved.