mongoose – 用$ sum使用聚合创build新的属性
我试图在包含数字数组之和的所有文档中添加一个新字段。
这里是架构(为简洁起见删除无关字段):
var PollSchema = new Schema( { votes: [Number] } );
我build立模型:
PollModel = mongoose.model('Poll', PollSchema);
我使用聚合创build一个新的字段,其中包含投票数组的总和。
PollModel.aggregate([ { $project: { totalVotes: { $sum: "$votes"} } } ]);
当我启动我的服务器,我没有得到任何错误; 但是, totalVotes
字段尚未创build。 我用这个文档来帮助我。 它同样使用$sum
运算符,我完全像文档说明的那样,但没有结果。
不幸的是,这并不像你想象的那样工作,因为“票数”实际上是一组数值,其次是因为$sum
只是在$group
pipe道阶段中使用的累加器操作符 。
所以为了让你获得数组的总数作为另一个属性,首先你必须$unwind
数组,然后在文档关键字上$group
到$sum
的元素:
PostModel.aggregate( [ { "$unwind": "$votes" }, { "$group": { "_id": "$_id", "anotherField": { "$first": "$anotherField" }, "totalVotes": { "$sum": "$votes" } }} ], function(err,results) { } );
另外还要注意,在$first
另一个累加器对于每个你想要的结果字段都是必需的,因为$group
和$project
只返回你要求的字段。
一般来说,由于性能方面的原因,最好在每个文档中保留属性,因为它比使用聚合更快。 所以要做到这一点只是增加一个总和,每次你$push
到一个数组同时也使用$inc
:
PostModel.update( { "_id": id }, { "$push": { "votes": 5 }, "$inc": { "totalVotes": 5 } }, function(err,numAffected) { } );
通过这种方式,“totalVotes”字段总是可以使用,而不需要解构数组和总结每个文档的值的开销。
MongoDb聚合不会将其结果保存到数据库中。 您只需在callback中获得内联聚合的结果。 所以聚合之后,你需要对你的数据库进行多重更新:
PollModel.aggregate([ { $project: { totalVotes: { $sum: "$votes"} } }]).exec( function(err, docs){ // bulk is used for updating all records within a single query var bulk = PollModel.collection.initializeUnorderedBulkOp(); // add all update operations to bulk docs.forEach(function(doc){ bulk.find({_id: doc._id}).update({$set: {totalVotes: doc.totalVotes}}); }); // execute all bulk operations bulk.execute(function(err) { }); }) });
您的架构中没有totalVotes
。 试试下面的代码。
var PollSchema = new Schema( { votes: [Number], totalVotes: Number } ); PollModel.aggregate([ { $project: { totalVotes: { $sum: "$votes"} } } ]);
要么
resultData.toJSON();
@Blakes七和@Volodymyr Synytskyi帮助我到达我的解决scheme! 我也发现这个文件特别有用。
PollModel.aggregate( [ { '$unwind': '$votes' }, { '$group': { '_id': '$_id', 'totalVotes': { '$sum': '$votes' } }} ], function(err,results) { // console.log(results); results.forEach(function(result){ var conditions = { _id: result._id }, update = { totalVotes: result.totalVotes }, options = { multi: true }; PollModel.update(conditions, update, options, callback); function callback (err, numAffected) { if(err) { console.error(err); return; } else { // console.log(numAffected); } } }); } );