我有两个模型,一个是用户
userSchema = new Schema({
userID: String,
age: Number
});
另一个是所有用户每天记录几次的分数
ScoreSchema = new Schema({
userID: {type: String, ref: 'User'},
score: Number,
created_date = Date,
....
})
我想对某些满足特定要求的用户的分数进行一些查询/计算,比如我想计算所有用户的平均分数超过 20 天。
我的想法是,首先在 Scores 上执行 populate 以填充用户的年龄,然后再执行 aggregate。
有点像
Score.
populate('userID','age').
aggregate([
{$match: {'userID.age': {$gt: 20}}},
{$group: ...},
{$group: ...}
], function(err, data){});
在聚合之前使用填充可以吗?还是我先找到所有符合要求的userID,保存在一个数组中,然后用$in来匹配分数文档?
最佳答案
不,你不能在 .aggregate()
之前调用 .populate()
,你不能这样做是有充分理由的。但是您可以采取不同的方法。
.populate()
方法在“客户端”工作,底层代码实际执行附加查询(或更准确地说是 $in
查询)以“查找”引用集合中的指定元素。
相比之下 .aggregate()
是“服务器端”操作,因此您基本上不能在“客户端”操作内容,然后将这些数据提供给聚合管道阶段。这一切都需要存在于您正在操作的集合中。
MongoDB 3.2 及更高版本提供了更好的方法,通过 $lookup
聚合管道操作。在这种情况下,最好从 User
集合中处理以缩小选择范围:
User.aggregate(
[
// Filter first
{ "$match": {
"age": { "$gt": 20 }
}},
// Then join
{ "$lookup": {
"from": "scores",
"localField": "userID",
"foriegnField": "userID",
"as": "score"
}},
// More stages
],
function(err,results) {
}
)
这基本上将在 User
对象中包含一个新字段“score”,作为在“查找”时与其他集合匹配的项目“数组”:
{
"userID": "abc",
"age": 21,
"score": [{
"userID": "abc",
"score": 42,
// other fields
}]
}
结果始终是一个数组,因为一般预期的用法是可能的“一对多”关系的“左连接”。如果没有结果匹配,那么它只是一个空数组。
要使用内容,只需以任何方式使用数组即可。例如,您可以使用 $arrayElemAt
运算符,以便在以后的任何操作中仅获取数组的单个第一个元素。然后你可以像使用任何普通的嵌入字段一样使用内容:
{ "$project": {
"userID": 1,
"age": 1,
"score": { "$arrayElemAt": [ "$score", 0 ] }
}}
如果您没有可用的 MongoDB 3.2,则处理受另一个集合的关系限制的查询的另一个选择是首先从该集合中获取结果,然后使用 $in
过滤第二个:
// Match the user collection
User.find({ "age": { "$gt": 20 } },function(err,users) {
// Get id list
userList = users.map(function(user) {
return user.userID;
});
Score.aggregate(
[
// use the id list to select items
{ "$match": {
"userId": { "$in": userList }
}},
// more stages
],
function(err,results) {
}
);
});
因此,通过从另一个集合获取有效用户列表到客户端,然后在查询中将其提供给另一个集合,这是在早期版本中实现这一点的唯一方法。
关于mongodb - 我可以在 Mongoose 聚合之前使用填充吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35119717/