MongoDB中的多对多

Posted

技术标签:

【中文标题】MongoDB中的多对多【英文标题】:Many-to-Many in MongoDB 【发布时间】:2015-09-23 04:31:41 【问题描述】:

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

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

谢谢,格里

【问题讨论】:

也就是说,您想返回一个附加字段,例如userCount,即每个角色的用户数? 是的,这就是我要找的。但是即使研究了半天,我仍然不知道该怎么做。 【参考方案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() 光标返回的角色,您可以将它们转换为普通的 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);
    );

【讨论】:

感谢您的回答,但这并不是我想要的。我已经相应地更新了我的问题。 不幸的是,新方法在发送响应时会导致此错误消息:“TypeError: Converting circular structure to JSON”。但是,我决定切换到每个角色一个请求的方法并计算每个角色的用户数。【参考方案2】:

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

角色集合:

 
  role_id : 
  name : 
 

用户收藏:


  user : 
  role_id : [ ]
  ....

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

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

【讨论】:

以上是关于MongoDB中的多对多的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB中的多对多

MongoDB中的多对多关系

了解 MongoDB 中的多对多关系以及如何取消引用集合

社交应用的多对多关系:Mongodb 或 Neo4j 等图形数据库

MongoDB/Mongoose:多对多关系

Access中的多对多关系来自单个表