使用 Mongoose 更新数组中的子文档

Posted

技术标签:

【中文标题】使用 Mongoose 更新数组中的子文档【英文标题】:Use Mongoose to update subdocument in array 【发布时间】:2017-03-31 05:49:53 【问题描述】:

有一些关于此的帖子(例如here 和here),但没有一个使用本机 Mongoose 方法。 (第一个使用$set,第二个使用extend npm 包。似乎应该有一种“原生猫鼬”方式来做到这一点。

架构:

var blogPostSchema = new mongoose.Schema(
    title: String,
    comments: [
        body: String
    ]
);

这是我最初尝试的:

BlogPost.findById(req.params.postId, function (err, post) 
    var subDoc = post.comments.id(req.params.commentId);
    subDoc = req.body;
    post.save(function (err) 
        if (err) return res.status(500).send(err);
        res.send(post);
    );
);

问题是这一行:subDoc = req.body 实际上并没有更改父文档的子文档,而是通过引用传递。在save() 调用之后,数据库中实际上没有任何变化。

extend 包通过将两个对象合并在一起来解决此问题,如上面链接的第二个 SO 帖子所示(同样是here)。但是有没有办法使用一些 Mongoose API 方法来解决这个问题?我在文档中找不到任何关于它的内容(只有如何添加新的子文档和删除子文档)。

我写的另一种选择是:

for (var key in subDoc) 
    subDoc[key] = req.body[key] || subDoc[key];

这也有效,但我不确定这样做是否有任何危险。也许这是最好的方法?

提前致谢!

【问题讨论】:

【参考方案1】:

subDoc = req.body 不起作用的原因是它正在用对req.body 的全新引用替换由第一次分配给subDoc 创建的引用(假设它是一个对象而不是字符串) .这与 Mongoose 无关,而与一般引用的工作方式有关。

var objA =  a: 1, b: 2;
var objB =  a: 'A', b: 'B';

var objC = objA;
console.log('Assigning ref to objC from objA', objA, objB, objC);
// It should console.log the following:
// Assigning ref to objC from objA 
//   "objA": 
//     /**id:2**/
//     "a": 1,
//     "b": 2
//   ,
//   "objB": 
//     "a": "A",
//     "b": "B"
//   ,
//   "objC": /**ref:2**/
// 
//

objC = objB;            
console.log('Replacing ref to objC with objB', objA, objB, objC);
// It should console.log the following:
// Replacing ref to objC with objB 
//   "objA": 
//     "a": 1,
//     "b": 2
//   ,
//   "objB": 
//     /**id:3**/
//     "a": "A",
//     "b": "B"
//   ,
//   "objC": /**ref:3**/
// 

您可以使用Document.#set method。只需为其提供一个对象(在本例中为 req.body),该对象使用您要设置的值来反映文档的结构。

BlogPost.findById(req.params.postId, function(err, post) 
  var subDoc = post.comments.id(req.params.commentId);
  subDoc.set(req.body);

  // Using a promise rather than a callback
  post.save().then(function(savedPost) 
    res.send(savedPost);
  ).catch(function(err) 
    res.status(500).send(err);
  );
);

【讨论】:

谢谢!我想我只需要离开电脑一分钟,因为我在一顿丰盛的午餐后想到了 .set 方法。我一回到家就试试这个。 @bobbyz 运气好吗? 还没试过,不过我今天进去就试试。出于好奇,在回调中使用 Promise 有什么好处?我在 Angular 中使用它们并喜欢它们,但好处主要在于它的外观,还是性能更好?另外,使用 document.set 是否也是处理父文档上的 PUT 请求的首选方式?它是否会保留我不包含在 req.body 中的属性,就像 PATCH 请求一样?我想当我进入时我可以玩这一切,但如果有另一个声音在里面会很好。 @bobbyz 那里有很多问题...?。使用回调、承诺、生成器等完全取决于您,但您可以阅读this article for more info。我不知道这是否是首选方式,但如果您有一个对象与一个值,并且需要将对象值设置到您的文档(例如PATCH 操作),那么set 会为您干净地处理这个问题。希望对您有所帮助。 谢谢!一切都很好,一切都很有意义。

以上是关于使用 Mongoose 更新数组中的子文档的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS - 我们可以从 Mongoose 更新填充的子文档吗?

Mongoose 中的子文档数组

Node.js/mongoose - 数组中的子文档不会删除/移除

Node.js/mongoose - 数组中的子文档不会删除/移除

将新对象插入到 mongoose 中的子文档数组字段中

将新对象插入到 mongoose 中的子文档数组字段中