Mongodb support

NodeBB Development
  • Hey everyone,

    Just letting everyone know that we merged a big branch into NodeBB on friday. This new codebase allows you to use MongoDB as the database for NodeBB.

    Some people had concerns about Redis as it is an in-memory database which means you need to have enough memory to fit all your data. Now you can pick which database to use during installation(node app --setup). The installation will ask you which database to use, the options are "redis"(default) and "mongo".

    For people already running on Redis this doesn't change anything but if you are just installing NodeBB you have the option to pick MongoDB.

    Further down the road we might write export/import tools between Redis and MongoDB so you can switch between the two without losing data.

    Now some technical details about the change for developers :

    We used to have code in NodeBB that used the redis object directly as follows :

    var RDB = require('./redis');
    
    RDB.hmset('user:' + uid, userData);
    

    Now the same functionality looks like this after the merge :

    var db = require('./database');
    
    db.setObject('user:' + uid, userData);
    

    NodeBB's code no longer deals directly with Redis or MongoDB. We just make a call to the database object to save an object and depending on which database you picked during installation the object will be saved in Redis or MongoDB.

    If you check out the contents of database.js it is very simple :

    var nconf = require('nconf'),
        databaseType = nconf.get('database'),
        winston = require('winston');
    
    if(!databaseType) {
        winston.info('Database type not set! Run node app --setup');
        process.exit();
    }
    
    var db = require('./database/' + databaseType);
    
    module.exports = db;
    

    It reads the database type from nconf and then requires the correct database file from the database folder which can be either datatase/redis.js or database/mongo.js.

    Both redis.js and mongo.js expose the same set of functions for example there is a setObject function in both redis.js and mongo.js :

    // from redis.js
    module.setObject = function(key, data, callback) {
        redisClient.hmset(key, data, function(err, res) {
            if(callback) {
                callback(err, res);
            }
        });
    }
    
    
    
    // from mongo.js
    module.setObject = function(key, data, callback) {
        data['_key'] = key;
        db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
            if(callback) {
                callback(err, result);
            }
        });
    }
    

    All NodeBB sees is the setObject(key, data, callback) method which takes a key and data and saves it in the respective database.

    Since we started out with Redis we make use of the special Redis data structures namely hash, set, sorted set and list. All the functions exposed from the database files implement these data structures.

    You can check out redis.js here and mongo.js here.

    So hopefully this will be useful for people who want to use another database than Redis. Another cool thing is if we decide to add another database let's say CouchDB, all we have to do is add couch.js into the database folder and implement the same interface. Add it as an option in the install steps and there is no need to change any code in NodeBB itself.

    Let us know what you think.

  • Awesome ! even though that broke almost any nodebb-* thing I wrote, but for the greater good ! and not for too long! Thanks for abstracting every "redis"-like function in the mongo module.

  • That's awesome! I'll use this new version on my project later 🙂

  • @bentael In cases where you are requiring the RDB object directly, yes, it would break (as it did in the case of the markdown plugin).

    Best bet is to rely on built-in methods, if at all possible:

    // To simulate a reply
    var Posts = module.parent.require('./posts');
    Posts.reply(tid, uid, 'derp derp', function() { ...
    
  • Case in point -- in my markdown plugin, I am relying on querying the config hash, when I really should've been using module.parent.require('./meta') instead!

  • gotcha! [comment to short error? not anymore]

  • Was getting an error with the nodejs driver with the latest version (1.3.23). Downgrading to 1.3.19 seems to fix the issue.

    https://github.com/mongodb/node-mongodb-native/releases

    https://groups.google.com/forum/#!topic/node-mongodb-native/EpyYqhTl-Ak

  • Both of those versions are the same minor version (1.3.x) -- Mongo, I am disappoint.


Suggested Topics


  • 0 Votes
    7 Posts
    3k Views

    @Bri said in MongoDB reliability called into question?:

    Something else I've been looking at are graph databases like Neo4j and Arango, and now I'm kind of anticipating that you're going to tell me that it's in the same boat (i.e. old tech with a new name)

    Nope, old with still with the old name 🙂 Graph databases are known to go back to at least 1969.

  • 0 Votes
    1 Posts
    2k Views

    Well, I have this issue that I need to do following things:
    1.Mongo Find
    2. Foreach
    3. MongoFind
    4 one more Foreach.

    because of asynchronaus mongo calls it is difficult to get proper result, however process.nextTick should handle this problem and still I have problem:

    function find(dbComm) {

    dbComm.forEach(function (current) { id= parseFloat(current.ID); db.anotherCollection.find({ ID: id}, function (error, result) { result.forEach(function (rank) { if (parseFloat(result.ID) == parseFloat(current.ID)) { rank.rank = 'Some calculations'; } }); }); });

    }

    app.get('/someparams', function (req, res) {

    db.collection.find().sort({ "rank": order }).skip(pagenumber).limit(5, function (err, result) { process.nextTick(function () { find(result, function () { }) }); process.nextTick(function () { res.send(result); }); });

    And on client side I should recive an array where the rank equals 'Some Calculations'
    but unfortunetly it is not...
    However...! If I add setTimeout(function(){}) before res.send(result) (and res.send(result) will be placed inside setTimeout) it actually does work and on client side I recive data with "Some Calculations" but it is just timeout and it fails sometimes and client dont recive "Some Calculations"
    if I put process.nextTick instead of setTimeout it doesnt work and "Some calc..." are not sent.

    Something is beeing done eralier then it should (probably in forEach in find() function)
    and my question is how to deal with it? maybe I should put process.nextTick in different function ?

  • 0 Votes
    15 Posts
    10k Views

    Out of curiosity, why don't you use Redis? the new redis cluster feature let you shard data to multiple servers.

  • 0 Votes
    3 Posts
    1k Views

    It's not a must, but doing it will save quite a bit of space when your forum gets large.

  • Setting up MongoDB

    NodeBB Development
    0 Votes
    3 Posts
    2k Views

    Once you have Mongo running, are you supposed to set up a schema somehow or is that done for you?

    @aaron you don't have to create a schema, collections are created as they are referenced in mongo. Also you need to enable text search in mongodb config file.

    Not sure about that last error. winston.err is defined in app.js