我对文档文件夹:
有以下架构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中有一个面板,我列出了所有文件夹及其权限。管理员可以编辑单个权限并保存。
我可以使用其权限数组轻松保存整个文件夹文档,其中只修改了一个权限。但是我不想保存所有文档(真正的模式有更多的字段)所以我这样做了:
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库无法查询权限数组。所以我想有一种更好的(和工作方式)方法来获取已获取文档的子文档。
有什么想法吗?
PS:我解决了使用“for”循环检查data.permission数组中的每个项目并检查data.permissions [i] ._ id == permission._id但我想要一个更聪明的解决方案,我知道有一个!
答案 0 :(得分:81)
正如您所注意到的,mongoose中的默认值是当您"嵌入"像这样的数组中的数据,每个数组条目都会得到一个_id
值,作为它自己的子文档属性的一部分。您实际上可以使用此值来确定要更新的项目的索引。 MongoDB这样做的方法是positional $
运算符变量,它保存"匹配的"数组中的位置:
Folder.findOneAndUpdate(
{ "_id": folderId, "permissions._id": permission._id },
{
"$set": {
"permissions.$": permission
}
},
function(err,doc) {
}
);
如果您不需要返回文档,那么.findOneAndUpdate()
方法将返回修改后的文档,否则您只能使用.update()
作为方法。主要部分是"匹配"要更新的数组元素和"识别"与前面提到的positional $
匹配。
当然,您正在使用$set
运算符,以便仅您指定的元素实际上是通过线路"到服务器。您可以使用"dot notation"进一步执行此操作,并指定实际要更新的元素。如:
Folder.findOneAndUpdate(
{ "_id": folderId, "permissions._id": permission._id },
{
"$set": {
"permissions.$.role": permission.role
}
},
function(err,doc) {
}
);
因此,这是MongoDB提供的灵活性,您可以非常有针对性地使用#34;在如何实际更新文档。
然而,它的作用是"绕过"你可能已经在你的猫鼬中建立的任何逻辑" mongoose"架构,例如"验证"或其他"预先保存的钩子"。那是因为"最佳"方式是MongoDB"功能"以及它是如何设计的。猫鼬本身试图成为一种方便的"包装这个逻辑。但是如果您准备自己采取一些控制措施,那么可以以最佳方式进行更新。
在可能的情况下,请保留您的数据"嵌入式"并且不使用参考模型。它允许" parent"的原子更新。和孩子"简单更新中的项目,您不必担心并发性。可能是你应该首先选择MongoDB的原因之一。
答案 1 :(得分:11)
为了在Mongoose中更新时验证子文档,你必须加载'它作为Schema对象,然后Mongoose将自动触发验证和挂钩。
const userSchema = new mongoose.Schema({
// ...
addresses: [addressSchema],
});
如果您有一个子文档数组,可以使用Mongoose提供的id()
方法获取所需的子文档。然后,您可以单独更新其字段,或者如果要一次更新多个字段,请使用set()
方法。
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
匹配的地址子文档的文档来获取它,如下所示:< / p>
User.findOne({
'addresses._id': addressId,
})
// .then() ... the same as the example above
请记住,在MongoDB中,子文档在保存父文档时仅保存 。
详细了解official documentation上的主题。
答案 2 :(得分:6)
如果您不想单独收集,只需将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);
带有权限文档引用的Folder模型。 您可以像这样引用另一个模式:
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);
然后调用Folder.findOne().populate('permissions')
让mongoose填充字段权限。
现在,以下内容:
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);
});
}
perm
字段不会被定义(如果permission._id实际上在权限数组中),因为它已被Mongoose填充。
答案 3 :(得分:0)
试试吧
let doc = await Folder.findOneAndUpdate(
{ "_id": folderId, "permissions._id": permission._id },
{ "permissions.$": permission},
);