在mongodb中从所有元素中移除一个字段

61

我在MongoDB(2.4.5)中有以下文档:

{
    "_id" : 235399,
    "casts" : {
        "crew" : [ 
            {
                "_id" : 1186343,
                "withBase" : true,
                "department" : "Directing",
                "job" : "Director",
                "name" : "Connie Rasinski"
            },
            {
                "_id" : 86342,
                "withBase" : true
            }
        ]
    },
    "likes" : 0,
    "rating" : 0,
    "rating_count" : 0,
    "release_date" : "1955-11-11"
}

我想从casts.crew中的数组元素中删除withBase字段

我尝试了这个:

db.coll.update({_id:235399},{$unset: {  "casts.crew.withBase" : 1 } },false,true)

没有任何改变。

我尝试过这个...

db.coll.update({_id:235399},{$unset: {  "casts.crew" : { $elemMatch: { "withBase": 1 } } } },false,true)

它从文档中删除了整个团队数组。

有人可以提供正确的查询吗?

3个回答

103
你可以使用新的 位置标识符 来在3.6中更新数组中的多个元素。

类似于:

 db.coll.update( {_id:235399}, {$unset: {"casts.crew.$[].withBase":""}} )

$[]crews数组中删除所有withBase属性。它作为更新数组中所有元素的占位符。

使用multi true来影响多个文档。


3
请勿无故点踩,如有问题,请告知,我很愿意解决您的疑虑。 - s7vr
3
抱歉,我给这个内容投了反对票,无法取消 :) - 这确实有效,但只适用于mongodb >=3.6。而且它也适用于updateMany() - mils
1
@mils 在回答中添加了版本号。 - s7vr
1
这是“更新所有数组元素”操作符文档的正确链接: https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up._S_[] - Adam Reis
db.alimentos.updateMany( {_id: ObjectId("5fb9f282d742c3e6884a8000")}, {$unset: {"proteins.$[].id":""}} ) 在我的蛋白质数组中,每个对象都有一个ID,我需要删除42个文档中的这些ID。 我需要用这种方式编写。 MongoDB shell版本v4.4.2 - 0x52
显示剩余3条评论

60

很抱歉让你失望,但是你的回答

db.coll.update({
   _id:235399,
   "casts.crew.withBase": {$exists: true}
},{
   $unset: {
      "casts.crew.$.withBase" : true
   }
},false,true)

不正确。实际上,它只会从子文档的第一个出现中删除该值,因为位置占位符运算符的工作方式

  

位置$运算符充当第一个匹配查询文档的元素的占位符

您也不能使用$unset(如以前尝试过的)因为它不能适用于数组(而您基本上正在尝试从其中一个文档中删除数组的键)。您也不能使用$pull将其删除,因为pull会删除整个数组,而不仅仅是其中的字段。

因此,据我所知,您无法使用简单的运算符执行此操作。因此,最后的手段是使用$find,然后使用forEach保存。您可以在此处找到我的答案中如何执行此操作。在您的情况下,您需要在forEach函数中有另一个循环来迭代数组,并删除一个关键字。我希望您能够修改它。如果不行,我会尽力帮助您。

P.S.如果有人在寻找方法来执行此操作-这是Sandra's函数

db.coll.find({_id:235399}).forEach( function(doc) {
  var arr = doc.casts.crew;
  var length = arr.length;
  for (var i = 0; i < length; i++) {
    delete arr[i]["withBase"];
  }
  db.coll.save(doc);
});

1
非常感谢Salvador。它起作用了。这是我的函数`db.coll.find({_id:235399}).forEach( function(doc) { var arr = doc.casts.cast; var length = arr.length; for (var i = 0; i < length; i++) { delete arr[i]["withBase"];} db.coll.save(doc); } );` - Maria
很高兴能够帮到你。我会将它与你的凭证一起添加到我的答案中。 - Salvador Dali
1
Nancy的解决方案更加实用的方法是:`db.coll.find({_id:235399}).forEach(function(doc) { doc.casts.crew.forEach(function(crew) { delete crew.withBase; }) db.coll.save(doc); });` - Tim N
1
请注意,这不是事务安全的。在 find()save() 之间发生的任何更改都将被覆盖。如果您使用 MongoDB 3.6+,请优先考虑 Veeram 的解决方案 - smonkey
请参考user2683814的答案。 - MickaelFM

14

我找到了一种方法来取消这个列表,而不必拉起对象(也就是说,只需要进行更新),这很狡猾,但如果你有一个巨大的数据库,它会起到作用:

db.coll.update({},{$unset: {"casts.crew.0.withBase" : 1, "casts.crew.1.withBase" : 1} }, {multi: 1})

换句话说,您需要计算文档列表中有多少对象,并显式添加这些数字,例如在此情况下为{casts.crew.NUMBER.withBase: 1}

此外,要计算mongodb对象中最长的数组,可以进行聚合操作,类似于以下内容:

db.coll.aggregate( [   { $unwind : "$casts.crew" },   { $group : { _id : "$_id", len : { $sum : 1 } } },   { $sort : { len : -1 } },   { $limit : 1 } ], {allowDiskUse: true} )

只想强调这不是一个美观的解决方案,但比获取和保存更快。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接