我有这两个架构:
module.exports = mongoose.model('Article', {
title : String,
text : String,
lang : { type: String, default: 'en'},
user : { type : mongoose.Schema.Types.ObjectId, ref: 'User' },
});
var userSchema = mongoose.Schema({
email : String,
name : String,
rating : [{
_id: false,
articleID: {type: mongoose.Schema.Types.ObjectId, ref: 'Article'},
rate: Number
}]
});
module.exports = mongoose.model('User', userSchema);
我希望计算用户的平均评分(其文章所有评分的平均值)。
我试过了:
User.aggregate([
{ $unwind: "$rating" },
{
"$lookup": {
"from": "Article",
"localField": "rating.articleID",
"foreignField": "_id",
"as": "article-origin"
}
}//,
//{ $match: { "article-origin.user" : mongoose.Types.ObjectId(article.user) } }//,
//{ $group : {_id : "$rating.articleID", avgRate : { $avg : "$rating.rate" } } }
]).exec(function (err,result) {
console.log(err);
console.log(JSON.stringify(result));
});
但没有成功,锁定总是返回字段article-origin null。
结果:{"_id":"590747e1af02570769c875dc","name":"name","email":"email","rating":{"rate":5,"articleID":"59074a357fe6a307981e7925"},"__v":0,"article-origin":[]}]
为什么这不起作用?
答案 0 :(得分:0)
当然不需要 $lookup
运算符,因为组聚合操作不使用articles
集合中的文档,它只需要一个字段,即{ {1}}用于分组。
有两种方法可以解决这个问题。如果您的MongoDB服务器版本为3.4或更高版本,那么 $divide
, $reduce
和 $size
可以在此处应用运算符来计算平均值而无需求助
首先展平评级数组,如果数组很大,可能会产生一些性能影响。
考虑运行以下管道:
articleID
如果使用MongoDB 3.2版,那么首先需要 $unwind
评级数组:
User.aggregate([
{ "$match": { "_id" : mongoose.Types.ObjectId(article.user) } },
{
"$addFields": {
"avgRate": {
"$divide": [
{
"$reduce": {
"input": "$rating",
"initialValue": 0,
"in": { "$sum": ["$$value", "$$this.rate"] }
}
},
{
"$cond": [
{ "$ne": [{ "$size": "$rating" }, 0] },
{ "$size": "$rating" },
1
]
}
]
}
}
}
]).exec(function (err, result) {
console.log(err);
console.log(JSON.stringify(result));
});
如果出于某种原因需要 $lookup
操作,则需要引用集合名称,而不是模型名称,因此正确的聚合操作应该是
User.aggregate([
{ "$match": { "_id" : mongoose.Types.ObjectId(article.user) } },
{ "$unwind": "$rating" },
{
"$group": {
"_id": "$_id",
"avgRate": { "$avg": "$rating.rate" }
}
}
]).exec(function (err, result) {
console.log(err);
console.log(JSON.stringify(result));
});