19

我在 mongodb 中有一个用户数据库,我想通过 JSON 中的 REST 接口导出它。问题是,在最坏的情况下,返回的行数远远超过 200 万。

首先我试过这个

var mongo = require('mongodb'),
  Server = mongo.Server,
  Db = mongo.Db;
var server = new Server('localhost', 27017, {auto_reconnect: true});
var db = new Db('tracking', server);
var http = require('http');

http.createServer(function (request, response) {
  db.collection('users', function(err, collection) {
    collection.find({}, function(err, cursor){
      cursor.toArray(function(err, items) {
        output = '{"users" : ' + JSON.stringify(items) + '}';

        response.setHeader("Content-Type", "application/json");
        response.end(output);
      });
    });
  });
}).listen(8008);
console.log('Server running at localhost:8008');

内存不足时失败。该示例使用 node-mongodb-native 驱动程序和基本的 http 包。

致命错误:CALL_AND_RETRY_2 分配失败 - 进程内存不足

(请注意,在实际场景中,我使用的参数会根据需要限制结果,但此示例会查询所有这些参数,这是最坏的情况)

数据本身很简单,比如

{“_id”:ObjectId(“4f993d1c5656d3320851aadb”),“userid”:“80ec39f7-37e2-4b13-b442-6bea57472537”,“user-agent”:“Mozilla/4.0(兼容;MSIE 8.0;Windows NT 5.1;Trident/ 4.0;.NET CLR 1.1.4322)","ip":"127.0.0.1","lastupdate":1335442716 }

我也尝试过类似的东西

while(cursor != null)
{
  cursor.nextObject(function(err, item) {
    response.write(JSON.stringify(item));
  });
}

但这也耗尽了内存。

我应该如何进行?应该有一种方法可以逐行流式传输数据,但我一直无法找到合适的示例。由于外部应用程序的要求,对数据进行分页是不可能的。我想过将数据写入文件然后发布,但这会导致不需要的 io。

4

5 回答 5

16

cursor.streamRecords()MongoDB驱动程序的方法已弃用,该方法stream()更快。

我已经解析了一个 40,000,000 行的 acatalog 文档,没有问题Mongodb+ stream()+process.nextTick()

于 2012-12-12T19:45:33.727 回答
9

我发现 node-mongodb-native Cursor 对象也有一个用于collection.find().streamRecords()记录的流选项(与 一起使用),即使它没有在驱动程序的 github 页面中提及。查看光标源代码并搜索“streamRecords”。

最后代码是这样的:

db.collection('users', function(err, collection) {
  var first = true;

  response.setHeader("Content-Type", "application/json");
  response.write('{"users" : [');

  var stream = collection.find().streamRecords();

  stream.on('data', function(item) {
    var prefix = first ? '' : ', ';
    response.write(prefix + JSON.stringify(item));
    first = false;
  });
  stream.on('end', function() {
    response.write(']}');
    response.end();
  });
});
于 2012-05-14T04:36:52.570 回答
5

像这样的东西应该工作。如果不是,您可能应该在mongodb-native bug tracker中打开一个问题。

http.createServer(function (request, response) {
  db.collection('users', function(err, collection) {
    collection.find({}, function(err, cursor){
      response.setHeader("Content-Type", "application/json");
      cursor.each(function(err, item) {
        if (item) {
          response.write(JSON.stringify(item));
        } else {
          response.end();
        }
      });
    });
  });
}).listen(8008);

PS:它只是一个存根,我的意思是我不记得确切的语法,但它是each您正在寻找的功能。

于 2012-05-11T07:25:03.767 回答
3

好吧,我不再使用 mongodb 原生 javascript 驱动程序,但是在mongoose中有很好的流实现。

这两个驱动程序的语法非常相似。你可以用猫鼬做到这一点:

response.setHeader("Content-Type", "application/json");
var stream = collection.find().stream();
stream.on('data', function(doc) {
   response.write(doc);  
});
stream.on('close', function() {
   response.end();
});
于 2012-05-11T06:23:41.133 回答
2

使用 Node 的stream.Transform类的一个小模块:

var stream = require('stream');

function createCursorStream(){

    var cursorStream = new stream.Transform({objectMode:true});

    cursorStream._transform = function(chunk,encoding,done){
        if(cursorStream.started){
            cursorStream.push(', ' + JSON.stringify(chunk));
        }else{
            cursorStream.push('[' + JSON.stringify(chunk));
            cursorStream.started = true;
        }
        done();
    };

    cursorStream._flush = function(done){
        cursorStream.push(']');
        done();
    };

    return cursorStream;
}

module.exports.streamCursorToResponse = function(cursor,response){
    cursor.stream().pipe(createCursorStream()).pipe(response);
};

您可以更改JSON.Stringify部件以对来自 mongodb 游标的对象进行任何其他类型的“即时”转换,并节省一些内存。

于 2014-03-25T11:13:14.440 回答