【问题标题】:Trying to remove a subdocument in Mongoose gives me an internal mongoose error试图在 Mongoose 中删除子文档会给我一个内部 mongoose 错误
【发布时间】:2012-12-24 01:32:00
【问题描述】:

我的架构如下(简化):

var Permission = new Schema({
  _id: String,  // email address
  role: String  // "admin" or "member"
});

var Org = new Schema({
  name: {type: String, index: {unique: true, dropDups: true}, trim: true},
  permissions: [Permission]
});

示例文档如下所示:

{
  "name": "My Org",
  "permissions" : [
    {"_id" : "joe@gmail.com", "role" : "admin"},
    {"_id" : "mary@gmail.com", "role" : "member"}
  ]
}

我正在尝试使用命令org.permissions.remove(req.params.email) 删除其中一个权限行,如下上下文所示:

exports.removePermissions = function(req, res) {
  var name = req.params.name;
  return Org
    .findOne({name: name})
    .select()
    .exec(function(err, org) {
      if (err) return Org.handleError(res, err);
      if (!org) return Org.handleError(res, new Error("#notfound " + name));
      org.permissions.remove(req.params.email);
      org.save(function(err, org) {
        if (err) return Org.handleError(res, err);
        else return res.send(org);
      });
    });
};

当我这样做时,我收到以下错误:

TypeError: Cannot use 'in' operator to search for '_id' in joe@gmail.com
    at EmbeddedDocument.Document._buildDoc (/../node_modules/mongoose/lib/document.js:162:27)
    at EmbeddedDocument.Document (/../node_modules/mongoose/lib/document.js:67:20)
    at EmbeddedDocument (/../node_modules/mongoose/lib/types/embedded.js:27:12)
    at new EmbeddedDocument (/../node_modules/mongoose/lib/schema/documentarray.js:26:17)
    at MongooseDocumentArray._cast (/../node_modules/mongoose/lib/types/documentarray.js:62:10)
    at Object.map (native)
    at MongooseDocumentArray.MongooseArray.remove (/../node_modules/mongoose/lib/types/array.js:360:21)
    at model.Org.methods.removePermissions (/../models/org.js:159:20)

我唯一能想到的是 Mongoose 不支持不是 ObjectID 的 _id 字段?这很奇怪,因为我在代码中的其他地方使用了这些,并且效果很好(例如 org.permissions.id("joe@gmail.com") 有效)。

非常感谢任何建议!

【问题讨论】:

    标签: node.js mongodb express mongoose


    【解决方案1】:

    我不确定为什么使用 remove 不起作用,但您可以使用 findOneAndUpdate$pull 运算符自动执行此操作:

    exports.removePermissions = function(req, res) {
      var name = req.params.name;
      return Org.findOneAndUpdate(
        {name: name}, 
        {$pull: {permissions: {_id: req.params.email}}},
        function(err, org) {
          // org contains the updated doc
          ...
        });
    };
    

    【讨论】:

    • 再次感谢!我在 Github 上的 mongoosejs 问题列表中添加了 remove() 问题:github.com/LearnBoost/mongoose/issues/1278
    • 这个问题现在已经修复了......但它仍处于预发布版本,3.6 分支。虽然它有点大错误......似乎我选择猫鼬而不是普通的猫鼬做出了错误的选择......
    • 感谢您,这是使操作原子化的好方法。唯一的问题是您不知道$pull 操作是否成功,即数据库中是否存在已删除的 Permission 对象。对于这种级别的错误检查,您需要使用findById 分两步进行,然后对返回的org 进行操作。
    【解决方案2】:

    根据this answer,您需要在要删除的子文档上调用remove(),而不是在整个子文档数组上。

    所以,改变:

    org.permissions.remove(req.params.email);
    

    到:

    org.permissions.id(req.params.email).remove();
    

    answer supplied by @JohnnyHK 相比,这种两步法的额外优势在于您可以在删除子文档之前验证它是否确实存在。如果您想发送 404 响应以指示子文档不存在,这将很有用 - 据我所知,使用 $pull 原子运算符是不可能的。

    请注意,这也仅在您的子文档数组具有架构时才有效,如问题所示。如果没有,或者它的模式类型为Mixed,则从数据库返回的集合将是一个普通数组,而不是 Mongoose 增强数组。这意味着没有.id() 函数。在这种情况下,我会改用lodash#remove

    _.remove(org.permissions, (function(permission) {
      return permission._id.toString() === req.params.email;
    }));
    

    【讨论】:

    • 感谢您指出这两个选项!请务必注意,必须保存顶级文档才能持久删除子文档。在这种情况下使用org.save()
    猜你喜欢
    • 1970-01-01
    • 2019-07-16
    • 1970-01-01
    • 2021-02-10
    • 2019-05-27
    • 2014-12-02
    • 2019-01-01
    • 2021-03-27
    • 1970-01-01
    相关资源
    最近更新 更多