MongoDB中的多对多

时间:2015-07-06 06:20:09

标签: node.js mongodb mongoose

我有两个MongoDB模型和#34;用户"和#34;角色"。每个用户可以拥有多个角色,每个角色都可以分配给许多用户。为了保持关系简单,我想在两个模型中仅存储"用户"已按预期工作的模型。但是当我使用.find({})一次加载所有角色时,我还想知道有多少用户被分配到这些角色(以检查是否可以修改角色)。

我使用Node.js + ExpressJS和mongoose。这就是我已经拥有的:

  var userSchema = new Schema({
    username: String,
    ...
    roles : [ {
      type : Number,
      ref : 'Role'
    } ]
  });

  var roleSchema = new Schema({
    _id : Number,
    name : String
  });
--------------------------------------------------
  function getRoles(request, response) {
    Role.find({}, function(err, roles) {
        ....
        response.send(roles);
    });
  }

但现在我想知道如何在每个角色中执行计数查询,并且仍然能够在一个响应中发送结果。

我想避免为每个角色发送一个请求,并尝试在getRoles()函数中进行计数。在关系数据库中,我会做这样的事情:

select r.*,
(select count(*) from user2role u2r where u2r.role_id = r.role_id)
from role r;

但MongoDB的等价物是什么?

任何帮助和提示都将不胜感激。

谢谢,格里

2 个答案:

答案 0 :(得分:0)

根据您对架构设计的上述说明,我可以假设您的架构如下:

角色集合:

 {
  role_id : 
  name : 
 }

用户收藏:

{
  user : 
  role_id : [ ]
  ....
}

您可以使用聚合来实现此目的:

 db.users.aggregate([{$unwind: "$role_id"},
 {$group: {_id: "$role_id", count : {"$sum":1}} },
 ]);

答案 1 :(得分:0)

要使用给定架构实现每个角色的用户计数查询,您可以使用 count() 方法,如下所示:

function getUsersCount(roleName, req, res) {
    Role.findOne({"name": roleName}, function(err, role) {
        var roleId = role._id;

        // var countQuery = User.where({ "role": roleId }).count();

        // User.count({ "roles": roleId }).count(callback)

        // User.count({ "roles": roleId }, callback)

        User.where({ "roles": roleId }).count(function (err, count) {
            if (err) return handleError(err);
            console.log("There are %d users with the role %d", count, roleName);
            res.send(count);
        })        
    });
}

或者您可以使用 aggregate() 方法,例如:

// Find the user count for a given role
function getUsersCount(roleName, req, res) {
    Role.findOne({"name": roleName}, function(err, role) {
        var roleId = role._id;
        var pipeline = [
            {
                "$match": { "roles": roleId } /* filter the documents that only have the roleId in the role array */
            },
            { "$unwind": "$roles" }, 
            {
                "$match": { "roles": roleId }
            },
            {
                "$group": {
                    "_id": null, "count": { "$sum": 1 }
                }
            },
            {
                "$project": {
                    "_id": 0, "count": 1
                }
            }
        ];

        Users.aggregate(pipeline, function (err, result) {
            if (err) return handleError(err);
            console.log(result); // [ { count: 55 } ] for example
            res.send(result);
        });

        // Or use the aggregation pipeline builder.
        Users.aggregate()
            .match({ "roles": roleId }) 
            .unwind("roles")
            .match({ "roles": roleId })
            .group({ "_id": null, "count": { "$sum": 1 } })
            .select("-id count")
            .exec(function (err, result) {
                if (err) return handleError(err);
                console.log(result); // [ { count: 55 } ] for example
                res.send(result);
            });
    });
}

- 编辑 -

您可以采用的一种方法是使用 lean() 方法获取可以操作的纯JavaScript对象,以便为每个角色添加额外的userCount字段。使用 lean() 是可能的,因为启用了精益选项的查询返回的文档是普通的javascript对象,而不是 MongooseDocuments 。因此,使用从 find() 游标返回的角色,您可以将它们转换为纯JS对象 您可以使用原生JavaScript map() 方法进行迭代,使用另一个考虑 count() 的查询获取每个角色的用户数上面解释的方法并返回修改后的对象。

以下演示了如何实现该功能:

function getRoles(request, response) {
    Role.find({}).lean().exec(function(err, roles) {
        var result = roles.map(function(r){
            var obj = {},
                countQuery = User.where({ "roles": r._id }).count();

            obj["userCount"] = countQuery;
            obj["_id"] = r._id;
            obj["name"] = r.name;

            return obj;
        });
        response.send(result);
    });
}