我有一个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个文档,其中一个文档有三个匹配查询的子文档,而所有其他文档的匹配子文档更少,则循环将执行三次。

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

其他回答

这也可以用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个文档,其中一个文档有三个匹配查询的子文档,而所有其他文档的匹配子文档更少,则循环将执行三次。

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

首先:您的代码无法工作,因为您使用了位置操作符$,该操作符仅标识数组中要更新的元素,但甚至没有显式地指定其在数组中的位置。

您需要的是筛选位置操作符$[<identifier>]。它将更新所有匹配数组筛选条件的元素。

解决方案:

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

点击这里访问mongodb doc

代码的作用:

{"events.profile":10} filters your collection and return the documents matching the filter The $set update operator: modifies matching fields of documents it acts on. {multi:true} It makes .update() modifies all documents matching the filter hence behaving like updateMany() { "events.$[elem].handled" : 0 } and arrayFilters: [ { "elem.profile": 10 } ] This technique involves the use of the filtered positional array with arrayFilters. the filtered positional array here $[elem] acts as a placeholder for all elements in the array fields that match the conditions specified in the array filter.

数组的过滤器

$[]操作符选择所有嵌套数组。使用'$[]'可以更新所有数组项

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

参考

实际上,save命令只在Document类的实例上。 有很多方法和属性。因此可以使用lean()函数来减少工作负载。 请参考这里。https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j

保存功能的另一个问题是,同时在多个保存中会产生冲突数据。 模型。更新将使数据一致。 因此要更新文档数组中的多个项目。使用你熟悉的编程语言,尝试这样的东西,我用mongoose:

User.findOne({'_id': '4d2d8deff4e6c1d71fc29a07'}).lean().exec()
  .then(usr =>{
    if(!usr)  return
    usr.events.forEach( e => {
      if(e && e.profile==10 ) e.handled = 0
    })
    User.findOneAndUpdate(
      {'_id': '4d2d8deff4e6c1d71fc29a07'},
      {$set: {events: usr.events}},
      {new: true}
    ).lean().exec().then(updatedUsr => console.log(updatedUsr))
})

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

    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,
      }
    );