查询父项时如何获取猫鼬子文档数组中的值的聚合总和?

Posted

技术标签:

【中文标题】查询父项时如何获取猫鼬子文档数组中的值的聚合总和?【英文标题】:How to get aggregated sum of values in an array of mongoose subdocuments when query parent? 【发布时间】:2017-09-09 07:48:55 【问题描述】:

我正在尝试在 express 和 mongoose 之上构建一些高级的 hello world 应用程序。假设我有下一个架构:

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/

但不能用于我的情况:(

任何建议将不胜感激。谢谢!

【问题讨论】:

现在我只是遍历选项并在变量中收集总数。 let sumVotes = 0; data.options.forEach((el) => sumVotes += el.votes ); 【参考方案1】:

$addFields 管道中使用 $reduce 运算符来创建 totalNumberOfVotes 字段。在您的聚合管道中,第一步是 $match 过滤文档流以仅允许匹配的文档未经修改地传递到下一个管道阶段并使用标准 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 管道步骤中对非规范化文档进行分组并与累加器 $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);
);

【讨论】:

以上是关于查询父项时如何获取猫鼬子文档数组中的值的聚合总和?的主要内容,如果未能解决你的问题,请参考以下文章

猫鼬子文档 field.id 给出未定义

无法“跳过”猫鼬子文档

BigQuery 如何获取 JSON 结构中的值的总和?

如何将对象响应对象的猫鼬聚合数组转换为json对象响应

如何使用聚合在猫鼬中对文档数组进行分页?

SQL Server:查询以获取表 1 的 Col1 中的值的总和,以获取表 2 的 Col2 中的条件