如何在MongoDB集合中单独汇总数月的数据

时间:2016-09-16 21:33:44

标签: node.js mongodb express monk

我正在尝试计算集合的子数组中的字段,我想每个月都这样做。我有这个在Mongo 2.6中工作,但最近升级到3.0.12导致查询中出现一些错误的结果。几乎看起来并没有为多个查询重置总和。

所以目前我正在做十二个查询异步并等待它们全部完成。这又是2.6。我的表结构是这样的:

{
"_id" : ObjectId("<id>"),
"userId" : ObjectId("<id>"),
"accountId" : "1234567890",
"transactionId" : "22222222333",
"date" : ISODate("2016-09-08T04:00:00.000Z"),
"description" : "SUPERCOOL STORE",
"amount" : -123.45,
"allocations" : [ 
    {
        "jarId" : ObjectId("566faf1937af9ae11ef84bc4"),
        "amount" : -100
    }, 
    {
        "jarId" : ObjectId("566faf1937af9ae11ef84bc1"),
        "amount" : -23.45
    }
],
"reconciled" : true,
"tally" : true,
"split" : true
}

这是我的代码:

var getTransactionAggregate = function (userId, month) {
  var deferred = q.defer();
  var nextMonth = moment(month).add(1, 'M');

  Transactions.aggregate([
    {$match: {
      userId: userId,
      tally: true,
      date: {
        $gte: month.toDate(),
        $lt: nextMonth.toDate()
      }
    }},
    {$project: { _id: 0, allocations: 1 } },
    {$unwind: '$allocations'},
    {$group: {_id: '$allocations.jarId', total: {$sum: '$allocations.amount'}}}
  ]).then(function(data) {
    deferred.resolve(data);
  })
  .catch(function (err) {
    logger.error(err);
    deferred.reject(err);
  });

  return deferred.promise;
};

summaryReport = function (req, res) {
  Jars.aggregate([
    { $match: {userId: new ObjectID(req.user._id)} },
    { $sort: {name: 1} }
  ])
  .then(function (jars) {
    var month = moment(moment().format('YYYY-MM') + '-01T00:00:00.000');
    var i, j;
    var promises = [];

    for (i = 0; i < 12; i++) {
      promises.push(getTransactionAggregate(new ObjectID(req.user._id), month));
      month.add(-1, 'M');
    }

    q.allSettled(promises).then(function (data) {
      for (i = 0; i < data.length; i++) {
        // data[i].value here is returned incorrectly from the queries

        ........
    });
  });
};

所以基本上发生的事情是第一个月包括正确的数据,但似乎总和继续包括前几个月的数据。如果我细分查询,则会在日期范围内返回正确的事务,并且unwind也会正常工作。就在groupBy步骤似乎是罪魁祸首时。在将Mongo升级到3.0.12之前,相同的逻辑工作正常。

有没有更好的方法一次执行此查询或以最佳方式执行十二个查询?

2 个答案:

答案 0 :(得分:1)

$match阶段似乎是一个问题。您的date字段有两个表达式,在这种情况下,您需要使用docs中指定的$and运算符:

  

MongoDB在指定逗号时提供隐式AND操作   分隔的表达式列表。使用显式AND与$和   当相同的字段或操作符必须时,操作符是必需的   在多个表达式中指定。

所以它变成了:

{$match: {
    userId: userId,
    tally: true,
    $and: [
        { date: { $gte : month.toDate() } },
        { date: { $lt: nextMonth.toDate() } }
    ]
}}

答案 1 :(得分:0)

最终与比赛有关,但不是因为上述答案中提到的$和案例。它与日期匹配有关,我猜测当时object.toDate()不会返回与使用新Date()时相同的日期对象,尽管我认为它们是相同的。

无论如何,工作逻辑如下所示:

Transactions.aggregate([
  {$match: {
    userId: userId,
    tally: true,
    $and: [
      { date: { $gt : new Date(month.toISOString()) } },
      { date: { $lt: new Date(nextMonth.toISOString()) } }
    ]
  }},
  {$unwind: '$allocations'},
  {$group: {_id: '$allocations.jarId', total: {$sum: '$allocations.amount'}}}
])

感谢Date query with ISODate in mongodb doesn't seem to work和@Leif指出我正确的方向。