82

我正在使用 Mongo 作为我的数据库。我有一个数据:

 {
   _id : '123'
   friends: [
     {name: 'allen', emails: [{email: '11111', using: 'true'}]}
   ]
 }

现在,我想对用户的朋友的电子邮件进行主题化,其 _id 是 '123' 我这样写:

db.users.update ({_id: '123'}, {$set: {"friends.0.emails.$.email" : '2222'} })

这很容易,但是,当 emails 数组有两个或更多数据时,这是错误的。所以,我的问题是:我怎样才能对嵌套字段中的数据进行主题化——只有两个或更多嵌套数组?谢谢。

4

5 回答 5

112

您需要对数组使用点表示法

也就是说,您应该将 替换为$您尝试更新的元素的从零开始的索引。

例如:

db.users.update ({_id: '123'}, { '$set': {"friends.0.emails.0.email" : '2222'} });

将更新第一个朋友的第一封电子邮件,并且

db.users.update ({_id: '123'}, { '$set': {"friends.0.emails.1.email" : '2222'} })

将更新第一个朋友的第二封电子邮件。

于 2013-10-26T13:15:36.583 回答
12

对多级数组进行更新的一种灵活方法是使用arrayFilters,它允许查询索引并将其分配给标识符。

db.collection.update(
   { <query selector> },
   { <update operator>: { "array.$[<identifier>].field" : value } },
   { arrayFilters: [ { <identifier>: <condition> } } ] }
)

这是您提供的示例以及带有两封电子邮件的新朋友:

{
   _id : '123'
   friends: [
     {name: 'allen', emails: [
        {email: '11111', using: 'true'}
     ]},
     {name: 'lucy' , emails: [
        {email: 'lucy@work.com', using:'true'}, 
        {email:'lucyGucy@zmail.com', using : 'false'}
     ]}
   ]
 }

假设 lucyGucy@zmail.com 正在更新为 lucy.is.gucy@zmail.com。我们可以使用数组过滤器中的名称电子邮件字段来识别我们要更新的索引。

db.users.update({_id:123}, {$set:{
    "friends.$[updateFriend].emails.$[updateEmail].email : "lucy.is.gucy@zmail.com"
}}, {
    "arrayFilters": [
      {"updateFriend.name" : "lucy"},
      {"updateEmail.email" : "lucyGucy@zmail.com"}
    ]
})

这样,更新对特定的数组顺序不敏感。上面的示例使用两个标识符updateFriendupdateEmail将匹配 - 对于它们也应用的数组 - 任何满足数组过滤条件的元素。如文档中所述:

<identifier> 必须以小写字母开头并且仅包含字母数字字符。

此外,虽然电子邮件可能是唯一的,但我建议在所有子文档中包含唯一的 id,以便 arrayFilters 可以准确。

于 2020-07-29T20:40:06.903 回答
3

使用猫鼬的解决方案:

    Users.findById("123", function(err, user) {

      var friends = user.friends;
        for ( i=0; i < friends.length; i++ ) {
          if (friends[i].name == 'allen') {
            friends[i].email = '2222';

            user.save(function(err) {
              if (err) throw err;
              console.log("email updated");
            });
          } else {
            console.log("no friends named allen");
          }
        }

    }
于 2017-10-02T02:36:53.780 回答
0

在多级数组中更新某些东西真的很痛苦,我的做法是:替换深层数组。

db.user.findOne({_id:'123'},{friends:1}).lean().exec(function(err,user){
   var whichArrayToUpdate;
   for (var ii = 0; ii < user.friends.length; ii++) {
        for (var jj = 0; i < user.friends[ii].emails; jj++) {
            if(user.friends[ii].emails[jj].email == '1111' ){// update it below

                user.friends[ii].emails[jj].email == 'what ever you want to set to.';

                whichArrayToReplace = user.friends[ii].emails;
                break;
            }
        };
   };

   db.user.update({'friends.name':'allen'},{$set{'friends.$.email': whichArrayToReplace} })
})

但是,为什么不使用 save() 方法呢?save() 将替换您的整个文档,如果您的文档很小,那没关系,但是如果您的文档很大,最好只替换文档的一部分。

或者做循环,使用顶级数组和二级数组(ii和jj)的位置来更新。

我的建议是:当你设计模式时,不要将数组放在另一个数组中,除非你不会对该数组进行任何更新。

于 2014-04-19T06:22:59.453 回答
-1

我有类似的情况,我有主要类别,例如:

{
    "service": {
        "isReviewed": false,
        "_id": "5ea36b27d7ae560845afb88d",
        "mainCategory": "message ",
        "SubCategory": [
            {
                "priceChanged": true,
                "_id": "5ea36b27d7ae560845afb88e",
                "subCatPrice": "26",
                "subCatName": "mustach trimming"
            }
        ],
        "shopName": "paddy the barber",
        "user": "5ea1fd5b69512dc72ad2f48c",
        "date_created": "2020-04-24T22:41:43.368Z",
        "__v": 5
    }
}

现在,花了一段时间才弄清楚如何更新子文档的一部分。因为子文档存储为对象。

所以所有 id,首先我在文档通过 put/:id 时检查文档的 _id。然后,我根据 req.user 检查了 doc 的所有者。

如果一切正常,我遍历数组然后生成一个新对象。然后,我所做的就是检查每个 req.body 的价格或名称,如果有新价格,我会更新对象中的价格值。如果有新值,则对象保持不变。

然后就是我所说的,service.SubCategory = newObject 并存储数据。

它工作正常,但是,我遇到了 subdoc _id 得到更新的问题,所以我通过保持旧值作为它来解决这个问题。

现在,在性能和优化方面,我不完全确定这是否是正确的方法,但它的工作原理,如果它们更好,我愿意改变。

这是代码:

const newObject = {
      subCatPrice: "",
      subCatName: "",
      _id: "",
      priceChanged: false
    };

    let oldDetails = service.SubCategory;
    for (const old of oldDetails) {
      newObject.subCatPrice = old.subCatPrice;
      newObject.subCatName = old.subCatName;
      newObject._id = old._id;
    }

    price ? (newObject.subCatPrice = price ) && (newObject.priceChanged = true)  : newObject.subCatPrice;
    name ? (newObject.subCatName = name) : newObject.subCatName;

    (service.SubCategory = newObject), await service.save();

老实说,我使用了状态的反应思想,我只是获取对象的副本并保留它并将更新应用于我想要更新的部分。

再次就干净的代码和所有这些东西而言,我不确定这是否是正确的方法,但我也是编程的学生,我也想学习。

希望这对某人有帮助

于 2020-04-24T22:56:36.117 回答