我有一个Mongo文档,其中包含一个元素数组。

我想重置.profile = XX数组中所有对象的.handled属性。

文件格式如下:

{
    "_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
    "user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
    "events": [{
            "handled": 1,
            "profile": 10,
            "data": "....."
        } {
            "handled": 1,
            "profile": 10,
            "data": "....."
        } {
            "handled": 1,
            "profile": 20,
            "data": "....."
        }
        ...
    ]
}

所以,我尝试了以下方法:

.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)

但是,它只更新每个文档中第一个匹配的数组元素。(这是$ -位置操作符的定义行为。)

如何更新所有匹配的数组元素?


当前回答

这也可以用while循环来完成,该循环检查是否有任何文档仍然有未更新的子文档。这种方法保留了更新的原子性(这里的许多其他解决方案都没有做到这一点)。

var query = {
    events: {
        $elemMatch: {
            profile: 10,
            handled: { $ne: 0 }
        }
    }
};

while (db.yourCollection.find(query).count() > 0) {
    db.yourCollection.update(
        query,
        { $set: { "events.$.handled": 0 } },
        { multi: true }
    );
}

循环执行的次数将等于profile为10且处理不等于0的子文档在集合中的任何文档中出现的最大次数。因此,如果您的集合中有100个文档,其中一个文档有三个匹配查询的子文档,而所有其他文档的匹配子文档更少,则循环将执行三次。

此方法避免了破坏其他数据的危险,这些数据可能在此脚本执行时由另一个进程更新。它还最大限度地减少了客户端和服务器之间传输的数据量。

其他回答

如果你想在数组中更新数组

    await Booking.updateOne(
      {
        userId: req.currentUser?.id,
        cart: {
          $elemMatch: {
            id: cartId,
            date: date,
            //timeSlots: {
              //$elemMatch: {
                //id: timeSlotId,
              //},
            //},
          },
        },
      },
      {
        $set: {
          version: booking.version + 1,
          'cart.$[i].timeSlots.$[j].spots': spots,
        },
      },
      {
        arrayFilters: [
          {
            'i.id': cartId,
          },
          {
            'j.id': timeSlotId,
          },
        ],
        new: true,
      }
    );

请注意,在这个帖子中建议使用$[]的一些答案是错误的。

db.collection.update(
   {"events.profile":10},
   {$set:{"events.$[].handled":0}},
   {multi:true}
)

上面的代码将“events”数组中的所有元素的“handled”更新为0,不管它的“profile”值是多少。查询{"events. events. "Profile ":10}仅用于过滤整个文档,而不是数组中的文档。在这种情况下,必须使用$[elem]和arrayFilters来指定数组项的条件,以便Neil Lunn的答案是正确的。

更新: 从Mongo 3.6版开始,这个答案不再有效,因为上面提到的问题已经修复,有办法实现这一点。请检查其他答案。


此时,不可能使用位置操作符来更新数组中的所有项。参见JIRA http://jira.mongodb.org/browse/SERVER-1243

作为一份工作,你可以:

单独更新每个项目 (events.0。处理events.1.handled )或… 阅读文件,进行编辑 手动保存并替换 旧版本(勾选“如果更新 如果你想确保“当前” 原子更新)

我很惊讶这个问题在mongo中还没有解决。总的来说,mongo在处理子数组时似乎不太好。例如,不能简单地计算子数组。

我用了哈维尔的第一个方法。将数组读入事件,然后循环并构建set exp:

var set = {}, i, l;
for(i=0,l=events.length;i<l;i++) {
  if(events[i].profile == 10) {
    set['events.' + i + '.handled'] = 0;
  }
}

.update(objId, {$set:set});

可以使用条件测试的回调将其抽象为一个函数

我尝试了以下,它的工作良好。

.update({'events.profile': 10}, { '$set': {'events.$.handled': 0 }},{ safe: true, multi:true }, callback function);

// nodejs情况下的回调函数