如何使用nodejs流式传输MongoDB查询结果?

时间:2011-09-10 15:13:18

标签: mongodb node.js streaming

我一直在寻找一个如何将MongoDB查询的结果流式传输到nodejs客户端的示例。到目前为止,我找到的所有解决方案似乎都会立即读取查询结果,然后将结果发送回服务器。

相反,我(显然)希望提供一个回调查询方法,并让MongoDB在结果集的下一个块可用时调用它。

我一直在看猫鼬 - 我应该使用不同的驱动程序吗?

5 个答案:

答案 0 :(得分:27)

在发布此问题之后,版本2.4.0中出现了Mongoose中的流式传输three months

Model.where('created').gte(twoWeeksAgo).stream().pipe(writeStream);

可以在documentation page上找到更详细的例子。

答案 1 :(得分:26)

node-mongodb-driver(每个mongoDB客户端在nodejs中使用的底层)除了其他人提到的游标API有一个很好的流API(#458)。不幸的是,我没有在其他地方找到它。

更新:there are docshere

可以像这样使用:

var stream = collection.find().stream()
stream.on('error', function (err) {
  console.error(err)
})
stream.on('data', function (doc) {
  console.log(doc)
})

它实际上实现了ReadableStream接口,因此它具有所有好处(暂停/恢复等)

答案 2 :(得分:10)

mongoose实际上不是“驱动程序”,它实际上是MongoDB驱动程序(node-mongodb-native)的ORM包装器。

要执行您正在执行的操作,请查看驱动程序的.find.each方法。以下是示例中的一些代码:

// Find all records. find() returns a cursor
collection.find(function(err, cursor) {
  sys.puts("Printing docs from Cursor Each")
  cursor.each(function(err, doc) {
    if(doc != null) sys.puts("Doc from Each " + sys.inspect(doc));
  })                    
});

要对结果进行流式传输,您基本上用“流”功能替换sys.puts。不确定您打算如何流式传输结果。我认为您可以response.write() + response.flush(),但您可能还想结帐socket.io

答案 3 :(得分:2)

这是我找到的解决方案(如果这是错误的方法,请纠正我的任何人): (也可以原谅糟糕的编码 - 现在为时尚晚,以此来美化)

var sys = require('sys')
var http = require("http");

var Db = require('/usr/local/src/npm/node_modules/mongodb/lib/mongodb').Db,
  Connection = require('/usr/local/src/npm/node_modules/mongodb/lib/mongodb').Connection,
  Collection = require('/usr/local/src/npm/node_modules/mongodb/lib/mongodb').Collection,
  Server = require('/usr/local/src/npm/node_modules/mongodb/lib/mongodb').Server;

var db = new Db('test', new Server('localhost',Connection.DEFAULT_PORT , {}));

var products;

db.open(function (error, client) {
  if (error) throw error;
  products = new Collection(client, 'products');
});

function ProductReader(collection) {
        this.collection = collection;
}

ProductReader.prototype = new process.EventEmitter();

ProductReader.prototype.do = function() {
        var self = this;

        this.collection.find(function(err, cursor) {
                if (err) {
                        self.emit('e1');
                        return;

                }
                sys.puts("Printing docs from Cursor Each");

                self.emit('start');
                cursor.each(function(err, doc) {
                        if (!err) {
                                self.emit('e2');
                                self.emit('end');
                                return;
                        }

                        if(doc != null) {
                                sys.puts("doc:" + doc.name);
                                self.emit('doc',doc);
                        } else {
                                self.emit('end');
                        }
                })
        });
};
http.createServer(function(req,res){
        pr = new ProductReader(products);
        pr.on('e1',function(){
                sys.puts("E1");
                res.writeHead(400,{"Content-Type": "text/plain"});
                res.write("e1 occurred\n");
                res.end();
        });
        pr.on('e2',function(){
                sys.puts("E2");
                res.write("ERROR\n");
        });

        pr.on('start',function(){
                sys.puts("START");
                res.writeHead(200,{"Content-Type": "text/plain"});
                res.write("<products>\n");
        });

        pr.on('doc',function(doc){
                sys.puts("A DOCUMENT" + doc.name);
                res.write("<product><name>" + doc.name + "</name></product>\n");
        });

        pr.on('end',function(){
                sys.puts("END");
                res.write("</products>");
                res.end();
        });

        pr.do();

  }).listen(8000);

答案 4 :(得分:0)

我自己一直在研究mongodb流,虽然我没有您想要的全部答案,但我确实有一部分。 您可以设置一个socket.io流

这是使用NPM上的javascript socket.io和socket.io-streaming 也为数据库使用mongodb,因为 使用有问题的40年历史数据库不正确,该进行现代化了 还有40岁的数据库是SQL,而据我所知SQL不会做流

因此,尽管您只询问了从服务器到客户端的数据,但我也想在客户端中获取从客户端到服务器的数据,因为在搜索时我永远找不到任何地方,并且我想在发送和接收位置设置一个位置流中的元素,这样每个人都可以很快掌握它。

客户端通过流发送数据到服务器

stream = ss.createStream();
blobstream=ss.createBlobReadStream(data);
blobstream.pipe(stream);
ss(socket).emit('data.stream',stream,{},function(err,successful_db_insert_id){
 //if you get back the id it went into the db and everything worked
});

服务器从客户端接收流,然后在完成后进行回复

ss(socket).on('data.stream.out',function(stream,o,c){
 buffer=[];
 stream.on('data',function(chunk){buffer.push(chunk);});
 stream.on('end',function(){
  buffer=Buffer.concat(buffer);
  db.insert(buffer,function(err,res){
   res=insertedId[0];
   c(null,res);
  });
 });
});

//这是获取数据并将其流式传输到客户端的另一半

客户端从服务器请求和接收流数据

stream=ss.createStream();
binarystring='';
stream.on('data',function(chunk){ 
 for(var I=0;i<chunk.length;i++){
  binarystring+=String.fromCharCode(chunk[i]); 
 }
});
stream.on('end',function(){ data=window.btoa(binarystring); c(null,data); });
ss(socket).emit('data.stream.get,stream,o,c);

服务器端回复流数据请求

ss(socket).on('data.stream.get',function(stream,o,c){
 stream.on('end',function(){
  c(null,true);
 });
 db.find().stream().pipe(stream);
});

最后一个是我仅有的一种,因为我还没有尝试过,所以我只是将它从屁股上拉出来了,但是应该可以。我实际上做了类似的事情,但是我将文件写入硬盘,然后使用fs.createReadStream将其流式传输到客户端。因此,不确定100%是否正确,但根据我的阅读结果,我会在测试后立即与您联系。

P.s。任何人都想问我关于我口语交流的方式,我是加拿大人,我喜欢说“ eh”带着你的拥抱冲我,击中兄弟/ sis':D