【问题标题】:mongodb update on JSON arrayJSON数组上的mongodb更新
【发布时间】:2017-07-06 14:45:51
【问题描述】:

我在 Mongo 中有这些数据:

{'_id':1,
             'name':'Root',
             'taskId':1,
             'parentId':"",
             'path':[1],
             'tasks':[  {"taskId":3,parentId:1,name:'A',type:'task'},
                        {"taskId":4,parentId:1,name:'D',type:'task'},
                        {"taskId":5,parentId:4,name:'B',type:'task'},
                        {'type':'project' , 'proRef':2},
                        {"taskId":6,parentId:3,name:'E',type:'task'},
                        {"taskId":7,parentId:6,name:'C',type:'task'}]

            }

现在我想用新的 Json 数据更新 taskId 6。

var jsonData = {"taskId":6,"name":'Sumeet','newField1':'Val1','newField2':'Val2'}

如果字段可用,则查询应更新,否则将新键添加到现有的 .Output Like

{"taskId":6,parentId:3,name:'Sumeet',type:'task','newField1':'Val1','newField2':'Val2'}]

我尝试了一些查询,但它完全取代了 json 。

 db.projectPlan.update({_id:1,'tasks.taskId':6},{$set :{'tasks.$':jsonData }});

提前感谢您的帮助! 苏梅特

【问题讨论】:

  • 如果不可能也请提出建议。
  • 这很奇怪。根据the documentation,它应该可以工作。
  • 确实这个查询只会替换taskId为6的任务。它不会将现有数据与新数据合并。请参阅下面的解决方案,了解如何合并数据。

标签: javascript node.js mongodb nosql


【解决方案1】:

您需要将jsonData 变量转换为可以传递给更新的东西。下面是一个完全符合您对示例文档的要求的示例:

var updateData = {};
for (f in jsonData) {
    if (f != "taskId") updateData["tasks.$."+f]=jsonData[f]; 
};
db.projectPlan.update({_id:1, 'tasks.taskId':6}, {$set:updateData})

结果:

{ "_id" : 1, 
  "name" : "Root",
  "taskId" : 1,
  "parentId" : "",
  "path" : [  1 ],
  "tasks" : [
    {   "taskId" : 3,   "parentId" : 1,     "name" : "A",   "type" : "task" },
    {   "taskId" : 4,   "parentId" : 1,     "name" : "D",   "type" : "task" },
    {   "taskId" : 5,   "parentId" : 4,     "name" : "B",   "type" : "task" },
    {   "type" : "project",     "proRef" : 2 },
    {   "taskId" : 6,   "parentId" : 3,     "name" : "Sumeet",  "type" : "task",    "newField1" : "Val1",   "newField2" : "Val2" },
    {   "taskId" : 7,   "parentId" : 6,     "name" : "C",   "type" : "task" } 
] }

【讨论】:

    【解决方案2】:

    您需要手动合并文档:

    var jsonData = {"taskId":5,"name":'Sumeet','newField1':'Val1','newField2':'Val2'};
    
    db.projectPlan.find({ _id: 1 }).forEach(
      function(entry) {
        for (var taskKey in entry.tasks) {
          if (entry.tasks[taskKey].taskId === jsonData.taskId) {
            printjson(entry.tasks[taskKey]);
            for (var taskSubKey in jsonData) {
               entry.tasks[taskKey][taskSubKey] = jsonData[taskSubKey];
            }
            printjson(entry.tasks[taskKey]);
          }
        }
        db.projectPlan.save(entry);
      }
    );
    

    显然你可以不用 printjson 语句。这只是为了看到原始任务与新任务的合并有效。请注意,只要 _id 字段是唯一的,此查询只会更新单个文档。

    【讨论】:

    • 这完全没有必要——你为什么需要查看每一个文档?
    • 此解决方案仅查看具有 '_id: 1' 的文档。通常这个字段有索引支持。所以这个查询会很快。
    • 那么当它可以在服务器上以原子方式和线程安全的方式完成时,为什么还要让所有的 javascript 来进行更新呢?如果您的代码运行两次,它可能会丢失更新的更改之一,因为您的查询和保存不是原子的。
    • 如果相同的代码运行两次,答案将是相同的,因为操作是幂等的(仅当输入保持不变时)。然而,我同意原子性是一个优点,因此我喜欢你的解决方案。但是,此解决方案更具可扩展性。
    • 我说的是两个线程试图更新同一个对象(但同一个对象的不同任务)。他们都将读取文档,然后保存,第二个保存的将覆盖第一个的更改。
    猜你喜欢
    • 2023-02-16
    • 1970-01-01
    • 2015-07-30
    • 2015-11-28
    • 1970-01-01
    • 2015-08-27
    • 2016-02-09
    • 2018-03-14
    • 2020-08-30
    相关资源
    最近更新 更多