【问题标题】:how get only 'unique' sub-subdocument in mongoose, removing the duplicates?如何在猫鼬中仅获取“唯一”子子文档,删除重复项?
【发布时间】:2016-03-15 20:30:44
【问题描述】:

我是 mongodb 和 mongoose 的新手,我在 mongoose 文档中搜索的越多,我就越迷失。
假设我有一个user,它的name 字段的值为John,John 有两个roles:

  • 超级用户
  • 管理员

每个roles 都有一些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。 所以:

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 吗?还是必须的? 或者代替distinct,我应该使用传递给回调的user项目,而不是????,使用user参数(使用find方法)来获取所有角色,以及所有角色获取权限,然后从permissionsArr 数组中删除重复项?

更新:这是我使用猫鼬制作的休息 api 的结果,获取用户角色(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直接。)

感谢您抽出宝贵时间以及您能够提供的任何帮助。

【问题讨论】:

  • 我建议您使用带有 MongoDB 副本的示例文档来更新您的帖子。
  • 我不知道在 mongodb shell 中查询后如何填充,但我提供了从我的 rest api 返回的 JSON,其中包含重复项。如果你给@Saleem一个提示,那就太好了。谢谢你。 :)

标签: node.js mongodb mongoose mongoose-schema


【解决方案1】:

在使用您的架构测试了一些代码后,我发现.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

【讨论】:

  • 我不知道Set() 和nest populate,我今天试试,非常感谢...
  • @asedsami, Set() 是 ES6 的新功能,最新的 node.js 版本可能会支持。
  • 对不起,为什么你提到的巢填充不在猫鼬文档中?除了 Stackoverflow,我在任何地方都没有看到它。
  • @asedsami ,在这个link中跨多个级别填充
【解决方案2】:

确实,您可以使用聚合框架对嵌套的权限数组进行重复数据删除。您还可以选择 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"
    ]
}

我很肯定你可以把它翻译成猫鼬,这很容易。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-24
    • 2019-06-06
    • 2021-07-28
    • 2016-07-30
    • 2021-05-10
    • 2013-04-16
    • 1970-01-01
    • 2014-11-12
    相关资源
    最近更新 更多