JavaScript-猫鼬查找/更新子文档

我的文件夹文件夹有以下架构:

var permissionSchema = new Schema({
    role: { type: String },
    create_folders: { type: Boolean },
    create_contents: { type: Boolean }
});

var folderSchema = new Schema({
    name: { type: string },
    permissions: [ permissionSchema ]
});

因此,对于每个页面,我可以拥有许多权限。 在CMS中,有一个面板,其中列出了所有文件夹及其权限。 管理员可以编辑一个权限并保存。

我可以很容易地用其权限数组保存整个Folder文档,其中只修改了一个权限。 但是我不想保存所有文档(实际架构中包含更多字段),所以我这样做了:

savePermission: function (folderId, permission, callback) {
    Folder.findOne({ _id: folderId }, function (err, data) {
        var perm = _.findWhere(data.permissions, { _id: permission._id });                

        _.extend(perm, permission);

        data.markModified("permissions");
        data.save(callback);
    });
}

但是问题是烫发总是不确定的! 我试图以这种方式“静态地”获取许可:

var perm = data.permissions[0];

而且效果很好,所以问题在于Underscore库无法查询权限数组。 因此,我想有一种更好的方法(和工作方式)来获取所提取文档的子文档。

任何想法?

附注:我解决了使用“ for”循环检查data.permission数组中的每个项目并检查data.permissions [i] ._ id == Permission._id的问题,但是我想要一个更聪明的解决方案,我知道有一个解决方案!

3个解决方案
94 votes

因此,正如您所注意到的,猫鼬的默认设置是,当您在这样的数组中“嵌入”数据时,每个数组条目都会得到一个$set值,作为其自身的子文档属性的一部分。 实际上,您可以使用此值来确定要更新的项目的索引。 MongoDB的实现方式是位置2998963264417170170432运算符,该变量在数组中保持“匹配”位置:

Folder.findOneAndUpdate(
    { "_id": folderId, "permissions._id": permission._id },
    { 
        "$set": {
            "permissions.$": permission
        }
    },
    function(err,doc) {

    }
);

$set方法将返回修改后的文档,否则,如果不需要返回文档,则可以使用$set作为方法。 主要部分是“匹配”要更新的数组元素和“识别”与位置2998963264417170170432匹配的元素,如前所述。

然后,您当然要使用$set运算符,这样,只有您指定的元素实际上才“通过电线”发送到服务器。 您可以使用“点符号”进一步说明这一点,只需指定您实际要更新的元素即可。 如:

Folder.findOneAndUpdate(
    { "_id": folderId, "permissions._id": permission._id },
    { 
        "$set": {
            "permissions.$.role": permission.role
        }
    },
    function(err,doc) {

    }
);

因此,这就是MongoDB提供的灵活性,您可以在其中真正地“定向”实际更新文档的方式。

但是,这样做是“绕过”您可能已构建到“猫鼬”模式中的任何逻辑,例如“验证”或其他“预保存钩子”。 这是因为“最佳”方式是MongoDB的“功能”及其设计方式。 猫鼬本身试图成为这种逻辑的“便利”包装。 但是,如果您准备自己进行一些控制,则可以以最佳方式进行更新。

因此,在可能的情况下,请保持数据“嵌入”并且不要使用引用的模型。 它允许您在不需要担心并发性的简单更新中对“父”和“子”项进行原子更新。 可能是您应该首先选择MongoDB的原因之一。

Neil Lunn answered 2020-08-06T04:57:49Z
14 votes

为了在Mongoose中进行更新时验证子文档,您必须将其“加载”为Schema对象,然后Mongoose将自动触发验证和挂钩。

const userSchema = new mongoose.Schema({
  // ...
  addresses: [addressSchema],
});

如果您有一系列子文档,则可以使用Mongoose提供的userId方法获取所需的子文档。 然后,您可以单独更新其字段,或者如果您想一次更新多个字段,请使用addressId方法。

User.findById(userId)
  .then((user) => {
    const address = user.addresses.id(addressId); // returns a matching subdocument
    address.set(req.body); // updates the address while keeping its schema       
    // address.zipCode = req.body.zipCode; // individual fields can be set directly

    return user.save(); // saves document with subdocuments and triggers validation
  })
  .then((user) => {
    res.send({ user });
  })
  .catch(e => res.status(400).send(e));

请注意,您并不是真正需要userId来查找用户文档,可以通过搜索地址子文档与addressId匹配的文档来获得它,如下所示:

User.findOne({
  'addresses._id': addressId,
})
// .then() ... the same as the example above

请记住,在MongoDB中,仅在保存父文档时才保存子文档。

在官方文档中阅读有关该主题的更多信息。

Arian Acosta answered 2020-08-06T04:58:27Z
6 votes

如果您不想单独收集,只需将permissionSchema嵌入到folderSchema中。

var folderSchema = new Schema({
    name: { type: string },
    permissions: [ {
        role: { type: String },
        create_folders: { type: Boolean },
        create_contents: { type: Boolean }
    } ]
});

如果您需要单独的集合,这是最好的方法:

您可能有一个权限模型:

var mongoose = require('mongoose');
var PermissionSchema = new Schema({
  role: { type: String },
  create_folders: { type: Boolean },
  create_contents: { type: Boolean }
});

module.exports = mongoose.model('Permission', PermissionSchema);

还有一个文件夹模型,其中包含对权限文档的引用。您可以像这样引用另一个模式:

var mongoose = require('mongoose');
var FolderSchema = new Schema({
  name: { type: string },
  permissions: [ { type: mongoose.Schema.Types.ObjectId, ref: 'Permission' } ]
});

module.exports = mongoose.model('Folder', FolderSchema);

然后致电perm要求猫鼬填充字段权限。

现在,以下内容:

savePermission: function (folderId, permission, callback) {
    Folder.findOne({ _id: folderId }).populate('permissions').exec(function (err, data) {
        var perm = _.findWhere(data.permissions, { _id: permission._id });                

        _.extend(perm, permission);

        data.markModified("permissions");
        data.save(callback);
    });
}

2998964658972972918784字段不会是未定义的(如果permission._id实际上在权限数组中),因为它是由Mongoose填充的。

RaphDG answered 2020-08-06T04:59:14Z
translate from https://stackoverflow.com:/questions/26156687/mongoose-find-update-subdocument