繁体   English   中英

MongoDB 中的多对多

[英]Many-to-Many in MongoDB

我有两个 MongoDB 模型“用户”和“角色”。 每个用户可以有多个角色,每个角色可以分配给多个用户。 为了保持关系简单,我只想将两个模型之间的引用存储在已经按预期工作的“用户”模型中。 但是当我使用 .find({}) 一次加载所有角色时,我还想知道有多少用户分配给这些角色(检查是否可以修改角色)。

我正在使用 Node.js + ExpressJS 和猫鼬。 这是我已经拥有的:

  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 的等价物是什么?

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

谢谢,格里

根据您上面关于架构设计的解释,我可以假设您的架构如下:

角色集合:

 {
  role_id : 
  name : 
 }

用户集合:

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

您可以使用聚合来实现这一点:

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

要使用给定的架构实现每个角色的用户计数查询,您可以使用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()游标返回的角色,您可以将它们转换为可以使用原生 JavaScript map()方法迭代的纯 JS 对象,使用另一个考虑count()查询获取每个角色的用户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);
    });
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM