如何在 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 数组中没有权限名称,我有引用 (ObjectIds).

我只有一个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 吗? 我可以在不先 populateing 的情况下使用 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 并由此获得用户拥有的 rolespermissions,但删除了重复项。 (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,而且非常简单。