MongoDB更新数组的多个记录

8

我最近开始使用MongoDB,有一个有关在文档中更新数组的问题。

我的结构如下:

{
"_id" : ObjectId(),
"post" : "",
"comments" : [
        {
                "user" : "test",
                "avatar" : "/static/avatars/asd.jpg",
                "text" : "....."
        }
        {
                "user" : "test",
                "avatar" : "/static/avatars/asd.jpg",
                "text" : "....."
        }
        {
                "user" : "test",
                "avatar" : "/static/avatars/asd.jpg",
                "text" : "....."
        }
        ...
   ]
}

我正在尝试执行以下查询:

update({"comments.user":"test"},{$set:{"comments.$.avatar": "new_avatar.jpg"}},false,true)

问题在于它更新了所有文档,但只更新了每个文档中的第一个数组元素。有没有办法更新所有数组元素,或者我应该尝试手动更新?谢谢。
4个回答

20

在单个更新操作中,无法修改多个数组元素。因此,您需要重复更新操作以迁移需要修改多个数组元素的文档。您可以通过迭代集合中的每个文档,并重复应用具有$elemMatch的更新操作,直到文档替换了所有相关评论为止来实现这一点,例如:

db.collection.find().forEach( function(doc) {
  do {
    db.collection.update({_id: doc._id,
                          comments:{$elemMatch:{user:"test",
                                                avatar:{$ne:"new_avatar.jpg"}}}},
                         {$set:{"comments.$.avatar":"new_avatar.jpg"}});
  } while (db.getPrevError().n != 0);
})

请注意,如果该操作的效率是应用程序的要求之一,则应将用户头像的位置存储在单个文档中,而不是在每个评论中。


感谢回答。既然MongoDB没有连接查询,我想建立一个带有评论的博客系统,并且我想在每个用户的评论旁边展示他们的头像。你有什么建议应该如何构建我的模式? - Viktor Kirilov
从概念上讲,您需要在某个地方存储用户=>头像映射。您可以将每个用户的头像存储在用户文档中(在您的用户集合中),或者您可以在该博客条目的文档的字段中为评论者存储用户=>头像映射。请参见http://docs.mongodb.org/manual/applications/database-references/以了解MongoDB中规范化的轻量级处理,以及http://docs.mongodb.org/manual/use-cases/storing-comments/用于“用户评论”用例分析。 - J Rassi
是的,我在看到你的答案之前就想出了那个解决方案。无论如何,非常感谢你,@Jason。 - Viktor Kirilov
TypeError: db.getPrevError不是函数 - Aryeh Armon

6

有一种解决方法是创建一个函数,可用于forEach并对其进行评估(以使其快速运行)。 假设您的集合是“article”,则可以运行以下操作:

var runUpdate = function(){
    db.article.find({"comments.user":"test").forEach( function(article) {
        for(var i in article.comments){
            article.comments[i].avatar = 'new_avatar.jpg';
        }
        db.article.save(article);
    });
};

db.eval(runUpdate);

1
如果您知道要更新的索引,可以轻松地按以下方式完成此操作:
var update = { $set: {} };
for (var i = 0; i < indexesToUpdate.length; ++i) {
  update.$set[`comments.${indexesToUpdate[i]}. avatar`] = "new_avatar.jpg";
}
Comments.update({ "comments.user":"test" }, update, function(error) {
  // ...
});
  • 请注意,大多数集成开发环境不接受这种语法,但您可以忽略它。

-1

看起来你可以这样做:

db.yourCollection.update({"comments.user":"test"},{$set:{"comments.0.avatar": "new_avatar.jpg", "comments.1.avatar": "new_avatar.jpg", etc...})

如果你有一个已知的小数组元素数量,这可能会更容易一些。如果你想要像“comments.*.avatar”这样的东西 - 不确定如何做到这一点。虽然你有很多数据重复,但这可能并不是那么好。


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