[英]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.