Search code examples
javascriptnode.jsmongodbcapped-collections

Mongodb: how to create a `tail -f` view on a capped collection?


I would like to create a sort of a 'dashboard' on a capped collection (which is used as a log table) on my Mongo database. This is how I create the collection:

db.createCollection( "messages", { capped: true, size: 100000 } );

I do a collection.find(), with options tailable:true, awaitdata:true, and numberOfRetries:-1 (infinite retries).

What puzzles me is that I'd expect the find().each() loop to wait for new data (messages)... instead (after a few seconds) it errors out (with No more documents in tailed cursor... :-()

This is the code I'm working with:

var mongo = require('mongodb');  
mongo.MongoClient.connect('mongodb://127.0.0.1/myDb', function (err, db) {
  db.collection('messages', function(err, collection) {
    if (err) {
      return console.error('error in status collection:', err);
    }
    collection.find( // open tailable cursor
      {},
      { tailable: true, awaitdata: true, numberOfRetries: -1 }
    ).each(function(err, doc) {
      if (err) {
        if (err.message === 'No more documents in tailed cursor') {
          console.log('finished!');
        } else {
          console.error('error in messages collection:', err);
        }
      } else {
        if (doc) {
          console.log('message:', doc.message);
        }
      }
    })
  });
});

What do I miss?

UPDATE:

Not having received any conclusive answer until now, I deduce MongoDb tailable collections is not ready for prime time... :-(((

Sadly giving up for a more classic and robust fs logging solution...


Solution

  • You could set up subscriber function that subscribes for new MongoDB documents using the tailable find() cursor as a node.js stream. The following demonstrates this:

    // subscriber function
    var subscribe = function(){
    
        var args = [].slice.call(arguments);
        var next = args.pop();
        var filter = args.shift() || {};
    
        if('function' !== typeof next) throw('Callback function not defined');
    
        var mongo = require('mongodb');  
        mongo.MongoClient.connect('mongodb://127.0.0.1/myDb', function(err, db){
    
            db.collection('messages', function(err, collection) {           
                var seekCursor = collection.find(filter).sort({$natural: -1}).limit(1);
                seekCursor.nextObject(function(err, latest) {
                    if (latest) {
                        filter._id = { $gt: latest._id }
                    }           
    
                    var cursorOptions = {
                        tailable: true,
                        awaitdata: true,
                        numberOfRetries: -1
                    };
    
                    var stream = collection.find(filter, cursorOptions).sort({$natural: -1}).stream();
                    stream.on('data', next);
                });
            });
        });
    
    };
    
    // subscribe to new messages
    subscribe( function(document) {
        console.log(document);  
    });
    

    Source: How to subscribe for new MongoDB documents in Node.js using tailable cursor