如何在查询父项时获取聚合的mongoose子文档数组中的值的总和?
我正在尝试在express和mongoose之上构build一些先进的Hello World应用程序。 假设我有下一个Schema:
const pollOptionsSchema = new Schema({ name: String, votes: { type: Number, default: 0 } }); const pollSchema = new Schema({ name: String, dateCreated: { type: Date, default: Date.now }, author: { type: Schema.Types.ObjectId }, options: [pollOptionsSchema] });
而当我只是打电话
Poll.findOne({_id: req.params.id}).exec((err, data) => { if (err) console.log(err); // I receive next data: // { _id: 58ef3d2c526ced15688bd1ea, // name: 'Question', // author: 58dcdadfaea29624982e2fc6, // __v: 0, // options: // [ { name: 'stack', _id: 58ef3d2c526ced15688bd1ec, votes: 5 }, // { name: 'overflow', _id: 58ef3d2c526ced15688bd1eb, votes: 3 } ], // dateCreated: 2017-04-13T08:56:12.044Z } });
问题是如何在模型级别上调用某种方法后可以得到相同的数据+总计票数(即上面的情况下为8),例如:
// I want to receive: // { _id: 58ef3d2c526ced15688bd1ea, // name: 'Question', // author: 58dcdadfaea29624982e2fc6, // __v: 0, // totalNumberOfVotes: 8, // options: // [ { name: 'stack', _id: 58ef3d2c526ced15688bd1ec, votes: 5 }, // { name: 'overflow', _id: 58ef3d2c526ced15688bd1eb, votes: 3 } ], // dateCreated: 2017-04-13T08:56:12.044Z }
或者,也许我需要在文档级别上实现一些额外的方法,即(data.aggregate)?
我已经阅读:
- http://mongoosejs.com/docs/api.html#model_Model.mapReduce
- http://mongoosejs.com/docs/api.html#aggregate_Aggregate
- https://docs.mongodb.com/manual/core/map-reduce/
- https://docs.mongodb.com/manual/tutorial/map-reduce-examples/
但不能用于我的情况:(
任何意见将不胜感激。 谢谢!
在$addFields
stream水线中使用$reduce
操作符来创build$addFields
字段。 在您的聚合pipe道中,第一步是$match
,它过滤文档stream,只允许匹配的文档未经修改地传递到下一个pipe道阶段,并使用标准的MongoDB查询。
考虑运行下面的集合操作来获得所需的结果:
Poll.aggregate([ { "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } }, { "$addFields": { "totalNumberOfVotes": { "$reduce": { "input": "$options", "initialValue": 0, "in": { "$add" : ["$$value", "$$this.votes"] } } } } } ]).exec((err, data) => { if (err) console.log(err); console.log(data); });
注意:以上将适用于MongoDB 3.4及更高版本。
对于其他较早的版本,您需要首先$unwind
options
数组,然后在$group
pipe道步骤中对非规范化文档进行分组,然后与累加器$sum
, $push
和$first
汇总。
以下示例显示了这种方法:
Poll.aggregate([ { "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } }, { "$unwind": { "path": "$options", "preserveNullAndEmptyArrays": true } }, { "$group": { "_id": "$_id", "totalNumberOfVotes": { "$sum": "$options.votes" }, "options": { "$push": "$options" }, "name": { "$first": "$name" }, "dateCreated": { "$first": "$dateCreated" }, "author": { "$first": "$author" } } } ]).exec((err, data) => { if (err) console.log(err); console.log(data); });