首页 文章

Mongodb聚合管道新日期

提问于
浏览
2

我尝试使用聚合管道根据之前的管道值附加/创建新日期并保存到新集合 . (请参阅下面的管道) . 但是,语法错误,我得到一个错误说

不允许的字段类型对象表达式中的日期(在'日期')//日期:新日期('$ _ id.year','$ _id.month','$ _id.day')

我想知道如何使用mongo聚合管道中的前一年,月,日值来创建日期?基本上,在将我的ISODate转换为分组的年,月和日之后,我想将它们转换回ISODate格式 .

pipeline = [{
    $match: {
        event: 'sample_event',
    }
}, {
    $project: {
        _id: false,
        uuid: '$properties.distinct_id',
        time: '$properties.time'
    }
}, {
    $group: {
        _id: {
            year: {
                $year: '$time'
            },
            month: {
                $month: '$time'
            },
            day: {
                $dayOfMonth: '$time'
            },
            uuid: '$uuid'
        }
    }
}, {
    $group: {
        _id: {
            year: '$_id.year',
            month: '$_id.month',
            day: '$_id.day'
        },
        value: { $sum: 1 }
    }
}, {
    $sort: {
        '_id.year': 1,
        '_id.month': 1,
        '_id.day': 1
    }
}, {
    $project: {
        _id: {
            $concat: [
                { $substr: ['$_id.year', 0, 4] },
                '-',
                {
                    $cond: [
                        { $lte: [ '$_id.month', 9 ] },
                        { $concat: [
                            '0',
                            { $substr: [ '$_id.month', 0, 2 ] }
                        ]},
                        { $substr: [ '$_id.month', 0, 2 ] }
                    ]
                },
                '-',
                {
                    $cond: [
                        { $lte: [ '$_id.day', 9 ] },
                        { $concat: [
                            '0',
                            { $substr: [ '$_id.day', 0, 2 ] }
                        ]},
                        { $substr: [ '$_id.day', 0, 2 ] }
                    ]
                },
            ]
        },
        date: new Date('$_id.year', '$_id.month', '$_id.day'), // errorrrr
        value: 1
    }
}, {
    $out: 'output_collection'
}];

1 回答

  • 1

    您不能在聚合管道中使用"cast"新数据类型 . 唯一真正允许的是在整数值(不是双精度)上使用$substr并从日期中提取时间戳值作为整数 . 但字符串或数字不能成为字符串或日期对象的数字 .

    此外,管道中实际上没有评估任何JavaScript,您可能看到的任何示例都纯粹是“单行程”,其中JavaScript函数在创建管道的文档中被“评估” . 但是你不能以任何方式对管道内的数据采取行动 .

    编写新集合的最佳方法是处理游标结果并使用Bulk Operations API写出 . 因此,如果这是为了定期构建聚合结果,那么您甚至可以基本上附加或更新到目标集合 .

    这意味着使用底层本机驱动程序中的本机方法 . 与那些使用mongojs的人看起来有点滑稽,但方法仍然存在:

    var async = require('async'),
        mongojs = require('mongojs'),
        db = mongojs('mongodb://localhost/test',['sample']);
    
    
    db._get(function(err,db) {
      if (err) throw err;
    
      var source = db.collection('source'),
          bulk = db.collection('target').initializeOrderedBulkOp(),
          count = 0;
    
      var cursor = source.aggregate(
        [
          { "$match": { "event" : "sample event" } },
          { "$group": {
            "_id": {
              "day": {
                "$subtract": [
                  { "$subtract": [ "$properties.time", new Date("1970-01-01") ] },
                  { "$mod": [ 
                    { "$subtract": [ "$properties.time", new Date("1970-01-01") ] },
                    1000 * 60 * 60 * 24 
                  ]}
                ]
              },
              "uuid": "$properties.distinct_id"
            }
          }}.
          { "$group": { "_id": "$_id.day", "count": { "$sum": 1 } }}
        ],
        { "cursor": { "batchSize": 101 } }
      );
    
      cursor.on("data",function(data) {
        bulk.insert({ "date": new Date(data._id), "count": data.count });
        count++;
    
        if ( count % 1000 == 0 ) {
          cursor.pause();
          bulk.execute(function(err,result) {
            if (err) throw err;
            bulk = db.collection('target').initializeOrderedBulkOp();
            cursor.resume();
          });
        }
    
      });
    
      cursor.on("end",function() {
        if ( count % 1000 != 0 )
          bulk.execute(function(err,result) {
            console.log("done");
          });
      });
    
    });
    

    这样,您的聚合操作就会大大简化并且运行得更快 . 输出是表示“日期”的纪元时间戳值,通过从另一个日期对象中减去一个日期对象时获得的时间值得到的结果 . 通用日期数学在这里将数字四舍五入到表示从提供日期开始的“日期”的值 .

    这比强制字符串更有效,并且适合作为新日期对象的构造函数提供给 Date() 函数,当然这是聚合管道的"outside" .

    这里的插入件是批量进行的,每千件产品只进行一次,因此非常有线 . 由于这实际上是一个流接口,因此您使用事件并使用 .pause().resume() 以避免过多的并发请求和/或过多的内存使用 . 根据需要调整,但无论大小如何,驱动程序实际上会分解为任何一个发送和返回的1000个请求 .

    或者当然只是在没有将值转换为此阶段的日期而只是使用 $out 来创建集合,然后使您的代码从任何读取的结果中转换日期 . 但是,您无法操纵日期并以这种方式从聚合管道本身返回日期对象 . 使用最适合您的方法 .

相关问题