mongodb.js
* MongoDB Database Backend Adapter
* @module greppy/db/adapter/mongodb
* @author Hermann Mayer <hermann.mayer92@gmail.com>
var extend = require('extend');
* @param {String} name - Name of the connection
* @param {Object} config - Config of the connection
var MongoDB = function(name, config)
this.mongo = require('mongodb');
this.mongoose = require('mongoose');
* Establish the connection and do some configurations for it.
* @param {Function} callback - Function to call on finish
MongoDB.prototype.configure = function(callback)
return callback && callback(new Error(
// Merge client options with defaults
extend(true, defaultOpts, self.config.options || {});
* Setup the plain MongoDB connection
var setupPlain = function(callback)
// Connect to the given URI with the given options
self.mongo.MongoClient.connect(self.config.uri, defaultOpts, function(err, db) {
return callback && callback(err);
callback && callback(null, db);
var setupOrm = function(callback)
// Connect to the given URI with the given options
self.mongoose.connect(self.config.uri, defaultOpts, function(err) {
return callback && callback(err);
// List all models for all modules
var modules = (new (require('../../helper/project'))()).listModelsForAllModules(
var connectionName = 'mongodb.' + self.name;
Schema = self.mongoose.Schema;
Object.keys(modules).forEach(function(module) {
// Skip modules which got no models for this connection
if (!modules[module].hasOwnProperty(connectionName)) {
modules[module][connectionName].forEach(function(model) {
var path = process.cwd() + '/modules/' +
module + '/models/' + connectionName + '/' + model;
// Import the current schema to compile a model out of it
self.mongoose.connection.model(model, require(path));
'Failed to load model ' + model.bold.green +
' of ' + connectionName.yellow
return callback && callback(e);
// Clear global variable pollution
// Rewrite ORM connection and models
self.orm.instance = self.mongoose.connection;
self.orm.models = self.mongoose.connection.models;
callback && callback(null, self.orm);
self.mongoose.connection.on('error', function(err) {
logger.error('Error occured on ' + self.name.yellow + ' backend. Details: ' + (err.stack || err));
// Prepare default config values
this.config.plain = ('undefined' === typeof this.config.plain) ? true : this.config.plain;
this.config.orm = ('undefined' === typeof this.config.orm) ? true : this.config.orm;
if (this.config.plain && this.config.orm) {
async.map([setupPlain, setupOrm], function(method, callback) {
callback && callback(null, results[0], results[1]);
callback && callback(err, undefined, orm);
callback && callback('Neither "plain" nor "orm" were selected.');
* Close the connection(s) which where established.
* @param {Function} callback - Function to call on finish
MongoDB.prototype.close = function(callback)
if (this.config.plain && this.config.orm) {
return this.connection.close(function() {
self.mongoose.disconnect(callback);
return this.connection.close(callback);
return this.mongoose.disconnect(callback);
* Management method - will create the database for the configured
* @param {Function} callback - Function to call on finish
MongoDB.prototype.create = function(callback)
this.configure.call(this, function(err, db) {
return callback && callback(err);
// Do the creation of the database
db.createCollection('__setup', function(err, collection) {
return callback && callback(err);
collection.drop(function(err) {
return self.close(function() {
* Management method - will drop the database for the configured
* @param {Function} callback - Function to call on finish
MongoDB.prototype.drop = function(callback)
this.configure.call(this, function(err, db) {
return callback && callback(err);
// Do the deletion of the database
db.dropDatabase(function(err) {
return self.close(function() {
* Management method - will run all migrations for the configured
* @param {Function} callback - Function to call on finish
MongoDB.prototype.migrate = function(callback)
this.configure.call(this, function(err, db, orm) {
return callback && callback(err);
async.each(Object.keys(orm.models), function(model, callback) {
logger.info('Create ' + model.collection.name.yellow + ' collection\n');
model.ensureIndexes(callback);
return self.close(function() {
var migrations = (new (require('../../helper/project'))()).listMigrationsForConnection(
process.cwd(), 'mongodb.' + self.name
// No migrations found for the current connection
if ('undefined' === typeof migrations.migrations || 0 === migrations.migrations.length) {
global.async = require('async');
global.greppy = require('../../greppy');
async.eachSeries(migrations.migrations, function(file, callback) {
'\033[0;34mExecuting migration: ' + file + '\033[0m\n'
var migration = require(migrations.path + file);
if (!migration.hasOwnProperty('up') || 'function' !== typeof migration.up) {
return callback && callback(new Error(
'Migration got no up() method or is no function'
migration.up(db, orm, function(err) {
return self.close(function() {
* Management method - will run all migrations for the configured
* @param {Function} callback - Function to call on finish
MongoDB.prototype.fill = function(callback)
this.configure.call(this, function(err) {
return callback && callback(err);
var fixtures = (new (require('../../helper/project'))()).listFixturesForConnection(
process.cwd(), 'mongodb.' + self.name
// No fixtures found for the current connection
if ('undefined' === typeof fixtures.fixtures || 0 === fixtures.fixtures.length) {
global.async = require('async');
global.greppy = require('../../greppy');
var utils = require('mongoose/lib/utils');
utils.content = greppy.helper.get('db.fixture');
async.eachSeries(fixtures.fixtures, function(file, callback) {
console.log('\033[0;34mExecuting fixture: ' + file + '\033[0m\n');
require(fixtures.path + file)(
self.orm.instance, self.orm.models, share, utils, function(err) {
return self.close(function() {
* Management method - will clear all data from all collections.
* @param {Function} callback - Function to call on finish
MongoDB.prototype.clear = function(callback)
var runOnAllTables = managementHelper.runOnAllCollections.bind(
this, function(collection, callback) {
'\033[0;34mClear collection ' + collection.collectionName + '\033[0m\n'
collection.remove({}, {fsync: true}, function(err) {
return self.close(function() {
* Management method - will remove all collections.
* @param {Function} callback - Function to call on finish
MongoDB.prototype.purge = function(callback)
var runOnAllTables = managementHelper.runOnAllCollections.bind(
this, function(collection, callback) {
'\033[0;34mRemove collection ' + collection.collectionName + '\033[0m\n'
collection.drop(function(err) {
return self.close(function() {
* Management helper method - will run a command on all collections.
* @param {Function} iterator - Function to run on each collection
* @param {Function} callback - Function to call on finish
managementHelper.runOnAllCollections = function(iterator, callback)
this.configure.call(this, function(err, db) {
return callback && callback(err);
db.collections(function(err, collections) {
return self.close(function() {
async.each(collections, function(collection, callback) {
return self.close(function() {
if (/^system./i.test(collection.collectionName)) {
return callback && callback();
iterator(collection, callback);