从多个集合中汇总

时间:2015-09-01 12:02:34

标签: node.js mongodb mongoose mongodb-query aggregation-framework

如何在不将集合替换为其他聚合输出的情况下将MongoDB聚合的结果输出到集合中?

我只需要使用$ out:'tempCollection'来获取数据,因为我有500万个文档,并获得pipeline stage limit

var q = [
  {$match: query},
  {$group: {_id: '$hash'}},
  {$out: 'tempCollection'}
];

async.parallel([
  function(callback) {
    firstCollection.aggregate(q, callback);
  },
  function(callback) {
    secondCollection.aggregate(q, callback);
  },

  ...

], function() {

  // I want to get all from tempCollection (with pagination) here

});

1 个答案:

答案 0 :(得分:1)

这里的底线是$out选项只能“替换”目标集合上的输出。所以要做其他事情,你必须通过客户端连接,而不是只输出到服务器。

此处使用mongoose的最佳选择是直接进入底层驱动程序并访问驱动程序支持的node stream interface

Trival示例,但它显示了构造的基本方法:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/aggtest');

var testSchema = new Schema({},{ "_id": false, strict: false });


var ModelA = mongoose.model( 'ModelA', testSchema ),
    ModelB = mongoose.model( 'ModelB', testSchema ),
    ModelC = mongoose.model( 'ModelC', testSchema );

function processCursor(cursor,target,callback) {

  cursor.on("end",callback);
  cursor.on("error",callback);

  cursor.on("data",function(data) {
    cursor.pause();
    target.update(
      { "_id": data._id },
      { "$setOnInsert": { "_id": data._id } },
      { "upsert": true },
      function(err) {
        if (err) callback(err);
        cursor.resume();
      }
    );
  });
}

async.series(
  [
    // Clean data
    function(callback) {
      async.each([ModelA,ModelB,ModelC],function(model,callback) {
        model.remove({},callback);
      },callback);
    },

    // Sample data
    function(callback) {
      async.each([ModelA,ModelB],function(model,callback) {
        async.each([1,2,3],function(id,callback) {
          model.create({ "_id": id },callback);
        },callback);
      },callback);
    },

    // Run merge
    function(callback) {
      async.parallel(
        [
          function(callback) {
            var cursor = ModelA.collection.aggregate(
              [
                { "$group": { "_id": "$_id" } }
              ],
              { "batchSize": 25 }
            );

            processCursor(cursor,ModelC,callback)
          },
          function(callback) {

            var cursor = ModelB.collection.aggregate(
              [
                { "$group": { "_id": "$_id" } }
              ],
              { "batchSize": 25 }
            );

            processCursor(cursor,ModelC,callback)
          }
        ],
        callback
      );
    },

    // Get merged
    function(callback) {
      ModelC.find({},function(err,results) {
        console.log(results);
        callback(err);
      });
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

Oustide,那么你将需要$out来“分离”集合,然后将它们与类似的.update()进程合并,但要保持它“服务器端”,那么你需要使用.eval()

这不好,但这是在服务器上保持操作的唯一方法。您还可以使用"Bulk"操作(再次通过相同的本地.collection接口)对此进行修改,以获得更高的吞吐量。但选项归结为“通读客户端”或“评估”。