2012-05-11 25 views
17

Tôi có cơ sở dữ liệu người dùng trong mongodb mà tôi muốn xuất qua giao diện REST trong JSON. Vấn đề là trong trường hợp xấu nhất, số lượng hàng trả về là hơn 2 triệu.Làm thế nào để trả lại số lượng lớn hàng từ mongodb bằng cách sử dụng máy chủ http node.js?

Đầu tiên tôi đã cố gắng này

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'); 

mà thất bại khi chạy ra khỏi bộ nhớ. Ví dụ này sử dụng trình điều khiển gốc-mongod-node và gói http cơ bản.

FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory

(lưu ý rằng trong kịch bản thực tôi sử dụng các thông số mà giới hạn kết quả khi cần thiết, nhưng ví dụ này yêu cầu tất cả chúng mà là kịch bản trường hợp xấu nhất không phân biệt)

Các dữ liệu chính nó là đơn giản, giống như

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

tôi cũng đã cố gắng một cái gì đó giống như

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

nhưng đó chạy ra khỏi bộ nhớ quá.

Tôi nên tiếp tục như thế nào? Nên có một cách để dòng dữ liệu theo hàng, nhưng tôi đã không thể tìm thấy một ví dụ phù hợp cho nó. Phân trang dữ liệu nằm ngoài câu hỏi vì yêu cầu ứng dụng bên ngoài. Tôi đã nghĩ đến việc ghi dữ liệu vào một tệp và sau đó đăng nó, nhưng điều đó dẫn đến io không mong muốn.

Trả lời

15

Phương pháp cursor.streamRecords() của người lái xe có nguồn gốc MongoDB bị phản đối, phương pháp stream() là nhanh hơn.

tôi đã phân tích một tài liệu 40.000.000 hàng acatalog không có vấn đề với Mongodb + stream() + process.nextTick()

+1

Tôi thấy rằng 'cursor.stream()' thực hiện chính xác giống như 'cursor.each()'. – Meekohi

+0

Đảm bảo chỉ định giá trị cho 'batchSize' cho hàng nghìn hoặc hàng triệu hàng – alexishacks

+3

Bạn có thể dán mã đầy đủ của mình vào đây – parkerproject

2

Vâng, tôi không còn sử dụng trình điều khiển javascript gốc mongodb, nhưng trong mongoose có triển khai khá tốt các luồng.

Cú pháp của hai trình điều khiển khá giống nhau. Bạn có thể làm điều này với cầy mangut:

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(); 
}); 
+1

Mongoose sẽ là cách tốt hơn để giải quyết hoàn toàn việc lưu trữ dữ liệu. Câu trả lời của bạn dẫn tôi đến đúng hướng khi chỉ sử dụng trình điều khiển này và tôi phát hiện ra rằng nút-mongodb-native có một tùy chọn trực tuyến trong Cursor gọi là 'streamResults'. Tôi sẽ đăng một câu trả lời hoàn chỉnh về vấn đề của tôi bằng cách sử dụng chỉ là nút-mongodb-native sau này. – Timo

4

Something như thế nên làm việc. Nếu không, có lẽ bạn nên mở một sự cố trong số 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: nó chỉ là sơ khai, tôi có nghĩa là tôi không nhớ cú pháp chính xác, nhưng đó là hàm each mà bạn đang tìm kiếm.

+0

Thực ra tôi đã thử điều đó, nhưng có vẻ như hàm 'toArray' trong câu hỏi ban đầu của tôi thực sự kết thúc tốt đẹp/sử dụng hàm' each' đó, vì vậy nó thất bại khi kịch bản hết bộ nhớ. – Timo

+0

Có, toArray cần phải đệm toàn bộ mảng, do đó sẽ không giúp được gì, nhưng cursor.each sẽ hoạt động. Bạn chỉ cần bao quanh nó với dấu ngoặc đơn. – danmactough

+0

Bây giờ tôi đã thử điều này một lần nữa nó hoạt động quá. Đối với một số lý do nó thất bại trước và tôi phải quay lại và kiểm tra những gì tôi đã làm sai. – Timo

8

Tôi phát hiện ra rằng đối tượng con trỏ chuột gốc-mongodb gốc có tùy chọn phát trực tuyến (được sử dụng với collection.find().streamRecords()) cho các bản ghi quá ngay cả khi nó không được đề cập trong github page of the driver. Xem Cursor source code và tìm kiếm "streamRecords".

Trong cuối mã kết thúc như thế này:

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(); 
    }); 
}); 
+0

Cảm ơn Timo vì đã chia sẻ giải pháp của bạn! – asuciu

1

Một mô-đun nhỏ để làm điều đó bằng stream.Transform lớp Node của:

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); 
}; 

Bạn có thể thay đổi JSON.Stringify phần làm bất kỳ loại nào khác "trên bay" biến đổi trên các đối tượng đến từ con trỏ mongodb, và lưu một số bộ nhớ.

Các vấn đề liên quan