【问题标题】:MongoDB: Is there a way to compute sum on past records?MongoDB:有没有办法计算过去记录的总和?
【发布时间】:2020-04-05 01:35:44
【问题描述】:

假设我有一个像这样的数据集:

yearMonth | amount
201908    | 100
201909    | 100
201910    | 200
201911    | 100
201912    | 200
202001    | 300
202002    | 200

有没有一种方法可以对通过记录进行求和/累加以获得如下结果集:

yearMonth | amount | balance
201908    | 100    | 100
201909    | 100    | 200
201910    | 200    | 400
201911    | 100    | 500
201912    | 200    | 700
202001    | 300    | 1000
202002    | 200    | 1200

【问题讨论】:

  • 这个answer 推荐mapReduce 超过聚合框架的运行总数。
  • @DaveStSomeWhere 你可以把它作为答案发布?

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

试试下面的聚合查询:

db.collection.aggregate([
   /** Sort on entire collection is not preferred, but you need it if 'yearMonth' field is not ordered */
  /** Group on empty & push all docs to 'data' array */
  { $group: { _id: "", data: { $push: "$$ROOT" } } },
  {
    $project: {
      data: {
        $let: {
          vars: {
            data: {
              $reduce: {
                input: "$data", /** Iterate over 'data' array & push newly formed docs to docs array */
                initialValue: { amount: 0, docs: [] },
                in: {
                    docs: {
                    $concatArrays: [
                      "$$value.docs",
                      [
                        {
                          _id: "$$this._id",
                          yearMonth: "$$this.yearMonth",
                          amount: "$$this.amount",
                          balance: {
                            $add: ["$$value.amount", "$$this.amount"],
                          },
                        },
                      ],
                    ],
                  },
                  amount: { $add: ["$$value.amount", "$$this.amount"] },
                },
              },
            },
          },
          in: "$$data.docs", /** Return only 'docs' array & ignore 'amount' field */
        },
      },
    },
  },
  /** unwind 'data' array(newly formed 'data' array field) */
  {
    $unwind: "$data",
  },
  /** Replace data object as new root for each document in collection  */
  {
    $replaceRoot: {
      newRoot: "$data",
    },
  },
]);

测试: MongoDB-Playground 参考: aggregation-pipeline-operators

【讨论】:

    【解决方案2】:

    在此answer 的指导下使用mapReduce 收集方法,您可以获得所需的结果。

    这是一个使用以下选项的 pymongo 解决方案:

    1. map 函数 - 对要发出的键、值对(年月和金额)进行初始映射。
    2. reduce 函数 - 在这种情况下不需要任何操作。
    3. out - 指定将输出放在哪里 - 可以是一个集合,或者在这种情况下只是内联处理。
    4. 范围 - 指定滚动总计字段 - 仅称为总计
    5. finalize - 这是实际总计。

    这是 python(pymongo) 代码:

    from pymongo import MongoClient
    from bson.code import Code
    
    client = MongoClient()
    db = client.tst1
    coll = db.mapr1
    
    map1 = Code('''
            function () {
                emit(
                    this.yearMonth, 
                    this.amount              
                    ); 
                }
        ''')
    
    reduce1 = Code('''
        function (key, values) {
            return value;
                   }
                   ''')
    
    fin1 = Code('''
                function(key, value) {
                    total += value;
                    return {amount: value, balance: total};
                }
    ''')
    
    result = coll.map_reduce(map1, reduce1, out={'inline': 1}, scope={'total': 0}, finalize=fin1)
    
    for doc in result['results']:
        print(f'The doc is {doc}')
    

    结果:

    The doc is {'_id': 201908.0, 'value': {'amount': 100.0, 'balance': 100.0}}
    The doc is {'_id': 201909.0, 'value': {'amount': 100.0, 'balance': 200.0}}
    The doc is {'_id': 201910.0, 'value': {'amount': 200.0, 'balance': 400.0}}
    The doc is {'_id': 201911.0, 'value': {'amount': 100.0, 'balance': 500.0}}
    The doc is {'_id': 201912.0, 'value': {'amount': 200.0, 'balance': 700.0}}
    The doc is {'_id': 202001.0, 'value': {'amount': 300.0, 'balance': 1000.0}}
    The doc is {'_id': 202002.0, 'value': {'amount': 200.0, 'balance': 1200.0}}
    

    集合中的文档:

    {'_id': ObjectId('5e89c410b187b1e1abb089af'),
     'amount': 100,
     'yearMonth': 201908}
    {'_id': ObjectId('5e89c410b187b1e1abb089b0'),
     'amount': 100,
     'yearMonth': 201909}
    {'_id': ObjectId('5e89c410b187b1e1abb089b1'),
     'amount': 200,
     'yearMonth': 201910}
    {'_id': ObjectId('5e89c410b187b1e1abb089b2'),
     'amount': 100,
     'yearMonth': 201911}
    {'_id': ObjectId('5e89c410b187b1e1abb089b3'),
     'amount': 200,
     'yearMonth': 201912}
    {'_id': ObjectId('5e89c410b187b1e1abb089b4'),
     'amount': 300,
     'yearMonth': 202001}
    {'_id': ObjectId('5e89c410b187b1e1abb089b5'),
     'amount': 200,
     'yearMonth': 202002}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-17
      • 2019-09-03
      • 1970-01-01
      • 2016-09-06
      相关资源
      最近更新 更多