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中的多对多的主要内容,如果未能解决你的问题,请参考以下文章