嵌入式文档属性的多次更新

时间:2011-12-14 13:43:39

标签: mongodb

我有以下系列:

{ 
"Milestones" : [      
    {       "ActualDate" : null,   
    "Index": 0,
    "Name" : "milestone1",  
    "TargetDate" : ISODate("2011-12-13T22:00:00Z"),         
    "_id" : ObjectId("4ee89ae7e60fc615c42e28d1")},         
    {       "ActualDate" : null,    
    "Index" : 0,    
    "Name" : "milestone2",  
    "TargetDate" : ISODate("2011-12-13T22:00:00Z"),         
    "_id" : ObjectId("4ee89ae7e60fc615c42e28d2") } ]
, 
"Name" : "a", "_id" : ObjectId("4ee89ae7e60fc615c42e28ce") 
}

我想更新明确的文档:已指定_id,列表里程碑._id和ActualDate为空。 我dotnet我的代码看起来像:

var query = Query.And(new[] { Query.EQ("_id", ObjectId.Parse(projectId)),
  Query.In("Milestones._id", new BsonArray(values.Select(ObjectId.Parse))), 
 Query.EQ("Milestones.ActualDate", BsonNull.Value) });                

var update = Update.Set("Milestones.$.ActualDate", DateTime.Now.Date);    

Coll.Update(query, update, UpdateFlags.Multi, SafeMode.True);

或者使用本机代码:

db.Projects.update({ "_id" : ObjectId("4ee89ae7e60fc615c42e28ce"), "Milestones._id" : { "$in" : [ObjectId("4ee89ae7e60fc615c42e28d1"), ObjectId("4ee89ae7e60fc615c42e28d2"), ObjectId("4ee8a648e60fc615c41d481e")] }, "Milestones.ActualDate" : null },{ "$set" : { "Milestones.$.ActualDate" : ISODate("2011-12-13T22:00:00Z") } }, false, true)

但只有第一项正在更新。

3 个答案:

答案 0 :(得分:10)

目前这是不可能的。更新中的标记multi表示更新多个根文档。位置运算符只能匹配一个嵌套数组项。在mongodb jira中有这样的feature。你可以投票并等待。

当前解决方案只能加载文档,根据需要更新,并为每个嵌套数组ID保存或多次原子更新。

来自mongodb.org的文档:

  

目前,$运算符仅适用于第一个匹配的项目   查询

答案 1 :(得分:1)

正如Andrew Orsich所回答的那样,目前这是不可能的,至少不是你想要的。但是加载文档,修改数组然后将其保存回来将会起作用。风险在于其他一些进程可能在此期间修改数组,因此您将覆盖其更改。为避免这种情况,您可以使用乐观锁定,尤其是在每秒都不修改数组时。

  1. 加载文档,包括新属性:milestones_version
  2. 根据需要修改数组
  3. 保存回mongodb,但现在在milestones_version上添加一个查询约束,并将其递增:

    db.Projects.findAndModify({
        query: {
            _id: your_project_id,
            milestones_version: expected_milestones_version
        },
        update: {
            $set: {
                Milestones: modified_milestones
            },
            $inc: {
                milestones_version: 1
            }
        },
        new: 1
    })
    
  4. 如果另一个进程在我们执行之前修改了里程碑数组(以及milestones_version),则此命令将不执行任何操作,只返回null。我们只需要重新加载文档然后再试一次。如果阵列没有每秒修改一次,那么这将非常罕见,并且不会对性能产生任何影响。

    此解决方案的主要问题是您必须逐个编辑每个项目(无multi: true)。你仍然可以写一个javascript函数,让它在服务器上运行。

答案 2 :(得分:0)

根据他们的JIRA页面"这个新功能从MongoDB 3.5.12开发版本开始提供,并包含在MongoDB 3.6生产版本中#34;

https://jira.mongodb.org/browse/SERVER-1243