如何在 mongoose 中只获取 'unique' 子子文档,删除重复项?
how get only 'unique' sub-subdocument in mongoose, removing the duplicates?
我是 mongodb 和 mongoose 的新手,我在 mongoose 文档中搜索得越多,就越迷路。
假设我有一个 user
,它的 name
字段的值为 John,John 有两个 role
:
- 超级用户
- 管理员
每个 role
都有一些 permissions
:
- 超级用户
- 添加问题
- 编辑问题
- 移除问题
- 管理员
- 添加问题
- 查看答案
如您所见,每个 role
都有重复项,我如何在没有重复项的数组中获得用户 John 的权限(本例中重复的一个)是 addQuestion
),像这样:
['addQuestion', 'editQuestion', 'removeQuestion', 'viewAnswer']
这是我的架构:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var deepPopulate = require('mongoose-deep-populate')(mongoose);
var UserSchema = new Schema({
name: String,
user_roles: [{
type: Schema.ObjectId,
ref: 'Role'
}]
});
var RoleSchema = new Schema({
name: String,
permissions: [
{
type: Schema.ObjectId,
ref: 'Permission'
}
]
});
var PermissionSchema = new Schema({
name: String
});
UserSchema.plugin(deepPopulate, {
whitelist: ['user_roles.permissions']
});
var User = mongoose.model('User', UserSchema),
Role = mongoose.model('Role', RoleSchema),
Permission = mongoose.model('Permission', PermissionSchema);
如您所见,我在 permissions
数组中没有权限名称,我有引用 (ObjectId
s).
我只有一个userId
来自url。
所以:
User.findById(userId)
.deepPopulate('user_roles')
.deepPopulate('user_roles.permissions')
.distinct('permissions')
.distinct('user_roles.permissions')
.exec((err, user)=> {
if(err) res.send(err);
????
res.json(permissionsArr);
});
我应该使用 distinct
吗?
我可以在不先 populate
ing 的情况下使用 distinct
吗?还是必须的?
或者我应该使用传递给回调的 user
项目而不是 distinct
而不是 ????
使用 user
参数(使用 find
方法)获取所有角色,并为所有角色获取权限,然后从 permissionsArr
数组中删除重复项?
更新:这是休息的结果 api 我使用 mongoose 获取用户角色(GET http://localhost/api/users/:userId/roles):
app.get('/api/users/:userId/roles', function(req,res) {
User.findById( req.params.userId )
.deepPopulate('user_roles.permissions')
.select('user_roles')
.exec((err, users)=> {
if(err) next(err);
res.json(users);
});
}
结果:
{
"name": "John",
"_id": "56e677c1a7e7007d5cc245ef",
"user_roles": [
{
"_id": "56e64f057e8008263dec2cc9",
"name": "superuser",
"permissions": [
{
"name": "removeQuestion",
"_id": "56e408a9d863a8cf4d7ab001"
},
{
"name": "addQuestion",
"_id": "56e64d657e8008263dec2cc4"
},
{
"name": "addAnswer",
"_id": "56e64cf87e8008263dec2cc3"
},
{
"name": "viewQuestion",
"_id": "56e64e1e7e8008263dec2cc5"
},
{
"name": "viewAnswer",
"_id": "56e64e267e8008263dec2cc6"
}
]
},
{
"_id": "56e64ec27e8008263dec2cc8",
"name": "admin",
"permissions": [
{
"name": "addQuestion",
"_id": "56e64d657e8008263dec2cc4"
},
{
"name": "addAnswer",
"_id": "56e64cf87e8008263dec2cc3"
},
{
"name": "viewQuestion",
"_id": "56e64e1e7e8008263dec2cc5"
},
{
"name": "viewAnswer",
"_id": "56e64e267e8008263dec2cc6"
}
]
}
现在我需要的是 GET
http://localhost/api/users/:userId/permissions 并由此获得用户拥有的 roles
的 permissions
,但删除了重复项。 (user
不应该直接有 permissions
。)
感谢您抽出宝贵时间提供帮助。
在用你的模式测试了一些代码后,我发现 .distinct
只是区分 user_roles
的值而不是 populate
的结果,例如 user_roles.permissions
。这可能是一种在通过过滤用户结果填充后删除重复项 permissions
的方法。
User.findById('56e7b1f74e7192381adbfdbc')
.populate({
path: 'user_roles',
model: 'Role',
populate: {
path: 'permissions',
model: 'Permission'
}
})
.exec(function(err, user) {
if (err)
console.log(err);
else {
//console.log(util.inspect(user, { showHidden: true, depth: null }));
var s = new Set();
// remove duplicate permission
user.user_roles.forEach(function(u){
u.permissions.forEach(function(p) {
s.add(p.name);
})
})
var arr = [...s]; // convert set to array
console.log(arr);
}
});
顺便说一句,您可以在不使用模块 mongoose-deep-populate
.
的情况下以这种方式嵌套填充结果
事实上,您可以使用聚合框架对嵌套的权限数组进行重复数据删除。您也可以选择 map-reduce,但推荐使用聚合框架并且性能更高。
db.users.aggregate([
{$match:{_id:"56e677c1a7e7007d5cc245ef"}},
{$unwind:"$user_roles"},
{$unwind:"$user_roles.permissions"},
{$group:{_id:"$_id", permissions:{$addToSet:"$user_roles.permissions.name"}}}
])
根据您的示例文档,它应该获取
{
"_id" : "56e677c1a7e7007d5cc245ef",
"permissions" : [
"addAnswer",
"viewAnswer",
"viewQuestion",
"addQuestion",
"removeQuestion"
]
}
我相信您可以将其翻译成 mongoose,而且非常简单。
我是 mongodb 和 mongoose 的新手,我在 mongoose 文档中搜索得越多,就越迷路。
假设我有一个 user
,它的 name
字段的值为 John,John 有两个 role
:
- 超级用户
- 管理员
每个 role
都有一些 permissions
:
- 超级用户
- 添加问题
- 编辑问题
- 移除问题
- 管理员
- 添加问题
- 查看答案
如您所见,每个 role
都有重复项,我如何在没有重复项的数组中获得用户 John 的权限(本例中重复的一个)是 addQuestion
),像这样:
['addQuestion', 'editQuestion', 'removeQuestion', 'viewAnswer']
这是我的架构:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var deepPopulate = require('mongoose-deep-populate')(mongoose);
var UserSchema = new Schema({
name: String,
user_roles: [{
type: Schema.ObjectId,
ref: 'Role'
}]
});
var RoleSchema = new Schema({
name: String,
permissions: [
{
type: Schema.ObjectId,
ref: 'Permission'
}
]
});
var PermissionSchema = new Schema({
name: String
});
UserSchema.plugin(deepPopulate, {
whitelist: ['user_roles.permissions']
});
var User = mongoose.model('User', UserSchema),
Role = mongoose.model('Role', RoleSchema),
Permission = mongoose.model('Permission', PermissionSchema);
如您所见,我在 permissions
数组中没有权限名称,我有引用 (ObjectId
s).
我只有一个userId
来自url。
所以:
User.findById(userId)
.deepPopulate('user_roles')
.deepPopulate('user_roles.permissions')
.distinct('permissions')
.distinct('user_roles.permissions')
.exec((err, user)=> {
if(err) res.send(err);
????
res.json(permissionsArr);
});
我应该使用 distinct
吗?
我可以在不先 populate
ing 的情况下使用 distinct
吗?还是必须的?
或者我应该使用传递给回调的 user
项目而不是 distinct
而不是 ????
使用 user
参数(使用 find
方法)获取所有角色,并为所有角色获取权限,然后从 permissionsArr
数组中删除重复项?
更新:这是休息的结果 api 我使用 mongoose 获取用户角色(GET http://localhost/api/users/:userId/roles):
app.get('/api/users/:userId/roles', function(req,res) {
User.findById( req.params.userId )
.deepPopulate('user_roles.permissions')
.select('user_roles')
.exec((err, users)=> {
if(err) next(err);
res.json(users);
});
}
结果:
{
"name": "John",
"_id": "56e677c1a7e7007d5cc245ef",
"user_roles": [
{
"_id": "56e64f057e8008263dec2cc9",
"name": "superuser",
"permissions": [
{
"name": "removeQuestion",
"_id": "56e408a9d863a8cf4d7ab001"
},
{
"name": "addQuestion",
"_id": "56e64d657e8008263dec2cc4"
},
{
"name": "addAnswer",
"_id": "56e64cf87e8008263dec2cc3"
},
{
"name": "viewQuestion",
"_id": "56e64e1e7e8008263dec2cc5"
},
{
"name": "viewAnswer",
"_id": "56e64e267e8008263dec2cc6"
}
]
},
{
"_id": "56e64ec27e8008263dec2cc8",
"name": "admin",
"permissions": [
{
"name": "addQuestion",
"_id": "56e64d657e8008263dec2cc4"
},
{
"name": "addAnswer",
"_id": "56e64cf87e8008263dec2cc3"
},
{
"name": "viewQuestion",
"_id": "56e64e1e7e8008263dec2cc5"
},
{
"name": "viewAnswer",
"_id": "56e64e267e8008263dec2cc6"
}
]
}
现在我需要的是 GET
http://localhost/api/users/:userId/permissions 并由此获得用户拥有的 roles
的 permissions
,但删除了重复项。 (user
不应该直接有 permissions
。)
感谢您抽出宝贵时间提供帮助。
在用你的模式测试了一些代码后,我发现 .distinct
只是区分 user_roles
的值而不是 populate
的结果,例如 user_roles.permissions
。这可能是一种在通过过滤用户结果填充后删除重复项 permissions
的方法。
User.findById('56e7b1f74e7192381adbfdbc')
.populate({
path: 'user_roles',
model: 'Role',
populate: {
path: 'permissions',
model: 'Permission'
}
})
.exec(function(err, user) {
if (err)
console.log(err);
else {
//console.log(util.inspect(user, { showHidden: true, depth: null }));
var s = new Set();
// remove duplicate permission
user.user_roles.forEach(function(u){
u.permissions.forEach(function(p) {
s.add(p.name);
})
})
var arr = [...s]; // convert set to array
console.log(arr);
}
});
顺便说一句,您可以在不使用模块 mongoose-deep-populate
.
事实上,您可以使用聚合框架对嵌套的权限数组进行重复数据删除。您也可以选择 map-reduce,但推荐使用聚合框架并且性能更高。
db.users.aggregate([
{$match:{_id:"56e677c1a7e7007d5cc245ef"}},
{$unwind:"$user_roles"},
{$unwind:"$user_roles.permissions"},
{$group:{_id:"$_id", permissions:{$addToSet:"$user_roles.permissions.name"}}}
])
根据您的示例文档,它应该获取
{
"_id" : "56e677c1a7e7007d5cc245ef",
"permissions" : [
"addAnswer",
"viewAnswer",
"viewQuestion",
"addQuestion",
"removeQuestion"
]
}
我相信您可以将其翻译成 mongoose,而且非常简单。