summaryrefslogtreecommitdiff
path: root/node_modules/mongodb/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/mongodb/lib')
-rw-r--r--node_modules/mongodb/lib/mongodb/admin.js390
-rw-r--r--node_modules/mongodb/lib/mongodb/collection.js1504
-rw-r--r--node_modules/mongodb/lib/mongodb/commands/base_command.js27
-rw-r--r--node_modules/mongodb/lib/mongodb/commands/db_command.js205
-rw-r--r--node_modules/mongodb/lib/mongodb/commands/delete_command.js111
-rw-r--r--node_modules/mongodb/lib/mongodb/commands/get_more_command.js83
-rw-r--r--node_modules/mongodb/lib/mongodb/commands/insert_command.js141
-rw-r--r--node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js98
-rw-r--r--node_modules/mongodb/lib/mongodb/commands/query_command.js209
-rw-r--r--node_modules/mongodb/lib/mongodb/commands/update_command.js174
-rw-r--r--node_modules/mongodb/lib/mongodb/connection/connection.js414
-rw-r--r--node_modules/mongodb/lib/mongodb/connection/connection_pool.js259
-rw-r--r--node_modules/mongodb/lib/mongodb/connection/connection_utils.js23
-rw-r--r--node_modules/mongodb/lib/mongodb/connection/repl_set_servers.js972
-rw-r--r--node_modules/mongodb/lib/mongodb/connection/server.js640
-rw-r--r--node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js125
-rw-r--r--node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js40
-rw-r--r--node_modules/mongodb/lib/mongodb/cursor.js702
-rw-r--r--node_modules/mongodb/lib/mongodb/cursorstream.js141
-rw-r--r--node_modules/mongodb/lib/mongodb/db.js1788
-rw-r--r--node_modules/mongodb/lib/mongodb/gridfs/chunk.js208
-rw-r--r--node_modules/mongodb/lib/mongodb/gridfs/grid.js98
-rw-r--r--node_modules/mongodb/lib/mongodb/gridfs/gridstore.js1109
-rw-r--r--node_modules/mongodb/lib/mongodb/gridfs/readstream.js179
-rw-r--r--node_modules/mongodb/lib/mongodb/index.js142
-rw-r--r--node_modules/mongodb/lib/mongodb/responses/mongo_reply.js131
-rw-r--r--node_modules/mongodb/lib/mongodb/utils.js74
27 files changed, 9987 insertions, 0 deletions
diff --git a/node_modules/mongodb/lib/mongodb/admin.js b/node_modules/mongodb/lib/mongodb/admin.js
new file mode 100644
index 0000000..cb008a3
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/admin.js
@@ -0,0 +1,390 @@
+/*!
+ * Module dependencies.
+ */
+var Collection = require('./collection').Collection,
+ Cursor = require('./cursor').Cursor,
+ DbCommand = require('./commands/db_command').DbCommand;
+
+/**
+ * Allows the user to access the admin functionality of MongoDB
+ *
+ * @class Represents the Admin methods of MongoDB.
+ * @param {Object} db Current db instance we wish to perform Admin operations on.
+ * @return {Function} Constructor for Admin type.
+ */
+function Admin(db) {
+ if(!(this instanceof Admin)) return new Admin(db);
+
+ this.db = db;
+};
+
+/**
+ * Retrieve the server information for the current
+ * instance of the db client
+ *
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.buildInfo = function(callback) {
+ this.serverInfo(callback);
+}
+
+/**
+ * Retrieve the server information for the current
+ * instance of the db client
+ *
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api private
+ */
+Admin.prototype.serverInfo = function(callback) {
+ var self = this;
+ var command = {buildinfo:1};
+ this.command(command, function(err, doc) {
+ if(err != null) return callback(err, null);
+ return callback(null, doc.documents[0]);
+ });
+}
+
+/**
+ * Retrieve this db's server status.
+ *
+ * @param {Function} callback returns the server status.
+ * @return {null}
+ * @api public
+ */
+Admin.prototype.serverStatus = function(callback) {
+ var self = this;
+
+ this.command({serverStatus: 1}, function(err, result) {
+ if (err == null && result.documents[0].ok == 1) {
+ callback(null, result.documents[0]);
+ } else {
+ if (err) {
+ callback(err, false);
+ } else {
+ callback(self.wrap(result.documents[0]), false);
+ }
+ }
+ });
+};
+
+/**
+ * Retrieve the current profiling Level for MongoDB
+ *
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.profilingLevel = function(callback) {
+ var self = this;
+ var command = {profile:-1};
+
+ this.command(command, function(err, doc) {
+ doc = doc.documents[0];
+
+ if(err == null && (doc.ok == 1 || typeof doc.was === 'number')) {
+ var was = doc.was;
+ if(was == 0) {
+ callback(null, "off");
+ } else if(was == 1) {
+ callback(null, "slow_only");
+ } else if(was == 2) {
+ callback(null, "all");
+ } else {
+ callback(new Error("Error: illegal profiling level value " + was), null);
+ }
+ } else {
+ err != null ? callback(err, null) : callback(new Error("Error with profile command"), null);
+ }
+ });
+};
+
+/**
+ * Ping the MongoDB server and retrieve results
+ *
+ * @param {Object} [options] Optional parameters to the command.
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.ping = function(options, callback) {
+ // Unpack calls
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+ // Set self
+ var self = this;
+ var databaseName = this.db.databaseName;
+ this.db.databaseName = 'admin';
+ this.db.executeDbCommand({ping:1}, options, function(err, result) {
+ self.db.databaseName = databaseName;
+ return callback(err, result);
+ })
+}
+
+/**
+ * Authenticate against MongoDB
+ *
+ * @param {String} username The user name for the authentication.
+ * @param {String} password The password for the authentication.
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.authenticate = function(username, password, callback) {
+ var self = this;
+ var databaseName = this.db.databaseName;
+ this.db.databaseName = 'admin';
+ this.db.authenticate(username, password, function(err, result) {
+ self.db.databaseName = databaseName;
+ return callback(err, result);
+ })
+}
+
+/**
+ * Logout current authenticated user
+ *
+ * @param {Object} [options] Optional parameters to the command.
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.logout = function(callback) {
+ var self = this;
+ var databaseName = this.db.databaseName;
+ this.db.databaseName = 'admin';
+ this.db.logout(function(err, result) {
+ return callback(err, result);
+ })
+
+ self.db.databaseName = databaseName;
+}
+
+/**
+ * Add a user to the MongoDB server, if the user exists it will
+ * overwrite the current password
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ *
+ * @param {String} username The user name for the authentication.
+ * @param {String} password The password for the authentication.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.addUser = function(username, password, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ var self = this;
+ var databaseName = this.db.databaseName;
+ this.db.databaseName = 'admin';
+ this.db.addUser(username, password, options, function(err, result) {
+ self.db.databaseName = databaseName;
+ return callback(err, result);
+ })
+}
+
+/**
+ * Remove a user from the MongoDB server
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ *
+ * @param {String} username The user name for the authentication.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.removeUser = function(username, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ var self = this;
+ var databaseName = this.db.databaseName;
+ this.db.databaseName = 'admin';
+ this.db.removeUser(username, options, function(err, result) {
+ self.db.databaseName = databaseName;
+ return callback(err, result);
+ })
+}
+
+/**
+ * Set the current profiling level of MongoDB
+ *
+ * @param {String} level The new profiling level (off, slow_only, all)
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.setProfilingLevel = function(level, callback) {
+ var self = this;
+ var command = {};
+ var profile = 0;
+
+ if(level == "off") {
+ profile = 0;
+ } else if(level == "slow_only") {
+ profile = 1;
+ } else if(level == "all") {
+ profile = 2;
+ } else {
+ return callback(new Error("Error: illegal profiling level value " + level));
+ }
+
+ // Set up the profile number
+ command['profile'] = profile;
+ // Execute the command to set the profiling level
+ this.command(command, function(err, doc) {
+ doc = doc.documents[0];
+
+ if(err == null && (doc.ok == 1 || typeof doc.was === 'number')) {
+ return callback(null, level);
+ } else {
+ return err != null ? callback(err, null) : callback(new Error("Error with profile command"), null);
+ }
+ });
+};
+
+/**
+ * Retrive the current profiling information for MongoDB
+ *
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.profilingInfo = function(callback) {
+ var self = this;
+ var databaseName = this.db.databaseName;
+ this.db.databaseName = 'admin';
+
+ try {
+ new Cursor(this.db, new Collection(this.db, DbCommand.SYSTEM_PROFILE_COLLECTION), {}).toArray(function(err, items) {
+ return callback(err, items);
+ });
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ self.db.databaseName = databaseName;
+};
+
+/**
+ * Execute a db command against the Admin database
+ *
+ * @param {Object} command A command object `{ping:1}`.
+ * @param {Object} [options] Optional parameters to the command.
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.command = function(command, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ // Execute a command
+ this.db.executeDbAdminCommand(command, options, function(err, result) {
+ // Ensure change before event loop executes
+ return callback != null ? callback(err, result) : null;
+ });
+}
+
+/**
+ * Validate an existing collection
+ *
+ * @param {String} collectionName The name of the collection to validate.
+ * @param {Object} [options] Optional parameters to the command.
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.validateCollection = function(collectionName, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ var self = this;
+ var command = {validate: collectionName};
+ var keys = Object.keys(options);
+
+ // Decorate command with extra options
+ for(var i = 0; i < keys.length; i++) {
+ if(options.hasOwnProperty(keys[i])) {
+ command[keys[i]] = options[keys[i]];
+ }
+ }
+
+ this.db.executeDbCommand(command, function(err, doc) {
+ if(err != null) return callback(err, null);
+ doc = doc.documents[0];
+
+ if(doc.ok == 0) {
+ return callback(new Error("Error with validate command"), null);
+ } else if(doc.result != null && doc.result.constructor != String) {
+ return callback(new Error("Error with validation data"), null);
+ } else if(doc.result != null && doc.result.match(/exception|corrupt/) != null) {
+ return callback(new Error("Error: invalid collection " + collectionName), null);
+ } else if(doc.valid != null && !doc.valid) {
+ return callback(new Error("Error: invalid collection " + collectionName), null);
+ } else {
+ return callback(null, doc);
+ }
+ });
+};
+
+/**
+ * List the available databases
+ *
+ * @param {Function} callback Callback function of format `function(err, result) {}`.
+ * @return {null} Returns no result
+ * @api public
+ */
+Admin.prototype.listDatabases = function(callback) {
+ // Execute the listAllDatabases command
+ this.db.executeDbAdminCommand({listDatabases:1}, {}, function(err, result) {
+ if(err != null) {
+ callback(err, null);
+ } else {
+ callback(null, result.documents[0]);
+ }
+ });
+}
+
+/**
+ * Get ReplicaSet status
+ *
+ * @param {Function} callback returns the replica set status (if available).
+ * @return {null}
+ * @api public
+ */
+Admin.prototype.replSetGetStatus = function(callback) {
+ var self = this;
+
+ this.command({replSetGetStatus:1}, function(err, result) {
+ if (err == null && result.documents[0].ok == 1) {
+ callback(null, result.documents[0]);
+ } else {
+ if (err) {
+ callback(err, false);
+ } else {
+ callback(self.db.wrap(result.documents[0]), false);
+ }
+ }
+ });
+};
+
+/**
+ * @ignore
+ */
+exports.Admin = Admin;
diff --git a/node_modules/mongodb/lib/mongodb/collection.js b/node_modules/mongodb/lib/mongodb/collection.js
new file mode 100644
index 0000000..1c57c5b
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/collection.js
@@ -0,0 +1,1504 @@
+/**
+ * Module dependencies.
+ * @ignore
+ */
+var InsertCommand = require('./commands/insert_command').InsertCommand
+ , QueryCommand = require('./commands/query_command').QueryCommand
+ , DeleteCommand = require('./commands/delete_command').DeleteCommand
+ , UpdateCommand = require('./commands/update_command').UpdateCommand
+ , DbCommand = require('./commands/db_command').DbCommand
+ , ObjectID = require('bson').ObjectID
+ , Code = require('bson').Code
+ , Cursor = require('./cursor').Cursor
+ , utils = require('./utils');
+
+/**
+ * Precompiled regexes
+ * @ignore
+**/
+const eErrorMessages = /No matching object found/;
+
+/**
+ * toString helper.
+ * @ignore
+ */
+var toString = Object.prototype.toString;
+
+/**
+ * Create a new Collection instance
+ *
+ * Options
+ * - **slaveOk** {Boolean, default:false}, Allow reads from secondaries.
+ * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document.
+ * - **raw** {Boolean, default:false}, perform all operations using raw bson objects.
+ * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation.
+ *
+ * @class Represents a Collection
+ * @param {Object} db db instance.
+ * @param {String} collectionName collection name.
+ * @param {Object} [pkFactory] alternative primary key factory.
+ * @param {Object} [options] additional options for the collection.
+ * @return {Object} a collection instance.
+ */
+function Collection (db, collectionName, pkFactory, options) {
+ if(!(this instanceof Collection)) return new Collection(db, collectionName, pkFactory, options);
+
+ checkCollectionName(collectionName);
+
+ this.db = db;
+ this.collectionName = collectionName;
+ this.internalHint;
+ this.opts = options != null && ('object' === typeof options) ? options : {};
+ this.slaveOk = options == null || options.slaveOk == null ? db.slaveOk : options.slaveOk;
+ this.serializeFunctions = options == null || options.serializeFunctions == null ? db.serializeFunctions : options.serializeFunctions;
+ this.raw = options == null || options.raw == null ? db.raw : options.raw;
+ this.pkFactory = pkFactory == null
+ ? ObjectID
+ : pkFactory;
+
+ var self = this
+ Object.defineProperty(this, "hint", {
+ enumerable: true
+ , get: function () {
+ return this.internalHint;
+ }
+ , set: function (v) {
+ this.internalHint = normalizeHintField(v);
+ }
+ });
+};
+
+/**
+ * Inserts a single document or a an array of documents into MongoDB.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ * - **keepGoing** {Boolean, default:false}, keep inserting documents even if one document has an error, *mongodb 1.9.1 >*.
+ * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document.
+ *
+ * @param {Array|Object} docs
+ * @param {Object} [options] optional options for insert command
+ * @param {Function} [callback] optional callback for the function, must be provided when using `safe` or `strict` mode
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.insert = function insert (docs, options, callback) {
+ if ('function' === typeof options) callback = options, options = {};
+ if(options == null) options = {};
+ if(!('function' === typeof callback)) callback = null;
+ var self = this;
+ insertAll(self, Array.isArray(docs) ? docs : [docs], options, callback);
+ return this;
+};
+
+/**
+ * @ignore
+ */
+var checkCollectionName = function checkCollectionName (collectionName) {
+ if ('string' !== typeof collectionName) {
+ throw Error("collection name must be a String");
+ }
+
+ if (!collectionName || collectionName.indexOf('..') != -1) {
+ throw Error("collection names cannot be empty");
+ }
+
+ if (collectionName.indexOf('$') != -1 &&
+ collectionName.match(/((^\$cmd)|(oplog\.\$main))/) == null) {
+ throw Error("collection names must not contain '$'");
+ }
+
+ if (collectionName.match(/^\.|\.$/) != null) {
+ throw Error("collection names must not start or end with '.'");
+ }
+};
+
+/**
+ * Removes documents specified by `selector` from the db.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ *
+ * @param {Object} [selector] optional select, no selector is equivalent to removing all documents.
+ * @param {Object} [options] additional options during remove.
+ * @param {Function} [callback] must be provided if you performing a safe remove
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.remove = function remove(selector, options, callback) {
+ if ('function' === typeof selector) {
+ callback = selector;
+ selector = options = {};
+ } else if ('function' === typeof options) {
+ callback = options;
+ options = {};
+ }
+
+ // Ensure options
+ if(options == null) options = {};
+ if(!('function' === typeof callback)) callback = null;
+ // Ensure we have at least an empty selector
+ selector = selector == null ? {} : selector;
+
+ var deleteCommand = new DeleteCommand(
+ this.db
+ , this.db.databaseName + "." + this.collectionName
+ , selector);
+
+ var self = this;
+ var errorOptions = options.safe != null ? options.safe : null;
+ errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
+ errorOptions = errorOptions == null && this.db.strict != null ? this.db.strict : errorOptions;
+
+ // If we have a write concern set and no callback throw error
+ if(errorOptions && errorOptions['safe'] != false && typeof callback !== 'function') throw new Error("safe cannot be used without a callback");
+ // Execute the command, do not add a callback as it's async
+ if (options && options.safe || this.opts.safe != null || this.db.strict) {
+ // Insert options
+ var commandOptions = {read:false};
+ // If we have safe set set async to false
+ if(errorOptions == null) commandOptions['async'] = true;
+ // Set safe option
+ commandOptions['safe'] = true;
+ // If we have an error option
+ if(typeof errorOptions == 'object') {
+ var keys = Object.keys(errorOptions);
+ for(var i = 0; i < keys.length; i++) {
+ commandOptions[keys[i]] = errorOptions[keys[i]];
+ }
+ }
+
+ // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection)
+ this.db._executeRemoveCommand(deleteCommand, commandOptions, function (err, error) {
+ error = error && error.documents;
+ if(!callback) return;
+
+ if(err) {
+ callback(err);
+ } else if(error[0].err || error[0].errmsg) {
+ callback(self.db.wrap(error[0]));
+ } else {
+ callback(null, error[0].n);
+ }
+ });
+ } else {
+ var result = this.db._executeRemoveCommand(deleteCommand);
+ // If no callback just return
+ if (!callback) return;
+ // If error return error
+ if (result instanceof Error) {
+ return callback(result);
+ }
+ // Otherwise just return
+ return callback();
+ }
+};
+
+/**
+ * Renames the collection.
+ *
+ * @param {String} newName the new name of the collection.
+ * @param {Function} callback the callback accepting the result
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.rename = function rename (newName, callback) {
+ var self = this;
+ // Ensure the new name is valid
+ checkCollectionName(newName);
+ // Execute the command, return the new renamed collection if successful
+ self.db._executeQueryCommand(DbCommand.createRenameCollectionCommand(self.db, self.collectionName, newName), function(err, result) {
+ if(err == null && result.documents[0].ok == 1) {
+ if(callback != null) {
+ // Set current object to point to the new name
+ self.collectionName = newName;
+ // Return the current collection
+ callback(null, self);
+ }
+ } else if(result.documents[0].errmsg != null) {
+ if(callback != null) {
+ err != null ? callback(err, null) : callback(self.db.wrap(result.documents[0]), null);
+ }
+ }
+ });
+};
+
+/**
+ * @ignore
+ */
+var insertAll = function insertAll (self, docs, options, callback) {
+ if('function' === typeof options) callback = options, options = {};
+ if(options == null) options = {};
+ if(!('function' === typeof callback)) callback = null;
+
+ // Insert options (flags for insert)
+ var insertFlags = {};
+ // If we have a mongodb version >= 1.9.1 support keepGoing attribute
+ if(options['keepGoing'] != null) {
+ insertFlags['keepGoing'] = options['keepGoing'];
+ }
+
+ // Either use override on the function, or go back to default on either the collection
+ // level or db
+ if(options['serializeFunctions'] != null) {
+ insertFlags['serializeFunctions'] = options['serializeFunctions'];
+ } else {
+ insertFlags['serializeFunctions'] = self.serializeFunctions;
+ }
+
+ // Pass in options
+ var insertCommand = new InsertCommand(
+ self.db
+ , self.db.databaseName + "." + self.collectionName, true, insertFlags);
+
+ // Add the documents and decorate them with id's if they have none
+ for (var index = 0, len = docs.length; index < len; ++index) {
+ var doc = docs[index];
+
+ // Add id to each document if it's not already defined
+ if (!(Buffer.isBuffer(doc)) && doc['_id'] == null && self.db.forceServerObjectId != true) {
+ doc['_id'] = self.pkFactory.createPk();
+ }
+
+ insertCommand.add(doc);
+ }
+
+ // Collect errorOptions
+ var errorOptions = options.safe != null ? options.safe : null;
+ errorOptions = errorOptions == null && self.opts.safe != null ? self.opts.safe : errorOptions;
+ errorOptions = errorOptions == null && self.db.strict != null ? self.db.strict : errorOptions;
+
+ // If we have a write concern set and no callback throw error
+ if(errorOptions && errorOptions['safe'] != false && typeof callback !== 'function') throw new Error("safe cannot be used without a callback");
+
+ // Default command options
+ var commandOptions = {};
+ // If safe is defined check for error message
+ if(errorOptions && errorOptions != false) {
+ // Insert options
+ commandOptions['read'] = false;
+ // If we have safe set set async to false
+ if(errorOptions == null) commandOptions['async'] = true;
+
+ // Set safe option
+ commandOptions['safe'] = errorOptions;
+ // If we have an error option
+ if(typeof errorOptions == 'object') {
+ var keys = Object.keys(errorOptions);
+ for(var i = 0; i < keys.length; i++) {
+ commandOptions[keys[i]] = errorOptions[keys[i]];
+ }
+ }
+
+ // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection)
+ self.db._executeInsertCommand(insertCommand, commandOptions, function (err, error) {
+ error = error && error.documents;
+ if(!callback) return;
+
+ if (err) {
+ callback(err);
+ } else if(error[0].err || error[0].errmsg) {
+ callback(self.db.wrap(error[0]));
+ } else {
+ callback(null, docs);
+ }
+ });
+ } else {
+ var result = self.db._executeInsertCommand(insertCommand, commandOptions);
+ // If no callback just return
+ if(!callback) return;
+ // If error return error
+ if(result instanceof Error) {
+ return callback(result);
+ }
+ // Otherwise just return
+ return callback(null, docs);
+ }
+};
+
+/**
+ * Save a document. Simple full document replacement function. Not recommended for efficiency, use atomic
+ * operators and update instead for more efficient operations.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ *
+ * @param {Object} [doc] the document to save
+ * @param {Object} [options] additional options during remove.
+ * @param {Function} [callback] must be provided if you performing a safe save
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.save = function save(doc, options, callback) {
+ if('function' === typeof options) callback = options, options = null;
+ if(options == null) options = {};
+ if(!('function' === typeof callback)) callback = null;
+
+ var errorOptions = options.safe != null ? options.safe : false;
+ errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
+ // Extract the id, if we have one we need to do a update command
+ var id = doc['_id'];
+
+ if(id) {
+ this.update({ _id: id }, doc, { upsert: true, safe: errorOptions }, callback);
+ } else {
+ this.insert(doc, { safe: errorOptions }, callback && function (err, docs) {
+ if (err) return callback(err, null);
+
+ if (Array.isArray(docs)) {
+ callback(err, docs[0]);
+ } else {
+ callback(err, docs);
+ }
+ });
+ }
+};
+
+/**
+ * Updates documents.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ * - **upsert** {Boolean, default:false}, perform an upsert operation.
+ * - **multi** {Boolean, default:false}, update all documents matching the selector.
+ * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document.
+ *
+ * @param {Object} selector the query to select the document/documents to be updated
+ * @param {Object} document the fields/vals to be updated, or in the case of an upsert operation, inserted.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} [callback] must be provided if you performing a safe update
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.update = function update(selector, document, options, callback) {
+ if('function' === typeof options) callback = options, options = null;
+ if(options == null) options = {};
+ if(!('function' === typeof callback)) callback = null;
+
+ // Either use override on the function, or go back to default on either the collection
+ // level or db
+ if(options['serializeFunctions'] != null) {
+ options['serializeFunctions'] = options['serializeFunctions'];
+ } else {
+ options['serializeFunctions'] = this.serializeFunctions;
+ }
+
+ var updateCommand = new UpdateCommand(
+ this.db
+ , this.db.databaseName + "." + this.collectionName
+ , selector
+ , document
+ , options);
+
+ var self = this;
+ // Unpack the error options if any
+ var errorOptions = (options && options.safe != null) ? options.safe : null;
+ errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
+ errorOptions = errorOptions == null && this.db.strict != null ? this.db.strict : errorOptions;
+
+ // If we have a write concern set and no callback throw error
+ if(errorOptions && errorOptions['safe'] != false && typeof callback !== 'function') throw new Error("safe cannot be used without a callback");
+
+ // If we are executing in strict mode or safe both the update and the safe command must happen on the same line
+ if(errorOptions && errorOptions != false) {
+ // Insert options
+ var commandOptions = {read:false};
+ // If we have safe set set async to false
+ if(errorOptions == null) commandOptions['async'] = true;
+ // Set safe option
+ commandOptions['safe'] = true;
+ // If we have an error option
+ if(typeof errorOptions == 'object') {
+ var keys = Object.keys(errorOptions);
+ for(var i = 0; i < keys.length; i++) {
+ commandOptions[keys[i]] = errorOptions[keys[i]];
+ }
+ }
+
+ // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection)
+ this.db._executeUpdateCommand(updateCommand, commandOptions, function (err, error) {
+ error = error && error.documents;
+ if(!callback) return;
+
+ if(err) {
+ callback(err);
+ } else if(error[0].err || error[0].errmsg) {
+ callback(self.db.wrap(error[0]));
+ } else {
+ callback(null, error[0].n);
+ }
+ });
+ } else {
+ // Execute update
+ var result = this.db._executeUpdateCommand(updateCommand);
+ // If no callback just return
+ if (!callback) return;
+ // If error return error
+ if (result instanceof Error) {
+ return callback(result);
+ }
+ // Otherwise just return
+ return callback();
+ }
+};
+
+/**
+ * The distinct command returns returns a list of distinct values for the given key across a collection.
+ *
+ * @param {String} key key to run distinct against.
+ * @param {Object} [query] option query to narrow the returned objects.
+ * @param {Function} callback must be provided.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.distinct = function distinct(key, query, callback) {
+ if ('function' === typeof query) callback = query, query = {};
+
+ var mapCommandHash = {
+ distinct: this.collectionName
+ , query: query
+ , key: key
+ };
+
+ var cmd = DbCommand.createDbSlaveOkCommand(this.db, mapCommandHash);
+
+ this.db._executeQueryCommand(cmd, {read:true}, function (err, result) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (result.documents[0].ok != 1) {
+ return callback(new Error(result.documents[0].errmsg));
+ }
+
+ callback(null, result.documents[0].values);
+ });
+};
+
+/**
+ * Count number of matching documents in the db to a query.
+ *
+ * @param {Object} [query] query to filter by before performing count.
+ * @param {Function} callback must be provided.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.count = function count (query, callback) {
+ if ('function' === typeof query) callback = query, query = {};
+
+ var final_query = {
+ count: this.collectionName
+ , query: query
+ , fields: null
+ };
+
+ var queryOptions = QueryCommand.OPTS_NO_CURSOR_TIMEOUT;
+ if (this.slaveOk || this.db.slaveOk) {
+ queryOptions |= QueryCommand.OPTS_SLAVE;
+ }
+
+ var queryCommand = new QueryCommand(
+ this.db
+ , this.db.databaseName + ".$cmd"
+ , queryOptions
+ , 0
+ , -1
+ , final_query
+ , null
+ );
+
+ var self = this;
+ this.db._executeQueryCommand(queryCommand, {read:true}, function (err, result) {
+ result = result && result.documents;
+ if(!callback) return;
+
+ if (err) {
+ callback(err);
+ } else if (result[0].ok != 1 || result[0].errmsg) {
+ callback(self.db.wrap(result[0]));
+ } else {
+ callback(null, result[0].n);
+ }
+ });
+};
+
+
+/**
+ * Drop the collection
+ *
+ * @param {Function} [callback] provide a callback to be notified when command finished executing
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.drop = function drop(callback) {
+ this.db.dropCollection(this.collectionName, callback);
+};
+
+/**
+ * Find and update a document.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ * - **remove** {Boolean, default:false}, set to true to remove the object before returning.
+ * - **upsert** {Boolean, default:false}, perform an upsert operation.
+ * - **new** {Boolean, default:false}, set to true if you want to return the modified object rather than the original. Ignored for remove.
+ *
+ * @param {Object} query query object to locate the object to modify
+ * @param {Array} sort - if multiple docs match, choose the first one in the specified sort order as the object to manipulate
+ * @param {Object} doc - the fields/vals to be updated
+ * @param {Object} [options] additional options during update.
+ * @param {Function} [callback] returns results.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.findAndModify = function findAndModify (query, sort, doc, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ sort = args.length ? args.shift() : [];
+ doc = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : {};
+ var self = this;
+
+ var queryObject = {
+ 'findandmodify': this.collectionName
+ , 'query': query
+ , 'sort': utils.formattedOrderClause(sort)
+ };
+
+ queryObject.new = options.new ? 1 : 0;
+ queryObject.remove = options.remove ? 1 : 0;
+ queryObject.upsert = options.upsert ? 1 : 0;
+
+ if (options.fields) {
+ queryObject.fields = options.fields;
+ }
+
+ if (doc && !options.remove) {
+ queryObject.update = doc;
+ }
+
+ // Either use override on the function, or go back to default on either the collection
+ // level or db
+ if(options['serializeFunctions'] != null) {
+ options['serializeFunctions'] = options['serializeFunctions'];
+ } else {
+ options['serializeFunctions'] = this.serializeFunctions;
+ }
+
+ // Unpack the error options if any
+ var errorOptions = (options && options.safe != null) ? options.safe : null;
+ errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
+ errorOptions = errorOptions == null && this.db.strict != null ? this.db.strict : errorOptions;
+
+ // Commands to send
+ var commands = [];
+ // Add the find and modify command
+ commands.push(DbCommand.createDbSlaveOkCommand(this.db, queryObject, options));
+ // If we have safe defined we need to return both call results
+ var chainedCommands = errorOptions != null ? true : false;
+ // Add error command if we have one
+ if(chainedCommands) {
+ commands.push(DbCommand.createGetLastErrorCommand(errorOptions, this.db));
+ }
+
+ // Fire commands and
+ this.db._executeQueryCommand(commands, function(err, result) {
+ result = result && result.documents;
+
+ if(err != null) {
+ callback(err);
+ } else if(result[0].err != null) {
+ callback(self.db.wrap(result[0]), null);
+ } else if(result[0].errmsg != null && !result[0].errmsg.match(eErrorMessages)) {
+ // Workaround due to 1.8.X returning an error on no matching object
+ // while 2.0.X does not not, making 2.0.X behaviour standard
+ callback(self.db.wrap(result[0]), null);
+ } else {
+ return callback(null, result[0].value);
+ }
+ });
+}
+
+/**
+ * Find and remove a document
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ *
+ * @param {Object} query query object to locate the object to modify
+ * @param {Array} sort - if multiple docs match, choose the first one in the specified sort order as the object to manipulate
+ * @param {Object} [options] additional options during update.
+ * @param {Function} [callback] returns results.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.findAndRemove = function(query, sort, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ sort = args.length ? args.shift() : [];
+ options = args.length ? args.shift() : {};
+ // Add the remove option
+ options['remove'] = true;
+ // Execute the callback
+ this.findAndModify(query, sort, null, options, callback);
+}
+
+var testForFields = {'limit' : 1, 'sort' : 1, 'fields' : 1, 'skip' : 1, 'hint' : 1, 'explain' : 1, 'snapshot' : 1
+ , 'timeout' : 1, 'tailable' : 1, 'batchSize' : 1, 'raw' : 1, 'read' : 1
+ , 'returnKey' : 1, 'maxScan' : 1, 'min' : 1, 'max' : 1, 'showDiskLoc' : 1, 'comment' : 1};
+
+/**
+ * Creates a cursor for a query that can be used to iterate over results from MongoDB
+ *
+ * Various argument possibilities
+ * - callback?
+ * - selector, callback?,
+ * - selector, fields, callback?
+ * - selector, options, callback?
+ * - selector, fields, options, callback?
+ * - selector, fields, skip, limit, callback?
+ * - selector, fields, skip, limit, timeout, callback?
+ *
+ * Options
+ * - **limit** {Number, default:0}, sets the limit of documents returned in the query.
+ * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc.
+ * - **fields** {Object}, the fields to return in the query. Object of fields to include or exclude (not both), {'a':1}
+ * - **skip** {Number, default:0}, set to skip N documents ahead in your query (useful for pagination).
+ * - **hint** {Object}, tell the query to use specific indexes in the query. Object of indexes to use, {'_id':1}
+ * - **explain** {Boolean, default:false}, explain the query instead of returning the data.
+ * - **snapshot** {Boolean, default:false}, snapshot query.
+ * - **timeout** {Boolean, default:false}, specify if the cursor can timeout.
+ * - **tailable** {Boolean, default:false}, specify if the cursor is tailable.
+ * - **batchSize** {Number, default:0}, set the batchSize for the getMoreCommand when iterating over the query results.
+ * - **returnKey** {Boolean, default:false}, only return the index key.
+ * - **maxScan** {Number}, Limit the number of items to scan.
+ * - **min** {Number}, Set index bounds.
+ * - **max** {Number}, Set index bounds.
+ * - **showDiskLoc** {Boolean, default:false}, Show disk location of results.
+ * - **comment** {String}, You can put a $comment field on a query to make looking in the profiler logs simpler.
+ * - **raw** {Boolean, default:false}, Return all BSON documents as Raw Buffer documents.
+ * - **read** {Boolean, default:false}, Tell the query to read from a secondary server.
+ *
+ * @param {Object} query query object to locate the object to modify
+ * @param {Object} [options] additional options during update.
+ * @param {Function} [callback] optional callback for cursor.
+ * @return {Cursor} returns a cursor to the query
+ * @api public
+ */
+Collection.prototype.find = function find () {
+ var options
+ , args = Array.prototype.slice.call(arguments, 0)
+ , has_callback = typeof args[args.length - 1] === 'function'
+ , has_weird_callback = typeof args[0] === 'function'
+ , callback = has_callback ? args.pop() : (has_weird_callback ? args.shift() : null)
+ , len = args.length
+ , selector = len >= 1 ? args[0] : {}
+ , fields = len >= 2 ? args[1] : undefined;
+
+ if(len === 1 && has_weird_callback) {
+ // backwards compat for callback?, options case
+ selector = {};
+ options = args[0];
+ }
+
+ if(len === 2 && !Array.isArray(fields)) {
+ var fieldKeys = Object.getOwnPropertyNames(fields);
+ var is_option = false;
+
+ for(var i = 0; i < fieldKeys.length; i++) {
+ if(testForFields[fieldKeys[i]] != null) {
+ is_option = true;
+ break;
+ }
+ }
+
+ if(is_option) {
+ options = fields;
+ fields = undefined;
+ } else {
+ options = {};
+ }
+ } else if(len === 2 && Array.isArray(fields) && !Array.isArray(fields[0])) {
+ var newFields = {};
+ // Rewrite the array
+ for(var i = 0; i < fields.length; i++) {
+ newFields[fields[i]] = 1;
+ }
+ // Set the fields
+ fields = newFields;
+ }
+
+ if(3 === len) {
+ options = args[2];
+ }
+
+ // Ensure selector is not null
+ selector = selector == null ? {} : selector;
+ // Validate correctness off the selector
+ var object = selector;
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) {
+ var error = new Error("query selector raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ error.name = 'MongoError';
+ throw error;
+ }
+ }
+
+ // Validate correctness of the field selector
+ var object = fields;
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) {
+ var error = new Error("query fields raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ error.name = 'MongoError';
+ throw error;
+ }
+ }
+
+ // Check special case where we are using an objectId
+ if(selector instanceof ObjectID) {
+ selector = {_id:selector};
+ }
+
+ // If it's a serialized fields field we need to just let it through
+ // user be warned it better be good
+ if(options && options.fields && !(Buffer.isBuffer(options.fields))) {
+ fields = {};
+
+ if(Array.isArray(options.fields)) {
+ if(!options.fields.length) {
+ fields['_id'] = 1;
+ } else {
+ for (var i = 0, l = options.fields.length; i < l; i++) {
+ fields[options.fields[i]] = 1;
+ }
+ }
+ } else {
+ fields = options.fields;
+ }
+ }
+
+ if (!options) options = {};
+ options.skip = len > 3 ? args[2] : options.skip ? options.skip : 0;
+ options.limit = len > 3 ? args[3] : options.limit ? options.limit : 0;
+ options.raw = options.raw != null && typeof options.raw === 'boolean' ? options.raw : this.raw;
+ options.hint = options.hint != null ? normalizeHintField(options.hint) : this.internalHint;
+ options.timeout = len == 5 ? args[4] : typeof options.timeout === 'undefined' ? undefined : options.timeout;
+ // If we have overridden slaveOk otherwise use the default db setting
+ options.slaveOk = options.slaveOk != null ? options.slaveOk : this.db.slaveOk;
+ var o = options;
+
+ // callback for backward compatibility
+ if(callback) {
+ // TODO refactor Cursor args
+ callback(null, new Cursor(this.db, this, selector, fields, o.skip, o.limit
+ , o.sort, o.hint, o.explain, o.snapshot, o.timeout, o.tailable, o.batchSize
+ , o.slaveOk, o.raw, o.read, o.returnKey, o.maxScan, o.min, o.max, o.showDiskLoc, o.comment));
+ } else {
+ return new Cursor(this.db, this, selector, fields, o.skip, o.limit
+ , o.sort, o.hint, o.explain, o.snapshot, o.timeout, o.tailable, o.batchSize
+ , o.slaveOk, o.raw, o.read, o.returnKey, o.maxScan, o.min, o.max, o.showDiskLoc, o.comment);
+ }
+};
+
+/**
+ * Normalizes a `hint` argument.
+ *
+ * @param {String|Object|Array} hint
+ * @return {Object}
+ * @api private
+ */
+var normalizeHintField = function normalizeHintField(hint) {
+ var finalHint = null;
+
+ if (null != hint) {
+ switch (hint.constructor) {
+ case String:
+ finalHint = {};
+ finalHint[hint] = 1;
+ break;
+ case Object:
+ finalHint = {};
+ for (var name in hint) {
+ finalHint[name] = hint[name];
+ }
+ break;
+ case Array:
+ finalHint = {};
+ hint.forEach(function(param) {
+ finalHint[param] = 1;
+ });
+ break;
+ }
+ }
+
+ return finalHint;
+};
+
+/**
+ * Finds a single document based on the query
+ *
+ * Various argument possibilities
+ * - callback?
+ * - selector, callback?,
+ * - selector, fields, callback?
+ * - selector, options, callback?
+ * - selector, fields, options, callback?
+ * - selector, fields, skip, limit, callback?
+ * - selector, fields, skip, limit, timeout, callback?
+ *
+ * Options
+ * - **limit** {Number, default:0}, sets the limit of documents returned in the query.
+ * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc.
+ * - **fields** {Object}, the fields to return in the query. Object of fields to include or exclude (not both), {'a':1}
+ * - **skip** {Number, default:0}, set to skip N documents ahead in your query (useful for pagination).
+ * - **hint** {Object}, tell the query to use specific indexes in the query. Object of indexes to use, {'_id':1}
+ * - **explain** {Boolean, default:false}, explain the query instead of returning the data.
+ * - **snapshot** {Boolean, default:false}, snapshot query.
+ * - **timeout** {Boolean, default:false}, specify if the cursor can timeout.
+ * - **tailable** {Boolean, default:false}, specify if the cursor is tailable.
+ * - **batchSize** {Number, default:0}, set the batchSize for the getMoreCommand when iterating over the query results.
+ * - **returnKey** {Boolean, default:false}, only return the index key.
+ * - **maxScan** {Number}, Limit the number of items to scan.
+ * - **min** {Number}, Set index bounds.
+ * - **max** {Number}, Set index bounds.
+ * - **showDiskLoc** {Boolean, default:false}, Show disk location of results.
+ * - **comment** {String}, You can put a $comment field on a query to make looking in the profiler logs simpler.
+ * - **raw** {Boolean, default:false}, Return all BSON documents as Raw Buffer documents.
+ * - **read** {Boolean, default:false}, Tell the query to read from a secondary server.
+ *
+ * @param {Object} query query object to locate the object to modify
+ * @param {Object} [options] additional options during update.
+ * @param {Function} [callback] optional callback for cursor.
+ * @return {Cursor} returns a cursor to the query
+ * @api public
+ */
+Collection.prototype.findOne = function findOne () {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 0);
+ var callback = args.pop();
+ var cursor = this.find.apply(this, args).limit(1).batchSize(1);
+ // Return the item
+ cursor.toArray(function(err, items) {
+ if(err != null) return callback(err instanceof Error ? err : self.db.wrap(new Error(err)), null);
+ if(items.length == 1) return callback(null, items[0]);
+ callback(null, null);
+ });
+};
+
+/**
+ * Creates an index on the collection.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a
+ * - **unique** {Boolean, default:false}, creates an unique index.
+ * - **sparse** {Boolean, default:false}, creates a sparse index.
+ * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible.
+ * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
+ * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates.
+ * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates.
+ *
+ * @param {Object} fieldOrSpec fieldOrSpec that defines the index.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback for results.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.createIndex = function createIndex (fieldOrSpec, options, callback) {
+ // Clean up call
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+ options = typeof callback === 'function' ? options : callback;
+ options = options == null ? {} : options;
+
+ // Collect errorOptions
+ var errorOptions = options.safe != null ? options.safe : null;
+ errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
+ errorOptions = errorOptions == null && this.db.strict != null ? this.db.strict : errorOptions;
+
+ // If we have a write concern set and no callback throw error
+ if(errorOptions != null && errorOptions != false && (typeof callback !== 'function' && typeof options !== 'function')) throw new Error("safe cannot be used without a callback");
+
+ // Execute create index
+ this.db.createIndex(this.collectionName, fieldOrSpec, options, callback);
+};
+
+/**
+ * Ensures that an index exists, if it does not it creates it
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a
+ * - **unique** {Boolean, default:false}, creates an unique index.
+ * - **sparse** {Boolean, default:false}, creates a sparse index.
+ * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible.
+ * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
+ * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates.
+ * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates.
+ * - **v** {Number}, specify the format version of the indexes.
+ *
+ * @param {Object} fieldOrSpec fieldOrSpec that defines the index.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback for results.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.ensureIndex = function ensureIndex (fieldOrSpec, options, callback) {
+ // Clean up call
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+ options = typeof callback === 'function' ? options : callback;
+ options = options == null ? {} : options;
+
+ // Collect errorOptions
+ var errorOptions = options.safe != null ? options.safe : null;
+ errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
+ errorOptions = errorOptions == null && this.db.strict != null ? this.db.strict : errorOptions;
+
+ // If we have a write concern set and no callback throw error
+ if(errorOptions != null && errorOptions != false && (typeof callback !== 'function' && typeof options !== 'function')) throw new Error("safe cannot be used without a callback");
+
+ // Execute create index
+ this.db.ensureIndex(this.collectionName, fieldOrSpec, options, callback);
+};
+
+/**
+ * Retrieves this collections index info.
+ *
+ * Options
+ * - **full** {Boolean, default:false}, returns the full raw index information.
+ *
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback returns the index information.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.indexInformation = function indexInformation (options, callback) {
+ // Unpack calls
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+ // Call the index information
+ this.db.indexInformation(this.collectionName, options, callback);
+};
+
+/**
+ * Drops an index from this collection.
+ *
+ * @param {String} name
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.dropIndex = function dropIndex (name, callback) {
+ this.db.dropIndex(this.collectionName, name, callback);
+};
+
+/**
+ * Drops all indexes from this collection.
+ *
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.dropAllIndexes = function dropIndexes (callback) {
+ this.db.dropIndex(this.collectionName, '*', function (err, result) {
+ if(err != null) {
+ callback(err, false);
+ } else if(result.documents[0].errmsg == null) {
+ callback(null, true);
+ } else {
+ callback(new Error(result.documents[0].errmsg), false);
+ }
+ });
+};
+
+/**
+ * Drops all indexes from this collection.
+ *
+ * @deprecated
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api private
+ */
+Collection.prototype.dropIndexes = Collection.prototype.dropAllIndexes;
+
+/**
+ * Reindex all indexes on the collection
+ * Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
+ *
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+**/
+Collection.prototype.reIndex = function(callback) {
+ this.db.reIndex(this.collectionName, callback);
+}
+
+/**
+ * Run Map Reduce across a collection.
+ *
+ * Options
+ * - **out** {Object, default:*{inline:1}*}, sets the output target for the map reduce job. *{inline:1} | {replace:'collectionName'} | {merge:'collectionName'} | {reduce:'collectionName'}*
+ * - **query** {Object}, query filter object.
+ * - **sort** {Object}, sorts the input objects using this key. Useful for optimization, like sorting by the emit key for fewer reduces.
+ * - **limit** {Number}, number of objects to return from collection.
+ * - **keeptemp** {Boolean, default:false}, keep temporary data.
+ * - **finalize** {Function | String}, finalize function.
+ * - **scope** {Object}, can pass in variables that can be access from map/reduce/finalize.
+ * - **jsMode** {Boolean, default:false}, it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X.
+ * - **verbose** {Boolean, default:false}, provide statistics on job execution time.
+ *
+ * @param {Function|String} map the mapping function.
+ * @param {Function|String} reduce the reduce function.
+ * @param {Objects} [options] options for the map reduce job.
+ * @param {Function} callback returns the result of the map reduce job, (error, results, [stats])
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.mapReduce = function mapReduce (map, reduce, options, callback) {
+ if ('function' === typeof options) callback = options, options = {};
+ // Out must allways be defined (make sure we don't break weirdly on pre 1.8+ servers)
+ if(null == options.out) {
+ throw new Error("the out option parameter must be defined, see mongodb docs for possible values");
+ }
+
+ if ('function' === typeof map) {
+ map = map.toString();
+ }
+
+ if ('function' === typeof reduce) {
+ reduce = reduce.toString();
+ }
+
+ if ('function' === typeof options.finalize) {
+ options.finalize = options.finalize.toString();
+ }
+
+ var mapCommandHash = {
+ mapreduce: this.collectionName
+ , map: map
+ , reduce: reduce
+ };
+
+ // Add any other options passed in
+ for (var name in options) {
+ mapCommandHash[name] = options[name];
+ }
+
+ var self = this;
+ var cmd = DbCommand.createDbSlaveOkCommand(this.db, mapCommandHash);
+
+ this.db._executeQueryCommand(cmd, {read:true}, function (err, result) {
+ if (err) {
+ return callback(err);
+ }
+
+ //
+ if (1 != result.documents[0].ok || result.documents[0].err || result.documents[0].errmsg) {
+ return callback(self.db.wrap(result.documents[0]));
+ }
+
+ // Create statistics value
+ var stats = {};
+ if(result.documents[0].timeMillis) stats['processtime'] = result.documents[0].timeMillis;
+ if(result.documents[0].counts) stats['counts'] = result.documents[0].counts;
+ if(result.documents[0].timing) stats['timing'] = result.documents[0].timing;
+
+ // invoked with inline?
+ if (result.documents[0].results) {
+ return callback(null, result.documents[0].results, stats);
+ }
+
+ // Create a collection object that wraps the result collection
+ self.db.collection(result.documents[0].result, function (err, collection) {
+ // If we wish for no verbosity
+ if(options['verbose'] == null || !options['verbose']) {
+ return callback(err, collection);
+ }
+
+ // Create statistics value
+ var stats = {};
+ if(result.documents[0].timeMillis) stats['processtime'] = result.documents[0].timeMillis;
+ if(result.documents[0].counts) stats['counts'] = result.documents[0].counts;
+ if(result.documents[0].timing) stats['timing'] = result.documents[0].timing;
+ // Return stats as third set of values
+ callback(err, collection, stats);
+ });
+ });
+};
+
+/**
+ * Group function helper
+ * @ignore
+ */
+var groupFunction = function () {
+ var c = db[ns].find(condition);
+ var map = new Map();
+ var reduce_function = reduce;
+
+ while (c.hasNext()) {
+ var obj = c.next();
+ var key = {};
+
+ for (var i = 0, len = keys.length; i < len; ++i) {
+ var k = keys[i];
+ key[k] = obj[k];
+ }
+
+ var aggObj = map.get(key);
+
+ if (aggObj == null) {
+ var newObj = Object.extend({}, key);
+ aggObj = Object.extend(newObj, initial);
+ map.put(key, aggObj);
+ }
+
+ reduce_function(obj, aggObj);
+ }
+
+ return { "result": map.values() };
+}.toString();
+
+/**
+ * Run a group command across a collection
+ *
+ * @param {Object|Array|Function|Code} keys an object, array or function expressing the keys to group by.
+ * @param {Object} condition an optional condition that must be true for a row to be considered.
+ * @param {Object} initial initial value of the aggregation counter object.
+ * @param {Function|Code} reduce the reduce function aggregates (reduces) the objects iterated
+ * @param {Function|Code} finalize an optional function to be run on each item in the result set just before the item is returned.
+ * @param {Boolean} command specify if you wish to run using the internal group command or using eval, default is true.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.group = function group(keys, condition, initial, reduce, finalize, command, callback) {
+ var args = Array.prototype.slice.call(arguments, 3);
+ callback = args.pop();
+ // Fetch all commands
+ reduce = args.length ? args.shift() : null;
+ finalize = args.length ? args.shift() : null;
+ command = args.length ? args.shift() : null;
+
+ // Make sure we are backward compatible
+ if(!(typeof finalize == 'function')) {
+ command = finalize;
+ finalize = null;
+ }
+
+ if (!Array.isArray(keys) && keys instanceof Object && typeof(keys) !== 'function') {
+ keys = Object.keys(keys);
+ }
+
+ if(typeof reduce === 'function') {
+ reduce = reduce.toString();
+ }
+
+ if(typeof finalize === 'function') {
+ finalize = finalize.toString();
+ }
+
+ // Set up the command as default
+ command = command == null ? true : command;
+
+ // Execute using the command
+ if(command) {
+ var reduceFunction = reduce instanceof Code
+ ? reduce
+ : new Code(reduce);
+
+ var selector = {
+ group: {
+ 'ns': this.collectionName
+ , '$reduce': reduceFunction
+ , 'cond': condition
+ , 'initial': initial
+ , 'out': "inline"
+ }
+ };
+
+ // if finalize is defined
+ if(finalize != null) selector.group['finalize'] = finalize;
+ // Set up group selector
+ if ('function' === typeof keys) {
+ selector.group.$keyf = keys instanceof Code
+ ? keys
+ : new Code(keys);
+ } else {
+ var hash = {};
+ keys.forEach(function (key) {
+ hash[key] = 1;
+ });
+ selector.group.key = hash;
+ }
+
+ var cmd = DbCommand.createDbSlaveOkCommand(this.db, selector);
+
+ this.db._executeQueryCommand(cmd, {read:true}, function (err, result) {
+ if(err != null) return callback(err);
+
+ var document = result.documents[0];
+ if (null == document.retval) {
+ return callback(new Error("group command failed: " + document.errmsg));
+ }
+
+ callback(null, document.retval);
+ });
+
+ } else {
+ // Create execution scope
+ var scope = reduce != null && reduce instanceof Code
+ ? reduce.scope
+ : {};
+
+ scope.ns = this.collectionName;
+ scope.keys = keys;
+ scope.condition = condition;
+ scope.initial = initial;
+
+ // Pass in the function text to execute within mongodb.
+ var groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';');
+
+ this.db.eval(new Code(groupfn, scope), function (err, results) {
+ if (err) return callback(err, null);
+ callback(null, results.result || results);
+ });
+ }
+};
+
+/**
+ * Returns the options of the collection.
+ *
+ * @param {Function} callback returns option results.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.options = function options(callback) {
+ this.db.collectionsInfo(this.collectionName, function (err, cursor) {
+ if (err) return callback(err);
+ cursor.nextObject(function (err, document) {
+ callback(err, document && document.options || null);
+ });
+ });
+};
+
+/**
+ * Returns if the collection is a capped collection
+ *
+ * @param {Function} callback returns if collection is capped.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.isCapped = function isCapped(callback) {
+ this.options(function(err, document) {
+ if(err != null) {
+ callback(err);
+ } else {
+ callback(null, document.capped);
+ }
+ });
+};
+
+/**
+ * Checks if one or more indexes exist on the collection
+ *
+ * @param {String|Array} indexNames check if one or more indexes exist on the collection.
+ * @param {Function} callback returns if the indexes exist.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.indexExists = function indexExists(indexes, callback) {
+ this.indexInformation(function(err, indexInformation) {
+ // If we have an error return
+ if(err != null) return callback(err, null);
+ // Let's check for the index names
+ if(Array.isArray(indexes)) {
+ for(var i = 0; i < indexes.length; i++) {
+ if(indexInformation[indexes[i]] == null) {
+ return callback(null, false);
+ }
+ }
+
+ // All keys found return true
+ return callback(null, true);
+ } else {
+ return callback(null, indexInformation[indexes] != null);
+ }
+ });
+}
+
+/**
+ * Execute the geoNear command to search for items in the collection
+ *
+ * Options
+ * - **num** {Number}, max number of results to return.
+ * - **maxDistance** {Number}, include results up to maxDistance from the point.
+ * - **distanceMultiplier** {Number}, include a value to multiply the distances with allowing for range conversions.
+ * - **query** {Object}, filter the results by a query.
+ * - **spherical** {Boolean, default:false}, perform query using a spherical model.
+ * - **uniqueDocs** {Boolean, default:false}, the closest location in a document to the center of the search region will always be returned MongoDB > 2.X.
+ * - **includeLocs** {Boolean, default:false}, include the location data fields in the top level of the results MongoDB > 2.X.
+ *
+ * @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order.
+ * @param {Number} y point to search on the y axis, ensure the indexes are ordered in the same order.
+ * @param {Objects} [options] options for the map reduce job.
+ * @param {Function} callback returns matching documents.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.geoNear = function geoNear(x, y, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ // Fetch all commands
+ options = args.length ? args.shift() : {};
+
+ // Build command object
+ var commandObject = {
+ geoNear:this.collectionName,
+ near: [x, y]
+ }
+
+ // Decorate object if any with known properties
+ if(options['num'] != null) commandObject['num'] = options['num'];
+ if(options['maxDistance'] != null) commandObject['maxDistance'] = options['maxDistance'];
+ if(options['distanceMultiplier'] != null) commandObject['distanceMultiplier'] = options['distanceMultiplier'];
+ if(options['query'] != null) commandObject['query'] = options['query'];
+ if(options['spherical'] != null) commandObject['spherical'] = options['spherical'];
+ if(options['uniqueDocs'] != null) commandObject['uniqueDocs'] = options['uniqueDocs'];
+ if(options['includeLocs'] != null) commandObject['includeLocs'] = options['includeLocs'];
+
+ // Execute the command
+ this.db.command(commandObject, callback);
+}
+
+/**
+ * Execute a geo search using a geo haystack index on a collection.
+ *
+ * Options
+ * - **maxDistance** {Number}, include results up to maxDistance from the point.
+ * - **search** {Object}, filter the results by a query.
+ * - **limit** {Number}, max number of results to return.
+ *
+ * @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order.
+ * @param {Number} y point to search on the y axis, ensure the indexes are ordered in the same order.
+ * @param {Objects} [options] options for the map reduce job.
+ * @param {Function} callback returns matching documents.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.geoHaystackSearch = function geoHaystackSearch(x, y, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ // Fetch all commands
+ options = args.length ? args.shift() : {};
+
+ // Build command object
+ var commandObject = {
+ geoSearch:this.collectionName,
+ near: [x, y]
+ }
+
+ // Decorate object if any with known properties
+ if(options['maxDistance'] != null) commandObject['maxDistance'] = options['maxDistance'];
+ if(options['query'] != null) commandObject['search'] = options['query'];
+ if(options['search'] != null) commandObject['search'] = options['search'];
+ if(options['limit'] != null) commandObject['limit'] = options['limit'];
+
+ // Execute the command
+ this.db.command(commandObject, callback);
+}
+
+/**
+ * Retrieve all the indexes on the collection.
+ *
+ * @param {Function} callback returns index information.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.indexes = function indexes(callback) {
+ // Return all the index information
+ this.db.indexInformation(this.collectionName, {full:true}, callback);
+}
+
+/**
+ * Execute an aggregation framework pipeline against the collection, needs MongoDB >= 2.1
+ *
+ * @param {Array|Objects} pipline a pipleline containing all the object for the execution.
+ * @param {Function} callback returns matching documents.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.aggregate = function(pipeline, callback) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ var self = this;
+
+ // Check if we have more than one argument then just make the pipeline
+ // the remaining arguments
+ if(args.length > 1) {
+ pipeline = args;
+ }
+
+ // Build the command
+ var command = { aggregate : this.collectionName, pipeline : pipeline};
+ // Execute the command
+ this.db.command(command, function(err, result) {
+ if(err) {
+ callback(err);
+ } else if(result['err'] || result['errmsg']) {
+ callback(self.db.wrap(result));
+ } else {
+ callback(null, result.result);
+ }
+ });
+}
+
+/**
+ * Get all the collection statistics.
+ *
+ * Options
+ * - **scale** {Number}, divide the returned sizes by scale value.
+ *
+ * @param {Objects} [options] options for the map reduce job.
+ * @param {Function} callback returns statistical information for the collection.
+ * @return {null}
+ * @api public
+ */
+Collection.prototype.stats = function stats(options, callback) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ // Fetch all commands
+ options = args.length ? args.shift() : {};
+
+ // Build command object
+ var commandObject = {
+ collStats:this.collectionName,
+ }
+
+ // Check if we have the scale value
+ if(options['scale'] != null) commandObject['scale'] = options['scale'];
+
+ // Execute the command
+ this.db.command(commandObject, callback);
+}
+
+/**
+ * Expose.
+ */
+exports.Collection = Collection;
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/node_modules/mongodb/lib/mongodb/commands/base_command.js b/node_modules/mongodb/lib/mongodb/commands/base_command.js
new file mode 100644
index 0000000..6e531d3
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/commands/base_command.js
@@ -0,0 +1,27 @@
+/**
+ Base object used for common functionality
+**/
+var BaseCommand = exports.BaseCommand = function() {
+};
+
+var id = 1;
+BaseCommand.prototype.getRequestId = function() {
+ if (!this.requestId) this.requestId = id++;
+ return this.requestId;
+};
+
+BaseCommand.prototype.updateRequestId = function() {
+ this.requestId = id++;
+ return this.requestId;
+};
+
+// OpCodes
+BaseCommand.OP_REPLY = 1;
+BaseCommand.OP_MSG = 1000;
+BaseCommand.OP_UPDATE = 2001;
+BaseCommand.OP_INSERT = 2002;
+BaseCommand.OP_GET_BY_OID = 2003;
+BaseCommand.OP_QUERY = 2004;
+BaseCommand.OP_GET_MORE = 2005;
+BaseCommand.OP_DELETE = 2006;
+BaseCommand.OP_KILL_CURSORS = 2007; \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/commands/db_command.js b/node_modules/mongodb/lib/mongodb/commands/db_command.js
new file mode 100644
index 0000000..7586886
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/commands/db_command.js
@@ -0,0 +1,205 @@
+var QueryCommand = require('./query_command').QueryCommand,
+ InsertCommand = require('./insert_command').InsertCommand,
+ inherits = require('util').inherits,
+ crypto = require('crypto');
+
+/**
+ Db Command
+**/
+var DbCommand = exports.DbCommand = function(dbInstance, collectionName, queryOptions, numberToSkip, numberToReturn, query, returnFieldSelector, options) {
+ QueryCommand.call(this);
+ this.collectionName = collectionName;
+ this.queryOptions = queryOptions;
+ this.numberToSkip = numberToSkip;
+ this.numberToReturn = numberToReturn;
+ this.query = query;
+ this.returnFieldSelector = returnFieldSelector;
+ this.db = dbInstance;
+
+ // Make sure we don't get a null exception
+ options = options == null ? {} : options;
+ // Let us defined on a command basis if we want functions to be serialized or not
+ if(options['serializeFunctions'] != null && options['serializeFunctions']) {
+ this.serializeFunctions = true;
+ }
+};
+
+inherits(DbCommand, QueryCommand);
+
+// Constants
+DbCommand.SYSTEM_NAMESPACE_COLLECTION = "system.namespaces";
+DbCommand.SYSTEM_INDEX_COLLECTION = "system.indexes";
+DbCommand.SYSTEM_PROFILE_COLLECTION = "system.profile";
+DbCommand.SYSTEM_USER_COLLECTION = "system.users";
+DbCommand.SYSTEM_COMMAND_COLLECTION = "$cmd";
+
+// New commands
+DbCommand.NcreateIsMasterCommand = function(db, databaseName) {
+ return new DbCommand(db, databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'ismaster':1}, null);
+};
+
+// Provide constructors for different db commands
+DbCommand.createIsMasterCommand = function(db) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'ismaster':1}, null);
+};
+
+DbCommand.createCollectionInfoCommand = function(db, selector) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_NAMESPACE_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, 0, selector, null);
+};
+
+DbCommand.createGetNonceCommand = function(db) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'getnonce':1}, null);
+};
+
+DbCommand.createAuthenticationCommand = function(db, username, password, nonce) {
+ // Use node md5 generator
+ var md5 = crypto.createHash('md5');
+ // Generate keys used for authentication
+ md5.update(username + ":mongo:" + password);
+ var hash_password = md5.digest('hex');
+ // Final key
+ md5 = crypto.createHash('md5');
+ md5.update(nonce + username + hash_password);
+ var key = md5.digest('hex');
+ // Creat selector
+ var selector = {'authenticate':1, 'user':username, 'nonce':nonce, 'key':key};
+ // Create db command
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NONE, 0, -1, selector, null);
+};
+
+DbCommand.createLogoutCommand = function(db) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'logout':1}, null);
+};
+
+DbCommand.createCreateCollectionCommand = function(db, collectionName, options) {
+ var selector = {'create':collectionName};
+ // Modify the options to ensure correct behaviour
+ for(var name in options) {
+ if(options[name] != null && options[name].constructor != Function) selector[name] = options[name];
+ }
+ // Execute the command
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, selector, null);
+};
+
+DbCommand.createDropCollectionCommand = function(db, collectionName) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'drop':collectionName}, null);
+};
+
+DbCommand.createRenameCollectionCommand = function(db, fromCollectionName, toCollectionName) {
+ var renameCollection = db.databaseName + "." + fromCollectionName;
+ var toCollection = db.databaseName + "." + toCollectionName;
+ return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'renameCollection':renameCollection, 'to':toCollection}, null);
+};
+
+DbCommand.createGetLastErrorCommand = function(options, db) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ db = args.pop();
+ options = args.length ? args.shift() : {};
+ // Final command
+ var command = {'getlasterror':1};
+ // If we have an options Object let's merge in the fields (fsync/wtimeout/w)
+ if('object' === typeof options) {
+ for(var name in options) {
+ command[name] = options[name]
+ }
+ }
+
+ // Execute command
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command, null);
+};
+
+DbCommand.createGetLastStatusCommand = DbCommand.createGetLastErrorCommand;
+
+DbCommand.createGetPreviousErrorsCommand = function(db) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'getpreverror':1}, null);
+};
+
+DbCommand.createResetErrorHistoryCommand = function(db) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'reseterror':1}, null);
+};
+
+DbCommand.createCreateIndexCommand = function(db, collectionName, fieldOrSpec, options) {
+ var fieldHash = {};
+ var indexes = [];
+ var keys;
+
+ // Get all the fields accordingly
+ if (fieldOrSpec.constructor === String) { // 'type'
+ indexes.push(fieldOrSpec + '_' + 1);
+ fieldHash[fieldOrSpec] = 1;
+ } else if (fieldOrSpec.constructor === Array) { // [{location:'2d'}, ...]
+ fieldOrSpec.forEach(function(f) {
+ if (f.constructor === String) { // [{location:'2d'}, 'type']
+ indexes.push(f + '_' + 1);
+ fieldHash[f] = 1;
+ } else if (f.constructor === Array) { // [['location', '2d'],['type', 1]]
+ indexes.push(f[0] + '_' + (f[1] || 1));
+ fieldHash[f[0]] = f[1] || 1;
+ } else if (f.constructor === Object) { // [{location:'2d'}, {type:1}]
+ keys = Object.keys(f);
+ keys.forEach(function(k) {
+ indexes.push(k + '_' + f[k]);
+ fieldHash[k] = f[k];
+ });
+ } else {
+ // undefined
+ }
+ });
+ } else if (fieldOrSpec.constructor === Object) { // {location:'2d', type:1}
+ keys = Object.keys(fieldOrSpec);
+ keys.forEach(function(key) {
+ indexes.push(key + '_' + fieldOrSpec[key]);
+ fieldHash[key] = fieldOrSpec[key];
+ });
+ }
+
+ // Generate the index name
+ var indexName = indexes.join("_");
+ // Build the selector
+ var selector = {'ns':(db.databaseName + "." + collectionName), 'key':fieldHash, 'name':indexName};
+
+ // Ensure we have a correct finalUnique
+ var finalUnique = options == null || 'object' === typeof options ? false : options;
+ // Set up options
+ options = options == null || typeof options == 'boolean' ? {} : options;
+
+ // Add all the options
+ var keys = Object.keys(options);
+ // Add all the fields to the selector
+ for(var i = 0; i < keys.length; i++) {
+ selector[keys[i]] = options[keys[i]];
+ }
+
+ // If we don't have the unique property set on the selector
+ if(selector['unique'] == null) selector['unique'] = finalUnique;
+ // Create the insert command for the index and return the document
+ return new InsertCommand(db, db.databaseName + "." + DbCommand.SYSTEM_INDEX_COLLECTION, false).add(selector);
+};
+
+DbCommand.logoutCommand = function(db, command_hash) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null);
+}
+
+DbCommand.createDropIndexCommand = function(db, collectionName, indexName) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'deleteIndexes':collectionName, 'index':indexName}, null);
+};
+
+DbCommand.createReIndexCommand = function(db, collectionName) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'reIndex':collectionName}, null);
+};
+
+DbCommand.createDropDatabaseCommand = function(db) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'dropDatabase':1}, null);
+};
+
+DbCommand.createDbCommand = function(db, command_hash, options) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null, options);
+};
+
+DbCommand.createAdminDbCommand = function(db, command_hash) {
+ return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null);
+};
+
+DbCommand.createDbSlaveOkCommand = function(db, command_hash, options) {
+ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT | QueryCommand.OPTS_SLAVE, 0, -1, command_hash, null, options);
+}; \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/commands/delete_command.js b/node_modules/mongodb/lib/mongodb/commands/delete_command.js
new file mode 100644
index 0000000..1adad07
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/commands/delete_command.js
@@ -0,0 +1,111 @@
+var BaseCommand = require('./base_command').BaseCommand,
+ inherits = require('util').inherits;
+
+/**
+ Insert Document Command
+**/
+var DeleteCommand = exports.DeleteCommand = function(db, collectionName, selector) {
+ BaseCommand.call(this);
+
+ // Validate correctness off the selector
+ var object = selector;
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) {
+ var error = new Error("delete raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ error.name = 'MongoError';
+ throw error;
+ }
+ }
+
+ this.collectionName = collectionName;
+ this.selector = selector;
+ this.db = db;
+};
+
+inherits(DeleteCommand, BaseCommand);
+
+DeleteCommand.OP_DELETE = 2006;
+
+/*
+struct {
+ MsgHeader header; // standard message header
+ int32 ZERO; // 0 - reserved for future use
+ cstring fullCollectionName; // "dbname.collectionname"
+ int32 ZERO; // 0 - reserved for future use
+ mongo.BSON selector; // query object. See below for details.
+}
+*/
+DeleteCommand.prototype.toBinary = function() {
+ // Calculate total length of the document
+ var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + this.db.bson.calculateObjectSize(this.selector, false, true) + (4 * 4);
+ // Let's build the single pass buffer command
+ var _index = 0;
+ var _command = new Buffer(totalLengthOfCommand);
+ // Write the header information to the buffer
+ _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
+ _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
+ _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
+ _command[_index] = totalLengthOfCommand & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write the request ID
+ _command[_index + 3] = (this.requestId >> 24) & 0xff;
+ _command[_index + 2] = (this.requestId >> 16) & 0xff;
+ _command[_index + 1] = (this.requestId >> 8) & 0xff;
+ _command[_index] = this.requestId & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ // Write the op_code for the command
+ _command[_index + 3] = (DeleteCommand.OP_DELETE >> 24) & 0xff;
+ _command[_index + 2] = (DeleteCommand.OP_DELETE >> 16) & 0xff;
+ _command[_index + 1] = (DeleteCommand.OP_DELETE >> 8) & 0xff;
+ _command[_index] = DeleteCommand.OP_DELETE & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+
+ // Write the collection name to the command
+ _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
+ _command[_index - 1] = 0;
+
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+
+ // Document binary length
+ var documentLength = 0
+
+ // Serialize the selector
+ // If we are passing a raw buffer, do minimal validation
+ if(Buffer.isBuffer(this.selector)) {
+ documentLength = this.selector.length;
+ // Copy the data into the current buffer
+ this.selector.copy(_command, _index);
+ } else {
+ documentLength = this.db.bson.serializeWithBufferAndIndex(this.selector, this.checkKeys, _command, _index) - _index + 1;
+ }
+
+ // Write the length to the document
+ _command[_index + 3] = (documentLength >> 24) & 0xff;
+ _command[_index + 2] = (documentLength >> 16) & 0xff;
+ _command[_index + 1] = (documentLength >> 8) & 0xff;
+ _command[_index] = documentLength & 0xff;
+ // Update index in buffer
+ _index = _index + documentLength;
+ // Add terminating 0 for the object
+ _command[_index - 1] = 0;
+ return _command;
+}; \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/commands/get_more_command.js b/node_modules/mongodb/lib/mongodb/commands/get_more_command.js
new file mode 100644
index 0000000..d3aac02
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/commands/get_more_command.js
@@ -0,0 +1,83 @@
+var BaseCommand = require('./base_command').BaseCommand,
+ inherits = require('util').inherits,
+ binaryutils = require('../utils');
+
+/**
+ Get More Document Command
+**/
+var GetMoreCommand = exports.GetMoreCommand = function(db, collectionName, numberToReturn, cursorId) {
+ BaseCommand.call(this);
+
+ this.collectionName = collectionName;
+ this.numberToReturn = numberToReturn;
+ this.cursorId = cursorId;
+ this.db = db;
+};
+
+inherits(GetMoreCommand, BaseCommand);
+
+GetMoreCommand.OP_GET_MORE = 2005;
+
+GetMoreCommand.prototype.toBinary = function() {
+ // Calculate total length of the document
+ var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 8 + (4 * 4);
+ // Let's build the single pass buffer command
+ var _index = 0;
+ var _command = new Buffer(totalLengthOfCommand);
+ // Write the header information to the buffer
+ _command[_index++] = totalLengthOfCommand & 0xff;
+ _command[_index++] = (totalLengthOfCommand >> 8) & 0xff;
+ _command[_index++] = (totalLengthOfCommand >> 16) & 0xff;
+ _command[_index++] = (totalLengthOfCommand >> 24) & 0xff;
+
+ // Write the request ID
+ _command[_index++] = this.requestId & 0xff;
+ _command[_index++] = (this.requestId >> 8) & 0xff;
+ _command[_index++] = (this.requestId >> 16) & 0xff;
+ _command[_index++] = (this.requestId >> 24) & 0xff;
+
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+
+ // Write the op_code for the command
+ _command[_index++] = GetMoreCommand.OP_GET_MORE & 0xff;
+ _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 8) & 0xff;
+ _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 16) & 0xff;
+ _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 24) & 0xff;
+
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+
+ // Write the collection name to the command
+ _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
+ _command[_index - 1] = 0;
+
+ // Number of documents to return
+ _command[_index++] = this.numberToReturn & 0xff;
+ _command[_index++] = (this.numberToReturn >> 8) & 0xff;
+ _command[_index++] = (this.numberToReturn >> 16) & 0xff;
+ _command[_index++] = (this.numberToReturn >> 24) & 0xff;
+
+ // Encode the cursor id
+ var low_bits = this.cursorId.getLowBits();
+ // Encode low bits
+ _command[_index++] = low_bits & 0xff;
+ _command[_index++] = (low_bits >> 8) & 0xff;
+ _command[_index++] = (low_bits >> 16) & 0xff;
+ _command[_index++] = (low_bits >> 24) & 0xff;
+
+ var high_bits = this.cursorId.getHighBits();
+ // Encode high bits
+ _command[_index++] = high_bits & 0xff;
+ _command[_index++] = (high_bits >> 8) & 0xff;
+ _command[_index++] = (high_bits >> 16) & 0xff;
+ _command[_index++] = (high_bits >> 24) & 0xff;
+ // Return command
+ return _command;
+}; \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/commands/insert_command.js b/node_modules/mongodb/lib/mongodb/commands/insert_command.js
new file mode 100644
index 0000000..3036a02
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/commands/insert_command.js
@@ -0,0 +1,141 @@
+var BaseCommand = require('./base_command').BaseCommand,
+ inherits = require('util').inherits;
+
+/**
+ Insert Document Command
+**/
+var InsertCommand = exports.InsertCommand = function(db, collectionName, checkKeys, options) {
+ BaseCommand.call(this);
+
+ this.collectionName = collectionName;
+ this.documents = [];
+ this.checkKeys = checkKeys == null ? true : checkKeys;
+ this.db = db;
+ this.flags = 0;
+ this.serializeFunctions = false;
+
+ // Ensure valid options hash
+ options = options == null ? {} : options;
+
+ // Check if we have keepGoing set -> set flag if it's the case
+ if(options['keepGoing'] != null && options['keepGoing']) {
+ // This will finish inserting all non-index violating documents even if it returns an error
+ this.flags = 1;
+ }
+
+ // Let us defined on a command basis if we want functions to be serialized or not
+ if(options['serializeFunctions'] != null && options['serializeFunctions']) {
+ this.serializeFunctions = true;
+ }
+};
+
+inherits(InsertCommand, BaseCommand);
+
+// OpCodes
+InsertCommand.OP_INSERT = 2002;
+
+InsertCommand.prototype.add = function(document) {
+ if(Buffer.isBuffer(document)) {
+ var object_size = document[0] | document[1] << 8 | document[2] << 16 | document[3] << 24;
+ if(object_size != document.length) {
+ var error = new Error("insert raw message size does not match message header size [" + document.length + "] != [" + object_size + "]");
+ error.name = 'MongoError';
+ throw error;
+ }
+ }
+
+ this.documents.push(document);
+ return this;
+};
+
+/*
+struct {
+ MsgHeader header; // standard message header
+ int32 ZERO; // 0 - reserved for future use
+ cstring fullCollectionName; // "dbname.collectionname"
+ BSON[] documents; // one or more documents to insert into the collection
+}
+*/
+InsertCommand.prototype.toBinary = function() {
+ // Calculate total length of the document
+ var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + (4 * 4);
+ // var docLength = 0
+ for(var i = 0; i < this.documents.length; i++) {
+ if(Buffer.isBuffer(this.documents[i])) {
+ totalLengthOfCommand += this.documents[i].length;
+ } else {
+ // Calculate size of document
+ totalLengthOfCommand += this.db.bson.calculateObjectSize(this.documents[i], this.serializeFunctions, true);
+ }
+ }
+
+ // Let's build the single pass buffer command
+ var _index = 0;
+ var _command = new Buffer(totalLengthOfCommand);
+ // Write the header information to the buffer
+ _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
+ _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
+ _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
+ _command[_index] = totalLengthOfCommand & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write the request ID
+ _command[_index + 3] = (this.requestId >> 24) & 0xff;
+ _command[_index + 2] = (this.requestId >> 16) & 0xff;
+ _command[_index + 1] = (this.requestId >> 8) & 0xff;
+ _command[_index] = this.requestId & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ // Write the op_code for the command
+ _command[_index + 3] = (InsertCommand.OP_INSERT >> 24) & 0xff;
+ _command[_index + 2] = (InsertCommand.OP_INSERT >> 16) & 0xff;
+ _command[_index + 1] = (InsertCommand.OP_INSERT >> 8) & 0xff;
+ _command[_index] = InsertCommand.OP_INSERT & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write flags if any
+ _command[_index + 3] = (this.flags >> 24) & 0xff;
+ _command[_index + 2] = (this.flags >> 16) & 0xff;
+ _command[_index + 1] = (this.flags >> 8) & 0xff;
+ _command[_index] = this.flags & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write the collection name to the command
+ _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
+ _command[_index - 1] = 0;
+
+ // Write all the bson documents to the buffer at the index offset
+ for(var i = 0; i < this.documents.length; i++) {
+ // Document binary length
+ var documentLength = 0
+ var object = this.documents[i];
+
+ // Serialize the selector
+ // If we are passing a raw buffer, do minimal validation
+ if(Buffer.isBuffer(object)) {
+ documentLength = object.length;
+ // Copy the data into the current buffer
+ object.copy(_command, _index);
+ } else {
+ // Serialize the document straight to the buffer
+ documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1;
+ }
+
+ // Write the length to the document
+ _command[_index + 3] = (documentLength >> 24) & 0xff;
+ _command[_index + 2] = (documentLength >> 16) & 0xff;
+ _command[_index + 1] = (documentLength >> 8) & 0xff;
+ _command[_index] = documentLength & 0xff;
+ // Update index in buffer
+ _index = _index + documentLength;
+ // Add terminating 0 for the object
+ _command[_index - 1] = 0;
+ }
+
+ return _command;
+};
diff --git a/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js b/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js
new file mode 100644
index 0000000..d8ccb0c
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js
@@ -0,0 +1,98 @@
+var BaseCommand = require('./base_command').BaseCommand,
+ inherits = require('util').inherits,
+ binaryutils = require('../utils');
+
+/**
+ Insert Document Command
+**/
+var KillCursorCommand = exports.KillCursorCommand = function(db, cursorIds) {
+ BaseCommand.call(this);
+
+ this.cursorIds = cursorIds;
+ this.db = db;
+};
+
+inherits(KillCursorCommand, BaseCommand);
+
+KillCursorCommand.OP_KILL_CURSORS = 2007;
+
+/*
+struct {
+ MsgHeader header; // standard message header
+ int32 ZERO; // 0 - reserved for future use
+ int32 numberOfCursorIDs; // number of cursorIDs in message
+ int64[] cursorIDs; // array of cursorIDs to close
+}
+*/
+KillCursorCommand.prototype.toBinary = function() {
+ // Calculate total length of the document
+ var totalLengthOfCommand = 4 + 4 + (4 * 4) + (this.cursorIds.length * 8);
+ // Let's build the single pass buffer command
+ var _index = 0;
+ var _command = new Buffer(totalLengthOfCommand);
+ // Write the header information to the buffer
+ _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
+ _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
+ _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
+ _command[_index] = totalLengthOfCommand & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write the request ID
+ _command[_index + 3] = (this.requestId >> 24) & 0xff;
+ _command[_index + 2] = (this.requestId >> 16) & 0xff;
+ _command[_index + 1] = (this.requestId >> 8) & 0xff;
+ _command[_index] = this.requestId & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ // Write the op_code for the command
+ _command[_index + 3] = (KillCursorCommand.OP_KILL_CURSORS >> 24) & 0xff;
+ _command[_index + 2] = (KillCursorCommand.OP_KILL_CURSORS >> 16) & 0xff;
+ _command[_index + 1] = (KillCursorCommand.OP_KILL_CURSORS >> 8) & 0xff;
+ _command[_index] = KillCursorCommand.OP_KILL_CURSORS & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+
+ // Number of cursors to kill
+ var numberOfCursors = this.cursorIds.length;
+ _command[_index + 3] = (numberOfCursors >> 24) & 0xff;
+ _command[_index + 2] = (numberOfCursors >> 16) & 0xff;
+ _command[_index + 1] = (numberOfCursors >> 8) & 0xff;
+ _command[_index] = numberOfCursors & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Encode all the cursors
+ for(var i = 0; i < this.cursorIds.length; i++) {
+ // Encode the cursor id
+ var low_bits = this.cursorIds[i].getLowBits();
+ // Encode low bits
+ _command[_index + 3] = (low_bits >> 24) & 0xff;
+ _command[_index + 2] = (low_bits >> 16) & 0xff;
+ _command[_index + 1] = (low_bits >> 8) & 0xff;
+ _command[_index] = low_bits & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ var high_bits = this.cursorIds[i].getHighBits();
+ // Encode high bits
+ _command[_index + 3] = (high_bits >> 24) & 0xff;
+ _command[_index + 2] = (high_bits >> 16) & 0xff;
+ _command[_index + 1] = (high_bits >> 8) & 0xff;
+ _command[_index] = high_bits & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ }
+
+ return _command;
+}; \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/commands/query_command.js b/node_modules/mongodb/lib/mongodb/commands/query_command.js
new file mode 100644
index 0000000..e07a8aa
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/commands/query_command.js
@@ -0,0 +1,209 @@
+var BaseCommand = require('./base_command').BaseCommand,
+ inherits = require('util').inherits;
+
+/**
+ Insert Document Command
+**/
+var QueryCommand = exports.QueryCommand = function(db, collectionName, queryOptions, numberToSkip, numberToReturn, query, returnFieldSelector, options) {
+ BaseCommand.call(this);
+
+ // Validate correctness off the selector
+ var object = query;
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) {
+ var error = new Error("query selector raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ error.name = 'MongoError';
+ throw error;
+ }
+ }
+
+ var object = returnFieldSelector;
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) {
+ var error = new Error("query fields raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ error.name = 'MongoError';
+ throw error;
+ }
+ }
+
+ // Make sure we don't get a null exception
+ options = options == null ? {} : options;
+ // Set up options
+ this.collectionName = collectionName;
+ this.queryOptions = queryOptions;
+ this.numberToSkip = numberToSkip;
+ this.numberToReturn = numberToReturn;
+ this.query = query;
+ this.returnFieldSelector = returnFieldSelector;
+ this.db = db;
+
+ // Let us defined on a command basis if we want functions to be serialized or not
+ if(options['serializeFunctions'] != null && options['serializeFunctions']) {
+ this.serializeFunctions = true;
+ }
+};
+
+inherits(QueryCommand, BaseCommand);
+
+QueryCommand.OP_QUERY = 2004;
+
+/*
+struct {
+ MsgHeader header; // standard message header
+ int32 opts; // query options. See below for details.
+ cstring fullCollectionName; // "dbname.collectionname"
+ int32 numberToSkip; // number of documents to skip when returning results
+ int32 numberToReturn; // number of documents to return in the first OP_REPLY
+ BSON query ; // query object. See below for details.
+ [ BSON returnFieldSelector; ] // OPTIONAL : selector indicating the fields to return. See below for details.
+}
+*/
+QueryCommand.prototype.toBinary = function() {
+ var totalLengthOfCommand = 0;
+ // Calculate total length of the document
+ if(Buffer.isBuffer(this.query)) {
+ totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 4 + this.query.length + (4 * 4);
+ } else {
+ totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 4 + this.db.bson.calculateObjectSize(this.query, this.serializeFunctions, true) + (4 * 4);
+ }
+
+ // Calculate extra fields size
+ if(this.returnFieldSelector != null && !(Buffer.isBuffer(this.returnFieldSelector))) {
+ if(Object.keys(this.returnFieldSelector).length > 0) {
+ totalLengthOfCommand += this.db.bson.calculateObjectSize(this.returnFieldSelector, this.serializeFunctions, true);
+ }
+ } else if(Buffer.isBuffer(this.returnFieldSelector)) {
+ totalLengthOfCommand += this.returnFieldSelector.length;
+ }
+
+ // Let's build the single pass buffer command
+ var _index = 0;
+ var _command = new Buffer(totalLengthOfCommand);
+ // Write the header information to the buffer
+ _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
+ _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
+ _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
+ _command[_index] = totalLengthOfCommand & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write the request ID
+ _command[_index + 3] = (this.requestId >> 24) & 0xff;
+ _command[_index + 2] = (this.requestId >> 16) & 0xff;
+ _command[_index + 1] = (this.requestId >> 8) & 0xff;
+ _command[_index] = this.requestId & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ // Write the op_code for the command
+ _command[_index + 3] = (QueryCommand.OP_QUERY >> 24) & 0xff;
+ _command[_index + 2] = (QueryCommand.OP_QUERY >> 16) & 0xff;
+ _command[_index + 1] = (QueryCommand.OP_QUERY >> 8) & 0xff;
+ _command[_index] = QueryCommand.OP_QUERY & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Write the query options
+ _command[_index + 3] = (this.queryOptions >> 24) & 0xff;
+ _command[_index + 2] = (this.queryOptions >> 16) & 0xff;
+ _command[_index + 1] = (this.queryOptions >> 8) & 0xff;
+ _command[_index] = this.queryOptions & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Write the collection name to the command
+ _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
+ _command[_index - 1] = 0;
+
+ // Write the number of documents to skip
+ _command[_index + 3] = (this.numberToSkip >> 24) & 0xff;
+ _command[_index + 2] = (this.numberToSkip >> 16) & 0xff;
+ _command[_index + 1] = (this.numberToSkip >> 8) & 0xff;
+ _command[_index] = this.numberToSkip & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Write the number of documents to return
+ _command[_index + 3] = (this.numberToReturn >> 24) & 0xff;
+ _command[_index + 2] = (this.numberToReturn >> 16) & 0xff;
+ _command[_index + 1] = (this.numberToReturn >> 8) & 0xff;
+ _command[_index] = this.numberToReturn & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Document binary length
+ var documentLength = 0
+ var object = this.query;
+
+ // Serialize the selector
+ if(Buffer.isBuffer(object)) {
+ documentLength = object.length;
+ // Copy the data into the current buffer
+ object.copy(_command, _index);
+ } else {
+ // Serialize the document straight to the buffer
+ documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1;
+ }
+
+ // Write the length to the document
+ _command[_index + 3] = (documentLength >> 24) & 0xff;
+ _command[_index + 2] = (documentLength >> 16) & 0xff;
+ _command[_index + 1] = (documentLength >> 8) & 0xff;
+ _command[_index] = documentLength & 0xff;
+ // Update index in buffer
+ _index = _index + documentLength;
+ // Add terminating 0 for the object
+ _command[_index - 1] = 0;
+
+ // Push field selector if available
+ if(this.returnFieldSelector != null && !(Buffer.isBuffer(this.returnFieldSelector))) {
+ if(Object.keys(this.returnFieldSelector).length > 0) {
+ var documentLength = this.db.bson.serializeWithBufferAndIndex(this.returnFieldSelector, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1;
+ // Write the length to the document
+ _command[_index + 3] = (documentLength >> 24) & 0xff;
+ _command[_index + 2] = (documentLength >> 16) & 0xff;
+ _command[_index + 1] = (documentLength >> 8) & 0xff;
+ _command[_index] = documentLength & 0xff;
+ // Update index in buffer
+ _index = _index + documentLength;
+ // Add terminating 0 for the object
+ _command[_index - 1] = 0;
+ }
+ } if(this.returnFieldSelector != null && Buffer.isBuffer(this.returnFieldSelector)) {
+ // Document binary length
+ var documentLength = 0
+ var object = this.returnFieldSelector;
+
+ // Serialize the selector
+ documentLength = object.length;
+ // Copy the data into the current buffer
+ object.copy(_command, _index);
+
+ // Write the length to the document
+ _command[_index + 3] = (documentLength >> 24) & 0xff;
+ _command[_index + 2] = (documentLength >> 16) & 0xff;
+ _command[_index + 1] = (documentLength >> 8) & 0xff;
+ _command[_index] = documentLength & 0xff;
+ // Update index in buffer
+ _index = _index + documentLength;
+ // Add terminating 0 for the object
+ _command[_index - 1] = 0;
+ }
+
+ // Return finished command
+ return _command;
+};
+
+// Constants
+QueryCommand.OPTS_NONE = 0;
+QueryCommand.OPTS_TAILABLE_CURSOR = 2;
+QueryCommand.OPTS_SLAVE = 4;
+QueryCommand.OPTS_OPLOG_REPLY = 8;
+QueryCommand.OPTS_NO_CURSOR_TIMEOUT = 16;
+QueryCommand.OPTS_AWAIT_DATA = 32;
+QueryCommand.OPTS_EXHAUST = 64; \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/commands/update_command.js b/node_modules/mongodb/lib/mongodb/commands/update_command.js
new file mode 100644
index 0000000..9829dea
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/commands/update_command.js
@@ -0,0 +1,174 @@
+var BaseCommand = require('./base_command').BaseCommand,
+ inherits = require('util').inherits;
+
+/**
+ Update Document Command
+**/
+var UpdateCommand = exports.UpdateCommand = function(db, collectionName, spec, document, options) {
+ BaseCommand.call(this);
+
+ var object = spec;
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) {
+ var error = new Error("update spec raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ error.name = 'MongoError';
+ throw error;
+ }
+ }
+
+ var object = document;
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) {
+ var error = new Error("update document raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ error.name = 'MongoError';
+ throw error;
+ }
+ }
+
+ this.collectionName = collectionName;
+ this.spec = spec;
+ this.document = document;
+ this.db = db;
+ this.serializeFunctions = false;
+
+ // Generate correct flags
+ var db_upsert = 0;
+ var db_multi_update = 0;
+ db_upsert = options != null && options['upsert'] != null ? (options['upsert'] == true ? 1 : 0) : db_upsert;
+ db_multi_update = options != null && options['multi'] != null ? (options['multi'] == true ? 1 : 0) : db_multi_update;
+
+ // Flags
+ this.flags = parseInt(db_multi_update.toString() + db_upsert.toString(), 2);
+ // Let us defined on a command basis if we want functions to be serialized or not
+ if(options['serializeFunctions'] != null && options['serializeFunctions']) {
+ this.serializeFunctions = true;
+ }
+};
+
+inherits(UpdateCommand, BaseCommand);
+
+UpdateCommand.OP_UPDATE = 2001;
+
+/*
+struct {
+ MsgHeader header; // standard message header
+ int32 ZERO; // 0 - reserved for future use
+ cstring fullCollectionName; // "dbname.collectionname"
+ int32 flags; // bit vector. see below
+ BSON spec; // the query to select the document
+ BSON document; // the document data to update with or insert
+}
+*/
+UpdateCommand.prototype.toBinary = function() {
+ // Calculate total length of the document
+ var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + this.db.bson.calculateObjectSize(this.spec, false, true) +
+ this.db.bson.calculateObjectSize(this.document, this.serializeFunctions, true) + (4 * 4);
+
+ // Let's build the single pass buffer command
+ var _index = 0;
+ var _command = new Buffer(totalLengthOfCommand);
+ // Write the header information to the buffer
+ _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
+ _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
+ _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
+ _command[_index] = totalLengthOfCommand & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write the request ID
+ _command[_index + 3] = (this.requestId >> 24) & 0xff;
+ _command[_index + 2] = (this.requestId >> 16) & 0xff;
+ _command[_index + 1] = (this.requestId >> 8) & 0xff;
+ _command[_index] = this.requestId & 0xff;
+ // Adjust index
+ _index = _index + 4;
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ // Write the op_code for the command
+ _command[_index + 3] = (UpdateCommand.OP_UPDATE >> 24) & 0xff;
+ _command[_index + 2] = (UpdateCommand.OP_UPDATE >> 16) & 0xff;
+ _command[_index + 1] = (UpdateCommand.OP_UPDATE >> 8) & 0xff;
+ _command[_index] = UpdateCommand.OP_UPDATE & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Write zero
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+ _command[_index++] = 0;
+
+ // Write the collection name to the command
+ _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
+ _command[_index - 1] = 0;
+
+ // Write the update flags
+ _command[_index + 3] = (this.flags >> 24) & 0xff;
+ _command[_index + 2] = (this.flags >> 16) & 0xff;
+ _command[_index + 1] = (this.flags >> 8) & 0xff;
+ _command[_index] = this.flags & 0xff;
+ // Adjust index
+ _index = _index + 4;
+
+ // Document binary length
+ var documentLength = 0
+ var object = this.spec;
+
+ // Serialize the selector
+ // If we are passing a raw buffer, do minimal validation
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) throw new Error("raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ documentLength = object.length;
+ // Copy the data into the current buffer
+ object.copy(_command, _index);
+ } else {
+ documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, false) - _index + 1;
+ }
+
+ // Write the length to the document
+ _command[_index + 3] = (documentLength >> 24) & 0xff;
+ _command[_index + 2] = (documentLength >> 16) & 0xff;
+ _command[_index + 1] = (documentLength >> 8) & 0xff;
+ _command[_index] = documentLength & 0xff;
+ // Update index in buffer
+ _index = _index + documentLength;
+ // Add terminating 0 for the object
+ _command[_index - 1] = 0;
+
+ // Document binary length
+ var documentLength = 0
+ var object = this.document;
+
+ // Serialize the document
+ // If we are passing a raw buffer, do minimal validation
+ if(Buffer.isBuffer(object)) {
+ var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24;
+ if(object_size != object.length) throw new Error("raw message size does not match message header size [" + object.length + "] != [" + object_size + "]");
+ documentLength = object.length;
+ // Copy the data into the current buffer
+ object.copy(_command, _index);
+ } else {
+ documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1;
+ }
+
+ // Write the length to the document
+ _command[_index + 3] = (documentLength >> 24) & 0xff;
+ _command[_index + 2] = (documentLength >> 16) & 0xff;
+ _command[_index + 1] = (documentLength >> 8) & 0xff;
+ _command[_index] = documentLength & 0xff;
+ // Update index in buffer
+ _index = _index + documentLength;
+ // Add terminating 0 for the object
+ _command[_index - 1] = 0;
+
+ return _command;
+};
+
+// Constants
+UpdateCommand.DB_UPSERT = 0;
+UpdateCommand.DB_MULTI_UPDATE = 1; \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/connection/connection.js b/node_modules/mongodb/lib/mongodb/connection/connection.js
new file mode 100644
index 0000000..86818bb
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/connection/connection.js
@@ -0,0 +1,414 @@
+var utils = require('./connection_utils'),
+ inherits = require('util').inherits,
+ net = require('net'),
+ EventEmitter = require('events').EventEmitter,
+ inherits = require('util').inherits,
+ binaryutils = require('../utils'),
+ tls = require('tls');
+
+var Connection = exports.Connection = function(id, socketOptions) {
+ // Set up event emitter
+ EventEmitter.call(this);
+ // Store all socket options
+ this.socketOptions = socketOptions ? socketOptions : {host:'localhost', port:27017};
+ // Id for the connection
+ this.id = id;
+ // State of the connection
+ this.connected = false;
+
+ //
+ // Connection parsing state
+ //
+ this.maxBsonSize = socketOptions.maxBsonSize ? socketOptions.maxBsonSize : Connection.DEFAULT_MAX_BSON_SIZE;
+ // Contains the current message bytes
+ this.buffer = null;
+ // Contains the current message size
+ this.sizeOfMessage = 0;
+ // Contains the readIndex for the messaage
+ this.bytesRead = 0;
+ // Contains spill over bytes from additional messages
+ this.stubBuffer = 0;
+
+ // Just keeps list of events we allow
+ this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[], end:[]};
+
+ // Just keeps list of events we allow
+ resetHandlers(this, false);
+}
+
+// Set max bson size
+Connection.DEFAULT_MAX_BSON_SIZE = 4 * 1024 * 1024 * 4 * 3;
+
+// Inherit event emitter so we can emit stuff wohoo
+inherits(Connection, EventEmitter);
+
+Connection.prototype.start = function() {
+ // If we have a normal connection
+ if(this.socketOptions.ssl) {
+ // Create a new stream
+ this.connection = new net.Socket();
+ // Set options on the socket
+ this.connection.setTimeout(this.socketOptions.timeout);
+ // Work around for 0.4.X
+ if(process.version.indexOf("v0.4") == -1) this.connection.setNoDelay(this.socketOptions.noDelay);
+ // Set keep alive if defined
+ if(process.version.indexOf("v0.4") == -1) {
+ if(this.socketOptions.keepAlive > 0) {
+ this.connection.setKeepAlive(true, this.socketOptions.keepAlive);
+ } else {
+ this.connection.setKeepAlive(false);
+ }
+ }
+
+ // Set up pair for tls with server, accept self-signed certificates as well
+ var pair = this.pair = tls.createSecurePair(false);
+ // Set up encrypted streams
+ this.pair.encrypted.pipe(this.connection);
+ this.connection.pipe(this.pair.encrypted);
+
+ // Setup clearText stream
+ this.writeSteam = this.pair.cleartext;
+ // Add all handlers to the socket to manage it
+ this.pair.on("secure", connectHandler(this));
+ this.pair.cleartext.on("data", createDataHandler(this));
+ // Add handlers
+ this.connection.on("error", errorHandler(this));
+ // this.connection.on("connect", connectHandler(this));
+ this.connection.on("end", endHandler(this));
+ this.connection.on("timeout", timeoutHandler(this));
+ this.connection.on("drain", drainHandler(this));
+ this.writeSteam.on("close", closeHandler(this));
+ // Start socket
+ this.connection.connect(this.socketOptions.port, this.socketOptions.host);
+ } else {
+ // // Create a new stream
+ // this.connection = new net.Stream();
+ // // Create new connection instance
+ // this.connection = new net.Socket();
+ this.connection = net.createConnection(this.socketOptions.port, this.socketOptions.host);
+ // Set options on the socket
+ this.connection.setTimeout(this.socketOptions.timeout);
+ // Work around for 0.4.X
+ if(process.version.indexOf("v0.4") == -1) this.connection.setNoDelay(this.socketOptions.noDelay);
+ // Set keep alive if defined
+ if(process.version.indexOf("v0.4") == -1) {
+ if(this.socketOptions.keepAlive > 0) {
+ this.connection.setKeepAlive(true, this.socketOptions.keepAlive);
+ } else {
+ this.connection.setKeepAlive(false);
+ }
+ }
+
+ // Set up write stream
+ this.writeSteam = this.connection;
+ // Add handlers
+ this.connection.on("error", errorHandler(this));
+ // Add all handlers to the socket to manage it
+ this.connection.on("connect", connectHandler(this));
+ // this.connection.on("end", endHandler(this));
+ this.connection.on("data", createDataHandler(this));
+ this.connection.on("timeout", timeoutHandler(this));
+ this.connection.on("drain", drainHandler(this));
+ this.connection.on("close", closeHandler(this));
+ // // Start socket
+ // this.connection.connect(this.socketOptions.port, this.socketOptions.host);
+ }
+}
+
+// Check if the sockets are live
+Connection.prototype.isConnected = function() {
+ return this.connected && !this.connection.destroyed && this.connection.writable && this.connection.readable;
+}
+
+// Write the data out to the socket
+Connection.prototype.write = function(command, callback) {
+ try {
+ // If we have a list off commands to be executed on the same socket
+ if(Array.isArray(command)) {
+ for(var i = 0; i < command.length; i++) {
+ var binaryCommand = command[i].toBinary()
+ if(this.logger != null && this.logger.doDebug) this.logger.debug("writing command to mongodb", binaryCommand);
+ var r = this.writeSteam.write(binaryCommand);
+ }
+ } else {
+ var binaryCommand = command.toBinary()
+ if(this.logger != null && this.logger.doDebug) this.logger.debug("writing command to mongodb", binaryCommand);
+ var r = this.writeSteam.write(binaryCommand);
+ }
+ } catch (err) {
+ if(typeof callback === 'function') callback(err);
+ }
+}
+
+// Force the closure of the connection
+Connection.prototype.close = function() {
+ // clear out all the listeners
+ resetHandlers(this, true);
+ // Add a dummy error listener to catch any weird last moment errors (and ignore them)
+ this.connection.on("error", function() {})
+ // destroy connection
+ this.connection.destroy();
+}
+
+// Reset all handlers
+var resetHandlers = function(self, clearListeners) {
+ self.eventHandlers = {error:[], connect:[], close:[], end:[], timeout:[], parseError:[], message:[]};
+
+ // If we want to clear all the listeners
+ if(clearListeners && self.connection != null) {
+ var keys = Object.keys(self.eventHandlers);
+ // Remove all listeners
+ for(var i = 0; i < keys.length; i++) {
+ self.connection.removeAllListeners(keys[i]);
+ }
+ }
+}
+
+//
+// Handlers
+//
+
+// Connect handler
+var connectHandler = function(self) {
+ return function() {
+ // Set connected
+ self.connected = true;
+ // Emit the connect event with no error
+ self.emit("connect", null, self);
+ }
+}
+
+var createDataHandler = exports.Connection.createDataHandler = function(self) {
+ // We need to handle the parsing of the data
+ // and emit the messages when there is a complete one
+ return function(data) {
+ // Parse until we are done with the data
+ while(data.length > 0) {
+ // If we still have bytes to read on the current message
+ if(self.bytesRead > 0 && self.sizeOfMessage > 0) {
+ // Calculate the amount of remaining bytes
+ var remainingBytesToRead = self.sizeOfMessage - self.bytesRead;
+ // Check if the current chunk contains the rest of the message
+ if(remainingBytesToRead > data.length) {
+ // Copy the new data into the exiting buffer (should have been allocated when we know the message size)
+ data.copy(self.buffer, self.bytesRead);
+ // Adjust the number of bytes read so it point to the correct index in the buffer
+ self.bytesRead = self.bytesRead + data.length;
+
+ // Reset state of buffer
+ data = new Buffer(0);
+ } else {
+ // Copy the missing part of the data into our current buffer
+ data.copy(self.buffer, self.bytesRead, 0, remainingBytesToRead);
+ // Slice the overflow into a new buffer that we will then re-parse
+ data = data.slice(remainingBytesToRead);
+
+ // Emit current complete message
+ try {
+ self.emit("message", self.buffer, self);
+
+ } catch(err) {
+ var errorObject = {err:"socketHandler", trace:err, bin:buffer, parseState:{
+ sizeOfMessage:self.sizeOfMessage,
+ bytesRead:self.bytesRead,
+ stubBuffer:self.stubBuffer}};
+ if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject);
+ // We got a parse Error fire it off then keep going
+ self.emit("parseError", errorObject, self);
+ }
+
+ // Reset state of buffer
+ self.buffer = null;
+ self.sizeOfMessage = 0;
+ self.bytesRead = 0;
+ self.stubBuffer = null;
+ }
+ } else {
+ // Stub buffer is kept in case we don't get enough bytes to determine the
+ // size of the message (< 4 bytes)
+ if(self.stubBuffer != null && self.stubBuffer.length > 0) {
+
+ // If we have enough bytes to determine the message size let's do it
+ if(self.stubBuffer.length + data.length > 4) {
+ // Prepad the data
+ var newData = new Buffer(self.stubBuffer.length + data.length);
+ self.stubBuffer.copy(newData, 0);
+ data.copy(newData, self.stubBuffer.length);
+ // Reassign for parsing
+ data = newData;
+
+ // Reset state of buffer
+ self.buffer = null;
+ self.sizeOfMessage = 0;
+ self.bytesRead = 0;
+ self.stubBuffer = null;
+
+ } else {
+
+ // Add the the bytes to the stub buffer
+ var newStubBuffer = new Buffer(self.stubBuffer.length + data.length);
+ // Copy existing stub buffer
+ self.stubBuffer.copy(newStubBuffer, 0);
+ // Copy missing part of the data
+ data.copy(newStubBuffer, self.stubBuffer.length);
+ // Exit parsing loop
+ data = new Buffer(0);
+ }
+ } else {
+ if(data.length > 4) {
+ // Retrieve the message size
+ var sizeOfMessage = binaryutils.decodeUInt32(data, 0);
+ // If we have a negative sizeOfMessage emit error and return
+ if(sizeOfMessage < 0 || sizeOfMessage > self.maxBsonSize) {
+ var errorObject = {err:"socketHandler", trace:'', bin:self.buffer, parseState:{
+ sizeOfMessage:sizeOfMessage,
+ bytesRead:self.bytesRead,
+ stubBuffer:self.stubBuffer}};
+ if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject);
+ // We got a parse Error fire it off then keep going
+ self.emit("parseError", errorObject, self);
+ return;
+ }
+
+ // Ensure that the size of message is larger than 0 and less than the max allowed
+ if(sizeOfMessage > 4 && sizeOfMessage < self.maxBsonSize && sizeOfMessage > data.length) {
+ self.buffer = new Buffer(sizeOfMessage);
+ // Copy all the data into the buffer
+ data.copy(self.buffer, 0);
+ // Update bytes read
+ self.bytesRead = data.length;
+ // Update sizeOfMessage
+ self.sizeOfMessage = sizeOfMessage;
+ // Ensure stub buffer is null
+ self.stubBuffer = null;
+ // Exit parsing loop
+ data = new Buffer(0);
+
+ } else if(sizeOfMessage > 4 && sizeOfMessage < self.maxBsonSize && sizeOfMessage == data.length) {
+ try {
+ self.emit("message", data, self);
+ // Reset state of buffer
+ self.buffer = null;
+ self.sizeOfMessage = 0;
+ self.bytesRead = 0;
+ self.stubBuffer = null;
+ // Exit parsing loop
+ data = new Buffer(0);
+
+ } catch (err) {
+ var errorObject = {err:"socketHandler", trace:err, bin:self.buffer, parseState:{
+ sizeOfMessage:self.sizeOfMessage,
+ bytesRead:self.bytesRead,
+ stubBuffer:self.stubBuffer}};
+ if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject);
+ // We got a parse Error fire it off then keep going
+ self.emit("parseError", errorObject, self);
+ }
+ } else if(sizeOfMessage <= 4 || sizeOfMessage > self.maxBsonSize) {
+ var errorObject = {err:"socketHandler", trace:null, bin:data, parseState:{
+ sizeOfMessage:sizeOfMessage,
+ bytesRead:0,
+ buffer:null,
+ stubBuffer:null}};
+ if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject);
+ // We got a parse Error fire it off then keep going
+ self.emit("parseError", errorObject, self);
+
+ // Clear out the state of the parser
+ self.buffer = null;
+ self.sizeOfMessage = 0;
+ self.bytesRead = 0;
+ self.stubBuffer = null;
+ // Exit parsing loop
+ data = new Buffer(0);
+
+ } else {
+ try {
+ self.emit("message", data.slice(0, sizeOfMessage), self);
+ // Reset state of buffer
+ self.buffer = null;
+ self.sizeOfMessage = 0;
+ self.bytesRead = 0;
+ self.stubBuffer = null;
+ // Copy rest of message
+ data = data.slice(sizeOfMessage);
+ } catch (err) {
+ var errorObject = {err:"socketHandler", trace:err, bin:self.buffer, parseState:{
+ sizeOfMessage:sizeOfMessage,
+ bytesRead:self.bytesRead,
+ stubBuffer:self.stubBuffer}};
+ if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject);
+ // We got a parse Error fire it off then keep going
+ self.emit("parseError", errorObject, self);
+ }
+
+ }
+ } else {
+ // Create a buffer that contains the space for the non-complete message
+ self.stubBuffer = new Buffer(data.length)
+ // Copy the data to the stub buffer
+ data.copy(self.stubBuffer, 0);
+ // Exit parsing loop
+ data = new Buffer(0);
+ }
+ }
+ }
+ }
+ }
+}
+
+var endHandler = function(self) {
+ return function() {
+ // Set connected to false
+ self.connected = false;
+ // Emit end event
+ self.emit("end", {err: 'connection received Fin packet from [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self);
+ }
+}
+
+var timeoutHandler = function(self) {
+ return function() {
+ self.emit("timeout", {err: 'connection to [' + self.socketOptions.host + ':' + self.socketOptions.port + '] timed out'}, self);
+ }
+}
+
+var drainHandler = function(self) {
+ return function() {
+ }
+}
+
+var errorHandler = function(self) {
+ return function(err) {
+ // Set connected to false
+ self.connected = false;
+ // Emit error
+ self.emit("error", {err: 'failed to connect to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self);
+ }
+}
+
+var closeHandler = function(self) {
+ return function(hadError) {
+ // If we have an error during the connection phase
+ if(hadError && !self.connected) {
+ // Set disconnected
+ self.connected = false;
+ // Emit error
+ self.emit("error", {err: 'failed to connect to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self);
+ } else {
+ // Set disconnected
+ self.connected = false;
+ // Emit close
+ self.emit("close", {err: 'connection closed to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self);
+ }
+ }
+}
+
+// Some basic defaults
+Connection.DEFAULT_PORT = 27017;
+
+
+
+
+
+
+
diff --git a/node_modules/mongodb/lib/mongodb/connection/connection_pool.js b/node_modules/mongodb/lib/mongodb/connection/connection_pool.js
new file mode 100644
index 0000000..421d1bd
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/connection/connection_pool.js
@@ -0,0 +1,259 @@
+var utils = require('./connection_utils'),
+ inherits = require('util').inherits,
+ net = require('net'),
+ EventEmitter = require('events').EventEmitter,
+ inherits = require('util').inherits,
+ MongoReply = require("../responses/mongo_reply").MongoReply,
+ Connection = require("./connection").Connection;
+
+var ConnectionPool = exports.ConnectionPool = function(host, port, poolSize, bson, socketOptions) {
+ if(typeof host !== 'string' || typeof port !== 'number') throw "host and port must be specified [" + host + ":" + port + "]";
+ // Set up event emitter
+ EventEmitter.call(this);
+ // Keep all options for the socket in a specific collection allowing the user to specify the
+ // Wished upon socket connection parameters
+ this.socketOptions = typeof socketOptions === 'object' ? socketOptions : {};
+ this.socketOptions.host = host;
+ this.socketOptions.port = port;
+ this.bson = bson;
+ // PoolSize is always + 1 for special reserved "measurment" socket (like ping, stats etc)
+ this.poolSize = poolSize;
+
+ // Set default settings for the socket options
+ utils.setIntegerParameter(this.socketOptions, 'timeout', 0);
+ // Delay before writing out the data to the server
+ utils.setBooleanParameter(this.socketOptions, 'noDelay', true);
+ // Delay before writing out the data to the server
+ utils.setIntegerParameter(this.socketOptions, 'keepAlive', 0);
+ // Set the encoding of the data read, default is binary == null
+ utils.setStringParameter(this.socketOptions, 'encoding', null);
+ // Allows you to set a throttling bufferSize if you need to stop overflows
+ utils.setIntegerParameter(this.socketOptions, 'bufferSize', 0);
+
+ // Internal structures
+ this.openConnections = [];
+ this.connections = [];
+
+ // Assign connection id's
+ this.connectionId = 0;
+
+ // Current index for selection of pool connection
+ this.currentConnectionIndex = 0;
+ // The pool state
+ this._poolState = 'disconnected';
+ // timeout control
+ this._timeout = false;
+}
+
+inherits(ConnectionPool, EventEmitter);
+
+ConnectionPool.prototype.setMaxBsonSize = function(maxBsonSize) {
+ if(maxBsonSize == null){
+ maxBsonSize = Connection.DEFAULT_MAX_BSON_SIZE;
+ }
+
+ for(var i = 0; i < this.openConnections.length; i++) {
+ this.openConnections[i].maxBsonSize = maxBsonSize;
+ }
+}
+
+// Start a function
+var _connect = function(_self) {
+ return new function() {
+ var connectionStatus = _self._poolState;
+ // Create a new connection instance
+ var connection = new Connection(_self.connectionId++, _self.socketOptions);
+ // Set logger on pool
+ connection.logger = _self.logger;
+ // Connect handler
+ connection.on("connect", function(err, connection) {
+ // Add connection to list of open connections
+ _self.openConnections.push(connection);
+ _self.connections.push(connection)
+
+ // If the number of open connections is equal to the poolSize signal ready pool
+ if(_self.connections.length === _self.poolSize && _self._poolState !== 'disconnected') {
+ // Set connected
+ _self._poolState = 'connected';
+ // Emit pool ready
+ _self.emit("poolReady");
+ } else if(_self.connections.length < _self.poolSize) {
+ // We need to open another connection, make sure it's in the next
+ // tick so we don't get a cascade of errors
+ process.nextTick(function() {
+ _connect(_self);
+ });
+ }
+ });
+
+ var numberOfErrors = 0
+
+ // Error handler
+ connection.on("error", function(err, connection) {
+ numberOfErrors++;
+ // If we are already disconnected ignore the event
+ if(connectionStatus != 'disconnected' && _self.listeners("error").length > 0) {
+ _self.emit("error", err);
+ }
+
+ // Set disconnected
+ connectionStatus = 'disconnected';
+ // Set disconnected
+ _self._poolState = 'disconnected';
+ // Clean up
+ _self.openConnections = [];
+ _self.connections = [];
+ });
+
+ // Close handler
+ connection.on("close", function() {
+ // If we are already disconnected ignore the event
+ if(connectionStatus !== 'disconnected' && _self.listeners("close").length > 0) {
+ _self.emit("close");
+ }
+
+ // Set disconnected
+ connectionStatus = 'disconnected';
+ // Set disconnected
+ _self._poolState = 'disconnected';
+ // Clean up
+ _self.openConnections = [];
+ _self.connections = [];
+ });
+
+ // Timeout handler
+ connection.on("timeout", function(err, connection) {
+ // If we are already disconnected ignore the event
+ if(connectionStatus !== 'disconnected' && _self.listeners("timeout").length > 0) {
+ _self.emit("timeout", err);
+ }
+
+ // Set disconnected
+ connectionStatus = 'disconnected';
+ // Set disconnected
+ _self._poolState = 'disconnected';
+ // Clean up
+ _self.openConnections = [];
+ _self.connections = [];
+ });
+
+ // Parse error, needs a complete shutdown of the pool
+ connection.on("parseError", function() {
+ // If we are already disconnected ignore the event
+ if(connectionStatus !== 'disconnected' && _self.listeners("parseError").length > 0) {
+ // if(connectionStatus == 'connected') {
+ _self.emit("parseError", new Error("parseError occured"));
+ }
+
+ // Set disconnected
+ connectionStatus = 'disconnected';
+ _self.stop();
+ });
+
+ connection.on("message", function(message) {
+ _self.emit("message", message);
+ });
+
+ // Start connection in the next tick
+ connection.start();
+ }();
+}
+
+
+// Start method, will throw error if no listeners are available
+// Pass in an instance of the listener that contains the api for
+// finding callbacks for a given message etc.
+ConnectionPool.prototype.start = function() {
+ var markerDate = new Date().getTime();
+ var self = this;
+
+ if(this.listeners("poolReady").length == 0) {
+ throw "pool must have at least one listener ready that responds to the [poolReady] event";
+ }
+
+ // Set pool state to connecting
+ this._poolState = 'connecting';
+ this._timeout = false;
+
+ _connect(self);
+}
+
+// Restart a connection pool (on a close the pool might be in a wrong state)
+ConnectionPool.prototype.restart = function() {
+ // Close all connections
+ this.stop(false);
+ // Now restart the pool
+ this.start();
+}
+
+// Stop the connections in the pool
+ConnectionPool.prototype.stop = function(removeListeners) {
+ removeListeners = removeListeners == null ? true : removeListeners;
+ // Set disconnected
+ this._poolState = 'disconnected';
+
+ // Clear all listeners if specified
+ if(removeListeners) {
+ this.removeAllEventListeners();
+ }
+
+ // Close all connections
+ for(var i = 0; i < this.connections.length; i++) {
+ this.connections[i].close();
+ }
+
+ // Clean up
+ // this.connectionsWithErrors = [];
+ this.openConnections = [];
+ this.connections = [];
+}
+
+// Check the status of the connection
+ConnectionPool.prototype.isConnected = function() {
+ return this._poolState === 'connected';
+}
+
+// Checkout a connection from the pool for usage, or grab a specific pool instance
+ConnectionPool.prototype.checkoutConnection = function(id) {
+ var index = (this.currentConnectionIndex++ % (this.openConnections.length));
+ var connection = this.openConnections[index];
+ return connection;
+}
+
+ConnectionPool.prototype.getAllConnections = function() {
+ return this.connections;
+}
+
+// Remove all non-needed event listeners
+ConnectionPool.prototype.removeAllEventListeners = function() {
+ this.removeAllListeners("close");
+ this.removeAllListeners("error");
+ this.removeAllListeners("timeout");
+ this.removeAllListeners("connect");
+ this.removeAllListeners("end");
+ this.removeAllListeners("parseError");
+ this.removeAllListeners("message");
+ this.removeAllListeners("poolReady");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/node_modules/mongodb/lib/mongodb/connection/connection_utils.js b/node_modules/mongodb/lib/mongodb/connection/connection_utils.js
new file mode 100644
index 0000000..5910924
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/connection/connection_utils.js
@@ -0,0 +1,23 @@
+exports.setIntegerParameter = function(object, field, defaultValue) {
+ if(object[field] == null) {
+ object[field] = defaultValue;
+ } else if(typeof object[field] !== "number" && object[field] !== parseInt(object[field], 10)) {
+ throw "object field [" + field + "] must be a numeric integer value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]";
+ }
+}
+
+exports.setBooleanParameter = function(object, field, defaultValue) {
+ if(object[field] == null) {
+ object[field] = defaultValue;
+ } else if(typeof object[field] !== "boolean") {
+ throw "object field [" + field + "] must be a boolean value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]";
+ }
+}
+
+exports.setStringParameter = function(object, field, defaultValue) {
+ if(object[field] == null) {
+ object[field] = defaultValue;
+ } else if(typeof object[field] !== "string") {
+ throw "object field [" + field + "] must be a string value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]";
+ }
+} \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/connection/repl_set_servers.js b/node_modules/mongodb/lib/mongodb/connection/repl_set_servers.js
new file mode 100644
index 0000000..3e0e15a
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/connection/repl_set_servers.js
@@ -0,0 +1,972 @@
+var Connection = require('./connection').Connection,
+ DbCommand = require('../commands/db_command').DbCommand,
+ MongoReply = require('../responses/mongo_reply').MongoReply,
+ debug = require('util').debug,
+ EventEmitter = require('events').EventEmitter,
+ inherits = require('util').inherits,
+ inspect = require('util').inspect,
+ Server = require('./server').Server,
+ PingStrategy = require('./strategies/ping_strategy').PingStrategy,
+ StatisticsStrategy = require('./strategies/statistics_strategy').StatisticsStrategy;
+
+const STATE_STARTING_PHASE_1 = 0;
+const STATE_PRIMARY = 1;
+const STATE_SECONDARY = 2;
+const STATE_RECOVERING = 3;
+const STATE_FATAL_ERROR = 4;
+const STATE_STARTING_PHASE_2 = 5;
+const STATE_UNKNOWN = 6;
+const STATE_ARBITER = 7;
+const STATE_DOWN = 8;
+const STATE_ROLLBACK = 9;
+
+/**
+* ReplSetServers constructor provides master-slave functionality
+*
+* @param serverArr{Array of type Server}
+* @return constructor of ServerCluster
+*
+*/
+var ReplSetServers = exports.ReplSetServers = function(servers, options) {
+ // Set up event emitter
+ EventEmitter.call(this);
+ // Set up basic
+ if(!(this instanceof ReplSetServers)) return new ReplSetServers(server, options);
+
+ var self = this;
+ // Contains the master server entry
+ this.options = options == null ? {} : options;
+ this.reconnectWait = this.options["reconnectWait"] != null ? this.options["reconnectWait"] : 1000;
+ this.retries = this.options["retries"] != null ? this.options["retries"] : 30;
+ this.replicaSet = this.options["rs_name"];
+
+ // Are we allowing reads from secondaries ?
+ this.readSecondary = this.options["read_secondary"];
+ this.slaveOk = true;
+ this.closedConnectionCount = 0;
+ this._used = false;
+
+ // Default poolSize for new server instances
+ this.poolSize = this.options.poolSize == null ? 1 : this.options.poolSize;
+
+ // Set up ssl connections
+ this.ssl = this.options.ssl == null ? false : this.options.ssl;
+
+ // Just keeps list of events we allow
+ this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[]};
+ // Internal state of server connection
+ this._serverState = 'disconnected';
+ // Read preference
+ this._readPreference = null;
+ // Do we record server stats or not
+ this.recordQueryStats = false;
+
+ // Get the readPreference
+ var readPreference = this.options['readPreference'];
+ // Read preference setting
+ if(readPreference != null) {
+ if(readPreference != Server.READ_PRIMARY && readPreference != Server.READ_SECONDARY_ONLY
+ && readPreference != Server.READ_SECONDARY) {
+ throw new Error("Illegal readPreference mode specified, " + readPreference);
+ }
+
+ // Set read Preference
+ this._readPreference = readPreference;
+ } else {
+ this._readPreference = null;
+ }
+
+ // Strategy for picking a secondary
+ this.strategy = this.options['strategy'] == null ? 'statistical' : this.options['strategy'];
+ // Make sure strategy is one of the two allowed
+ if(this.strategy != null && (this.strategy != 'ping' && this.strategy != 'statistical')) throw new Error("Only ping or statistical strategies allowed");
+ // Let's set up our strategy object for picking secodaries
+ if(this.strategy == 'ping') {
+ // Create a new instance
+ this.strategyInstance = new PingStrategy(this);
+ } else if(this.strategy == 'statistical') {
+ // Set strategy as statistical
+ this.strategyInstance = new StatisticsStrategy(this);
+ // Add enable query information
+ this.enableRecordQueryStats(true);
+ }
+
+ // Set default connection pool options
+ this.socketOptions = this.options.socketOptions != null ? this.options.socketOptions : {};
+
+ // Set up logger if any set
+ this.logger = this.options.logger != null
+ && (typeof this.options.logger.debug == 'function')
+ && (typeof this.options.logger.error == 'function')
+ && (typeof this.options.logger.debug == 'function')
+ ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}};
+
+ // Ensure all the instances are of type server and auto_reconnect is false
+ if(!Array.isArray(servers) || servers.length == 0) {
+ throw Error("The parameter must be an array of servers and contain at least one server");
+ } else if(Array.isArray(servers) || servers.length > 0) {
+ var count = 0;
+ servers.forEach(function(server) {
+ if(server instanceof Server) count = count + 1;
+ // Ensure no server has reconnect on
+ server.options.auto_reconnect = false;
+ });
+
+ if(count < servers.length) {
+ throw Error("All server entries must be of type Server");
+ } else {
+ this.servers = servers;
+ }
+ }
+
+ // Auto Reconnect property
+ Object.defineProperty(this, "autoReconnect", { enumerable: true
+ , get: function () {
+ return true;
+ }
+ });
+
+ // Get Read Preference method
+ Object.defineProperty(this, "readPreference", { enumerable: true
+ , get: function () {
+ if(this._readPreference == null && this.readSecondary) {
+ return Server.READ_SECONDARY;
+ } else if(this._readPreference == null && !this.readSecondary) {
+ return Server.READ_PRIMARY;
+ } else {
+ return this._readPreference;
+ }
+ }
+ });
+
+ // Db Instances
+ Object.defineProperty(this, "dbInstances", {enumerable:true
+ , get: function() {
+ var servers = this.allServerInstances();
+ return servers[0].dbInstances;
+ }
+ })
+
+ // Auto Reconnect property
+ Object.defineProperty(this, "host", { enumerable: true
+ , get: function () {
+ if (this.primary != null) return this.primary.host;
+ }
+ });
+
+ Object.defineProperty(this, "port", { enumerable: true
+ , get: function () {
+ if (this.primary != null) return this.primary.port;
+ }
+ });
+
+ Object.defineProperty(this, "read", { enumerable: true
+ , get: function () {
+ return this.secondaries.length > 0 ? this.secondaries[0] : null;
+ }
+ });
+
+ // Get list of secondaries
+ Object.defineProperty(this, "secondaries", {enumerable: true
+ , get: function() {
+ var keys = Object.keys(this._state.secondaries);
+ var array = new Array(keys.length);
+ // Convert secondaries to array
+ for(var i = 0; i < keys.length; i++) {
+ array[i] = this._state.secondaries[keys[i]];
+ }
+ return array;
+ }
+ });
+
+ // Get list of all secondaries including passives
+ Object.defineProperty(this, "allSecondaries", {enumerable: true
+ , get: function() {
+ return this.secondaries.concat(this.passives);
+ }
+ });
+
+ // Get list of arbiters
+ Object.defineProperty(this, "arbiters", {enumerable: true
+ , get: function() {
+ var keys = Object.keys(this._state.arbiters);
+ var array = new Array(keys.length);
+ // Convert arbiters to array
+ for(var i = 0; i < keys.length; i++) {
+ array[i] = this._state.arbiters[keys[i]];
+ }
+ return array;
+ }
+ });
+
+ // Get list of passives
+ Object.defineProperty(this, "passives", {enumerable: true
+ , get: function() {
+ var keys = Object.keys(this._state.passives);
+ var array = new Array(keys.length);
+ // Convert arbiters to array
+ for(var i = 0; i < keys.length; i++) {
+ array[i] = this._state.passives[keys[i]];
+ }
+ return array;
+ }
+ });
+
+ // Master connection property
+ Object.defineProperty(this, "primary", { enumerable: true
+ , get: function () {
+ return this._state != null ? this._state.master : null;
+ }
+ });
+};
+
+inherits(ReplSetServers, EventEmitter);
+
+// Allow setting the read preference at the replicaset level
+ReplSetServers.prototype.setReadPreference = function(preference) {
+ // Set read preference
+ this._readPreference = preference;
+ // Ensure slaveOk is correct for secodnaries read preference and tags
+ if((this._readPreference == Server.READ_SECONDARY || this._readPreference == Server.READ_SECONDARY_ONLY)
+ || (this._readPreference != null && typeof this._readPreference == 'object')) {
+ this.slaveOk = true;
+ }
+}
+
+// Return the used state
+ReplSetServers.prototype._isUsed = function() {
+ return this._used;
+}
+
+ReplSetServers.prototype.setTarget = function(target) {
+ this.target = target;
+};
+
+ReplSetServers.prototype.isConnected = function() {
+ // Return the state of the replicaset server
+ return this.primary != null && this._state.master != null && this._state.master.isConnected();
+}
+
+Server.prototype.isSetMember = function() {
+ return false;
+}
+
+ReplSetServers.prototype.isPrimary = function(config) {
+ return this.readSecondary && this.secondaries.length > 0 ? false : true;
+}
+
+ReplSetServers.prototype.isReadPrimary = ReplSetServers.prototype.isPrimary;
+
+// Clean up dead connections
+var cleanupConnections = ReplSetServers.cleanupConnections = function(connections, addresses, byTags) {
+ // Ensure we don't have entries in our set with dead connections
+ var keys = Object.keys(connections);
+ for(var i = 0; i < keys.length; i++) {
+ var server = connections[keys[i]];
+ // If it's not connected remove it from the list
+ if(!server.isConnected()) {
+ // Remove from connections and addresses
+ delete connections[keys[i]];
+ delete addresses[keys[i]];
+ // Clean up tags if needed
+ if(server.tags != null && typeof server.tags === 'object') {
+ cleanupTags(server, byTags);
+ }
+ }
+ }
+}
+
+var cleanupTags = ReplSetServers._cleanupTags = function(server, byTags) {
+ var serverTagKeys = Object.keys(server.tags);
+ // Iterate over all server tags and remove any instances for that tag that matches the current
+ // server
+ for(var i = 0; i < serverTagKeys.length; i++) {
+ // Fetch the value for the tag key
+ var value = server.tags[serverTagKeys[i]];
+
+ // If we got an instance of the server
+ if(byTags[serverTagKeys[i]] != null
+ && byTags[serverTagKeys[i]][value] != null
+ && Array.isArray(byTags[serverTagKeys[i]][value])) {
+ // List of clean servers
+ var cleanInstances = [];
+ // We got instances for the particular tag set
+ var instances = byTags[serverTagKeys[i]][value];
+ for(var j = 0; j < instances.length; j++) {
+ var serverInstance = instances[j];
+ // If we did not find an instance add it to the clean instances
+ if((serverInstance.host + ":" + serverInstance.port) !== (server.host + ":" + server.port)) {
+ cleanInstances.push(serverInstance);
+ }
+ }
+
+ // Update the byTags list
+ byTags[serverTagKeys[i]][value] = cleanInstances;
+ }
+ }
+}
+
+ReplSetServers.prototype.allServerInstances = function() {
+ var self = this;
+ // Close all the servers (concatenate entire list of servers first for ease)
+ var allServers = self._state.master != null ? [self._state.master] : [];
+
+ // Secondary keys
+ var keys = Object.keys(self._state.secondaries);
+ // Add all secondaries
+ for(var i = 0; i < keys.length; i++) {
+ allServers.push(self._state.secondaries[keys[i]]);
+ }
+
+ // Arbiter keys
+ var keys = Object.keys(self._state.arbiters);
+ // Add all arbiters
+ for(var i = 0; i < keys.length; i++) {
+ allServers.push(self._state.arbiters[keys[i]]);
+ }
+
+ // Passive keys
+ var keys = Object.keys(self._state.passives);
+ // Add all arbiters
+ for(var i = 0; i < keys.length; i++) {
+ allServers.push(self._state.passives[keys[i]]);
+ }
+
+ // Return complete list of all servers
+ return allServers;
+}
+
+// Ensure no callback is left hanging when we have an error
+var __executeAllCallbacksWithError = function(dbInstance, error) {
+ var keys = Object.keys(dbInstance._callBackStore._notReplied);
+ // Iterate over all callbacks
+ for(var i = 0; i < keys.length; i++) {
+ // Delete info object
+ delete dbInstance._callBackStore._notReplied[keys[i]];
+ // Emit the error
+ dbInstance._callBackStore.emit(keys[i], error);
+ }
+}
+
+ReplSetServers.prototype.connect = function(parent, options, callback) {
+ var self = this;
+ var dateStamp = new Date().getTime();
+ if('function' === typeof options) callback = options, options = {};
+ if(options == null) options = {};
+ if(!('function' === typeof callback)) callback = null;
+
+ // Keep reference to parent
+ this.db = parent;
+ // Set server state to connecting
+ this._serverState = 'connecting';
+ // Reference to the instance
+ var replSetSelf = this;
+ var serverConnections = this.servers;
+ // Ensure parent can do a slave query if it's set
+ parent.slaveOk = this.slaveOk ? this.slaveOk : parent.slaveOk;
+ // Number of total servers that need to initialized (known servers)
+ var numberOfServersLeftToInitialize = serverConnections.length;
+
+ // Clean up state
+ replSetSelf._state = {'master':null, 'secondaries':{}, 'arbiters':{}, 'passives':{}, 'errors':{}, 'addresses':{}, 'byTags':{}, 'setName':null, 'errorMessages':[], 'members':[]};
+
+ // Create a connection handler
+ var connectionHandler = function(instanceServer) {
+ return function(err, result) {
+ // Don't attempt to connect if we are done
+ // if(replSetSelf._serverState === 'disconnected') return;
+ // Remove a server from the list of intialized servers we need to perform
+ numberOfServersLeftToInitialize = numberOfServersLeftToInitialize - 1;
+ // Add enable query information
+ instanceServer.enableRecordQueryStats(replSetSelf.recordQueryStats);
+
+ if(err == null && result.documents[0].hosts != null) {
+ // Fetch the isMaster command result
+ var document = result.documents[0];
+ // Break out the results
+ var setName = document.setName;
+ var isMaster = document.ismaster;
+ var secondary = document.secondary;
+ var passive = document.passive;
+ var arbiterOnly = document.arbiterOnly;
+ var hosts = Array.isArray(document.hosts) ? document.hosts : [];
+ var arbiters = Array.isArray(document.arbiters) ? document.arbiters : [];
+ var passives = Array.isArray(document.passives) ? document.passives : [];
+ var tags = document.tags ? document.tags : {};
+ var primary = document.primary;
+ var me = document.me;
+
+ // Only add server to our internal list if it's a master, secondary or arbiter
+ if(isMaster == true || secondary == true || arbiterOnly == true) {
+ // Handle a closed connection
+ var closeHandler = function(err, server) {
+ var closeServers = function() {
+ // Set the state to disconnected
+ parent._state = 'disconnected';
+ // Shut down the replicaset for now and Fire off all the callbacks sitting with no reply
+ if(replSetSelf._serverState == 'connected') {
+ // Close the replicaset
+ replSetSelf.close(function() {
+ __executeAllCallbacksWithError(parent, err);
+ // Ensure single callback only
+ if(callback != null) {
+ // Single callback only
+ var internalCallback = callback;
+ callback = null;
+ // Return the error
+ internalCallback(err, null);
+ } else {
+ // If the parent has listeners trigger an event
+ if(parent.listeners("close").length > 0) {
+ parent.emit("close", err);
+ }
+ }
+ });
+ }
+ }
+
+ // Check if this is the primary server, then disconnect otherwise keep going
+ if(replSetSelf._state.master != null) {
+ var primaryAddress = replSetSelf._state.master.host + ":" + replSetSelf._state.master.port;
+ var errorServerAddress = server.host + ":" + server.port;
+
+ // Only shut down the set if we have a primary server error
+ if(primaryAddress == errorServerAddress) {
+ closeServers();
+ } else {
+ // Remove from the list of servers
+ delete replSetSelf._state.addresses[errorServerAddress];
+ // Locate one of the lists and remove
+ if(replSetSelf._state.secondaries[errorServerAddress] != null) {
+ delete replSetSelf._state.secondaries[errorServerAddress];
+ } else if(replSetSelf._state.arbiters[errorServerAddress] != null) {
+ delete replSetSelf._state.arbiters[errorServerAddress];
+ } else if(replSetSelf._state.passives[errorServerAddress] != null) {
+ delete replSetSelf._state.passives[errorServerAddress];
+ }
+
+ // Check if we are reading from Secondary only
+ if(replSetSelf._readPreference == Server.READ_SECONDARY_ONLY && Object.keys(replSetSelf._state.secondaries).length == 0) {
+ closeServers();
+ }
+ }
+ } else {
+ closeServers();
+ }
+ }
+
+ // Handle a connection timeout
+ var timeoutHandler = function(err, server) {
+ var closeServers = function() {
+ // Set the state to disconnected
+ parent._state = 'disconnected';
+ // Shut down the replicaset for now and Fire off all the callbacks sitting with no reply
+ if(replSetSelf._serverState == 'connected') {
+ // Close the replicaset
+ replSetSelf.close(function() {
+ __executeAllCallbacksWithError(parent, err);
+ // Ensure single callback only
+ if(callback != null) {
+ // Single callback only
+ var internalCallback = callback;
+ callback = null;
+ // Return the error
+ internalCallback(new Error("connection timed out"), null);
+ } else {
+ // If the parent has listeners trigger an event
+ if(parent.listeners("error").length > 0) {
+ parent.emit("timeout", new Error("connection timed out"));
+ }
+ }
+ });
+ }
+ }
+
+ // Check if this is the primary server, then disconnect otherwise keep going
+ if(replSetSelf._state.master != null) {
+ var primaryAddress = replSetSelf._state.master.host + ":" + replSetSelf._state.master.port;
+ var errorServerAddress = server.host + ":" + server.port;
+
+ // Only shut down the set if we have a primary server error
+ if(primaryAddress == errorServerAddress) {
+ closeServers();
+ } else {
+ // Remove from the list of servers
+ delete replSetSelf._state.addresses[errorServerAddress];
+ // Locate one of the lists and remove
+ if(replSetSelf._state.secondaries[errorServerAddress] != null) {
+ delete replSetSelf._state.secondaries[errorServerAddress];
+ } else if(replSetSelf._state.arbiters[errorServerAddress] != null) {
+ delete replSetSelf._state.arbiters[errorServerAddress];
+ } else if(replSetSelf._state.passives[errorServerAddress] != null) {
+ delete replSetSelf._state.passives[errorServerAddress];
+ }
+
+ // Check if we are reading from Secondary only
+ if(replSetSelf._readPreference == Server.READ_SECONDARY_ONLY && Object.keys(replSetSelf._state.secondaries).length == 0) {
+ closeServers();
+ }
+ }
+ } else {
+ closeServers();
+ }
+ }
+
+ // Handle an error
+ var errorHandler = function(err, server) {
+ var closeServers = function() {
+ // Set the state to disconnected
+ parent._state = 'disconnected';
+ // Shut down the replicaset for now and Fire off all the callbacks sitting with no reply
+ if(replSetSelf._serverState == 'connected') {
+ // Close the replicaset
+ replSetSelf.close(function() {
+ __executeAllCallbacksWithError(parent, err);
+ // Ensure single callback only
+ if(callback != null) {
+ // Single callback only
+ var internalCallback = callback;
+ callback = null;
+ // Return the error
+ internalCallback(err, null);
+ } else {
+ // If the parent has listeners trigger an event
+ if(parent.listeners("error").length > 0) {
+ parent.emit("error", err);
+ }
+ }
+ });
+ }
+ }
+
+ // Check if this is the primary server, then disconnect otherwise keep going
+ if(replSetSelf._state.master != null) {
+ var primaryAddress = replSetSelf._state.master.host + ":" + replSetSelf._state.master.port;
+ var errorServerAddress = server.host + ":" + server.port;
+
+ // Only shut down the set if we have a primary server error
+ if(primaryAddress == errorServerAddress) {
+ closeServers();
+ } else {
+ // Remove from the list of servers
+ delete replSetSelf._state.addresses[errorServerAddress];
+ // Locate one of the lists and remove
+ if(replSetSelf._state.secondaries[errorServerAddress] != null) {
+ delete replSetSelf._state.secondaries[errorServerAddress];
+ } else if(replSetSelf._state.arbiters[errorServerAddress] != null) {
+ delete replSetSelf._state.arbiters[errorServerAddress];
+ } else if(replSetSelf._state.passives[errorServerAddress] != null) {
+ delete replSetSelf._state.passives[errorServerAddress];
+ }
+
+ // Check if we are reading from Secondary only
+ if(replSetSelf._readPreference == Server.READ_SECONDARY_ONLY && Object.keys(replSetSelf._state.secondaries).length == 0) {
+ closeServers();
+ }
+ }
+ } else {
+ closeServers();
+ }
+ }
+
+ // Ensure we don't have duplicate handlers
+ instanceServer.removeAllListeners("close");
+ instanceServer.removeAllListeners("error");
+ instanceServer.removeAllListeners("timeout");
+
+ // Add error handler to the instance of the server
+ instanceServer.on("close", closeHandler);
+ // Add error handler to the instance of the server
+ instanceServer.on("error", errorHandler);
+ // instanceServer.on("timeout", errorHandler);
+ instanceServer.on("timeout", timeoutHandler);
+ // Add tag info
+ instanceServer.tags = tags;
+
+ // For each tag in tags let's add the instance Server to the list for that tag
+ if(tags != null && typeof tags === 'object') {
+ var tagKeys = Object.keys(tags);
+ // For each tag file in the server add it to byTags
+ for(var i = 0; i < tagKeys.length; i++) {
+ var value = tags[tagKeys[i]];
+ // Check if we have a top level tag object
+ if(replSetSelf._state.byTags[tagKeys[i]] == null) replSetSelf._state.byTags[tagKeys[i]] = {};
+ // For the value check if we have an array of server instances
+ if(!Array.isArray(replSetSelf._state.byTags[tagKeys[i]][value])) replSetSelf._state.byTags[tagKeys[i]][value] = [];
+ // Check that the instance is not already registered there
+ var valueArray = replSetSelf._state.byTags[tagKeys[i]][value];
+ var found = false;
+
+ // Iterate over all values
+ for(var j = 0; j < valueArray.length; j++) {
+ if(valueArray[j].host == instanceServer.host && valueArray[j].port == instanceServer.port) {
+ found = true;
+ break;
+ }
+ }
+
+ // If it was not found push the instance server to the list
+ if(!found) valueArray.push(instanceServer);
+ }
+ }
+
+ // Remove from error list
+ delete replSetSelf._state.errors[me];
+
+ // Add our server to the list of finished servers
+ replSetSelf._state.addresses[me] = instanceServer;
+
+ // Assign the set name
+ if(replSetSelf.replicaSet == null) {
+ replSetSelf._state.setName = setName;
+ } else if(replSetSelf.replicaSet != setName && replSetSelf._serverState != 'disconnected') {
+ replSetSelf._state.errorMessages.push(new Error("configured mongodb replicaset does not match provided replicaset [" + setName + "] != [" + replSetSelf.replicaSet + "]"));
+ // Set done
+ replSetSelf._serverState = 'disconnected';
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Return error message ignoring rest of calls
+ return internalCallback(replSetSelf._state.errorMessages[0], parent);
+ }
+
+ // Let's add the server to our list of server types
+ if(secondary == true && (passive == false || passive == null)) {
+ replSetSelf._state.secondaries[me] = instanceServer;
+ } else if(arbiterOnly == true) {
+ replSetSelf._state.arbiters[me] = instanceServer;
+ } else if(secondary == true && passive == true) {
+ replSetSelf._state.passives[me] = instanceServer;
+ } else if(isMaster == true) {
+ replSetSelf._state.master = instanceServer;
+ } else if(isMaster == false && primary != null && replSetSelf._state.addresses[primary]) {
+ replSetSelf._state.master = replSetSelf._state.addresses[primary];
+ }
+
+ // Let's go throught all the "possible" servers in the replicaset
+ var candidateServers = hosts.concat(arbiters).concat(passives);
+
+ // If we have new servers let's add them
+ for(var i = 0; i < candidateServers.length; i++) {
+ // Fetch the server string
+ var candidateServerString = candidateServers[i];
+ // Add the server if it's not defined
+ if(replSetSelf._state.addresses[candidateServerString] == null) {
+ // Split the server string
+ var parts = candidateServerString.split(/:/);
+ if(parts.length == 1) {
+ parts = [parts[0], Connection.DEFAULT_PORT];
+ }
+
+ // Default empty socket options object
+ var socketOptions = {};
+ // If a socket option object exists clone it
+ if(replSetSelf.socketOptions != null) {
+ var keys = Object.keys(replSetSelf.socketOptions);
+ for(var i = 0; i < keys.length;i++) socketOptions[keys[i]] = replSetSelf.socketOptions[keys[i]];
+ }
+
+ // Add host information to socket options
+ socketOptions['host'] = parts[0];
+ socketOptions['port'] = parseInt(parts[1]);
+
+ // Create a new server instance
+ var newServer = new Server(parts[0], parseInt(parts[1]), {auto_reconnect:false, 'socketOptions':socketOptions
+ , logger:replSetSelf.logger, ssl:replSetSelf.ssl, poolSize:replSetSelf.poolSize});
+ // Set the replicaset instance
+ newServer.replicasetInstance = replSetSelf;
+
+ // Add handlers
+ newServer.on("close", closeHandler);
+ newServer.on("timeout", timeoutHandler);
+ newServer.on("error", errorHandler);
+
+ // Add server to list, ensuring we don't get a cascade of request to the same server
+ replSetSelf._state.addresses[candidateServerString] = newServer;
+
+ // Add a new server to the total number of servers that need to initialized before we are done
+ numberOfServersLeftToInitialize = numberOfServersLeftToInitialize + 1;
+
+ // Let's set up a new server instance
+ newServer.connect(parent, {returnIsMasterResults: true, eventReceiver:newServer}, connectionHandler(newServer));
+ }
+ }
+ } else {
+ // Remove the instance from out list of servers
+ delete replSetSelf._state.addresses[me];
+ }
+ }
+
+ // If done finish up
+ if((numberOfServersLeftToInitialize == 0) && replSetSelf._serverState === 'connecting' && replSetSelf._state.errorMessages.length == 0) {
+ // Set db as connected
+ replSetSelf._serverState = 'connected';
+ // If we don't expect a master let's call back, otherwise we need a master before
+ // the connection is successful
+ if(replSetSelf.masterNotNeeded || replSetSelf._state.master != null) {
+ // If we have a read strategy boot it
+ if(replSetSelf.strategyInstance != null) {
+ // Ensure we have a proper replicaset defined
+ replSetSelf.strategyInstance.replicaset = replSetSelf;
+ // Start strategy
+ replSetSelf.strategyInstance.start(function(err) {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(null, parent);
+ })
+ } else {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(null, parent);
+ }
+ } else if(replSetSelf.readSecondary == true && Object.keys(replSetSelf._state.secondaries).length > 0) {
+ // If we have a read strategy boot it
+ if(replSetSelf.strategyInstance != null) {
+ // Ensure we have a proper replicaset defined
+ replSetSelf.strategyInstance.replicaset = replSetSelf;
+ // Start strategy
+ replSetSelf.strategyInstance.start(function(err) {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(null, parent);
+ })
+ } else {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(null, parent);
+ }
+ } else if(replSetSelf.readSecondary == true && Object.keys(replSetSelf._state.secondaries).length == 0) {
+ replSetSelf._serverState = 'disconnected';
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Force close all server instances
+ replSetSelf.close();
+ // Perform callback
+ internalCallback(new Error("no secondary server found"), null);
+ } else if(typeof callback === 'function'){
+ replSetSelf._serverState = 'disconnected';
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Force close all server instances
+ replSetSelf.close();
+ // Perform callback
+ internalCallback(new Error("no primary server found"), null);
+ }
+ } else if((numberOfServersLeftToInitialize == 0) && replSetSelf._state.errorMessages.length > 0 && replSetSelf._serverState != 'disconnected') {
+ // Set done
+ replSetSelf._serverState = 'disconnected';
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Force close all server instances
+ replSetSelf.close();
+ // Callback to signal we are done
+ internalCallback(replSetSelf._state.errorMessages[0], null);
+ }
+ }
+ }
+
+ // Ensure we have all registered servers in our set
+ for(var i = 0; i < serverConnections.length; i++) {
+ replSetSelf._state.addresses[serverConnections[i].host + ':' + serverConnections[i].port] = serverConnections[i];
+ }
+
+ // Initialize all the connections
+ for(var i = 0; i < serverConnections.length; i++) {
+ // Set up the logger for the server connection
+ serverConnections[i].logger = replSetSelf.logger;
+ // Default empty socket options object
+ var socketOptions = {};
+ // If a socket option object exists clone it
+ if(this.socketOptions != null && typeof this.socketOptions === 'object') {
+ var keys = Object.keys(this.socketOptions);
+ for(var j = 0; j < keys.length;j++) socketOptions[keys[j]] = this.socketOptions[keys[j]];
+ }
+
+ // If ssl is specified
+ if(replSetSelf.ssl) serverConnections[i].ssl = true;
+
+ // Add host information to socket options
+ socketOptions['host'] = serverConnections[i].host;
+ socketOptions['port'] = serverConnections[i].port;
+
+ // Set the socket options
+ serverConnections[i].socketOptions = socketOptions;
+ // Set the replicaset instance
+ serverConnections[i].replicasetInstance = replSetSelf;
+ // Connect to server
+ serverConnections[i].connect(parent, {returnIsMasterResults: true, eventReceiver:serverConnections[i]}, connectionHandler(serverConnections[i]));
+ }
+
+ // Check if we have an error in the inital set of servers and callback with error
+ if(replSetSelf._state.errorMessages.length > 0 && typeof callback === 'function') {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(replSetSelf._state.errorMessages[0], null);
+ }
+}
+
+ReplSetServers.prototype.checkoutWriter = function() {
+ // Establish connection
+ var connection = this._state.master != null ? this._state.master.checkoutWriter() : null;
+ // Return the connection
+ return connection;
+}
+
+ReplSetServers.prototype.checkoutReader = function() {
+ var connection = null;
+ // If we have specified to read from a secondary server grab a random one and read
+ // from it, otherwise just pass the primary connection
+ if((this.readSecondary == true || this._readPreference == Server.READ_SECONDARY || this._readPreference == Server.READ_SECONDARY_ONLY) && Object.keys(this._state.secondaries).length > 0) {
+ // Checkout a secondary server from the passed in set of servers
+ if(this.strategyInstance != null) {
+ connection = this.strategyInstance.checkoutSecondary();
+ } else {
+ // Pick a random key
+ var keys = Object.keys(this._state.secondaries);
+ var key = keys[Math.floor(Math.random() * keys.length)];
+ connection = this._state.secondaries[key].checkoutReader();
+ }
+ } else if(this._readPreference == Server.READ_SECONDARY_ONLY && Object.keys(this._state.secondaries).length == 0) {
+ connection = null;
+ } else if(this._readPreference != null && typeof this._readPreference === 'object') {
+ // Get all tag keys (used to try to find a server that is valid)
+ var keys = Object.keys(this._readPreference);
+ // final instance server
+ var instanceServer = null;
+ // for each key look for an avilable instance
+ for(var i = 0; i < keys.length; i++) {
+ // Grab subkey value
+ var value = this._readPreference[keys[i]];
+
+ // Check if we have any servers for the tag, if we do pick a random one
+ if(this._state.byTags[keys[i]] != null
+ && this._state.byTags[keys[i]][value] != null
+ && Array.isArray(this._state.byTags[keys[i]][value])
+ && this._state.byTags[keys[i]][value].length > 0) {
+ // Let's grab an available server from the list using a random pick
+ var serverInstances = this._state.byTags[keys[i]][value];
+ // Set instance to return
+ instanceServer = serverInstances[Math.floor(Math.random() * serverInstances.length)];
+ break;
+ }
+ }
+
+ // Return the instance of the server
+ connection = instanceServer != null ? instanceServer.checkoutReader() : this.checkoutWriter();
+ } else {
+ connection = this.checkoutWriter();
+ }
+ // Return the connection
+ return connection;
+}
+
+ReplSetServers.prototype.allRawConnections = function() {
+ // Neeed to build a complete list of all raw connections, start with master server
+ var allConnections = [];
+ // Get connection object
+ var allMasterConnections = this._state.master.connectionPool.getAllConnections();
+ // Add all connections to list
+ allConnections = allConnections.concat(allMasterConnections);
+
+ // If we have read secondary let's add all secondary servers
+ if(this.readSecondary && Object.keys(this._state.secondaries).length > 0) {
+ // Get all the keys
+ var keys = Object.keys(this._state.secondaries);
+ // For each of the secondaries grab the connections
+ for(var i = 0; i < keys.length; i++) {
+ // Get connection object
+ var secondaryPoolConnections = this._state.secondaries[keys[i]].connectionPool.getAllConnections();
+ // Add all connections to list
+ allConnections = allConnections.concat(secondaryPoolConnections);
+ }
+ }
+
+ // Return all the conections
+ return allConnections;
+}
+
+ReplSetServers.prototype.enableRecordQueryStats = function(enable) {
+ // Set the global enable record query stats
+ this.recordQueryStats = enable;
+ // Ensure all existing servers already have the flag set, even if the
+ // connections are up already or we have not connected yet
+ if(this._state != null && this._state.addresses != null) {
+ var keys = Object.keys(this._state.addresses);
+ // Iterate over all server instances and set the enableRecordQueryStats flag
+ for(var i = 0; i < keys.length; i++) {
+ this._state.addresses[keys[i]].enableRecordQueryStats(enable);
+ }
+ } else if(Array.isArray(this.servers)) {
+ for(var i = 0; i < this.servers.length; i++) {
+ this.servers[i].enableRecordQueryStats(enable);
+ }
+ }
+}
+
+ReplSetServers.prototype.disconnect = function(callback) {
+ this.close(callback);
+}
+
+ReplSetServers.prototype.close = function(callback) {
+ var self = this;
+ // Set server status as disconnected
+ this._serverState = 'disconnected';
+ // Get all the server instances and close them
+ var allServers = [];
+ // Make sure we have servers
+ if(this._state['addresses'] != null) {
+ var keys = Object.keys(this._state.addresses);
+ for(var i = 0; i < keys.length; i++) {
+ allServers.push(this._state.addresses[keys[i]]);
+ }
+ }
+
+ // Let's process all the closing
+ var numberOfServersToClose = allServers.length;
+
+ // Remove all the listeners
+ self.removeAllListeners();
+
+ // Special case where there are no servers
+ if(allServers.length == 0 && typeof callback === 'function') return callback(null, null);
+
+ // Close the servers
+ for(var i = 0; i < allServers.length; i++) {
+ var server = allServers[i];
+ if(server.isConnected()) {
+ // Close each server
+ server.close(function() {
+ numberOfServersToClose = numberOfServersToClose - 1;
+ // Clear out state if we are done
+ if(numberOfServersToClose == 0) {
+ // Clear out state
+ self._state = {'master':null, 'secondaries':{}, 'arbiters':{}, 'passives':{}, 'errors':{}, 'addresses':{}, 'byTags':{}, 'setName':null, 'errorMessages':[], 'members':[]};
+ }
+
+ // If we are finished perform the call back
+ if(numberOfServersToClose == 0 && typeof callback === 'function') {
+ callback(null);
+ }
+ })
+ } else {
+ numberOfServersToClose = numberOfServersToClose - 1;
+ // If we have no more servers perform the callback
+ if(numberOfServersToClose == 0 && typeof callback === 'function') {
+ callback(null);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/connection/server.js b/node_modules/mongodb/lib/mongodb/connection/server.js
new file mode 100644
index 0000000..80c3b27
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/connection/server.js
@@ -0,0 +1,640 @@
+var Connection = require('./connection').Connection,
+ DbCommand = require('../commands/db_command').DbCommand,
+ MongoReply = require('../responses/mongo_reply').MongoReply,
+ ConnectionPool = require('./connection_pool').ConnectionPool,
+ EventEmitter = require('events').EventEmitter,
+ MongoReply = require("../responses/mongo_reply").MongoReply,
+ inherits = require('util').inherits;
+
+var Server = exports.Server = function(host, port, options) {
+ // Set up event emitter
+ EventEmitter.call(this);
+ // Set up Server instance
+ if(!(this instanceof Server)) return new Server(host, port, options);
+
+ var self = this;
+ this.host = host;
+ this.port = port;
+ this.options = options == null ? {} : options;
+ this.internalConnection;
+ this.internalMaster = false;
+ this.connected = false;
+ this.poolSize = this.options.poolSize == null ? 1 : this.options.poolSize;
+ this.ssl = this.options.ssl == null ? false : this.options.ssl;
+ this.slaveOk = this.options["slave_ok"];
+ this._used = false;
+
+ // Get the readPreference
+ var readPreference = this.options['readPreference'];
+ // Read preference setting
+ if(readPreference != null) {
+ if(readPreference != Server.READ_PRIMARY && readPreference != Server.READ_SECONDARY_ONLY
+ && readPreference != Server.READ_SECONDARY) {
+ throw new Error("Illegal readPreference mode specified, " + readPreference);
+ }
+
+ // Set read Preference
+ this._readPreference = readPreference;
+ } else {
+ this._readPreference = null;
+ }
+
+ // Contains the isMaster information returned from the server
+ this.isMasterDoc;
+
+ // Set default connection pool options
+ this.socketOptions = this.options.socketOptions != null ? this.options.socketOptions : {};
+ // Set ssl up if it's defined
+ if(this.ssl) {
+ this.socketOptions.ssl = true;
+ }
+
+ // Set up logger if any set
+ this.logger = this.options.logger != null
+ && (typeof this.options.logger.debug == 'function')
+ && (typeof this.options.logger.error == 'function')
+ && (typeof this.options.logger.debug == 'function')
+ ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}};
+
+ // Just keeps list of events we allow
+ this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[]};
+ // Internal state of server connection
+ this._serverState = 'disconnected';
+ // this._timeout = false;
+ // Contains state information about server connection
+ this._state = {'runtimeStats': {'queryStats':new RunningStats()}};
+ // Do we record server stats or not
+ this.recordQueryStats = false;
+
+ // Setters and getters
+ Object.defineProperty(this, "autoReconnect", { enumerable: true
+ , get: function () {
+ return this.options['auto_reconnect'] == null ? false : this.options['auto_reconnect'];
+ }
+ });
+
+ Object.defineProperty(this, "connection", { enumerable: true
+ , get: function () {
+ return this.internalConnection;
+ }
+ , set: function(connection) {
+ this.internalConnection = connection;
+ }
+ });
+
+ Object.defineProperty(this, "master", { enumerable: true
+ , get: function () {
+ return this.internalMaster;
+ }
+ , set: function(value) {
+ this.internalMaster = value;
+ }
+ });
+
+ Object.defineProperty(this, "primary", { enumerable: true
+ , get: function () {
+ return this;
+ }
+ });
+
+ // Getter for query Stats
+ Object.defineProperty(this, "queryStats", { enumerable: true
+ , get: function () {
+ return this._state.runtimeStats.queryStats;
+ }
+ });
+
+ Object.defineProperty(this, "runtimeStats", { enumerable: true
+ , get: function () {
+ return this._state.runtimeStats;
+ }
+ });
+
+ // Get Read Preference method
+ Object.defineProperty(this, "readPreference", { enumerable: true
+ , get: function () {
+ if(this._readPreference == null && this.readSecondary) {
+ return Server.READ_SECONDARY;
+ } else if(this._readPreference == null && !this.readSecondary) {
+ return Server.READ_PRIMARY;
+ } else {
+ return this._readPreference;
+ }
+ }
+ });
+};
+
+// Inherit simple event emitter
+inherits(Server, EventEmitter);
+// Read Preferences
+Server.READ_PRIMARY = 'primary';
+Server.READ_SECONDARY = 'secondary';
+Server.READ_SECONDARY_ONLY = 'secondaryOnly';
+
+// Always ourselves
+Server.prototype.setReadPreference = function() {}
+
+// Return the used state
+Server.prototype._isUsed = function() {
+ return this._used;
+}
+
+// Server close function
+Server.prototype.close = function(callback) {
+ // Remove all local listeners
+ this.removeAllListeners();
+
+ if(this.connectionPool != null) {
+ // Remove all the listeners on the pool so it does not fire messages all over the place
+ this.connectionPool.removeAllEventListeners();
+ // Close the connection if it's open
+ this.connectionPool.stop();
+ }
+
+ // Set server status as disconnected
+ this._serverState = 'disconnected';
+ // Peform callback if present
+ if(typeof callback === 'function') callback();
+};
+
+Server.prototype.isConnected = function() {
+ return this.connectionPool != null && this.connectionPool.isConnected();
+}
+
+Server.prototype.allServerInstances = function() {
+ return [this];
+}
+
+Server.prototype.isSetMember = function() {
+ return this['replicasetInstance'] != null;
+}
+
+Server.prototype.connect = function(dbInstance, options, callback) {
+ if('function' === typeof options) callback = options, options = {};
+ if(options == null) options = {};
+ if(!('function' === typeof callback)) callback = null;
+
+ // Currently needed to work around problems with multiple connections in a pool with ssl
+ // TODO fix if possible
+ if(this.ssl == true) {
+ // Set up socket options for ssl
+ this.socketOptions.ssl = true;
+ }
+
+ // Let's connect
+ var server = this;
+ // Let's us override the main receiver of events
+ var eventReceiver = options.eventReceiver != null ? options.eventReceiver : this;
+ // Creating dbInstance
+ this.dbInstance = dbInstance;
+ // Save reference to dbInstance
+ this.dbInstances = [dbInstance];
+
+ // Set server state to connecting
+ this._serverState = 'connecting';
+ // Ensure dbInstance can do a slave query if it's set
+ dbInstance.slaveOk = this.slaveOk ? this.slaveOk : dbInstance.slaveOk;
+ // Create connection Pool instance with the current BSON serializer
+ var connectionPool = new ConnectionPool(this.host, this.port, this.poolSize, dbInstance.bson, this.socketOptions);
+ // Set logger on pool
+ connectionPool.logger = this.logger;
+
+ // Set up a new pool using default settings
+ server.connectionPool = connectionPool;
+
+ // Set basic parameters passed in
+ var returnIsMasterResults = options.returnIsMasterResults == null ? false : options.returnIsMasterResults;
+
+ // Create a default connect handler, overriden when using replicasets
+ var connectCallback = function(err, reply) {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // If something close down the connection and removed the callback before
+ // proxy killed connection etc, ignore the erorr as close event was isssued
+ if(err != null && internalCallback == null) return;
+ // Internal callback
+ if(err != null) return internalCallback(err, null);
+ server.master = reply.documents[0].ismaster == 1 ? true : false;
+ server.connectionPool.setMaxBsonSize(reply.documents[0].maxBsonObjectSize);
+ // Set server as connected
+ server.connected = true;
+ // Save document returned so we can query it
+ server.isMasterDoc = reply.documents[0];
+
+ // If we have it set to returnIsMasterResults
+ if(returnIsMasterResults) {
+ internalCallback(null, reply);
+ } else {
+ internalCallback(null, dbInstance);
+ }
+ };
+
+ // Let's us override the main connect callback
+ var connectHandler = options.connectHandler == null ? connectCallback : options.connectHandler;
+
+ // Set up on connect method
+ connectionPool.on("poolReady", function() {
+ // Create db command and Add the callback to the list of callbacks by the request id (mapping outgoing messages to correct callbacks)
+ var db_command = DbCommand.NcreateIsMasterCommand(dbInstance, dbInstance.databaseName);
+ // Check out a reader from the pool
+ var connection = connectionPool.checkoutConnection();
+ // Set server state to connected
+ server._serverState = 'connected';
+
+ // Register handler for messages
+ dbInstance._registerHandler(db_command, false, connection, connectHandler);
+
+ // Write the command out
+ connection.write(db_command);
+ })
+
+ // Set up item connection
+ connectionPool.on("message", function(message) {
+ // Attempt to parse the message
+ try {
+ // Create a new mongo reply
+ var mongoReply = new MongoReply()
+ // Parse the header
+ mongoReply.parseHeader(message, connectionPool.bson)
+ // If message size is not the same as the buffer size
+ // something went terribly wrong somewhere
+ if(mongoReply.messageLength != message.length) {
+ // Emit the error
+ eventReceiver.emit("error", new Error("bson length is different from message length"), server);
+ // Remove all listeners
+ server.removeAllListeners();
+ } else {
+ var startDate = new Date().getTime();
+
+ // Callback instance
+ var callbackInfo = null;
+ var dbInstanceObject = null;
+
+ // Locate a callback instance and remove any additional ones
+ for(var i = 0; i < server.dbInstances.length; i++) {
+ var dbInstanceObjectTemp = server.dbInstances[i];
+ var hasHandler = dbInstanceObjectTemp._hasHandler(mongoReply.responseTo.toString());
+ // Assign the first one we find and remove any duplicate ones
+ if(hasHandler && callbackInfo == null) {
+ callbackInfo = dbInstanceObjectTemp._findHandler(mongoReply.responseTo.toString());
+ dbInstanceObject = dbInstanceObjectTemp;
+ } else if(hasHandler) {
+ dbInstanceObjectTemp._removeHandler(mongoReply.responseTo.toString());
+ }
+ }
+
+ // Only execute callback if we have a caller
+ if(callbackInfo.callback && Array.isArray(callbackInfo.info.chained)) {
+ // Check if callback has already been fired (missing chain command)
+ var chained = callbackInfo.info.chained;
+ var numberOfFoundCallbacks = 0;
+ for(var i = 0; i < chained.length; i++) {
+ if(dbInstanceObject._hasHandler(chained[i])) numberOfFoundCallbacks++;
+ }
+
+ // If we have already fired then clean up rest of chain and move on
+ if(numberOfFoundCallbacks != chained.length) {
+ for(var i = 0; i < chained.length; i++) {
+ dbInstanceObject._removeHandler(chained[i]);
+ }
+
+ // Just return from function
+ return;
+ }
+
+ // Parse the body
+ mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) {
+ var callbackInfo = dbInstanceObject._findHandler(mongoReply.responseTo.toString());
+ // If we have an error let's execute the callback and clean up all other
+ // chained commands
+ var firstResult = mongoReply && mongoReply.documents;
+ // Check for an error, if we have one let's trigger the callback and clean up
+ // The chained callbacks
+ if(firstResult[0].err != null || firstResult[0].errmsg != null) {
+ // Trigger the callback for the error
+ dbInstanceObject._callHandler(mongoReply.responseTo, mongoReply, null);
+ } else {
+ var chainedIds = callbackInfo.info.chained;
+
+ if(chainedIds.length > 0 && chainedIds[chainedIds.length - 1] == mongoReply.responseTo) {
+ // Cleanup all other chained calls
+ chainedIds.pop();
+ // Remove listeners
+ for(var i = 0; i < chainedIds.length; i++) dbInstanceObject._removeHandler(chainedIds[i]);
+ // Call the handler
+ dbInstanceObject._callHandler(mongoReply.responseTo, callbackInfo.info.results.shift(), null);
+ } else{
+ // Add the results to all the results
+ for(var i = 0; i < chainedIds.length; i++) {
+ var handler = dbInstanceObject._findHandler(chainedIds[i]);
+ // Check if we have an object, if it's the case take the current object commands and
+ // and add this one
+ if(handler.info != null) {
+ handler.info.results = Array.isArray(callbackInfo.info.results) ? callbackInfo.info.results : [];
+ handler.info.results.push(mongoReply);
+ }
+ }
+ }
+ }
+ });
+ } else if(callbackInfo.callback) {
+ // Parse the body
+ mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) {
+ // Let's record the stats info if it's enabled
+ if(server.recordQueryStats == true && server._state['runtimeStats'] != null
+ && server._state.runtimeStats['queryStats'] instanceof RunningStats) {
+ // Add data point to the running statistics object
+ server._state.runtimeStats.queryStats.push(new Date().getTime() - callbackInfo.info.start);
+ }
+
+ // Trigger the callback
+ dbInstanceObject._callHandler(mongoReply.responseTo, mongoReply, null);
+ });
+ }
+ }
+ } catch (err) {
+ // Throw error in next tick
+ process.nextTick(function() {
+ throw err;
+ })
+ }
+ });
+
+ // Handle timeout
+ connectionPool.on("timeout", function(err) {
+ // If pool connection is already closed
+ if(server._serverState === 'disconnected') return;
+ // Set server state to disconnected
+ server._serverState = 'disconnected';
+ // Close the pool
+ connectionPool.stop();
+ // If we have a callback return the error
+ if(typeof callback === 'function') {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(err, null);
+ } else if(server.isSetMember()) {
+ server.emit("timeout", err, server);
+ } else {
+ eventReceiver.emit("timeout", err, server);
+ }
+
+ // If we are a single server connection fire errors correctly
+ if(!server.isSetMember()) {
+ // Fire all callback errors
+ _fireCallbackErrors(server, err);
+ // Emit error
+ _emitAcrossAllDbInstances(server, eventReceiver, "timeout", err, server);
+ }
+ });
+
+ // Handle errors
+ connectionPool.on("error", function(message) {
+ // If pool connection is already closed
+ if(server._serverState === 'disconnected') return;
+ // Set server state to disconnected
+ server._serverState = 'disconnected';
+ // Close the pool
+ connectionPool.stop();
+ // If we have a callback return the error
+ if(typeof callback === 'function') {// && !server.isSetMember()) {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(new Error(message && message.err ? message.err : message), null);
+ } else if(server.isSetMember()) {
+ server.emit("error", new Error(message && message.err ? message.err : message), server);
+ } else {
+ eventReceiver.emit("error", new Error(message && message.err ? message.err : message), server);
+ }
+
+ // If we are a single server connection fire errors correctly
+ if(!server.isSetMember()) {
+ // Fire all callback errors
+ _fireCallbackErrors(server, new Error(message && message.err ? message.err : message));
+ // Emit error
+ _emitAcrossAllDbInstances(server, eventReceiver, "error", new Error(message && message.err ? message.err : message), server);
+ }
+ });
+
+ // Handle close events
+ connectionPool.on("close", function() {
+ // If pool connection is already closed
+ if(server._serverState === 'disconnected') return;
+ // Set server state to disconnected
+ server._serverState = 'disconnected';
+ // Close the pool
+ connectionPool.stop(true);
+ // If we have a callback return the error
+ if(typeof callback == 'function') {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(new Error("connection closed"), null);
+ } else if(server.isSetMember()) {
+ server.emit("close", new Error("connection closed"), server);
+ } else {
+ eventReceiver.emit("close", new Error("connection closed"), server);
+ }
+
+ // If we are a single server connection fire errors correctly
+ if(!server.isSetMember()) {
+ // Fire all callback errors
+ _fireCallbackErrors(server, new Error("connection closed"));
+ // Emit error
+ _emitAcrossAllDbInstances(server, eventReceiver, "close", server);
+ }
+ });
+
+ // If we have a parser error we are in an unknown state, close everything and emit
+ // error
+ connectionPool.on("parseError", function(message) {
+ // If pool connection is already closed
+ if(server._serverState === 'disconnected') return;
+ // Set server state to disconnected
+ server._serverState = 'disconnected';
+ // Close the pool
+ connectionPool.stop();
+ // If we have a callback return the error
+ if(typeof callback === 'function') {
+ // ensure no callbacks get called twice
+ var internalCallback = callback;
+ callback = null;
+ // Perform callback
+ internalCallback(new Error("connection closed due to parseError"), null);
+ } else if(server.isSetMember()) {
+ server.emit("parseError", new Error("connection closed due to parseError"), server);
+ } else {
+ eventReceiver.emit("parseError", new Error("connection closed due to parseError"), server);
+ }
+
+ // If we are a single server connection fire errors correctly
+ if(!server.isSetMember()) {
+ // Fire all callback errors
+ _fireCallbackErrors(server, new Error("connection closed due to parseError"));
+ // Emit error
+ _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server);
+ }
+ });
+
+ // Boot up connection poole, pass in a locator of callbacks
+ connectionPool.start();
+}
+
+// Fire all the errors
+var _fireCallbackErrors = function(server, err) {
+ // Locate all the possible callbacks that need to return
+ for(var i = 0; i < server.dbInstances.length; i++) {
+ // Fetch the db Instance
+ var dbInstance = server.dbInstances[i];
+ // Check all callbacks
+ var keys = Object.keys(dbInstance._callBackStore._notReplied);
+ // For each key check if it's a callback that needs to be returned
+ for(var j = 0; j < keys.length; j++) {
+ var info = dbInstance._callBackStore._notReplied[keys[j]];
+ if(info.connection.socketOptions.host === server.host && info.connection.socketOptions.port === server.port) {
+ dbInstance._callBackStore.emit(keys[j], err, null);
+ }
+ }
+ }
+}
+
+var _emitAcrossAllDbInstances = function(server, filterDb, event, message, object) {
+ // Emit close event across all db instances sharing the sockets
+ var allServerInstances = server.allServerInstances();
+ // Fetch the first server instance
+ var serverInstance = allServerInstances[0];
+ // For all db instances signal all db instances
+ if(Array.isArray(serverInstance.dbInstances) && serverInstance.dbInstances.length > 1) {
+ for(var i = 0; i < serverInstance.dbInstances.length; i++) {
+ var dbInstance = serverInstance.dbInstances[i];
+ // Check if it's our current db instance and skip if it is
+ if(filterDb == null || filterDb.databaseName !== dbInstance.databaseName || filterDb.tag !== dbInstance.tag) {
+ dbInstance.emit(event, message, object);
+ }
+ }
+ }
+}
+
+Server.prototype.allRawConnections = function() {
+ return this.connectionPool.getAllConnections();
+}
+
+// Check if a writer can be provided
+var canCheckoutWriter = function(self, read) {
+ // We cannot write to an arbiter or secondary server
+ if(self.isMasterDoc['arbiterOnly'] == true) {
+ return new Error("Cannot write to an arbiter");
+ } if(self.isMasterDoc['secondary'] == true) {
+ return new Error("Cannot write to a secondary");
+ } else if(read == true && self._readPreference == Server.READ_SECONDARY_ONLY && self.isMasterDoc['ismaster'] == true) {
+ return new Error("Cannot read from primary when secondary only specified");
+ }
+
+ // Return no error
+ return null;
+}
+
+Server.prototype.checkoutWriter = function(read) {
+ if(read == true) return this.connectionPool.checkoutConnection();
+ // Check if are allowed to do a checkout (if we try to use an arbiter f.ex)
+ var result = canCheckoutWriter(this, read);
+ // If the result is null check out a writer
+ if(result == null) {
+ return this.connectionPool.checkoutConnection();
+ } else {
+ return result;
+ }
+}
+
+// Check if a reader can be provided
+var canCheckoutReader = function(self) {
+ // We cannot write to an arbiter or secondary server
+ if(self.isMasterDoc['arbiterOnly'] == true) {
+ return new Error("Cannot write to an arbiter");
+ } else if(self._readPreference != null) {
+ // If the read preference is Primary and the instance is not a master return an error
+ if(self._readPreference == Server.READ_PRIMARY && self.isMasterDoc['ismaster'] != true) {
+ return new Error("Read preference is " + Server.READ_PRIMARY + " and server is not master");
+ } else if(self._readPreference == Server.READ_SECONDARY_ONLY && self.isMasterDoc['ismaster'] == true) {
+ return new Error("Cannot read from primary when secondary only specified");
+ }
+ }
+
+ // Return no error
+ return null;
+}
+
+Server.prototype.checkoutReader = function() {
+ // Check if are allowed to do a checkout (if we try to use an arbiter f.ex)
+ var result = canCheckoutReader(this);
+ // If the result is null check out a writer
+ if(result == null) {
+ return this.connectionPool.checkoutConnection();
+ } else {
+ return result;
+ }
+}
+
+Server.prototype.enableRecordQueryStats = function(enable) {
+ this.recordQueryStats = enable;
+}
+
+//
+// Internal statistics object used for calculating average and standard devitation on
+// running queries
+var RunningStats = function() {
+ var self = this;
+ this.m_n = 0;
+ this.m_oldM = 0.0;
+ this.m_oldS = 0.0;
+ this.m_newM = 0.0;
+ this.m_newS = 0.0;
+
+ // Define getters
+ Object.defineProperty(this, "numDataValues", { enumerable: true
+ , get: function () { return this.m_n; }
+ });
+
+ Object.defineProperty(this, "mean", { enumerable: true
+ , get: function () { return (this.m_n > 0) ? this.m_newM : 0.0; }
+ });
+
+ Object.defineProperty(this, "variance", { enumerable: true
+ , get: function () { return ((this.m_n > 1) ? this.m_newS/(this.m_n - 1) : 0.0); }
+ });
+
+ Object.defineProperty(this, "standardDeviation", { enumerable: true
+ , get: function () { return Math.sqrt(this.variance); }
+ });
+
+ Object.defineProperty(this, "sScore", { enumerable: true
+ , get: function () {
+ var bottom = this.mean + this.standardDeviation;
+ if(bottom == 0) return 0;
+ return ((2 * this.mean * this.standardDeviation)/(bottom));
+ }
+ });
+}
+
+RunningStats.prototype.push = function(x) {
+ // Update the number of samples
+ this.m_n = this.m_n + 1;
+ // See Knuth TAOCP vol 2, 3rd edition, page 232
+ if(this.m_n == 1) {
+ this.m_oldM = this.m_newM = x;
+ this.m_oldS = 0.0;
+ } else {
+ this.m_newM = this.m_oldM + (x - this.m_oldM) / this.m_n;
+ this.m_newS = this.m_oldS + (x - this.m_oldM) * (x - this.m_newM);
+
+ // set up for next iteration
+ this.m_oldM = this.m_newM;
+ this.m_oldS = this.m_newS;
+ }
+}
diff --git a/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js b/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js
new file mode 100644
index 0000000..6bb36cf
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js
@@ -0,0 +1,125 @@
+var Server = require("../server").Server;
+
+// The ping strategy uses pings each server and records the
+// elapsed time for the server so it can pick a server based on lowest
+// return time for the db command {ping:true}
+var PingStrategy = exports.PingStrategy = function(replicaset) {
+ this.replicaset = replicaset;
+ this.state = 'disconnected';
+ // Class instance
+ this.Db = require("../../db").Db;
+}
+
+// Starts any needed code
+PingStrategy.prototype.start = function(callback) {
+ this.state = 'connected';
+ // Start ping server
+ this._pingServer(callback);
+}
+
+// Stops and kills any processes running
+PingStrategy.prototype.stop = function(callback) {
+ // Stop the ping process
+ this.state = 'disconnected';
+ // Call the callback
+ callback(null, null);
+}
+
+PingStrategy.prototype.checkoutSecondary = function() {
+ // Get all secondary server keys
+ var keys = Object.keys(this.replicaset._state.secondaries);
+ // Contains the picked instance
+ var minimumPingMs = null;
+ var selectedInstance = null;
+ // Pick server key by the lowest ping time
+ for(var i = 0; i < keys.length; i++) {
+ // Fetch a server
+ var server = this.replicaset._state.secondaries[keys[i]];
+ // If we don't have a ping time use it
+ if(server.runtimeStats['pingMs'] == null) {
+ // Set to 0 ms for the start
+ server.runtimeStats['pingMs'] = 0;
+ // Pick server
+ selectedInstance = server;
+ break;
+ } else {
+ // If the next server's ping time is less than the current one choose than one
+ if(minimumPingMs == null || server.runtimeStats['pingMs'] < minimumPingMs) {
+ minimumPingMs = server.runtimeStats['pingMs'];
+ selectedInstance = server;
+ }
+ }
+ }
+
+ // Return the selected instance
+ return selectedInstance != null ? selectedInstance.checkoutReader() : null;
+}
+
+PingStrategy.prototype._pingServer = function(callback) {
+ var self = this;
+
+ // Ping server function
+ var pingFunction = function() {
+ if(self.state == 'disconnected') return;
+ var addresses = self.replicaset._state != null && self.replicaset._state.addresses != null ? self.replicaset._state.addresses : null;
+ // Grab all servers
+ var serverKeys = Object.keys(addresses);
+ // Number of server entries
+ var numberOfEntries = serverKeys.length;
+ // We got keys
+ for(var i = 0; i < serverKeys.length; i++) {
+ // We got a server instance
+ var server = addresses[serverKeys[i]];
+ // Create a new server object, avoid using internal connections as they might
+ // be in an illegal state
+ new function(serverInstance) {
+ var server = new Server(serverInstance.host, serverInstance.port, {poolSize:1, timeout:500});
+ var db = new self.Db(self.replicaset.db.databaseName, server);
+ // Add error listener
+ db.on("error", function(err) {
+ // Adjust the number of checks
+ numberOfEntries = numberOfEntries - 1;
+ // Close connection
+ db.close();
+ // If we are done with all results coming back trigger ping again
+ if(numberOfEntries == 0 && self.state == 'connected') {
+ setTimeout(pingFunction, 1000);
+ }
+ })
+
+ // Open the db instance
+ db.open(function(err, p_db) {
+ if(err != null) {
+ db.close();
+ } else {
+ // Startup time of the command
+ var startTime = new Date().getTime();
+ // Execute ping on this connection
+ p_db.executeDbCommand({ping:1}, function(err, result) {
+ // Adjust the number of checks
+ numberOfEntries = numberOfEntries - 1;
+ // Get end time of the command
+ var endTime = new Date().getTime();
+ // Store the ping time in the server instance state variable, if there is one
+ if(serverInstance != null && serverInstance.runtimeStats != null && serverInstance.isConnected()) {
+ serverInstance.runtimeStats['pingMs'] = (endTime - startTime);
+ }
+
+ // Close server
+ p_db.close();
+ // If we are done with all results coming back trigger ping again
+ if(numberOfEntries == 0 && self.state == 'connected') {
+ setTimeout(pingFunction, 1000);
+ }
+ })
+ }
+ })
+ }(server);
+ }
+ }
+
+ // Start pingFunction
+ setTimeout(pingFunction, 1000);
+ // Do the callback
+ callback(null);
+}
diff --git a/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js b/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js
new file mode 100644
index 0000000..0c8b1c0
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js
@@ -0,0 +1,40 @@
+// The Statistics strategy uses the measure of each end-start time for each
+// query executed against the db to calculate the mean, variance and standard deviation
+// and pick the server which the lowest mean and deviation
+var StatisticsStrategy = exports.StatisticsStrategy = function(replicaset) {
+ this.replicaset = replicaset;
+}
+
+// Starts any needed code
+StatisticsStrategy.prototype.start = function(callback) {
+ callback(null, null);
+}
+
+StatisticsStrategy.prototype.stop = function(callback) {
+ // Remove reference to replicaset
+ this.replicaset = null;
+ // Perform callback
+ callback(null, null);
+}
+
+StatisticsStrategy.prototype.checkoutSecondary = function() {
+ // Get all secondary server keys
+ var keys = Object.keys(this.replicaset._state.secondaries);
+ // Contains the picked instance
+ var minimumSscore = null;
+ var selectedInstance = null;
+
+ // Pick server key by the lowest ping time
+ for(var i = 0; i < keys.length; i++) {
+ // Fetch a server
+ var server = this.replicaset._state.secondaries[keys[i]];
+ // Pick server by lowest Sscore
+ if(minimumSscore == null || (server.queryStats.sScore < minimumSscore)) {
+ minimumSscore = server.queryStats.sScore;
+ selectedInstance = server;
+ }
+ }
+
+ // Return the selected instance
+ return selectedInstance != null ? selectedInstance.checkoutReader() : null;
+}
diff --git a/node_modules/mongodb/lib/mongodb/cursor.js b/node_modules/mongodb/lib/mongodb/cursor.js
new file mode 100644
index 0000000..28c9da4
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/cursor.js
@@ -0,0 +1,702 @@
+var QueryCommand = require('./commands/query_command').QueryCommand,
+ GetMoreCommand = require('./commands/get_more_command').GetMoreCommand,
+ KillCursorCommand = require('./commands/kill_cursor_command').KillCursorCommand,
+ Long = require('bson').Long,
+ CursorStream = require('./cursorstream'),
+ utils = require('./utils');
+
+/**
+ * Constructor for a cursor object that handles all the operations on query result
+ * using find. This cursor object is unidirectional and cannot traverse backwards. Clients should not be creating a cursor directly,
+ * but use find to acquire a cursor.
+ *
+ * @class Represents a Cursor.
+ * @param {Db} db the database object to work with.
+ * @param {Collection} collection the collection to query.
+ * @param {Object} selector the query selector.
+ * @param {Object} fields an object containing what fields to include or exclude from objects returned.
+ * @param {Number} skip number of documents to skip.
+ * @param {Number} limit the number of results to return. -1 has a special meaning and is used by Db.eval. A value of 1 will also be treated as if it were -1.
+ * @param {String|Array|Object} sort the required sorting for the query.
+ * @param {Object} hint force the query to use a specific index.
+ * @param {Boolean} explain return the explaination of the query.
+ * @param {Boolean} snapshot Snapshot mode assures no duplicates are returned.
+ * @param {Boolean} timeout allow the query to timeout.
+ * @param {Boolean} tailable allow the cursor to be tailable.
+ * @param {Number} batchSize the number of the subset of results to request the database to return for every request. This should initially be greater than 1 otherwise the database will automatically close the cursor. The batch size can be set to 1 with cursorInstance.batchSize after performing the initial query to the database.
+ * @param {Boolean} raw return all query documents as raw buffers (default false).
+ * @param {Boolean} read specify override of read from source (primary/secondary).
+ * @param {Boolean} returnKey only return the index key.
+ * @param {Number} maxScan limit the number of items to scan.
+ * @param {Number} min set index bounds.
+ * @param {Number} max set index bounds.
+ * @param {Boolean} showDiskLoc show disk location of results.
+ * @param {String} comment you can put a $comment field on a query to make looking in the profiler logs simpler.
+ */
+function Cursor(db, collection, selector, fields, skip, limit
+ , sort, hint, explain, snapshot, timeout, tailable, batchSize, slaveOk, raw, read
+ , returnKey, maxScan, min, max, showDiskLoc, comment) {
+ this.db = db;
+ this.collection = collection;
+ this.selector = selector;
+ this.fields = fields;
+ this.skipValue = skip == null ? 0 : skip;
+ this.limitValue = limit == null ? 0 : limit;
+ this.sortValue = sort;
+ this.hint = hint;
+ this.explainValue = explain;
+ this.snapshot = snapshot;
+ this.timeout = timeout == null ? true : timeout;
+ this.tailable = tailable;
+ this.batchSizeValue = batchSize == null ? 0 : batchSize;
+ this.slaveOk = slaveOk == null ? collection.slaveOk : slaveOk;
+ this.raw = raw == null ? false : raw;
+ this.read = read == null ? true : read;
+ this.returnKey = returnKey;
+ this.maxScan = maxScan;
+ this.min = min;
+ this.max = max;
+ this.showDiskLoc = showDiskLoc;
+ this.comment = comment;
+
+ this.totalNumberOfRecords = 0;
+ this.items = [];
+ this.cursorId = Long.fromInt(0);
+
+ // State variables for the cursor
+ this.state = Cursor.INIT;
+ // Keep track of the current query run
+ this.queryRun = false;
+ this.getMoreTimer = false;
+ this.collectionName = (this.db.databaseName ? this.db.databaseName + "." : '') + this.collection.collectionName;
+};
+
+/**
+ * Resets this cursor to its initial state. All settings like the query string,
+ * tailable, batchSizeValue, skipValue and limits are preserved.
+ *
+ * @return {Cursor} returns itself with rewind applied.
+ * @api public
+ */
+Cursor.prototype.rewind = function() {
+ var self = this;
+
+ if (self.state != Cursor.INIT) {
+ if (self.state != Cursor.CLOSED) {
+ self.close(function() {});
+ }
+
+ self.numberOfReturned = 0;
+ self.totalNumberOfRecords = 0;
+ self.items = [];
+ self.cursorId = Long.fromInt(0);
+ self.state = Cursor.INIT;
+ self.queryRun = false;
+ }
+
+ return self;
+};
+
+
+/**
+ * Returns an array of documents. The caller is responsible for making sure that there
+ * is enough memory to store the results. Note that the array only contain partial
+ * results when this cursor had been previouly accessed. In that case,
+ * cursor.rewind() can be used to reset the cursor.
+ *
+ * @param {Function} callback This will be called after executing this method successfully. The first paramter will contain the Error object if an error occured, or null otherwise. The second paramter will contain an array of BSON deserialized objects as a result of the query.
+ * @return {null}
+ * @api public
+ */
+Cursor.prototype.toArray = function(callback) {
+ var self = this;
+
+ if(!callback) {
+ throw new Error('callback is mandatory');
+ }
+
+ if(this.tailable) {
+ callback(new Error("Tailable cursor cannot be converted to array"), null);
+ } else if(this.state != Cursor.CLOSED) {
+ var items = [];
+
+ this.each(function(err, item) {
+ if(err != null) return callback(err, null);
+
+ if (item != null) {
+ items.push(item);
+ } else {
+ callback(err, items);
+ items = null;
+ self.items = [];
+ }
+ });
+ } else {
+ callback(new Error("Cursor is closed"), null);
+ }
+};
+
+/**
+ * Iterates over all the documents for this cursor. As with **{cursor.toArray}**,
+ * not all of the elements will be iterated if this cursor had been previouly accessed.
+ * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike
+ * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements
+ * at any given time if batch size is specified. Otherwise, the caller is responsible
+ * for making sure that the entire result can fit the memory.
+ *
+ * @param {Function} callback this will be called for while iterating every document of the query result. The first paramter will contain the Error object if an error occured, or null otherwise. While the second paramter will contain the document.
+ * @return {null}
+ * @api public
+ */
+Cursor.prototype.each = function(callback) {
+ var self = this;
+
+ if (!callback) {
+ throw new Error('callback is mandatory');
+ }
+
+ if(this.state != Cursor.CLOSED) {
+ //FIX: stack overflow (on deep callback) (cred: https://github.com/limp/node-mongodb-native/commit/27da7e4b2af02035847f262b29837a94bbbf6ce2)
+ process.nextTick(function(){
+ // Fetch the next object until there is no more objects
+ self.nextObject(function(err, item) {
+ if(err != null) return callback(err, null);
+
+ if(item != null) {
+ callback(null, item);
+ self.each(callback);
+ } else {
+ // Close the cursor if done
+ self.state = Cursor.CLOSED;
+ callback(err, null);
+ }
+
+ item = null;
+ });
+ });
+ } else {
+ callback(new Error("Cursor is closed"), null);
+ }
+};
+
+/**
+ * Determines how many result the query for this cursor will return
+ *
+ * @param {Function} callback this will be after executing this method. The first paramter will contain the Error object if an error occured, or null otherwise. While the second paramter will contain the number of results or null if an error occured.
+ * @return {null}
+ * @api public
+ */
+Cursor.prototype.count = function(callback) {
+ this.collection.count(this.selector, callback);
+};
+
+/**
+ * Sets the sort parameter of this cursor to the given value.
+ *
+ * This method has the following method signatures:
+ * (keyOrList, callback)
+ * (keyOrList, direction, callback)
+ *
+ * @param {String|Array|Object} keyOrList This can be a string or an array. If passed as a string, the string will be the field to sort. If passed an array, each element will represent a field to be sorted and should be an array that contains the format [string, direction].
+ * @param {String|Number} direction this determines how the results are sorted. "asc", "ascending" or 1 for asceding order while "desc", "desceding or -1 for descending order. Note that the strings are case insensitive.
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution.
+ * @return {Cursor} an instance of this object.
+ * @api public
+ */
+Cursor.prototype.sort = function(keyOrList, direction, callback) {
+ callback = callback || function(){};
+ if(typeof direction === "function") { callback = direction; direction = null; }
+
+ if(this.tailable) {
+ callback(new Error("Tailable cursor doesn't support sorting"), null);
+ } else if(this.queryRun == true || this.state == Cursor.CLOSED) {
+ callback(new Error("Cursor is closed"), null);
+ } else {
+ var order = keyOrList;
+
+ if(direction != null) {
+ order = [[keyOrList, direction]];
+ }
+
+ this.sortValue = order;
+ callback(null, this);
+ }
+ return this;
+};
+
+/**
+ * Sets the limit parameter of this cursor to the given value.
+ *
+ * @param {Number} limit the new limit.
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object when the limit given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution.
+ * @return {Cursor} an instance of this object.
+ * @api public
+ */
+Cursor.prototype.limit = function(limit, callback) {
+ callback = callback || function(){};
+
+ if(this.tailable) {
+ callback(new Error("Tailable cursor doesn't support limit"), null);
+ } else if(this.queryRun == true || this.state == Cursor.CLOSED) {
+ callback(new Error("Cursor is closed"), null);
+ } else {
+ if(limit != null && limit.constructor != Number) {
+ callback(new Error("limit requires an integer"), null);
+ } else {
+ this.limitValue = limit;
+ callback(null, this);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Sets the skip parameter of this cursor to the given value.
+ *
+ * @param {Number} skip the new skip value.
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object when the skip value given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution.
+ * @return {Cursor} an instance of this object.
+ * @api public
+ */
+Cursor.prototype.skip = function(skip, callback) {
+ callback = callback || function(){};
+
+ if(this.tailable) {
+ callback(new Error("Tailable cursor doesn't support skip"), null);
+ } else if(this.queryRun == true || this.state == Cursor.CLOSED) {
+ callback(new Error("Cursor is closed"), null);
+ } else {
+ if(skip != null && skip.constructor != Number) {
+ callback(new Error("skip requires an integer"), null);
+ } else {
+ this.skipValue = skip;
+ callback(null, this);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Sets the batch size parameter of this cursor to the given value.
+ *
+ * @param {Number} batchSize the new batch size.
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object when the batchSize given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution.
+ * @return {Cursor} an instance of this object.
+ * @api public
+ */
+Cursor.prototype.batchSize = function(batchSize, callback) {
+ if(this.state == Cursor.CLOSED) {
+ if(callback != null) {
+ return callback(new Error("Cursor is closed"), null);
+ } else {
+ throw new Error("Cursor is closed");
+ }
+ } else if(batchSize != null && batchSize.constructor != Number) {
+ if(callback != null) {
+ return callback(new Error("batchSize requires an integer"), null);
+ } else {
+ throw new Error("batchSize requires an integer");
+ }
+ } else {
+ this.batchSizeValue = batchSize;
+ if(callback != null) return callback(null, this);
+ }
+
+ return this;
+};
+
+/**
+ * The limit used for the getMore command
+ *
+ * @return {Number} The number of records to request per batch.
+ * @ignore
+ * @api private
+ */
+var limitRequest = function(self) {
+ var requestedLimit = self.limitValue;
+
+ if(self.limitValue > 0) {
+ if (self.batchSizeValue > 0) {
+ requestedLimit = self.limitValue < self.batchSizeValue ?
+ self.limitValue : self.batchSizeValue;
+ }
+ } else {
+ requestedLimit = self.batchSizeValue;
+ }
+
+ return requestedLimit;
+};
+
+
+/**
+ * Generates a QueryCommand object using the parameters of this cursor.
+ *
+ * @return {QueryCommand} The command object
+ * @ignore
+ * @api private
+ */
+var generateQueryCommand = function(self) {
+ // Unpack the options
+ var queryOptions = QueryCommand.OPTS_NONE;
+ if(!self.timeout) {
+ queryOptions |= QueryCommand.OPTS_NO_CURSOR_TIMEOUT;
+ }
+
+ if(self.tailable != null) {
+ queryOptions |= QueryCommand.OPTS_TAILABLE_CURSOR;
+ self.skipValue = self.limitValue = 0;
+ }
+
+ if(self.slaveOk) {
+ queryOptions |= QueryCommand.OPTS_SLAVE;
+ }
+
+ // limitValue of -1 is a special case used by Db#eval
+ var numberToReturn = self.limitValue == -1 ? -1 : limitRequest(self);
+
+ // Check if we need a special selector
+ if(self.sortValue != null || self.explainValue != null || self.hint != null || self.snapshot != null
+ || self.returnKey != null || self.maxScan != null || self.min != null || self.max != null
+ || self.showDiskLoc != null || self.comment != null) {
+
+ // Build special selector
+ var specialSelector = {'query':self.selector};
+ if(self.sortValue != null) specialSelector['orderby'] = utils.formattedOrderClause(self.sortValue);
+ if(self.hint != null && self.hint.constructor == Object) specialSelector['$hint'] = self.hint;
+ if(self.explainValue != null) specialSelector['$explain'] = true;
+ if(self.snapshot != null) specialSelector['$snapshot'] = true;
+ if(self.returnKey != null) specialSelector['$returnKey'] = self.returnKey;
+ if(self.maxScan != null) specialSelector['$maxScan'] = self.maxScan;
+ if(self.min != null) specialSelector['$min'] = self.min;
+ if(self.max != null) specialSelector['$max'] = self.max;
+ if(self.showDiskLoc != null) specialSelector['$showDiskLoc'] = self.showDiskLoc;
+ if(self.comment != null) specialSelector['$comment'] = self.comment;
+
+ // Return the query
+ return new QueryCommand(self.db, self.collectionName, queryOptions, self.skipValue, numberToReturn, specialSelector, self.fields);
+ } else {
+ return new QueryCommand(self.db, self.collectionName, queryOptions, self.skipValue, numberToReturn, self.selector, self.fields);
+ }
+};
+
+/**
+ * @return {Object} Returns an object containing the sort value of this cursor with
+ * the proper formatting that can be used internally in this cursor.
+ * @ignore
+ * @api private
+ */
+Cursor.prototype.formattedOrderClause = function() {
+ return utils.formattedOrderClause(this.sortValue);
+};
+
+/**
+ * Converts the value of the sort direction into its equivalent numerical value.
+ *
+ * @param sortDirection {String|number} Range of acceptable values:
+ * 'ascending', 'descending', 'asc', 'desc', 1, -1
+ *
+ * @return {number} The equivalent numerical value
+ * @throws Error if the given sortDirection is invalid
+ * @ignore
+ * @api private
+ */
+Cursor.prototype.formatSortValue = function(sortDirection) {
+ return utils.formatSortValue(sortDirection);
+};
+
+/**
+ * Gets the next document from the cursor.
+ *
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object on error while the second parameter will contain a document from the returned result or null if there are no more results.
+ * @api public
+ */
+Cursor.prototype.nextObject = function(callback) {
+ var self = this;
+
+ if(self.state == Cursor.INIT) {
+ var cmd;
+ try {
+ cmd = generateQueryCommand(self);
+ } catch (err) {
+ return callback(err, null);
+ }
+
+ var commandHandler = function(err, result) {
+ if(err != null && result == null) return callback(err, null);
+
+ if(!err && result.documents[0] && result.documents[0]['$err']) {
+ return self.close(function() {callback(result.documents[0]['$err'], null);});
+ }
+
+ self.queryRun = true;
+ self.state = Cursor.OPEN; // Adjust the state of the cursor
+ self.cursorId = result.cursorId;
+ self.totalNumberOfRecords = result.numberReturned;
+
+ // Add the new documents to the list of items
+ self.items = self.items.concat(result.documents);
+ self.nextObject(callback);
+ result = null;
+ };
+
+ self.db._executeQueryCommand(cmd, {read:self.read, raw:self.raw}, commandHandler);
+ commandHandler = null;
+ } else if(self.items.length) {
+ callback(null, self.items.shift());
+ } else if(self.cursorId.greaterThan(Long.fromInt(0))) {
+ getMore(self, callback);
+ } else {
+ // Force cursor to stay open
+ return self.close(function() {callback(null, null);});
+ }
+}
+
+/**
+ * Gets more results from the database if any.
+ *
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object on error while the second parameter will contain a document from the returned result or null if there are no more results.
+ * @ignore
+ * @api private
+ */
+var getMore = function(self, callback) {
+ var limit = 0;
+
+ if (!self.tailable && self.limitValue > 0) {
+ limit = self.limitValue - self.totalNumberOfRecords;
+ if (limit < 1) {
+ self.close(function() {callback(null, null);});
+ return;
+ }
+ }
+ try {
+ var getMoreCommand = new GetMoreCommand(self.db, self.collectionName, limitRequest(self), self.cursorId);
+ // Execute the command
+ self.db._executeQueryCommand(getMoreCommand, {read:self.read, raw:self.raw}, function(err, result) {
+ try {
+ if(err != null) callback(err, null);
+
+ self.cursorId = result.cursorId;
+ self.totalNumberOfRecords += result.numberReturned;
+ // Determine if there's more documents to fetch
+ if(result.numberReturned > 0) {
+ if (self.limitValue > 0) {
+ var excessResult = self.totalNumberOfRecords - self.limitValue;
+
+ if (excessResult > 0) {
+ result.documents.splice(-1 * excessResult, excessResult);
+ }
+ }
+
+ self.items = self.items.concat(result.documents);
+ callback(null, self.items.shift());
+ } else if(self.tailable) {
+ self.getMoreTimer = setTimeout(function() {getMore(self, callback);}, 500);
+ } else {
+ self.close(function() {callback(null, null);});
+ }
+
+ result = null;
+ } catch(err) {
+ callback(err, null);
+ }
+ });
+
+ getMoreCommand = null;
+ } catch(err) {
+ var handleClose = function() {
+ callback(err, null);
+ };
+
+ self.close(handleClose);
+ handleClose = null;
+ }
+}
+
+/**
+ * Gets a detailed information about how the query is performed on this cursor and how
+ * long it took the database to process it.
+ *
+ * @param {Function} callback this will be called after executing this method. The first parameter will always be null while the second parameter will be an object containing the details.
+ * @api public
+ */
+Cursor.prototype.explain = function(callback) {
+ var limit = (-1)*Math.abs(this.limitValue);
+ // Create a new cursor and fetch the plan
+ var cursor = new Cursor(this.db, this.collection, this.selector, this.fields, this.skipValue, limit,
+ this.sortValue, this.hint, true, this.snapshot, this.timeout, this.tailable, this.batchSizeValue);
+ // Fetch the explaination document
+ cursor.nextObject(function(err, item) {
+ if(err != null) return callback(err, null);
+ // close the cursor
+ cursor.close(function(err, result) {
+ if(err != null) return callback(err, null);
+ callback(null, item);
+ });
+ });
+};
+
+/**
+ * Returns a stream object that can be used to listen to and stream records
+ * (**Use the CursorStream object instead as this is deprected**)
+ *
+ * Options
+ * - **fetchSize** {Number} the number of records to fetch in each batch (steam specific batchSize).
+ *
+ * Events
+ * - **data** {function(item) {}} the data event triggers when a document is ready.
+ * - **error** {function(err) {}} the error event triggers if an error happens.
+ * - **end** {function() {}} the end event triggers when there is no more documents available.
+ *
+ * @param {Object} [options] additional options for streamRecords.
+ * @return {EventEmitter} returns a stream object.
+ * @api public
+ */
+Cursor.prototype.streamRecords = function(options) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ options = args.length ? args.shift() : {};
+
+ var
+ self = this,
+ stream = new process.EventEmitter(),
+ recordLimitValue = this.limitValue || 0,
+ emittedRecordCount = 0,
+ queryCommand = generateQueryCommand(self);
+
+ // see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol
+ queryCommand.numberToReturn = options.fetchSize ? options.fetchSize : 500;
+ // Execute the query
+ execute(queryCommand);
+
+ function execute(command) {
+ self.db._executeQueryCommand(command, {read:self.read, raw:self.raw}, function(err,result) {
+ if(err) {
+ stream.emit('error', err);
+ self.close(function(){});
+ return;
+ }
+
+ if (!self.queryRun && result) {
+ self.queryRun = true;
+ self.cursorId = result.cursorId;
+ self.state = Cursor.OPEN;
+ self.getMoreCommand = new GetMoreCommand(self.db, self.collectionName, queryCommand.numberToReturn, result.cursorId);
+ }
+
+ var resflagsMap = {
+ CursorNotFound:1<<0,
+ QueryFailure:1<<1,
+ ShardConfigStale:1<<2,
+ AwaitCapable:1<<3
+ };
+
+ if(result.documents && result.documents.length && !(result.responseFlag & resflagsMap.QueryFailure)) {
+ try {
+ result.documents.forEach(function(doc){
+ if(recordLimitValue && emittedRecordCount>=recordLimitValue) {
+ throw("done");
+ }
+ emittedRecordCount++;
+ stream.emit('data', doc);
+ });
+ } catch(err) {
+ if (err != "done") { throw err; }
+ else {
+ self.close(function(){
+ stream.emit('end', recordLimitValue);
+ });
+ self.close(function(){});
+ return;
+ }
+ }
+ // rinse & repeat
+ execute(self.getMoreCommand);
+ } else {
+ self.close(function(){
+ stream.emit('end', recordLimitValue);
+ });
+ }
+ });
+ }
+
+ return stream;
+};
+
+/**
+ * Returns a Node ReadStream interface for this cursor.
+ *
+ * @return {CursorStream} returns a stream object.
+ * @api public
+ */
+Cursor.prototype.stream = function stream () {
+ return new CursorStream(this);
+}
+
+/**
+ * Close the cursor.
+ *
+ * @param {Function} callback this will be called after executing this method. The first parameter will always contain null while the second parameter will contain a reference to this cursor.
+ * @return {null}
+ * @api public
+ */
+Cursor.prototype.close = function(callback) {
+ var self = this
+ this.getMoreTimer && clearTimeout(this.getMoreTimer);
+ // Close the cursor if not needed
+ if(this.cursorId instanceof Long && this.cursorId.greaterThan(Long.fromInt(0))) {
+ try {
+ var command = new KillCursorCommand(this.db, [this.cursorId]);
+ this.db._executeQueryCommand(command, {read:self.read, raw:self.raw}, null);
+ } catch(err) {}
+ }
+
+ // Reset cursor id
+ this.cursorId = Long.fromInt(0);
+ // Set to closed status
+ this.state = Cursor.CLOSED;
+
+ if(callback) {
+ callback(null, self);
+ self.items = [];
+ }
+
+ return this;
+};
+
+/**
+ * Check if the cursor is closed or open.
+ *
+ * @return {Boolean} returns the state of the cursor.
+ * @api public
+ */
+Cursor.prototype.isClosed = function() {
+ return this.state == Cursor.CLOSED ? true : false;
+};
+
+/**
+ * Init state
+ *
+ * @classconstant INIT
+ **/
+Cursor.INIT = 0;
+
+/**
+ * Cursor open
+ *
+ * @classconstant OPEN
+ **/
+Cursor.OPEN = 1;
+
+/**
+ * Cursor closed
+ *
+ * @classconstant CLOSED
+ **/
+Cursor.CLOSED = 2;
+
+/**
+ * @ignore
+ * @api private
+ */
+exports.Cursor = Cursor;
diff --git a/node_modules/mongodb/lib/mongodb/cursorstream.js b/node_modules/mongodb/lib/mongodb/cursorstream.js
new file mode 100644
index 0000000..fd2ff65
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/cursorstream.js
@@ -0,0 +1,141 @@
+/**
+ * Module dependecies.
+ */
+var Stream = require('stream').Stream;
+
+/**
+ * CursorStream
+ *
+ * Returns a stream interface for the **cursor**.
+ *
+ * Events
+ * - **data** {function(item) {}} the data event triggers when a document is ready.
+ * - **error** {function(err) {}} the error event triggers if an error happens.
+ * - **end** {function() {}} the end event triggers when there is no more documents available.
+ *
+ * @class Represents a CursorStream.
+ * @param {Cursor} cursor a cursor object that the stream wraps.
+ * @return {Stream}
+ */
+function CursorStream(cursor) {
+ if(!(this instanceof CursorStream)) return new CursorStream(cursor);
+
+ Stream.call(this);
+
+ this.readable = true;
+ this.paused = false;
+ this._cursor = cursor;
+ this._destroyed = null;
+
+ // give time to hook up events
+ var self = this;
+ process.nextTick(function () {
+ self._init();
+ });
+}
+
+/**
+ * Inherit from Stream
+ * @ignore
+ * @api private
+ */
+CursorStream.prototype.__proto__ = Stream.prototype;
+
+/**
+ * Flag stating whether or not this stream is readable.
+ */
+CursorStream.prototype.readable;
+
+/**
+ * Flag stating whether or not this stream is paused.
+ */
+CursorStream.prototype.paused;
+
+/**
+ * Initialize the cursor.
+ * @ignore
+ * @api private
+ */
+CursorStream.prototype._init = function () {
+ if (this._destroyed) return;
+ this._next();
+}
+
+/**
+ * Pull the next document from the cursor.
+ * @ignore
+ * @api private
+ */
+CursorStream.prototype._next = function () {
+ if (this.paused || this._destroyed) return;
+
+ var self = this;
+
+ // nextTick is necessary to avoid stack overflows when
+ // dealing with large result sets.
+ process.nextTick(function () {
+ self._cursor.nextObject(function (err, doc) {
+ self._onNextObject(err, doc);
+ });
+ });
+}
+
+/**
+ * Handle each document as its returned from the cursor.
+ * @ignore
+ * @api private
+ */
+CursorStream.prototype._onNextObject = function (err, doc) {
+ if (err) return this.destroy(err);
+
+ // when doc is null we hit the end of the cursor
+ if (!doc) return this.destroy();
+
+ this.emit('data', doc);
+ this._next();
+}
+
+/**
+ * Pauses the stream.
+ *
+ * @api public
+ */
+CursorStream.prototype.pause = function () {
+ this.paused = true;
+}
+
+/**
+ * Resumes the stream.
+ *
+ * @api public
+ */
+CursorStream.prototype.resume = function () {
+ this.paused = false;
+ this._next();
+}
+
+/**
+ * Destroys the stream, closing the underlying
+ * cursor. No more events will be emitted.
+ *
+ * @api public
+ */
+CursorStream.prototype.destroy = function (err) {
+ if (this._destroyed) return;
+ this._destroyed = true;
+ this.readable = false;
+
+ this._cursor.close();
+
+ if (err) {
+ this.emit('error', err);
+ }
+
+ this.emit('close');
+}
+
+// TODO - maybe implement the raw option to pass binary?
+//CursorStream.prototype.setEncoding = function () {
+//}
+
+module.exports = exports = CursorStream;
diff --git a/node_modules/mongodb/lib/mongodb/db.js b/node_modules/mongodb/lib/mongodb/db.js
new file mode 100644
index 0000000..b892fbd
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/db.js
@@ -0,0 +1,1788 @@
+/**
+ * Module dependencies.
+ * @ignore
+ */
+var QueryCommand = require('./commands/query_command').QueryCommand,
+ DbCommand = require('./commands/db_command').DbCommand,
+ MongoReply = require('./responses/mongo_reply').MongoReply,
+ Admin = require('./admin').Admin,
+ Collection = require('./collection').Collection,
+ Server = require('./connection/server').Server,
+ ReplSetServers = require('./connection/repl_set_servers').ReplSetServers,
+ Cursor = require('./cursor').Cursor,
+ EventEmitter = require('events').EventEmitter,
+ inherits = require('util').inherits,
+ crypto = require('crypto');
+
+/**
+ * Internal class for callback storage
+ * @ignore
+ */
+var CallbackStore = function() {
+ // Make class an event emitter
+ EventEmitter.call(this);
+ // Add a info about call variable
+ this._notReplied = {};
+}
+
+/**
+ * @ignore
+ */
+inherits(CallbackStore, EventEmitter);
+
+/**
+ * Create a new Db instance.
+ *
+ * Options
+ * - **strict** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, execute insert with a getLastError command returning the result of the insert command.
+ * - **native_parser** {Boolean, default:false}, use c++ bson parser.
+ * - **forceServerObjectId** {Boolean, default:false}, force server to create _id fields instead of client.
+ * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation.
+ * - **slaveOk** {Boolean, default:false}, allow reads from secondaries.
+ * - **serializeFunctions** {Boolean, default:false}, serialize functions.
+ * - **raw** {Boolean, default:false}, peform operations using raw bson buffers.
+ * - **recordQueryStats** {Boolean, default:false}, record query statistics during execution.
+ * - **reaper** {Boolean, default:false}, enables the reaper, timing out calls that never return.
+ * - **reaperInterval** {Number, default:10000}, number of miliseconds between reaper wakups.
+ * - **reaperTimeout** {Number, default:30000}, the amount of time before a callback times out.
+ * - **retryMiliSeconds** {Number, default:5000}, number of miliseconds between retries.
+ * - **numberOfRetries** {Number, default:5}, number of retries off connection.
+ *
+ * @class Represents a Collection
+ * @param {String} databaseName name of the database.
+ * @param {Object} serverConfig server config object.
+ * @param {Object} [options] additional options for the collection.
+ */
+function Db(databaseName, serverConfig, options) {
+
+ if(!(this instanceof Db)) return new Db(databaseName, serverConfig, options);
+
+ EventEmitter.call(this);
+ this.databaseName = databaseName;
+ this.serverConfig = serverConfig;
+ this.options = options == null ? {} : options;
+ // State to check against if the user force closed db
+ this._applicationClosed = false;
+ // Fetch the override flag if any
+ var overrideUsedFlag = this.options['override_used_flag'] == null ? false : this.options['override_used_flag'];
+ // Verify that nobody is using this config
+ if(!overrideUsedFlag && typeof this.serverConfig == 'object' && this.serverConfig._isUsed()) {
+ throw new Error("A Server or ReplSetServers instance cannot be shared across multiple Db instances");
+ } else if(!overrideUsedFlag && typeof this.serverConfig == 'object'){
+ // Set being used
+ this.serverConfig._used = true;
+ }
+
+ // Ensure we have a valid db name
+ validateDatabaseName(databaseName);
+
+ // Contains all the connections for the db
+ try {
+ this.native_parser = this.options.native_parser;
+ // The bson lib
+ var bsonLib = this.bsonLib = this.options.native_parser ? require('bson').BSONNative : new require('bson').BSONPure;
+ // Fetch the serializer object
+ var BSON = bsonLib.BSON;
+ // Create a new instance
+ this.bson = new BSON([bsonLib.Long, bsonLib.ObjectID, bsonLib.Binary, bsonLib.Code, bsonLib.DBRef, bsonLib.Symbol, bsonLib.Double, bsonLib.Timestamp, bsonLib.MaxKey, bsonLib.MinKey]);
+ // Backward compatibility to access types
+ this.bson_deserializer = bsonLib;
+ this.bson_serializer = bsonLib;
+ } catch (err) {
+ // If we tried to instantiate the native driver
+ throw "Native bson parser not compiled, please compile or avoid using native_parser=true";
+ }
+
+ // Internal state of the server
+ this._state = 'disconnected';
+
+ this.pkFactory = this.options.pk == null ? bsonLib.ObjectID : this.options.pk;
+ this.forceServerObjectId = this.options.forceServerObjectId != null ? this.options.forceServerObjectId : false;
+ // Added strict
+ this.strict = this.options.strict == null ? false : this.options.strict;
+ this.notReplied ={};
+ this.isInitializing = true;
+ this.auths = [];
+ this.openCalled = false;
+
+ // Command queue, keeps a list of incoming commands that need to be executed once the connection is up
+ this.commands = [];
+
+ // Contains all the callbacks
+ this._callBackStore = new CallbackStore();
+
+ // Set up logger
+ this.logger = this.options.logger != null
+ && (typeof this.options.logger.debug == 'function')
+ && (typeof this.options.logger.error == 'function')
+ && (typeof this.options.logger.log == 'function')
+ ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}};
+ // Allow slaveOk
+ this.slaveOk = this.options["slave_ok"] == null ? false : this.options["slave_ok"];
+
+ var self = this;
+ // Associate the logger with the server config
+ this.serverConfig.logger = this.logger;
+ this.tag = new Date().getTime();
+ // Just keeps list of events we allow
+ this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[]};
+
+ // Controls serialization options
+ this.serializeFunctions = this.options.serializeFunctions != null ? this.options.serializeFunctions : false;
+
+ // Raw mode
+ this.raw = this.options.raw != null ? this.options.raw : false;
+
+ // Record query stats
+ this.recordQueryStats = this.options.recordQueryStats != null ? this.options.recordQueryStats : false;
+
+ // If we have server stats let's make sure the driver objects have it enabled
+ if(this.recordQueryStats == true) {
+ this.serverConfig.enableRecordQueryStats(true);
+ }
+
+ // Reaper enable setting
+ this.reaperEnabled = this.options.reaper != null ? this.options.reaper : false;
+ this._lastReaperTimestamp = new Date().getTime();
+
+ // Retry information
+ this.retryMiliSeconds = this.options.retryMiliSeconds != null ? this.options.retryMiliSeconds : 5000;
+ this.numberOfRetries = this.options.numberOfRetries != null ? this.options.numberOfRetries : 5;
+
+ // Reaper information
+ this.reaperInterval = this.options.reaperInterval != null ? this.options.reaperInterval : 10000;
+ this.reaperTimeout = this.options.reaperTimeout != null ? this.options.reaperTimeout : 30000;
+
+ // get self
+ var self = this;
+ // State of the db connection
+ Object.defineProperty(this, "state", { enumerable: true
+ , get: function () {
+ return this.serverConfig._serverState;
+ }
+ });
+};
+
+/**
+ * The reaper cleans up any callbacks that have not returned inside the space set by
+ * the parameter reaperTimeout, it will only attempt to reap if the time since last reap
+ * is bigger or equal to the reaperInterval value
+ * @ignore
+ */
+var reaper = function(dbInstance, reaperInterval, reaperTimeout) {
+ // Get current time, compare to reaper interval
+ var currentTime = new Date().getTime();
+ // Now calculate current time difference to check if it's time to reap
+ if((currentTime - dbInstance._lastReaperTimestamp) >= reaperInterval) {
+ // Save current timestamp for next reaper iteration
+ dbInstance._lastReaperTimestamp = currentTime;
+ // Get all non-replied to messages
+ var keys = Object.keys(dbInstance._callBackStore._notReplied);
+ // Iterate over all callbacks
+ for(var i = 0; i < keys.length; i++) {
+ // Fetch the current key
+ var key = keys[i];
+ // Get info element
+ var info = dbInstance._callBackStore._notReplied[key];
+ // If it's timed out let's remove the callback and return an error
+ if((currentTime - info.start) > reaperTimeout) {
+ // Cleanup
+ delete dbInstance._callBackStore._notReplied[key];
+ // Perform callback in next Tick
+ process.nextTick(function() {
+ dbInstance._callBackStore.emit(key, new Error("operation timed out"), null);
+ });
+ }
+ }
+ // Return reaping was done
+ return true;
+ } else {
+ // No reaping done
+ return false;
+ }
+}
+
+/**
+ * @ignore
+ */
+function validateDatabaseName(databaseName) {
+ if(typeof databaseName !== 'string') throw new Error("database name must be a string");
+ if(databaseName.length === 0) throw new Error("database name cannot be the empty string");
+
+ var invalidChars = [" ", ".", "$", "/", "\\"];
+ for(var i = 0; i < invalidChars.length; i++) {
+ if(databaseName.indexOf(invalidChars[i]) != -1) throw new Error("database names cannot contain the character '" + invalidChars[i] + "'");
+ }
+}
+
+/**
+ * @ignore
+ */
+inherits(Db, EventEmitter);
+
+/**
+ * Initialize the database connection.
+ *
+ * @param {Function} callback returns index information.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.open = function(callback) {
+ var self = this;
+
+ // Check that the user has not called this twice
+ if(this.openCalled) {
+ // Close db
+ this.close();
+ // Throw error
+ throw new Error("db object already connecting, open cannot be called multiple times");
+ }
+
+ // Set that db has been opened
+ this.openCalled = true;
+
+ // Set the status of the server
+ self._state = 'connecting';
+ // Set up connections
+ if(self.serverConfig instanceof Server || self.serverConfig instanceof ReplSetServers) {
+ self.serverConfig.connect(self, {firstCall: true}, function(err, result) {
+ if(err != null) {
+ // Return error from connection
+ return callback(err, null);
+ }
+ // Set the status of the server
+ self._state = 'connected';
+ // Callback
+ return callback(null, self);
+ });
+ } else {
+ return callback(Error("Server parameter must be of type Server or ReplSetServers"), null);
+ }
+};
+
+/**
+ * Create a new Db instance sharing the current socket connections.
+ *
+ * @param {String} dbName the name of the database we want to use.
+ * @return {Db} a db instance using the new database.
+ * @api public
+ */
+Db.prototype.db = function(dbName) {
+ // Copy the options and add out internal override of the not shared flag
+ var options = {};
+ for(var key in this.options) {
+ options[key] = this.options[key];
+ }
+ // Add override flag
+ options['override_used_flag'] = true;
+ // Create a new db instance
+ var newDbInstance = new Db(dbName, this.serverConfig, options);
+ // Add the instance to the list of approved db instances
+ var allServerInstances = this.serverConfig.allServerInstances();
+ // Add ourselves to all server callback instances
+ for(var i = 0; i < allServerInstances.length; i++) {
+ var server = allServerInstances[i];
+ server.dbInstances.push(newDbInstance);
+ }
+ // Return new db object
+ return newDbInstance;
+}
+
+/**
+ * Close the current db connection, including all the child db instances. Emits close event if no callback is provided.
+ *
+ * @param {Boolean} [forceClose] connection can never be reused.
+ * @param {Function} [callback] returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.close = function(forceClose, callback) {
+ // Unpack calls
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ // Ensure we force close all connections
+ this._applicationClosed = args.length ? args.shift() : false;
+ // Remove all listeners and close the connection
+ this.serverConfig.close(callback);
+ // Emit the close event
+ if(typeof callback !== 'function') this.emit("close");
+
+ // Emit close event across all db instances sharing the sockets
+ var allServerInstances = this.serverConfig.allServerInstances();
+ // Fetch the first server instance
+ if(Array.isArray(allServerInstances) && allServerInstances.length > 0) {
+ var server = allServerInstances[0];
+ // For all db instances signal all db instances
+ if(Array.isArray(server.dbInstances) && server.dbInstances.length > 1) {
+ for(var i = 0; i < server.dbInstances.length; i++) {
+ var dbInstance = server.dbInstances[i];
+ // Check if it's our current db instance and skip if it is
+ if(dbInstance.databaseName !== this.databaseName && dbInstance.tag !== this.tag) {
+ server.dbInstances[i].emit("close");
+ }
+ }
+ }
+ }
+
+ // Remove all listeners
+ this.removeAllEventListeners();
+ // You can reuse the db as everything is shut down
+ this.openCalled = false;
+};
+
+/**
+ * Access the Admin database
+ *
+ * @param {Function} [callback] returns the results.
+ * @return {Admin} the admin db object.
+ * @api public
+ */
+Db.prototype.admin = function(callback) {
+ if(callback == null) return new Admin(this);
+ callback(null, new Admin(this));
+};
+
+/**
+ * Returns a cursor to all the collection information.
+ *
+ * @param {String} [collectionName] the collection name we wish to retrieve the information from.
+ * @param {Function} callback returns option results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.collectionsInfo = function(collectionName, callback) {
+ if(callback == null && typeof collectionName == 'function') { callback = collectionName; collectionName = null; }
+ // Create selector
+ var selector = {};
+ // If we are limiting the access to a specific collection name
+ if(collectionName != null) selector.name = this.databaseName + "." + collectionName;
+
+ // Return Cursor
+ // callback for backward compatibility
+ if(callback) {
+ callback(null, new Cursor(this, new Collection(this, DbCommand.SYSTEM_NAMESPACE_COLLECTION), selector));
+ } else {
+ return new Cursor(this, new Collection(this, DbCommand.SYSTEM_NAMESPACE_COLLECTION), selector);
+ }
+};
+
+/**
+ * Get the list of all collection names for the specified db
+ *
+ * @param {String} [collectionName] the collection name we wish to filter by.
+ * @param {Function} callback returns option results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.collectionNames = function(collectionName, callback) {
+ if(callback == null && typeof collectionName == 'function') { callback = collectionName; collectionName = null; }
+ var self = this;
+ // Let's make our own callback to reuse the existing collections info method
+ self.collectionsInfo(collectionName, function(err, cursor) {
+ if(err != null) return callback(err, null);
+
+ cursor.toArray(function(err, documents) {
+ if(err != null) return callback(err, null);
+
+ // List of result documents that have been filtered
+ var filtered_documents = [];
+ // Remove any collections that are not part of the db or a system db signed with $
+ documents.forEach(function(document) {
+ if(!(document.name.indexOf(self.databaseName) == -1 || document.name.indexOf('$') != -1))
+ filtered_documents.push(document);
+ });
+ // Return filtered items
+ callback(null, filtered_documents);
+ });
+ });
+};
+
+/**
+ * Fetch a specific collection (containing the actual collection information)
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ * - **slaveOk** {Boolean, default:false}, Allow reads from secondaries.
+ * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document.
+ * - **raw** {Boolean, default:false}, perform all operations using raw bson objects.
+ * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation.
+ *
+ * @param {String} collectionName the collection name we wish to access.
+ * @param {Object} [options] returns option results.
+ * @param {Function} [callback] returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.collection = function(collectionName, options, callback) {
+ var self = this;
+ if(typeof options === "function") { callback = options; options = {}; }
+ // Execute safe
+ if(options && options.safe || this.strict) {
+ self.collectionNames(collectionName, function(err, collections) {
+ if(err != null) return callback(err, null);
+
+ if(collections.length == 0) {
+ return callback(new Error("Collection " + collectionName + " does not exist. Currently in strict mode."), null);
+ } else {
+ try {
+ var collection = new Collection(self, collectionName, self.pkFactory, options);
+ } catch(err) {
+ return callback(err, null);
+ }
+ return callback(null, collection);
+ }
+ });
+ } else {
+ try {
+ var collection = new Collection(self, collectionName, self.pkFactory, options);
+ } catch(err) {
+ if(callback == null) {
+ throw err;
+ } else {
+ return callback(err, null);
+ }
+ }
+
+ // If we have no callback return collection object
+ return callback == null ? collection : callback(null, collection);
+ }
+};
+
+/**
+ * Fetch all collections for the current db.
+ *
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.collections = function(callback) {
+ var self = this;
+ // Let's get the collection names
+ self.collectionNames(function(err, documents) {
+ if(err != null) return callback(err, null);
+ var collections = [];
+ documents.forEach(function(document) {
+ collections.push(new Collection(self, document.name.replace(self.databaseName + ".", ''), self.pkFactory));
+ });
+ // Return the collection objects
+ callback(null, collections);
+ });
+};
+
+/**
+ * Evaluate javascript on the server
+ *
+ * Options
+ * - **nolock** {Boolean, default:false}, Tell MongoDB not to block on the evaulation of the javascript.
+ *
+ * @param {Code} code javascript to execute on server.
+ * @param {Object|Array} [parameters] the parameters for the call.
+ * @param {Object} [options] the options
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.eval = function(code, parameters, options, callback) {
+ // Unpack calls
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ parameters = args.length ? args.shift() : parameters;
+ options = args.length ? args.shift() : {};
+
+ var finalCode = code;
+ var finalParameters = [];
+ // If not a code object translate to one
+ if(!(finalCode instanceof this.bsonLib.Code)) {
+ finalCode = new this.bsonLib.Code(finalCode);
+ }
+
+ // Ensure the parameters are correct
+ if(parameters != null && parameters.constructor != Array && typeof parameters !== 'function') {
+ finalParameters = [parameters];
+ } else if(parameters != null && parameters.constructor == Array && typeof parameters !== 'function') {
+ finalParameters = parameters;
+ }
+ // Create execution selector
+ var selector = {'$eval':finalCode, 'args':finalParameters};
+ // Check if the nolock parameter is passed in
+ if(options['nolock']) {
+ selector['nolock'] = options['nolock'];
+ }
+
+ // Iterate through all the fields of the index
+ new Cursor(this, new Collection(this, DbCommand.SYSTEM_COMMAND_COLLECTION), selector, options, 0, -1).nextObject(function(err, result) {
+ if(err != null) return callback(err, null);
+
+ if(result.ok == 1) {
+ callback(null, result.retval);
+ } else {
+ callback(new Error("eval failed: " + result.errmsg), null); return;
+ }
+ });
+};
+
+/**
+ * Dereference a dbref, against a db
+ *
+ * @param {DBRef} dbRef db reference object we wish to resolve.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.dereference = function(dbRef, callback) {
+ var db = this;
+ // If we have a db reference then let's get the db first
+ if(dbRef.db != null) db = this.db(dbRef.db);
+ // Fetch the collection and find the reference
+ db.collection(dbRef.namespace, function(err, collection) {
+ if(err != null) return callback(err, null);
+
+ collection.findOne({'_id':dbRef.oid}, function(err, result) {
+ callback(err, result);
+ });
+ });
+};
+
+/**
+ * Logout user from server, fire off on all connections and remove all auth info
+ *
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.logout = function(callback) {
+ var self = this;
+ // Let's generate the logout command object
+ var logoutCommand = DbCommand.logoutCommand(self, {logout:1});
+ self._executeQueryCommand(logoutCommand, {onAll:true}, function(err, result) {
+ // Reset auth
+ self.auths = [];
+ // Handle any errors
+ if(err == null && result.documents[0].ok == 1) {
+ callback(null, true);
+ } else {
+ err != null ? callback(err, false) : callback(new Error(result.documents[0].errmsg), false);
+ }
+ });
+}
+
+/**
+ * Authenticate a user against the server.
+ *
+ * @param {String} username username.
+ * @param {String} password password.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.authenticate = function(username, password, callback) {
+ var self = this;
+
+ // Push the new auth if we have no previous record
+ self.auths = [{'username':username, 'password':password}];
+ // Get the amount of connections in the pool to ensure we have authenticated all comments
+ var numberOfConnections = this.serverConfig.allRawConnections().length;
+ var errorObject = null;
+
+ // Execute all four
+ this._executeQueryCommand(DbCommand.createGetNonceCommand(self), {onAll:true}, function(err, result, connection) {
+ // Execute on all the connections
+ if(err == null) {
+ // Nonce used to make authentication request with md5 hash
+ var nonce = result.documents[0].nonce;
+ // Execute command
+ self._executeQueryCommand(DbCommand.createAuthenticationCommand(self, username, password, nonce), {connection:connection}, function(err, result) {
+ // Ensure we save any error
+ if(err) {
+ errorObject = err;
+ } else if(result.documents[0].err != null || result.documents[0].errmsg != null){
+ errorObject = self.wrap(result.documents[0]);
+ }
+
+ // Count down
+ numberOfConnections = numberOfConnections - 1;
+
+ // If we are done with the callbacks return
+ if(numberOfConnections <= 0) {
+ if(errorObject == null && result.documents[0].ok == 1) {
+ callback(errorObject, true);
+ } else {
+ callback(errorObject, false);
+ }
+ }
+ });
+ }
+ });
+};
+
+/**
+ * Add a user to the database.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ *
+ * @param {String} username username.
+ * @param {String} password password.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.addUser = function(username, password, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ // Figure out the safe mode settings
+ var safe = self.strict != null && self.strict == false ? true : self.strict;
+ // Override with options passed in if applicable
+ safe = options != null && options['safe'] != null ? options['safe'] : safe;
+ // Ensure it's at least set to safe
+ safe = safe == null ? true : safe;
+
+ // Use node md5 generator
+ var md5 = crypto.createHash('md5');
+ // Generate keys used for authentication
+ md5.update(username + ":mongo:" + password);
+ var userPassword = md5.digest('hex');
+ // Fetch a user collection
+ this.collection(DbCommand.SYSTEM_USER_COLLECTION, function(err, collection) {
+ collection.find({user: username}).toArray(function(err, documents) {
+ // We got an error (f.ex not authorized)
+ if(err != null) return callback(err, null);
+ // We have a user, let's update the password
+ if(documents.length > 0) {
+ collection.update({user: username},{user: username, pwd: userPassword}, {safe:safe}, function(err, results) {
+ callback(err, documents);
+ });
+ } else {
+ collection.insert({user: username, pwd: userPassword}, {safe:safe}, function(err, documents) {
+ callback(err, documents);
+ });
+ }
+ });
+ });
+};
+
+/**
+ * Remove a user from a database
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ *
+ * @param {String} username username.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.removeUser = function(username, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ // Figure out the safe mode settings
+ var safe = self.strict != null && self.strict == false ? true : self.strict;
+ // Override with options passed in if applicable
+ safe = options != null && options['safe'] != null ? options['safe'] : safe;
+ // Ensure it's at least set to safe
+ safe = safe == null ? true : safe;
+
+ // Fetch a user collection
+ this.collection(DbCommand.SYSTEM_USER_COLLECTION, function(err, collection) {
+ collection.findOne({user: username}, function(err, user) {
+ if(user != null) {
+ collection.remove({user: username}, {safe:safe}, function(err, result) {
+ callback(err, true);
+ });
+ } else {
+ callback(err, false);
+ }
+ });
+ });
+};
+
+/**
+ * Creates a collection on a server pre-allocating space, need to create f.ex capped collections.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ * - **slaveOk** {Boolean, default:false}, Allow reads from secondaries.
+ * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document.
+ * - **raw** {Boolean, default:false}, perform all operations using raw bson objects.
+ * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation.
+ * - **capped** {Boolean, default:false}, create a capped collection.
+ * - **size** {Number}, the size of the capped collection in bytes.
+ * - **max** {Number}, the maximum number of documents in the capped collection.
+ * - **autoIndexId** {Boolean, default:false}, create an index on the _id field of the document, not created automatically on capped collections.
+ *
+ * @param {String} collectionName the collection name we wish to access.
+ * @param {Object} [options] returns option results.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.createCollection = function(collectionName, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : null;
+ var self = this;
+
+ // Figure out the safe mode settings
+ var safe = self.strict != null && self.strict == false ? true : self.strict;
+ // Override with options passed in if applicable
+ safe = options != null && options['safe'] != null ? options['safe'] : safe;
+ // Ensure it's at least set to safe
+ safe = safe == null ? true : safe;
+
+ // Check if we have the name
+ this.collectionNames(collectionName, function(err, collections) {
+ if(err != null) return callback(err, null);
+
+ var found = false;
+ collections.forEach(function(collection) {
+ if(collection.name == self.databaseName + "." + collectionName) found = true;
+ });
+
+ // If the collection exists either throw an exception (if db in strict mode) or return the existing collection
+ if(found && ((options && options.safe) || self.strict)) {
+ return callback(new Error("Collection " + collectionName + " already exists. Currently in strict mode."), null);
+ } else if(found){
+ try {
+ var collection = new Collection(self, collectionName, self.pkFactory, options);
+ } catch(err) {
+ return callback(err, null);
+ }
+ return callback(null, collection);
+ }
+
+ // Create a new collection and return it
+ self._executeQueryCommand(DbCommand.createCreateCollectionCommand(self, collectionName, options), {read:false, safe:safe}, function(err, result) {
+ var document = result.documents[0];
+ // If we have no error let's return the collection
+ if(err == null && document.ok == 1) {
+ try {
+ var collection = new Collection(self, collectionName, self.pkFactory, options);
+ } catch(err) {
+ return callback(err, null);
+ }
+ return callback(null, collection);
+ } else {
+ err != null ? callback(err, null) : callback(self.wrap(document), null);
+ }
+ });
+ });
+};
+
+/**
+ * Execute a command hash against MongoDB. This lets you acess any commands not available through the api on the server.
+ *
+ * @param {Object} selector the command hash to send to the server, ex: {ping:1}.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.command = function(selector, callback) {
+ var cursor = new Cursor(this, new Collection(this, DbCommand.SYSTEM_COMMAND_COLLECTION), selector, {}, 0, -1, null, null, null, null, QueryCommand.OPTS_NO_CURSOR_TIMEOUT);
+ cursor.nextObject(callback);
+};
+
+/**
+ * Drop a collection from the database, removing it permanently. New accesses will create a new collection.
+ *
+ * @param {String} collectionName the name of the collection we wish to drop.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.dropCollection = function(collectionName, callback) {
+ var self = this;
+
+ // Drop the collection
+ this._executeQueryCommand(DbCommand.createDropCollectionCommand(this, collectionName), function(err, result) {
+ if(err == null && result.documents[0].ok == 1) {
+ if(callback != null) return callback(null, true);
+ } else {
+ if(callback != null) err != null ? callback(err, null) : callback(self.wrap(result.documents[0]), null);
+ }
+ });
+};
+
+/**
+ * Rename a collection.
+ *
+ * @param {String} fromCollection the name of the current collection we wish to rename.
+ * @param {String} toCollection the new name of the collection.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.renameCollection = function(fromCollection, toCollection, callback) {
+ var self = this;
+
+ // Execute the command, return the new renamed collection if successful
+ this._executeQueryCommand(DbCommand.createRenameCollectionCommand(this, fromCollection, toCollection), function(err, result) {
+ if(err == null && result.documents[0].ok == 1) {
+ if(callback != null) return callback(null, new Collection(self, toCollection, self.pkFactory));
+ } else {
+ if(callback != null) err != null ? callback(err, null) : callback(self.wrap(result.documents[0]), null);
+ }
+ });
+};
+
+/**
+ * Return last error message for the given connection, note options can be combined.
+ *
+ * Options
+ * - **fsync** {Boolean, default:false}, option forces the database to fsync all files before returning.
+ * - **j** {Boolean, default:false}, awaits the journal commit before returning, > MongoDB 2.0.
+ * - **w** {Number}, until a write operation has been replicated to N servers.
+ * - **wtimeout** {Number}, number of miliseconds to wait before timing out.
+ *
+ * Connection Options
+ * - **connection** {Connection}, fire the getLastError down a specific connection.
+ *
+ * @param {Object} [options] returns option results.
+ * @param {Object} [connectionOptions] returns option results.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.lastError = function(options, connectionOptions, callback) {
+ // Unpack calls
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+ connectionOptions = args.length ? args.shift() : {};
+
+ this._executeQueryCommand(DbCommand.createGetLastErrorCommand(options, this), connectionOptions, function(err, error) {
+ callback(err, error && error.documents);
+ });
+};
+
+/**
+ * Legacy method calls.
+ *
+ * @ignore
+ * @api private
+ */
+Db.prototype.error = Db.prototype.lastError;
+Db.prototype.lastStatus = Db.prototype.lastError;
+
+/**
+ * Return all errors up to the last time db reset_error_history was called.
+ *
+ * Options
+ * - **connection** {Connection}, fire the getLastError down a specific connection.
+ *
+ * @param {Object} [options] returns option results.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.previousErrors = function(options, callback) {
+ // Unpack calls
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ this._executeQueryCommand(DbCommand.createGetPreviousErrorsCommand(this), options, function(err, error) {
+ callback(err, error.documents);
+ });
+};
+
+/**
+ * Runs a command on the database.
+ * @ignore
+ * @api private
+ */
+Db.prototype.executeDbCommand = function(command_hash, options, callback) {
+ if(callback == null) { callback = options; options = {}; }
+ this._executeQueryCommand(DbCommand.createDbSlaveOkCommand(this, command_hash, options), options, callback);
+};
+
+/**
+ * Runs a command on the database as admin.
+ * @ignore
+ * @api private
+ */
+Db.prototype.executeDbAdminCommand = function(command_hash, options, callback) {
+ if(callback == null) { callback = options; options = {}; }
+ this._executeQueryCommand(DbCommand.createAdminDbCommand(this, command_hash, options), callback);
+};
+
+/**
+ * Resets the error history of the mongo instance.
+ *
+ * Options
+ * - **connection** {Connection}, fire the getLastError down a specific connection.
+ *
+ * @param {Object} [options] returns option results.
+ * @param {Function} callback returns the results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.resetErrorHistory = function(options, callback) {
+ // Unpack calls
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ this._executeQueryCommand(DbCommand.createResetErrorHistoryCommand(this), options, function(err, error) {
+ callback(err, error.documents);
+ });
+};
+
+/**
+ * Creates an index on the collection.
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB.
+ * - **unique** {Boolean, default:false}, creates an unique index.
+ * - **sparse** {Boolean, default:false}, creates a sparse index.
+ * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible.
+ * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
+ * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates.
+ * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates.
+ *
+ * @param {String} collectionName name of the collection to create the index on.
+ * @param {Object} fieldOrSpec fieldOrSpec that defines the index.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback for results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.createIndex = function(collectionName, fieldOrSpec, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+ options = typeof callback === 'function' ? options : callback;
+ options = options == null ? {} : options;
+
+ // Collect errorOptions
+ var errorOptions = options.safe != null ? options.safe : null;
+ errorOptions = errorOptions == null && self.strict != null ? self.strict : errorOptions;
+
+ // If we have a write concern set and no callback throw error
+ if(errorOptions != null && errorOptions != false && (typeof callback !== 'function' && typeof options !== 'function')) throw new Error("safe cannot be used without a callback");
+
+ // Create command
+ var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, options);
+ // Default command options
+ var commandOptions = {};
+
+ // If we have error conditions set handle them
+ if(errorOptions && errorOptions != false) {
+ // Insert options
+ commandOptions['read'] = false;
+ // If we have safe set set async to false
+ if(errorOptions == null) commandOptions['async'] = true;
+
+ // Set safe option
+ commandOptions['safe'] = errorOptions;
+ // If we have an error option
+ if(typeof errorOptions == 'object') {
+ var keys = Object.keys(errorOptions);
+ for(var i = 0; i < keys.length; i++) {
+ commandOptions[keys[i]] = errorOptions[keys[i]];
+ }
+ }
+
+ // Execute insert command
+ this._executeInsertCommand(command, commandOptions, function(err, result) {
+ if(err != null) return callback(err, null);
+
+ result = result && result.documents;
+ if (result[0].err) {
+ callback(self.wrap(result[0]));
+ } else {
+ callback(null, command.documents[0].name);
+ }
+ });
+ } else {
+ // Execute insert command
+ var result = this._executeInsertCommand(command, commandOptions);
+ // If no callback just return
+ if(!callback) return;
+ // If error return error
+ if(result instanceof Error) {
+ return callback(result);
+ }
+ // Otherwise just return
+ return callback(null, null);
+ }
+};
+
+/**
+ * Ensures that an index exists, if it does not it creates it
+ *
+ * Options
+ * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a
+ * - **unique** {Boolean, default:false}, creates an unique index.
+ * - **sparse** {Boolean, default:false}, creates a sparse index.
+ * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible.
+ * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
+ * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates.
+ * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates.
+ * - **v** {Number}, specify the format version of the indexes.
+ *
+ * @param {String} collectionName name of the collection to create the index on.
+ * @param {Object} fieldOrSpec fieldOrSpec that defines the index.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback for results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.ensureIndex = function(collectionName, fieldOrSpec, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+ options = typeof callback === 'function' ? options : callback;
+ options = options == null ? {} : options;
+
+ // Collect errorOptions
+ var errorOptions = options.safe != null ? options.safe : null;
+ errorOptions = errorOptions == null && self.strict != null ? self.strict : errorOptions;
+
+ // If we have a write concern set and no callback throw error
+ if(errorOptions != null && errorOptions != false && (typeof callback !== 'function' && typeof options !== 'function')) throw new Error("safe cannot be used without a callback");
+
+ // Create command
+ var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, options);
+ var index_name = command.documents[0].name;
+
+ // Default command options
+ var commandOptions = {};
+ // Check if the index allready exists
+ this.indexInformation(collectionName, function(err, collectionInfo) {
+ if(err != null) return callback(err, null);
+
+ if(!collectionInfo[index_name]) {
+ // If we have error conditions set handle them
+ if(errorOptions && errorOptions != false) {
+ // Insert options
+ commandOptions['read'] = false;
+ // If we have safe set set async to false
+ if(errorOptions == null) commandOptions['async'] = true;
+
+ // Set safe option
+ commandOptions['safe'] = errorOptions;
+ // If we have an error option
+ if(typeof errorOptions == 'object') {
+ var keys = Object.keys(errorOptions);
+ for(var i = 0; i < keys.length; i++) {
+ commandOptions[keys[i]] = errorOptions[keys[i]];
+ }
+ }
+
+ self._executeInsertCommand(command, commandOptions, function(err, result) {
+ // Only callback if we have one specified
+ if(typeof callback === 'function') {
+ if(err != null) return callback(err, null);
+
+ result = result && result.documents;
+ if (result[0].err) {
+ callback(self.wrap(result[0]));
+ } else {
+ callback(null, command.documents[0].name);
+ }
+ }
+ });
+ } else {
+ // Execute insert command
+ var result = self._executeInsertCommand(command, commandOptions);
+ // If no callback just return
+ if(!callback) return;
+ // If error return error
+ if(result instanceof Error) {
+ return callback(result);
+ }
+ // Otherwise just return
+ return callback(null, index_name);
+ }
+ } else {
+ if(typeof callback === 'function') return callback(null, index_name);
+ }
+ });
+};
+
+/**
+ * Returns the information available on allocated cursors.
+ *
+ * @param {Function} callback for results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.cursorInfo = function(callback) {
+ this._executeQueryCommand(DbCommand.createDbSlaveOkCommand(this, {'cursorInfo':1}), function(err, result) {
+ callback(err, result.documents[0]);
+ });
+};
+
+/**
+ * Drop an index on a collection.
+ *
+ * @param {String} collectionName the name of the collection where the command will drop an index.
+ * @param {String} indexName name of the index to drop.
+ * @param {Function} callback for results.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.dropIndex = function(collectionName, indexName, callback) {
+ this._executeQueryCommand(DbCommand.createDropIndexCommand(this, collectionName, indexName), callback);
+};
+
+/**
+ * Reindex all indexes on the collection
+ * Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
+ *
+ * @param {String} collectionName the name of the collection.
+ * @param {Function} callback returns the results.
+ * @api public
+**/
+Db.prototype.reIndex = function(collectionName, callback) {
+ this._executeQueryCommand(DbCommand.createReIndexCommand(this, collectionName), function(err, result) {
+ if(err != null) {
+ callback(err, false);
+ } else if(result.documents[0].errmsg == null) {
+ callback(null, true);
+ } else {
+ callback(new Error(result.documents[0].errmsg), false);
+ }
+ });
+};
+
+/**
+ * Retrieves this collections index info.
+ *
+ * Options
+ * - **full** {Boolean, default:false}, returns the full raw index information.
+ *
+ * @param {String} collectionName the name of the collection.
+ * @param {Object} [options] additional options during update.
+ * @param {Function} callback returns the index information.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.indexInformation = function(collectionName, options, callback) {
+ // Unpack calls
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ collectionName = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : {};
+
+ // If we specified full information
+ var full = options['full'] == null ? false : options['full'];
+ // Build selector for the indexes
+ var selector = collectionName != null ? {ns: (this.databaseName + "." + collectionName)} : {};
+ // Iterate through all the fields of the index
+ new Cursor(this, new Collection(this, DbCommand.SYSTEM_INDEX_COLLECTION), selector).toArray(function(err, indexes) {
+ if(err != null) return callback(err, null);
+ // Contains all the information
+ var info = {};
+
+ // if full defined just return all the indexes directly
+ if(full) return callback(null, indexes);
+
+ // Process all the indexes
+ for(var i = 0; i < indexes.length; i++) {
+ var index = indexes[i];
+ // Let's unpack the object
+ info[index.name] = [];
+ for(var name in index.key) {
+ info[index.name].push([name, index.key[name]]);
+ }
+ }
+
+ // Return all the indexes
+ callback(null, info);
+ });
+};
+
+/**
+ * Drop a database.
+ *
+ * @param {Function} callback returns the index information.
+ * @return {null}
+ * @api public
+ */
+Db.prototype.dropDatabase = function(callback) {
+ var self = this;
+
+ this._executeQueryCommand(DbCommand.createDropDatabaseCommand(this), function(err, result) {
+ if (err == null && result.documents[0].ok == 1) {
+ callback(null, true);
+ } else {
+ if (err) {
+ callback(err, false);
+ } else {
+ callback(self.wrap(result.documents[0]), false);
+ }
+ }
+ });
+};
+
+/**
+ * Register a handler
+ * @ignore
+ * @api private
+ */
+Db.prototype._registerHandler = function(db_command, raw, connection, callback) {
+ // If we have an array of commands, chain them
+ var chained = Array.isArray(db_command);
+
+ // If they are chained we need to add a special handler situation
+ if(chained) {
+ // List off chained id's
+ var chainedIds = [];
+ // Add all id's
+ for(var i = 0; i < db_command.length; i++) chainedIds.push(db_command[i].getRequestId().toString());
+
+ // Register all the commands together
+ for(var i = 0; i < db_command.length; i++) {
+ var command = db_command[i];
+ // Add the callback to the store
+ this._callBackStore.once(command.getRequestId(), callback);
+ // Add the information about the reply
+ this._callBackStore._notReplied[command.getRequestId().toString()] = {start: new Date().getTime(), 'raw': raw, chained:chainedIds, connection:connection};
+ }
+ } else {
+ // Add the callback to the list of handlers
+ this._callBackStore.once(db_command.getRequestId(), callback);
+ // Add the information about the reply
+ this._callBackStore._notReplied[db_command.getRequestId().toString()] = {start: new Date().getTime(), 'raw': raw, connection:connection};
+ }
+}
+
+/**
+ *
+ * @ignore
+ * @api private
+ */
+Db.prototype._callHandler = function(id, document, err) {
+ // If there is a callback peform it
+ if(this._callBackStore.listeners(id).length >= 1) {
+ // Get info object
+ var info = this._callBackStore._notReplied[id];
+ // Delete the current object
+ delete this._callBackStore._notReplied[id];
+ // Emit to the callback of the object
+ this._callBackStore.emit(id, err, document, info.connection);
+ }
+}
+
+/**
+ *
+ * @ignore
+ * @api private
+ */
+Db.prototype._hasHandler = function(id) {
+ // If there is a callback peform it
+ return this._callBackStore.listeners(id).length >= 1;
+}
+
+/**
+ *
+ * @ignore
+ * @api private
+ */
+Db.prototype._removeHandler = function(id) {
+ // Remove the information
+ if(this._callBackStore._notReplied[id] != null) delete this._callBackStore._notReplied[id];
+ // Remove the callback if it's registered
+ this._callBackStore.removeAllListeners(id);
+ // Force cleanup _events, node.js seems to set it as a null value
+ if(this._callBackStore._events != null) delete this._callBackStore._events[id];
+}
+
+/**
+ *
+ * @ignore
+ * @api private
+ */
+Db.prototype._findHandler = function(id) {
+ var info = this._callBackStore._notReplied[id];
+ // Return the callback
+ return {info:info, callback:(this._callBackStore.listeners(id).length >= 1)}
+}
+
+/**
+ * @ignore
+ */
+var __executeQueryCommand = function(self, db_command, options, callback) {
+ // Options unpacking
+ var read = options['read'] != null ? options['read'] : false;
+ var raw = options['raw'] != null ? options['raw'] : self.raw;
+ var onAll = options['onAll'] != null ? options['onAll'] : false;
+ var specifiedConnection = options['connection'] != null ? options['connection'] : null;
+
+ // If we got a callback object
+ if(typeof callback === 'function' && !onAll) {
+ // Fetch either a reader or writer dependent on the specified read option
+ var connection = read == true || read === 'secondary' ? self.serverConfig.checkoutReader() : self.serverConfig.checkoutWriter(true);
+ // Override connection if needed
+ connection = specifiedConnection != null ? specifiedConnection : connection;
+ // Ensure we have a valid connection
+ if(connection == null) {
+ return callback(new Error("no open connections"));
+ } else if(connection instanceof Error) {
+ return callback(connection);
+ }
+
+ // Perform reaping of any dead connection
+ if(self.reaperEnabled) reaper(self, self.reaperInterval, self.reaperTimeout);
+
+ // Register the handler in the data structure
+ self._registerHandler(db_command, raw, connection, callback);
+
+ // Write the message out and handle any errors if there are any
+ connection.write(db_command, function(err) {
+ if(err != null) {
+ // Call the handler with an error
+ self._callHandler(db_command.getRequestId(), null, err);
+ }
+ });
+ } else if(typeof callback === 'function' && onAll) {
+ var connections = self.serverConfig.allRawConnections();
+ var numberOfEntries = connections.length;
+ // Go through all the connections
+ for(var i = 0; i < connections.length; i++) {
+ // Fetch a connection
+ var connection = connections[i];
+ // Override connection if needed
+ connection = specifiedConnection != null ? specifiedConnection : connection;
+ // Ensure we have a valid connection
+ if(connection == null) {
+ return callback(new Error("no open connections"));
+ } else if(connection instanceof Error) {
+ return callback(connection);
+ }
+
+ // Register the handler in the data structure
+ self._registerHandler(db_command, raw, connection, callback);
+
+ // Write the message out
+ connection.write(db_command, function(err) {
+ // Adjust the number of entries we need to process
+ numberOfEntries = numberOfEntries - 1;
+ // Remove listener
+ if(err != null) {
+ // Clean up listener and return error
+ self._removeHandler(db_command.getRequestId());
+ }
+
+ // No more entries to process callback with the error
+ if(numberOfEntries <= 0) {
+ callback(err);
+ }
+ });
+
+ // Update the db_command request id
+ db_command.updateRequestId();
+ }
+ } else {
+ // Fetch either a reader or writer dependent on the specified read option
+ var connection = read == true || read === 'secondary' ? self.serverConfig.checkoutReader() : self.serverConfig.checkoutWriter();
+ // Override connection if needed
+ connection = specifiedConnection != null ? specifiedConnection : connection;
+ // Ensure we have a valid connection
+ if(connection == null || connection instanceof Error) return null;
+ // Write the message out
+ connection.write(db_command, function(err) {
+ if(err != null) {
+ // Emit the error
+ self.emit("error", err);
+ }
+ });
+ }
+}
+
+/**
+ * @ignore
+ */
+var __retryCommandOnFailure = function(self, retryInMilliseconds, numberOfTimes, command, db_command, options, callback) {
+ if(this._state == 'connected' || this._state == 'disconnected') this._state = 'connecting';
+ // Number of retries done
+ var numberOfRetriesDone = numberOfTimes;
+ // Retry function, execute once
+ var retryFunction = function(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback) {
+ _self.serverConfig.connect(_self, {}, function(err, result) {
+ // Adjust the number of retries left
+ _numberOfRetriesDone = _numberOfRetriesDone - 1;
+ // Definitively restart
+ if(err != null && _numberOfRetriesDone > 0) {
+ _self._state = 'connecting';
+ // Force close the current connections
+ _self.serverConfig.close(function(err) {
+ // Retry the connect
+ setTimeout(function() {
+ retryFunction(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback);
+ }, _retryInMilliseconds);
+ });
+ } else if(err != null && _numberOfRetriesDone <= 0) {
+ _self._state = 'disconnected';
+ // Force close the current connections
+ _self.serverConfig.close(function(_err) {
+ // Force close the current connections
+ _callback(err, null);
+ });
+ } else if(err == null && _self.serverConfig.isConnected() == true && Array.isArray(_self.auths) && _self.auths.length > 0) {
+ _self._state = 'connected';
+ // Get number of auths we need to execute
+ var numberOfAuths = _self.auths.length;
+ // Apply all auths
+ for(var i = 0; i < _self.auths.length; i++) {
+ _self.authenticate(_self.auths[i].username, _self.auths[i].password, function(err, authenticated) {
+ numberOfAuths = numberOfAuths - 1;
+
+ // If we have no more authentications to replay
+ if(numberOfAuths == 0) {
+ if(err != null || !authenticated) {
+ return _callback(err, null);
+ } else {
+ // Execute command
+ command(_self, _db_command, _options, function(err, result) {
+ // Peform the command callback
+ _callback(err, result);
+ // Execute any backed up commands
+ while(_self.commands.length > 0) {
+ // Fetch the command
+ var command = _self.commands.shift();
+ // Execute based on type
+ if(command['type'] == 'query') {
+ __executeQueryCommand(_self, command['db_command'], command['options'], command['callback']);
+ } else if(command['type'] == 'insert') {
+ __executeInsertCommand(_self, command['db_command'], command['options'], command['callback']);
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+ } else if(err == null && _self.serverConfig.isConnected() == true) {
+ _self._state = 'connected';
+ // Execute command
+ command(_self, _db_command, _options, function(err, result) {
+ // Peform the command callback
+ _callback(err, result);
+ // Execute any backed up commands
+ while(_self.commands.length > 0) {
+ // Fetch the command
+ var command = _self.commands.shift();
+ // Execute based on type
+ if(command['type'] == 'query') {
+ __executeQueryCommand(_self, command['db_command'], command['options'], command['callback']);
+ } else if(command['type'] == 'insert') {
+ __executeInsertCommand(_self, command['db_command'], command['options'], command['callback']);
+ }
+ }
+ });
+ } else {
+ _self._state = 'connecting';
+ // Force close the current connections
+ _self.serverConfig.close(function(err) {
+ // Retry the connect
+ setTimeout(function() {
+ retryFunction(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback);
+ }, _retryInMilliseconds);
+ });
+ }
+ });
+ };
+
+ // Execute function first time
+ retryFunction(self, numberOfRetriesDone, retryInMilliseconds, numberOfTimes, command, db_command, options, callback);
+}
+
+/**
+ * Execute db query command (not safe)
+ * @ignore
+ * @api private
+ */
+Db.prototype._executeQueryCommand = function(db_command, options, callback) {
+ var self = this;
+ // Unpack the parameters
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ // Check if the user force closed the command
+ if(this._applicationClosed) {
+ if(typeof callback == 'function') {
+ return callback(new Error("db closed by application"), null);
+ } else {
+ throw new Error("db closed by application");
+ }
+ }
+
+ // If the pool is not connected, attemp to reconnect to send the message
+ if(this._state == 'connecting' && this.serverConfig.autoReconnect) {
+ process.nextTick(function() {
+ self.commands.push({type:'query', 'db_command':db_command, 'options':options, 'callback':callback});
+ })
+ } else if(!this.serverConfig.isConnected() && this.serverConfig.autoReconnect) {
+ this._state = 'connecting';
+ // Retry command
+ __retryCommandOnFailure(this, this.retryMiliSeconds, this.numberOfRetries, __executeQueryCommand, db_command, options, callback);
+ } else {
+ __executeQueryCommand(self, db_command, options, callback)
+ }
+};
+
+/**
+ * @ignore
+ */
+var __executeInsertCommand = function(self, db_command, options, callback) {
+ // Always checkout a writer for this kind of operations
+ var connection = self.serverConfig.checkoutWriter();
+ // Get strict mode
+ var safe = options['safe'] != null ? options['safe'] : false;
+ var raw = options['raw'] != null ? options['raw'] : self.raw;
+ var specifiedConnection = options['connection'] != null ? options['connection'] : null;
+ // Override connection if needed
+ connection = specifiedConnection != null ? specifiedConnection : connection;
+
+ // Ensure we have a valid connection
+ if(typeof callback === 'function') {
+ // Ensure we have a valid connection
+ if(connection == null) {
+ return callback(new Error("no open connections"));
+ } else if(connection instanceof Error) {
+ return callback(connection);
+ }
+
+ // We are expecting a check right after the actual operation
+ if(safe != null && safe != false) {
+ // db command is now an array of commands (original command + lastError)
+ db_command = [db_command, DbCommand.createGetLastErrorCommand(safe, self)];
+
+ // Register the handler in the data structure
+ self._registerHandler(db_command[1], raw, connection, callback);
+ }
+ }
+
+ // If we have no callback and there is no connection
+ if(connection == null) return null;
+ if(connection instanceof Error && typeof callback == 'function') return callback(connection, null);
+ if(connection instanceof Error) return null;
+ if(connection == null && typeof callback == 'function') return callback(new Error("no primary server found"), null);
+
+ // Write the message out
+ connection.write(db_command, function(err) {
+ // Return the callback if it's not a safe operation and the callback is defined
+ if(typeof callback === 'function' && (safe == null || safe == false)) {
+ // Perform reaping
+ if(self.reaperEnabled) reaper(self, self.reaperInterval, self.reaperTimeout);
+ // Perform the callback
+ callback(err, null);
+ } else if(typeof callback === 'function'){
+ // Call the handler with an error
+ self._callHandler(db_command[1].getRequestId(), null, err);
+ } else {
+ self.emit("error", err);
+ }
+ });
+}
+
+/**
+ * Execute an insert Command
+ * @ignore
+ * @api private
+ */
+Db.prototype._executeInsertCommand = function(db_command, options, callback) {
+ var self = this;
+ // Unpack the parameters
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+
+ // Check if the user force closed the command
+ if(this._applicationClosed) {
+ if(typeof callback == 'function') {
+ return callback(new Error("db closed by application"), null);
+ } else {
+ throw new Error("db closed by application");
+ }
+ }
+
+ // If the pool is not connected, attemp to reconnect to send the message
+ if(self._state == 'connecting' && this.serverConfig.autoReconnect) {
+ process.nextTick(function() {
+ self.commands.push({type:'insert', 'db_command':db_command, 'options':options, 'callback':callback});
+ })
+ } else if(!this.serverConfig.isConnected() && this.serverConfig.autoReconnect) {
+ this._state = 'connecting';
+ // Retry command
+ __retryCommandOnFailure(this, this.retryMiliSeconds, this.numberOfRetries, __executeInsertCommand, db_command, options, callback);
+ } else {
+ __executeInsertCommand(self, db_command, options, callback)
+ }
+}
+
+/**
+ * Update command is the same
+ * @ignore
+ * @api private
+ */
+Db.prototype._executeUpdateCommand = Db.prototype._executeInsertCommand;
+/**
+ * Remove command is the same
+ * @ignore
+ * @api private
+ */
+Db.prototype._executeRemoveCommand = Db.prototype._executeInsertCommand;
+
+/**
+ * Wrap a Mongo error document into an Error instance
+ * @ignore
+ * @api private
+ */
+Db.prototype.wrap = function(error) {
+ var msg = error.err || error.errmsg || error;
+ var e = new Error(msg);
+ e.name = 'MongoError';
+
+ // Get all object keys
+ var keys = Object.keys(error);
+ // Populate error object with properties
+ for(var i = 0; i < keys.length; i++) {
+ e[keys[i]] = error[keys[i]];
+ }
+
+ return e;
+}
+
+/**
+ * Default URL
+ *
+ * @classconstant DEFAULT_URL
+ **/
+Db.DEFAULT_URL = 'mongodb://localhost:27017/default';
+
+/**
+ * Connect to MongoDB using a url as documented at
+ *
+ * www.mongodb.org/display/DOCS/Connections
+ *
+ * @param {String} url connection url for MongoDB.
+ * @param {Object} options additional options not covered by the url.
+ * @param {Function} callback callback returns the initialized db.
+ * @return {null}
+ * @api public
+ */
+Db.connect = function(url, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = typeof args[args.length - 1] == 'function' ? args.pop() : null;
+ options = args.length ? args.shift() : null;
+ options = options || {};
+ var serverOptions = options.server || {};
+ var replSetServersOptions = options.replSetServers || {};
+ var dbOptions = options.db || {};
+
+ var urlRE = new RegExp('^mongo(?:db)?://(?:|([^@/]*)@)([^@/]*)(?:|/([^?]*)(?:|\\?([^?]*)))$');
+ var match = (url || Db.DEFAULT_URL).match(urlRE);
+ if (!match)
+ throw Error("URL must be in the format mongodb://user:pass@host:port/dbname");
+
+ var authPart = match[1] || '';
+ var auth = authPart.split(':', 2);
+ var hostPart = match[2];
+ var dbname = match[3] || 'default';
+ var urlOptions = (match[4] || '').split(/[&;]/);
+
+ // Ugh, we have to figure out which options go to which constructor manually.
+ urlOptions.forEach(function(opt) {
+ if (!opt) return;
+ var splitOpt = opt.split('='), name = splitOpt[0], value = splitOpt[1];
+
+ // Server options:
+ if (name == 'slaveOk' || name == 'slave_ok')
+ serverOptions.slave_ok = (value == 'true');
+ if (name == 'poolSize')
+ serverOptions.poolSize = Number(value);
+ if (name == 'autoReconnect' || name == 'auto_reconnect')
+ serverOptions.auto_reconnect = (value == 'true');
+ if (name == 'ssl' || name == 'ssl')
+ serverOptions.ssl = (value == 'true');
+
+ // ReplSetServers options:
+ if (name == 'replicaSet' || name == 'rs_name')
+ replSetServersOptions.rs_name = value;
+ if (name == 'reconnectWait')
+ replSetServersOptions.reconnectWait = Number(value);
+ if (name == 'retries')
+ replSetServersOptions.retries = Number(value);
+ if (name == 'readSecondary' || name == 'read_secondary')
+ replSetServersOptions.read_secondary = (value == 'true');
+
+ // DB options:
+ if (name == 'safe')
+ dbOptions.safe = (value == 'true');
+ // Not supported by Db: safe, w, wtimeoutMS, fsync, journal, connectTimeoutMS, socketTimeoutMS
+ if (name == 'nativeParser' || name == 'native_parser')
+ dbOptions.native_parser = (value == 'true');
+ if (name == 'strict')
+ dbOptions.strict = (value == 'true');
+ });
+
+ var servers = hostPart.split(',').map(function(h) {
+ var hostPort = h.split(':', 2);
+ return new Server(hostPort[0] || 'localhost', hostPort[1] != null ? parseInt(hostPort[1]) : 27017, serverOptions);
+ });
+
+ var server;
+ if (servers.length == 1) {
+ server = servers[0];
+ } else {
+ server = new ReplSetServers(servers, replSetServersOptions);
+ }
+
+ var db = new Db(dbname, server, dbOptions);
+ if(options.noOpen)
+ return db;
+
+ // If callback is null throw an exception
+ if(callback == null) throw new Error("no callback function provided");
+
+ db.open(function(err, db){
+ if(err == null && authPart){
+ db.authenticate(auth[0], auth[1], function(err, success){
+ if(success){
+ callback(null, db);
+ } else {
+ callback(err ? err : new Error('Could not authenticate user ' + auth[0]), db);
+ }
+ });
+ } else {
+ callback(err, db);
+ }
+ });
+}
+
+/**
+ * Legacy support
+ *
+ * @ignore
+ * @api private
+ */
+exports.connect = Db.connect;
+exports.Db = Db;
+
+/**
+ * Remove all listeners to the db instance.
+ * @ignore
+ * @api private
+ */
+Db.prototype.removeAllEventListeners = function() {
+ this.removeAllListeners("close");
+ this.removeAllListeners("error");
+ this.removeAllListeners("timeout");
+ this.removeAllListeners("parseError");
+ this.removeAllListeners("poolReady");
+ this.removeAllListeners("message");
+}
diff --git a/node_modules/mongodb/lib/mongodb/gridfs/chunk.js b/node_modules/mongodb/lib/mongodb/gridfs/chunk.js
new file mode 100644
index 0000000..cfb9009
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/gridfs/chunk.js
@@ -0,0 +1,208 @@
+var Binary = require('bson').Binary,
+ ObjectID = require('bson').ObjectID;
+
+/**
+ * Class for representing a single chunk in GridFS.
+ *
+ * @class
+ *
+ * @param file {GridStore} The {@link GridStore} object holding this chunk.
+ * @param mongoObject {object} The mongo object representation of this chunk.
+ *
+ * @throws Error when the type of data field for {@link mongoObject} is not
+ * supported. Currently supported types for data field are instances of
+ * {@link String}, {@link Array}, {@link Binary} and {@link Binary}
+ * from the bson module
+ *
+ * @see Chunk#buildMongoObject
+ */
+var Chunk = exports.Chunk = function(file, mongoObject) {
+ if(!(this instanceof Chunk)) return new Chunk(file, mongoObject);
+
+ this.file = file;
+ var self = this;
+ var mongoObjectFinal = mongoObject == null ? {} : mongoObject;
+
+ this.objectId = mongoObjectFinal._id == null ? new ObjectID() : mongoObjectFinal._id;
+ this.chunkNumber = mongoObjectFinal.n == null ? 0 : mongoObjectFinal.n;
+ this.data = new Binary();
+
+ if(mongoObjectFinal.data == null) {
+ } else if(typeof mongoObjectFinal.data == "string") {
+ var buffer = new Buffer(mongoObjectFinal.data.length);
+ buffer.write(mongoObjectFinal.data, 'binary', 0);
+ this.data = new Binary(buffer);
+ } else if(Array.isArray(mongoObjectFinal.data)) {
+ var buffer = new Buffer(mongoObjectFinal.data.length);
+ buffer.write(mongoObjectFinal.data.join(''), 'binary', 0);
+ this.data = new Binary(buffer);
+ } else if(mongoObjectFinal.data instanceof Binary || Object.prototype.toString.call(mongoObjectFinal.data) == "[object Binary]") {
+ this.data = mongoObjectFinal.data;
+ } else if(Buffer.isBuffer(mongoObjectFinal.data)) {
+ } else {
+ throw Error("Illegal chunk format");
+ }
+ // Update position
+ this.internalPosition = 0;
+
+ /**
+ * The position of the read/write head
+ * @name position
+ * @lends Chunk#
+ * @field
+ */
+ Object.defineProperty(this, "position", { enumerable: true
+ , get: function () {
+ return this.internalPosition;
+ }
+ , set: function(value) {
+ this.internalPosition = value;
+ }
+ });
+};
+
+/**
+ * Writes a data to this object and advance the read/write head.
+ *
+ * @param data {string} the data to write
+ * @param callback {function(*, GridStore)} This will be called after executing
+ * this method. The first parameter will contain null and the second one
+ * will contain a reference to this object.
+ */
+Chunk.prototype.write = function(data, callback) {
+ this.data.write(data, this.internalPosition);
+ this.internalPosition = this.data.length();
+ callback(null, this);
+};
+
+/**
+ * Reads data and advances the read/write head.
+ *
+ * @param length {number} The length of data to read.
+ *
+ * @return {string} The data read if the given length will not exceed the end of
+ * the chunk. Returns an empty String otherwise.
+ */
+Chunk.prototype.read = function(length) {
+ // Default to full read if no index defined
+ length = length == null || length == 0 ? this.length() : length;
+
+ if(this.length() - this.internalPosition + 1 >= length) {
+ var data = this.data.read(this.internalPosition, length);
+ this.internalPosition = this.internalPosition + length;
+ return data;
+ } else {
+ return '';
+ }
+};
+
+Chunk.prototype.readSlice = function(length) {
+ if ((this.length() - this.internalPosition + 1) >= length) {
+ var data = null;
+ if (this.data.buffer != null) { //Pure BSON
+ data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length);
+ } else { //Native BSON
+ data = new Buffer(length);
+ length = this.data.readInto(data, this.internalPosition);
+ }
+ this.internalPosition = this.internalPosition + length;
+ return data;
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Checks if the read/write head is at the end.
+ *
+ * @return {boolean} Whether the read/write head has reached the end of this
+ * chunk.
+ */
+Chunk.prototype.eof = function() {
+ return this.internalPosition == this.length() ? true : false;
+};
+
+/**
+ * Reads one character from the data of this chunk and advances the read/write
+ * head.
+ *
+ * @return {string} a single character data read if the the read/write head is
+ * not at the end of the chunk. Returns an empty String otherwise.
+ */
+Chunk.prototype.getc = function() {
+ return this.read(1);
+};
+
+/**
+ * Clears the contents of the data in this chunk and resets the read/write head
+ * to the initial position.
+ */
+Chunk.prototype.rewind = function() {
+ this.internalPosition = 0;
+ this.data = new Binary();
+};
+
+/**
+ * Saves this chunk to the database. Also overwrites existing entries having the
+ * same id as this chunk.
+ *
+ * @param callback {function(*, GridStore)} This will be called after executing
+ * this method. The first parameter will contain null and the second one
+ * will contain a reference to this object.
+ */
+Chunk.prototype.save = function(callback) {
+ var self = this;
+
+ self.file.chunkCollection(function(err, collection) {
+ collection.remove({'_id':self.objectId}, {safe:true}, function(err, result) {
+ if(self.data.length() > 0) {
+ self.buildMongoObject(function(mongoObject) {
+ collection.insert(mongoObject, {safe:true}, function(err, collection) {
+ callback(null, self);
+ });
+ });
+ } else {
+ callback(null, self);
+ }
+ });
+ });
+};
+
+/**
+ * Creates a mongoDB object representation of this chunk.
+ *
+ * @param callback {function(Object)} This will be called after executing this
+ * method. The object will be passed to the first parameter and will have
+ * the structure:
+ *
+ * <pre><code>
+ * {
+ * '_id' : , // {number} id for this chunk
+ * 'files_id' : , // {number} foreign key to the file collection
+ * 'n' : , // {number} chunk number
+ * 'data' : , // {bson#Binary} the chunk data itself
+ * }
+ * </code></pre>
+ *
+ * @see <a href="http://www.mongodb.org/display/DOCS/GridFS+Specification#GridFSSpecification-{{chunks}}">MongoDB GridFS Chunk Object Structure</a>
+ */
+Chunk.prototype.buildMongoObject = function(callback) {
+ var mongoObject = {'_id': this.objectId,
+ 'files_id': this.file.fileId,
+ 'n': this.chunkNumber,
+ 'data': this.data};
+ callback(mongoObject);
+};
+
+/**
+ * @return {number} the length of the data
+ */
+Chunk.prototype.length = function() {
+ return this.data.length();
+};
+
+/**
+ * The default chunk size
+ * @constant
+ */
+Chunk.DEFAULT_CHUNK_SIZE = 1024 * 256;
diff --git a/node_modules/mongodb/lib/mongodb/gridfs/grid.js b/node_modules/mongodb/lib/mongodb/gridfs/grid.js
new file mode 100644
index 0000000..d42c3d6
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/gridfs/grid.js
@@ -0,0 +1,98 @@
+var GridStore = require('./gridstore').GridStore,
+ ObjectID = require('bson').ObjectID;
+
+/**
+ * A class representation of a simple Grid interface.
+ *
+ * @class Represents the Grid.
+ * @param {Db} db A database instance to interact with.
+ * @param {String} [fsName] optional different root collection for GridFS.
+ * @return {Grid}
+ */
+function Grid(db, fsName) {
+
+ if(!(this instanceof Grid)) return new Grid(db, fsName);
+
+ this.db = db;
+ this.fsName = fsName == null ? GridStore.DEFAULT_ROOT_COLLECTION : fsName;
+}
+
+/**
+ * Puts binary data to the grid
+ *
+ * @param {Buffer} data buffer with Binary Data.
+ * @param {Object} [options] the options for the files.
+ * @callback {Function} this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object.
+ * @return {null}
+ * @api public
+ */
+Grid.prototype.put = function(data, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ options = args.length ? args.shift() : {};
+ // If root is not defined add our default one
+ options['root'] = options['root'] == null ? this.fsName : options['root'];
+
+ // Return if we don't have a buffer object as data
+ if(!(Buffer.isBuffer(data))) return callback(new Error("Data object must be a buffer object"), null);
+ // Get filename if we are using it
+ var filename = options['filename'];
+ // Create gridstore
+ var gridStore = new GridStore(this.db, filename, "w", options);
+ gridStore.open(function(err, gridStore) {
+ if(err) return callback(err, null);
+
+ gridStore.write(data, function(err, result) {
+ if(err) return callback(err, null);
+
+ gridStore.close(function(err, result) {
+ if(err) return callback(err, null);
+ callback(null, result);
+ })
+ })
+ })
+}
+
+/**
+ * Get binary data to the grid
+ *
+ * @param {ObjectID} id ObjectID for file.
+ * @callback {Function} this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object.
+ * @return {null}
+ * @api public
+ */
+Grid.prototype.get = function(id, callback) {
+ // Validate that we have a valid ObjectId
+ if(!(id instanceof ObjectID)) return callback(new Error("Not a valid ObjectID", null));
+ // Create gridstore
+ var gridStore = new GridStore(this.db, id, "r", {root:this.fsName});
+ gridStore.open(function(err, gridStore) {
+ if(err) return callback(err, null);
+
+ // Return the data
+ gridStore.read(function(err, data) {
+ return callback(err, data)
+ });
+ })
+}
+
+/**
+ * Delete file from grid
+ *
+ * @param {ObjectID} id ObjectID for file.
+ * @callback {Function} this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object.
+ * @return {null}
+ * @api public
+ */
+Grid.prototype.delete = function(id, callback) {
+ // Validate that we have a valid ObjectId
+ if(!(id instanceof ObjectID)) return callback(new Error("Not a valid ObjectID", null));
+ // Create gridstore
+ GridStore.unlink(this.db, id, {root:this.fsName}, function(err, result) {
+ if(err) return callback(err, false);
+ return callback(null, true);
+ });
+}
+
+exports.Grid = Grid;
diff --git a/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js b/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js
new file mode 100644
index 0000000..6b56cf1
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js
@@ -0,0 +1,1109 @@
+/**
+ * @fileOverview GridFS is a tool for MongoDB to store files to the database.
+ * Because of the restrictions of the object size the database can hold, a
+ * facility to split a file into several chunks is needed. The {@link GridStore}
+ * class offers a simplified api to interact with files while managing the
+ * chunks of split files behind the scenes. More information about GridFS can be
+ * found <a href="http://www.mongodb.org/display/DOCS/GridFS">here</a>.
+ */
+var Chunk = require('./chunk').Chunk,
+ DbCommand = require('../commands/db_command').DbCommand,
+ ObjectID = require('bson').ObjectID,
+ Buffer = require('buffer').Buffer,
+ fs = require('fs'),
+ util = require('util'),
+ ReadStream = require('./readstream').ReadStream;
+
+var REFERENCE_BY_FILENAME = 0,
+ REFERENCE_BY_ID = 1;
+
+/**
+ * A class representation of a file stored in GridFS.
+ *
+ * Modes
+ * - **"r"** - read only. This is the default mode.
+ * - **"w"** - write in truncate mode. Existing data will be overwriten.
+ * - **w+"** - write in edit mode.
+ *
+ * Options
+ * - **root** {String}, root collection to use. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
+ * - **chunk_type** {String}, mime type of the file. Defaults to **{GridStore.DEFAULT_CONTENT_TYPE}**.
+ * - **chunk_size** {Number}, size for the chunk. Defaults to **{Chunk.DEFAULT_CHUNK_SIZE}**.
+ * - **metadata** {Object}, arbitrary data the user wants to store.
+ *
+ * @class Represents the GridStore.
+ * @param {Db} db A database instance to interact with.
+ * @param {ObjectID} id an unique ObjectID for this file
+ * @param {String} [filename] optional a filename for this file, no unique constrain on the field
+ * @param {String} mode set the mode for this file.
+ * @param {Object} options optional properties to specify. Recognized keys:
+ * @return {GridStore}
+ */
+function GridStore(db, id, filename, mode, options) {
+ if(!(this instanceof GridStore)) return new GridStore(db, id, filename, mode, options);
+
+ var self = this;
+ this.db = db;
+ var _filename = filename;
+
+ if(typeof filename == 'string' && typeof mode == 'string') {
+ _filename = filename;
+ } else if(typeof filename == 'string' && typeof mode == 'object' && mode != null) {
+ var _mode = mode;
+ mode = filename;
+ options = _mode;
+ _filename = id;
+ } else if(typeof filename == 'string' && mode == null) {
+ mode = filename;
+ _filename = id;
+ }
+
+ // set grid referencetype
+ this.referenceBy = typeof id == 'string' ? 0 : 1;
+ this.filename = _filename;
+ this.fileId = id;
+
+ // Set up the rest
+ this.mode = mode == null ? "r" : mode;
+ this.options = options == null ? {} : options;
+ this.root = this.options['root'] == null ? exports.GridStore.DEFAULT_ROOT_COLLECTION : this.options['root'];
+ this.position = 0;
+ // Set default chunk size
+ this.internalChunkSize = this.options['chunkSize'] == null ? Chunk.DEFAULT_CHUNK_SIZE : this.options['chunkSize'];
+
+ /**
+ * Returns the current chunksize of the file.
+ *
+ * @field chunkSize
+ * @type {Number}
+ * @getter
+ * @setter
+ * @property return number of bytes in the current chunkSize.
+ */
+ Object.defineProperty(this, "chunkSize", { enumerable: true
+ , get: function () {
+ return this.internalChunkSize;
+ }
+ , set: function(value) {
+ if(!(this.mode[0] == "w" && this.position == 0 && this.uploadDate == null)) {
+ this.internalChunkSize = this.internalChunkSize;
+ } else {
+ this.internalChunkSize = value;
+ }
+ }
+ });
+
+ /**
+ * The md5 checksum for this file.
+ *
+ * @field md5
+ * @type {Number}
+ * @getter
+ * @setter
+ * @property return this files md5 checksum.
+ */
+ Object.defineProperty(this, "md5", { enumerable: true
+ , get: function () {
+ return this.internalMd5;
+ }
+ });
+};
+
+/**
+ * Opens the file from the database and initialize this object. Also creates a
+ * new one if file does not exist.
+ *
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain an **{Error}** object and the second parameter will be null if an error occured. Otherwise, the first parameter will be null and the second will contain the reference to this object.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.open = function(callback) {
+ if( this.mode != "w" && this.mode != "w+" && this.mode != "r"){
+ callback(new Error("Illegal mode " + this.mode), null);
+ return;
+ }
+
+ var self = this;
+
+ if((self.mode == "w" || self.mode == "w+") && self.db.serverConfig.primary != null) {
+ // Get files collection
+ self.collection(function(err, collection) {
+ // Get chunk collection
+ self.chunkCollection(function(err, chunkCollection) {
+ // Ensure index on chunk collection
+ chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], function(err, index) {
+ _open(self, callback);
+ });
+ });
+ });
+ } else {
+ _open(self, callback);
+ }
+}
+
+/**
+ * Hidding the _open function
+ * @ignore
+ * @api private
+ */
+var _open = function(self, callback) {
+ self.collection(function(err, collection) {
+ if(err!==null) {
+ callback(new Error("at collection: "+err), null);
+ return;
+ }
+
+ // Create the query
+ var query = self.referenceBy == REFERENCE_BY_ID ? {_id:self.fileId} : {filename:self.filename};
+ query = self.fileId == null && this.filename == null ? null : query;
+
+ // Fetch the chunks
+ if(query != null) {
+ collection.find(query, function(err, cursor) {
+ // Fetch the file
+ cursor.nextObject(function(err, doc) {
+ // Chek if the collection for the files exists otherwise prepare the new one
+ if(doc != null) {
+ self.fileId = doc._id;
+ self.contentType = doc.contentType;
+ self.internalChunkSize = doc.chunkSize;
+ self.uploadDate = doc.uploadDate;
+ self.aliases = doc.aliases;
+ self.length = doc.length;
+ self.metadata = doc.metadata;
+ self.internalMd5 = doc.md5;
+ } else {
+ self.fileId = self.fileId instanceof ObjectID ? self.fileId : new ObjectID();
+ self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE;
+ self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize;
+ self.length = 0;
+ }
+
+ // Process the mode of the object
+ if(self.mode == "r") {
+ nthChunk(self, 0, function(err, chunk) {
+ self.currentChunk = chunk;
+ self.position = 0;
+ callback(null, self);
+ });
+ } else if(self.mode == "w") {
+ // Delete any existing chunks
+ deleteChunks(self, function(err, result) {
+ self.currentChunk = new Chunk(self, {'n':0});
+ self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type'];
+ self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size'];
+ self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.position = 0;
+ callback(null, self);
+ });
+ } else if(self.mode == "w+") {
+ nthChunk(self, lastChunkNumber(self), function(err, chunk) {
+ // Set the current chunk
+ self.currentChunk = chunk == null ? new Chunk(self, {'n':0}) : chunk;
+ self.currentChunk.position = self.currentChunk.data.length();
+ self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.position = self.length;
+ callback(null, self);
+ });
+ }
+ });
+ });
+ } else {
+ // Write only mode
+ self.fileId = new ObjectID();
+ self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE;
+ self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize;
+ self.length = 0;
+
+ self.chunkCollection(function(err, collection2) {
+ // No file exists set up write mode
+ if(self.mode == "w") {
+ // Delete any existing chunks
+ deleteChunks(self, function(err, result) {
+ self.currentChunk = new Chunk(self, {'n':0});
+ self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type'];
+ self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size'];
+ self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.position = 0;
+ callback(null, self);
+ });
+ } else if(self.mode == "w+") {
+ nthChunk(self, lastChunkNumber(self), function(err, chunk) {
+ // Set the current chunk
+ self.currentChunk = chunk == null ? new Chunk(self, {'n':0}) : chunk;
+ self.currentChunk.position = self.currentChunk.data.length();
+ self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
+ self.position = self.length;
+ callback(null, self);
+ });
+ }
+ });
+ };
+ });
+};
+
+/**
+ * Stores a file from the file system to the GridFS database.
+ *
+ * @param {String|Buffer|FileHandle} file the file to store.
+ * @param {Function} callback this will be called after this method is executed. The first parameter will be null and the the second will contain the reference to this object.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.writeFile = function (file, callback) {
+ var self = this;
+ if (typeof file === 'string') {
+ fs.open(file, 'r', 0666, function (err, fd) {
+ // TODO Handle err
+ self.writeFile(fd, callback);
+ });
+ return;
+ }
+
+ self.open(function (err, self) {
+ fs.fstat(file, function (err, stats) {
+ var offset = 0;
+ var index = 0;
+ var numberOfChunksLeft = Math.min(stats.size / self.chunkSize);
+
+ // Write a chunk
+ var writeChunk = function() {
+ fs.read(file, self.chunkSize, offset, 'binary', function(err, data, bytesRead) {
+ offset = offset + bytesRead;
+ // Create a new chunk for the data
+ var chunk = new Chunk(self, {n:index++});
+ chunk.write(data, function(err, chunk) {
+ chunk.save(function(err, result) {
+ // Point to current chunk
+ self.currentChunk = chunk;
+
+ if(offset >= stats.size) {
+ fs.close(file);
+ self.close(function(err, result) {
+ return callback(null, result);
+ })
+ } else {
+ return process.nextTick(writeChunk);
+ }
+ });
+ });
+ });
+ }
+
+ // Process the first write
+ process.nextTick(writeChunk);
+ });
+ });
+};
+
+/**
+ * Writes some data. This method will work properly only if initialized with mode "w" or "w+".
+ *
+ * @param {String|Buffer} data the data to write.
+ * @param {Boolean} [close] closes this file after writing if set to true.
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.write = function(data, close, callback) {
+ // If we have a buffer write it using the writeBuffer method
+ if(Buffer.isBuffer(data)) return writeBuffer(this, data, close, callback);
+ // Otherwise check for the callback
+ if(typeof close === "function") { callback = close; close = null; }
+ var self = this;
+ var finalClose = close == null ? false : close;
+ // Otherwise let's write the data
+ if(self.mode[0] != "w") {
+ callback(new Error((self.referenceBy == REFERENCE_BY_ID ? self.toHexString() : self.filename) + " not opened for writing"), null);
+ } else {
+ if((self.currentChunk.position + data.length) > self.chunkSize) {
+ var previousChunkNumber = self.currentChunk.chunkNumber;
+ var leftOverDataSize = self.chunkSize - self.currentChunk.position;
+ var previousChunkData = data.slice(0, leftOverDataSize);
+ var leftOverData = data.slice(leftOverDataSize, (data.length - leftOverDataSize));
+ // Save out current Chunk as another variable and assign a new Chunk for overflow data
+ var saveChunk = self.currentChunk;
+ // Create a new chunk at once (avoid wrong writing of chunks)
+ self.currentChunk = new Chunk(self, {'n': (previousChunkNumber + 1)});
+
+ // Let's finish the current chunk and then call write again for the remaining data
+ saveChunk.write(previousChunkData, function(err, chunk) {
+ chunk.save(function(err, result) {
+ self.position = self.position + leftOverDataSize;
+ // Write the remaining data
+ self.write(leftOverData, function(err, gridStore) {
+ if(finalClose) {
+ self.close(function(err, result) {
+ callback(null, gridStore);
+ });
+ } else {
+ callback(null, gridStore);
+ }
+ });
+ });
+ });
+ } else {
+ self.currentChunk.write(data, function(err, chunk) {
+ self.position = self.position + data.length;
+ if(finalClose) {
+ self.close(function(err, result) {
+ callback(null, self);
+ });
+ } else {
+ callback(null, self);
+ }
+ });
+ }
+ }
+};
+
+/**
+ * Writes some data. This method will work properly only if initialized with mode
+ * "w" or "w+".
+ *
+ * @param string {string} The data to write.
+ * @param close {boolean=false} opt_argument Closes this file after writing if
+ * true.
+ * @param callback {function(*, GridStore)} This will be called after executing
+ * this method. The first parameter will contain null and the second one
+ * will contain a reference to this object.
+ *
+ * @ignore
+ * @api private
+ */
+var writeBuffer = function(self, buffer, close, callback) {
+ if(typeof close === "function") { callback = close; close = null; }
+ var finalClose = (close == null) ? false : close;
+
+ if(self.mode[0] != "w") {
+ callback(new Error((self.referenceBy == REFERENCE_BY_ID ? self.toHexString() : self.filename) + " not opened for writing"), null);
+ } else {
+ if((self.currentChunk.position + buffer.length) > self.chunkSize) {
+ // Data exceeds current chunk remaining free size; fill up current chunk and write the rest
+ // to a new chunk (recursively)
+ var previousChunkNumber = self.currentChunk.chunkNumber;
+ var leftOverDataSize = self.chunkSize - self.currentChunk.position;
+ var firstChunkData = buffer.slice(0, leftOverDataSize);
+ var leftOverData = buffer.slice(leftOverDataSize);
+ // Save out current Chunk as another variable and assign a new Chunk for overflow data
+ var saveChunk = self.currentChunk;
+ // Create a new chunk at once (avoid wrong writing of chunks)
+ self.currentChunk = new Chunk(self, {'n': (previousChunkNumber + 1)});
+
+ // Let's finish the current chunk and then call write again for the remaining data
+ saveChunk.write(firstChunkData, function(err, chunk) {
+ chunk.save(function(err, result) {
+ self.position = self.position + leftOverDataSize;
+
+ // Write the remaining data
+ writeBuffer(self, leftOverData, function(err, gridStore) {
+ if(finalClose) {
+ self.close(function(err, result) {
+ callback(null, gridStore);
+ });
+ }
+ else {
+ callback(null, gridStore);
+ }
+ });
+ });
+ });
+ }
+ else {
+ // Write buffer to chunk all at once
+ self.currentChunk.write(buffer, function(err, chunk) {
+ self.position = self.position + buffer.length;
+ if(finalClose) {
+ self.close(function(err, result) {
+ callback(null, self);
+ });
+ }
+ else {
+ callback(null, self);
+ }
+ });
+ }
+ }
+};
+
+/**
+ * Creates a mongoDB object representation of this object.
+ *
+ * @param callback {function(object)} This will be called after executing this
+ * method. The object will be passed to the first parameter and will have
+ * the structure:
+ *
+ * <pre><code>
+ * {
+ * '_id' : , // {number} id for this file
+ * 'filename' : , // {string} name for this file
+ * 'contentType' : , // {string} mime type for this file
+ * 'length' : , // {number} size of this file?
+ * 'chunksize' : , // {number} chunk size used by this file
+ * 'uploadDate' : , // {Date}
+ * 'aliases' : , // {array of string}
+ * 'metadata' : , // {string}
+ * }
+ * </code></pre>
+ *
+ * @ignore
+ * @api private
+ */
+var buildMongoObject = function(self, callback) {
+ var length = self.currentChunk != null ? (self.currentChunk.chunkNumber * self.chunkSize + self.currentChunk.position) : 0;
+ var mongoObject = {
+ '_id': self.fileId,
+ 'filename': self.filename,
+ 'contentType': self.contentType,
+ 'length': length < 0 ? 0 : length,
+ 'chunkSize': self.chunkSize,
+ 'uploadDate': self.uploadDate,
+ 'aliases': self.aliases,
+ 'metadata': self.metadata
+ };
+
+ var md5Command = {filemd5:self.fileId, root:self.root};
+ self.db.command(md5Command, function(err, results) {
+ mongoObject.md5 = results.md5;
+ callback(mongoObject);
+ });
+};
+
+/**
+ * Saves this file to the database. This will overwrite the old entry if it
+ * already exists. This will work properly only if mode was initialized to
+ * "w" or "w+".
+ *
+ * @param {Function} callback this will be called after executing this method. Passes an **{Error}** object to the first parameter and null to the second if an error occured. Otherwise, passes null to the first and a reference to this object to the second.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.close = function(callback) {
+ var self = this;
+
+ if(self.mode[0] == "w") {
+ if(self.currentChunk != null && self.currentChunk.position > 0) {
+ self.currentChunk.save(function(err, chuck) {
+ self.collection(function(err, files) {
+ // Build the mongo object
+ if(self.uploadDate != null) {
+ files.remove({'_id':self.fileId}, {safe:true}, function(err, collection) {
+ buildMongoObject(self, function(mongoObject) {
+ files.save(mongoObject, {safe:true}, function(err, doc) {
+ callback(err, mongoObject);
+ });
+ });
+ });
+ } else {
+ self.uploadDate = new Date();
+ buildMongoObject(self, function(mongoObject) {
+ files.save(mongoObject, {safe:true}, function(err, doc) {
+ callback(err, mongoObject);
+ });
+ });
+ }
+ });
+ });
+ } else {
+ self.collection(function(err, files) {
+ self.uploadDate = new Date();
+ buildMongoObject(self, function(mongoObject) {
+ files.save(mongoObject, {safe:true}, function(err, doc) {
+ callback(err, mongoObject);
+ });
+ });
+ });
+ }
+ } else if(self.mode[0] == "r") {
+ callback(null, null);
+ } else {
+ callback(new Error("Illegal mode " + self.mode), null);
+ }
+};
+
+/**
+ * Gets the nth chunk of this file.
+ *
+ * @param chunkNumber {number} The nth chunk to retrieve.
+ * @param callback {function(*, Chunk|object)} This will be called after
+ * executing this method. null will be passed to the first parameter while
+ * a new {@link Chunk} instance will be passed to the second parameter if
+ * the chunk was found or an empty object {} if not.
+ *
+ * @ignore
+ * @api private
+ */
+var nthChunk = function(self, chunkNumber, callback) {
+ self.chunkCollection(function(err, collection) {
+ collection.find({'files_id':self.fileId, 'n':chunkNumber}, function(err, cursor) {
+ cursor.nextObject(function(err, chunk) {
+ var finalChunk = chunk == null ? {} : chunk;
+ callback(null, new Chunk(self, finalChunk));
+ });
+ });
+ });
+};
+
+/**
+ *
+ * @ignore
+ * @api private
+ */
+GridStore.prototype._nthChunk = function(chunkNumber, callback) {
+ nthChunk(this, chunkNumber, callback);
+}
+
+/**
+ * @return {Number} The last chunk number of this file.
+ *
+ * @ignore
+ * @api private
+ */
+var lastChunkNumber = function(self) {
+ return Math.floor(self.length/self.chunkSize);
+};
+
+/**
+ * Retrieve this file's chunks collection.
+ *
+ * @param {Function} callback this will be called after executing this method. An exception object will be passed to the first parameter when an error occured or null otherwise. A new **{Collection}** object will be passed to the second parameter if no error occured.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.chunkCollection = function(callback) {
+ this.db.collection((this.root + ".chunks"), callback);
+};
+
+/**
+ * Deletes all the chunks of this file in the database.
+ *
+ * @param callback {function(*, boolean)} This will be called after this method
+ * executes. Passes null to the first and true to the second argument.
+ *
+ * @ignore
+ * @api private
+ */
+var deleteChunks = function(self, callback) {
+ if(self.fileId != null) {
+ self.chunkCollection(function(err, collection) {
+ if(err!==null) {
+ callback(err, false);
+ }
+ collection.remove({'files_id':self.fileId}, {safe:true}, function(err, result) {
+ callback(null, true);
+ });
+ });
+ } else {
+ callback(null, true);
+ }
+};
+
+/**
+ * Deletes all the chunks of this file in the database.
+ *
+ * @param {Function} callback this will be called after this method executes. Passes null to the first and true to the second argument.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.unlink = function(callback) {
+ var self = this;
+ deleteChunks(this, function(err) {
+ if(err!==null) {
+ callback("at deleteChunks: "+err);
+ return;
+ }
+
+ self.collection(function(err, collection) {
+ if(err!==null) {
+ callback("at collection: "+err);
+ return;
+ }
+
+ collection.remove({'_id':self.fileId}, {safe:true}, function(err, collection) {
+ callback(err, self);
+ });
+ });
+ });
+};
+
+/**
+ * Retrieves the file collection associated with this object.
+ *
+ * @param {Function} callback this will be called after executing this method. An exception object will be passed to the first parameter when an error occured or null otherwise. A new **{Collection}** object will be passed to the second parameter if no error occured.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.collection = function(callback) {
+ this.db.collection(this.root + ".files", function(err, collection) {
+ callback(err, collection);
+ });
+};
+
+/**
+ * Reads the data of this file.
+ *
+ * @param {String} [separator] the character to be recognized as the newline separator.
+ * @param {Function} callback This will be called after this method is executed. The first parameter will be null and the second parameter will contain an array of strings representing the entire data, each element representing a line including the separator character.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.readlines = function(separator, callback) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ separator = args.length ? args.shift() : "\n";
+
+ this.read(function(err, data) {
+ var items = data.toString().split(separator);
+ items = items.length > 0 ? items.splice(0, items.length - 1) : [];
+ for(var i = 0; i < items.length; i++) {
+ items[i] = items[i] + separator;
+ }
+
+ callback(null, items);
+ });
+};
+
+/**
+ * Deletes all the chunks of this file in the database if mode was set to "w" or
+ * "w+" and resets the read/write head to the initial position.
+ *
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.rewind = function(callback) {
+ var self = this;
+
+ if(this.currentChunk.chunkNumber != 0) {
+ if(this.mode[0] == "w") {
+ deleteChunks(self, function(err, gridStore) {
+ self.currentChunk = new Chunk(self, {'n': 0});
+ self.position = 0;
+ callback(null, self);
+ });
+ } else {
+ self.currentChunk(0, function(err, chunk) {
+ self.currentChunk = chunk;
+ self.currentChunk.rewind();
+ self.position = 0;
+ callback(null, self);
+ });
+ }
+ } else {
+ self.currentChunk.rewind();
+ self.position = 0;
+ callback(null, self);
+ }
+};
+
+/**
+ * Retrieves the contents of this file and advances the read/write head. Works with Buffers only.
+ *
+ * There are 3 signatures for this method:
+ *
+ * (callback)
+ * (length, callback)
+ * (length, buffer, callback)
+ *
+ * @param {Number} [length] the number of characters to read. Reads all the characters from the read/write head to the EOF if not specified.
+ * @param {String|Buffer} [buffer] a string to hold temporary data. This is used for storing the string data read so far when recursively calling this method.
+ * @param {Function} callback this will be called after this method is executed. null will be passed to the first parameter and a string containing the contents of the buffer concatenated with the contents read from this file will be passed to the second.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.read = function(length, buffer, callback) {
+ var self = this;
+
+ var args = Array.prototype.slice.call(arguments, 0);
+ callback = args.pop();
+ length = args.length ? args.shift() : null;
+ buffer = args.length ? args.shift() : null;
+
+ // The data is a c-terminated string and thus the length - 1
+ var finalLength = length == null ? self.length - self.position : length;
+ var finalBuffer = buffer == null ? new Buffer(finalLength) : buffer;
+ // Add a index to buffer to keep track of writing position or apply current index
+ finalBuffer._index = buffer != null && buffer._index != null ? buffer._index : 0;
+
+ if((self.currentChunk.length() - self.currentChunk.position + 1 + finalBuffer._index) >= finalLength) {
+ var slice = self.currentChunk.readSlice(finalLength - finalBuffer._index);
+ // Copy content to final buffer
+ slice.copy(finalBuffer, finalBuffer._index);
+ // Update internal position
+ self.position = finalBuffer.length;
+ // Check if we don't have a file at all
+ if(finalLength == 0 && finalBuffer.length == 0) return callback(new Error("File does not exist"), null);
+ // Else return data
+ callback(null, finalBuffer);
+ } else {
+ var slice = self.currentChunk.readSlice(self.currentChunk.length());
+ // Copy content to final buffer
+ slice.copy(finalBuffer, finalBuffer._index);
+ // Update index position
+ finalBuffer._index += slice.length;
+
+ // Load next chunk and read more
+ nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) {
+ if(chunk.length() > 0) {
+ self.currentChunk = chunk;
+ self.read(length, finalBuffer, callback);
+ } else {
+ finalBuffer._index > 0 ? callback(null, finalBuffer) : callback(new Error("no chunks found for file, possibly corrupt"), null);
+ }
+ });
+ }
+}
+
+/**
+ * Retrieves the position of the read/write head of this file.
+ *
+ * @param {Function} callback This gets called after this method terminates. null is passed to the first parameter and the position is passed to the second.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.tell = function(callback) {
+ callback(null, this.position);
+};
+
+/**
+ * Moves the read/write head to a new location.
+ *
+ * There are 3 signatures for this method
+ *
+ * Seek Location Modes
+ * - **GridStore.IO_SEEK_SET**, **(default)** set the position from the start of the file.
+ * - **GridStore.IO_SEEK_CUR**, set the position from the current position in the file.
+ * - **GridStore.IO_SEEK_END**, set the position from the end of the file.
+ *
+ * @param {Number} [position] the position to seek to
+ * @param {Number} [seekLocation] seek mode. Use one of the Seek Location modes.
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.seek = function(position, seekLocation, callback) {
+ var self = this;
+
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ seekLocation = args.length ? args.shift() : null;
+
+ var seekLocationFinal = seekLocation == null ? exports.GridStore.IO_SEEK_SET : seekLocation;
+ var finalPosition = position;
+ var targetPosition = 0;
+ if(seekLocationFinal == exports.GridStore.IO_SEEK_CUR) {
+ targetPosition = self.position + finalPosition;
+ } else if(seekLocationFinal == exports.GridStore.IO_SEEK_END) {
+ targetPosition = self.length + finalPosition;
+ } else {
+ targetPosition = finalPosition;
+ }
+
+ var newChunkNumber = Math.floor(targetPosition/self.chunkSize);
+ if(newChunkNumber != self.currentChunk.chunkNumber) {
+ var seekChunk = function() {
+ nthChunk(self, newChunkNumber, function(err, chunk) {
+ self.currentChunk = chunk;
+ self.position = targetPosition;
+ self.currentChunk.position = (self.position % self.chunkSize);
+ callback(null, self);
+ });
+ };
+
+ if(self.mode[0] == 'w') {
+ self.currentChunk.save(function(err, chunk) {
+ seekChunk();
+ });
+ } else {
+ seekChunk();
+ }
+ } else {
+ self.position = targetPosition;
+ self.currentChunk.position = (self.position % self.chunkSize);
+ callback(null, self);
+ }
+};
+
+/**
+ * Verify if the file is at EOF.
+ *
+ * @return {Boolean} true if the read/write head is at the end of this file.
+ * @api public
+ */
+GridStore.prototype.eof = function() {
+ return this.position == this.length ? true : false;
+};
+
+/**
+ * Retrieves a single character from this file.
+ *
+ * @param {Function} callback this gets called after this method is executed. Passes null to the first parameter and the character read to the second or null to the second if the read/write head is at the end of the file.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.getc = function(callback) {
+ var self = this;
+
+ if(self.eof()) {
+ callback(null, null);
+ } else if(self.currentChunk.eof()) {
+ nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) {
+ self.currentChunk = chunk;
+ self.position = self.position + 1;
+ callback(null, self.currentChunk.getc());
+ });
+ } else {
+ self.position = self.position + 1;
+ callback(null, self.currentChunk.getc());
+ }
+};
+
+/**
+ * Writes a string to the file with a newline character appended at the end if
+ * the given string does not have one.
+ *
+ * @param {String} string the string to write.
+ * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object.
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.puts = function(string, callback) {
+ var finalString = string.match(/\n$/) == null ? string + "\n" : string;
+ this.write(finalString, callback);
+};
+
+/**
+ * Returns read stream based on this GridStore file
+ *
+ * Events
+ * - **data** {function(item) {}} the data event triggers when a document is ready.
+ * - **end** {function() {}} the end event triggers when there is no more documents available.
+ * - **close** {function() {}} the close event triggers when the stream is closed.
+ * - **error** {function(err) {}} the error event triggers if an error happens.
+ *
+ * @param {Boolean} autoclose if true current GridStore will be closed when EOF and 'close' event will be fired
+ * @return {null}
+ * @api public
+ */
+GridStore.prototype.stream = function(autoclose) {
+ return new ReadStream(autoclose, this);
+};
+
+/**
+* The collection to be used for holding the files and chunks collection.
+*
+* @classconstant DEFAULT_ROOT_COLLECTION
+**/
+GridStore.DEFAULT_ROOT_COLLECTION = 'fs';
+
+/**
+* Default file mime type
+*
+* @classconstant DEFAULT_CONTENT_TYPE
+**/
+GridStore.DEFAULT_CONTENT_TYPE = 'binary/octet-stream';
+
+/**
+* Seek mode where the given length is absolute.
+*
+* @classconstant IO_SEEK_SET
+**/
+GridStore.IO_SEEK_SET = 0;
+
+/**
+* Seek mode where the given length is an offset to the current read/write head.
+*
+* @classconstant IO_SEEK_CUR
+**/
+GridStore.IO_SEEK_CUR = 1;
+
+/**
+* Seek mode where the given length is an offset to the end of the file.
+*
+* @classconstant IO_SEEK_END
+**/
+GridStore.IO_SEEK_END = 2;
+
+/**
+ * Checks if a file exists in the database.
+ *
+ * @param {Db} db the database to query.
+ * @param {String} name the name of the file to look for.
+ * @param {String} [rootCollection] the root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
+ * @param {Function} callback this will be called after this method executes. Passes null to the first and passes true to the second if the file exists and false otherwise.
+ * @return {null}
+ * @api public
+ */
+GridStore.exist = function(db, fileIdObject, rootCollection, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ rootCollection = args.length ? args.shift() : null;
+
+ // Fetch collection
+ var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION;
+ db.collection(rootCollectionFinal + ".files", function(err, collection) {
+ // Build query
+ var query = (typeof fileIdObject == 'string' || Object.prototype.toString.call(fileIdObject) == '[object RegExp]' )
+ ? {'filename':fileIdObject} : {'_id':fileIdObject}; // Attempt to locate file
+ collection.find(query, function(err, cursor) {
+ cursor.nextObject(function(err, item) {
+ callback(null, item == null ? false : true);
+ });
+ });
+ });
+};
+
+/**
+ * Gets the list of files stored in the GridFS.
+ *
+ * @param {Db} db the database to query.
+ * @param {String} [rootCollection] the root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
+ * @param {Function} callback this will be called after this method executes. Passes null to the first and passes an array of strings containing the names of the files.
+ * @return {null}
+ * @api public
+ */
+GridStore.list = function(db, rootCollection, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ callback = args.pop();
+ rootCollection = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : {};
+
+ // Ensure we have correct values
+ if(rootCollection != null && typeof rootCollection == 'object') {
+ options = rootCollection;
+ rootCollection = null;
+ }
+
+ // Check if we are returning by id not filename
+ var byId = options['id'] != null ? options['id'] : false;
+ // Fetch item
+ var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION;
+ var items = [];
+ db.collection((rootCollectionFinal + ".files"), function(err, collection) {
+ collection.find(function(err, cursor) {
+ cursor.each(function(err, item) {
+ if(item != null) {
+ items.push(byId ? item._id : item.filename);
+ } else {
+ callback(null, items);
+ }
+ });
+ });
+ });
+};
+
+/**
+ * Reads the contents of a file.
+ *
+ * This method has the following signatures
+ *
+ * (db, name, callback)
+ * (db, name, length, callback)
+ * (db, name, length, offset, callback)
+ * (db, name, length, offset, options, callback)
+ *
+ * @param {Db} db the database to query.
+ * @param {String} name the name of the file.
+ * @param {Number} [length] the size of data to read.
+ * @param {Number} [offset] the offset from the head of the file of which to start reading from.
+ * @param {Object} [options] the options for the file.
+ * @param {Function} callback this will be called after this method executes. A string with an error message will be passed to the first parameter when the length and offset combination exceeds the length of the file while an Error object will be passed if other forms of error occured, otherwise, a string is passed. The second parameter will contain the data read if successful or null if an error occured.
+ * @return {null}
+ * @api public
+ */
+GridStore.read = function(db, name, length, offset, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ length = args.length ? args.shift() : null;
+ offset = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : null;
+
+ new GridStore(db, name, "r", options).open(function(err, gridStore) {
+ // Make sure we are not reading out of bounds
+ if(offset && offset >= gridStore.length) return callback("offset larger than size of file", null);
+ if(length && length > gridStore.length) return callback("length is larger than the size of the file", null);
+ if(offset && length && (offset + length) > gridStore.length) return callback("offset and length is larger than the size of the file", null);
+
+ if(offset != null) {
+ gridStore.seek(offset, function(err, gridStore) {
+ gridStore.read(length, function(err, data) {
+ callback(err, data);
+ });
+ });
+ } else {
+ gridStore.read(length, function(err, data) {
+ callback(err, data);
+ });
+ }
+ });
+};
+
+/**
+ * Reads the data of this file.
+ *
+ * @param {Db} db the database to query.
+ * @param {String} name the name of the file.
+ * @param {String} [separator] the character to be recognized as the newline separator.
+ * @param {Object} [options] file options.
+ * @param {Function} callback this will be called after this method is executed. The first parameter will be null and the second parameter will contain an array of strings representing the entire data, each element representing a line including the separator character.
+ * @return {null}
+ * @api public
+ */
+GridStore.readlines = function(db, name, separator, options, callback) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ separator = args.length ? args.shift() : null;
+ options = args.length ? args.shift() : null;
+
+ var finalSeperator = separator == null ? "\n" : separator;
+ new GridStore(db, name, "r", options).open(function(err, gridStore) {
+ gridStore.readlines(finalSeperator, function(err, lines) {
+ callback(err, lines);
+ });
+ });
+};
+
+/**
+ * Deletes the chunks and metadata information of a file from GridFS.
+ *
+ * @param {Db} db the database to interact with.
+ * @param {String|Array} names the name/names of the files to delete.
+ * @param {Object} [options] the options for the files.
+ * @callback {Function} this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object.
+ * @return {null}
+ * @api public
+ */
+GridStore.unlink = function(db, names, options, callback) {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 2);
+ callback = args.pop();
+ options = args.length ? args.shift() : null;
+
+ if(names.constructor == Array) {
+ var tc = 0;
+ for(var i = 0; i < names.length; i++) {
+ ++tc;
+ self.unlink(db, names[i], function(result) {
+ if(--tc == 0) {
+ callback(null, self);
+ }
+ });
+ }
+ } else {
+ new GridStore(db, names, "w", options).open(function(err, gridStore) {
+ deleteChunks(gridStore, function(err, result) {
+ gridStore.collection(function(err, collection) {
+ collection.remove({'_id':gridStore.fileId}, {safe:true}, function(err, collection) {
+ callback(err, self);
+ });
+ });
+ });
+ });
+ }
+};
+
+/**
+ * @ignore
+ * @api private
+ */
+exports.GridStore = GridStore;
diff --git a/node_modules/mongodb/lib/mongodb/gridfs/readstream.js b/node_modules/mongodb/lib/mongodb/gridfs/readstream.js
new file mode 100644
index 0000000..2d3e574
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/gridfs/readstream.js
@@ -0,0 +1,179 @@
+var Stream = require('stream').Stream,
+ util = require('util');
+
+/**
+ * ReadStream
+ *
+ * Returns a stream interface for the **file**.
+ *
+ * Events
+ * - **data** {function(item) {}} the data event triggers when a document is ready.
+ * - **end** {function() {}} the end event triggers when there is no more documents available.
+ * - **close** {function() {}} the close event triggers when the stream is closed.
+ * - **error** {function(err) {}} the error event triggers if an error happens.
+ *
+ * @class Represents a GridFS File Stream.
+ * @param {Boolean} autoclose automatically close file when the stream reaches the end.
+ * @param {GridStore} cursor a cursor object that the stream wraps.
+ * @return {ReadStream}
+ */
+function ReadStream(autoclose, gstore) {
+ if (!(this instanceof ReadStream)) return new ReadStream(autoclose, gstore);
+ Stream.call(this);
+
+ this.autoclose = !!autoclose;
+ this.gstore = gstore;
+
+ this.finalLength = gstore.length - gstore.position;
+ this.completedLength = 0;
+
+ this.paused = false;
+ this.readable = true;
+ this.pendingChunk = null;
+ this.executing = false;
+
+ var self = this;
+ process.nextTick(function() {
+ self._execute();
+ });
+};
+
+/**
+ * Inherit from Stream
+ * @ignore
+ * @api private
+ */
+ReadStream.prototype.__proto__ = Stream.prototype;
+
+/**
+ * Flag stating whether or not this stream is readable.
+ */
+ReadStream.prototype.readable;
+
+/**
+ * Flag stating whether or not this stream is paused.
+ */
+ReadStream.prototype.paused;
+
+/**
+ * @ignore
+ * @api private
+ */
+ReadStream.prototype._execute = function() {
+ if(this.paused === true || this.readable === false) {
+ return;
+ }
+
+ var gstore = this.gstore;
+ var self = this;
+ // Set that we are executing
+ this.executing = true;
+
+ var last = false;
+ var toRead = 0;
+
+ if ((gstore.currentChunk.length() - gstore.currentChunk.position + 1 + self.completedLength) >= self.finalLength) {
+ toRead = self.finalLength - self.completedLength;
+ self.executing = false;
+ last = true;
+ } else {
+ toRead = gstore.currentChunk.length();
+ }
+
+ var data = gstore.currentChunk.readSlice(toRead);
+
+ if(data != null) {
+ self.completedLength += data.length;
+ self.pendingChunk = null;
+ self.emit("data", data);
+ }
+
+ if(last === true) {
+ self.readable = false;
+ self.emit("end");
+
+ if(self.autoclose === true) {
+ if(gstore.mode[0] == "w") {
+ gstore.close(function(err, doc) {
+ if (err) {
+ self.emit("error", err);
+ return;
+ }
+ self.readable = false;
+ self.emit("close", doc);
+ });
+ } else {
+ self.readable = false;
+ self.emit("close");
+ }
+ }
+ } else {
+ gstore._nthChunk(gstore.currentChunk.chunkNumber + 1, function(err, chunk) {
+ if(err) {
+ self.readable = false;
+ self.emit("error", err);
+ self.executing = false;
+ return;
+ }
+
+ self.pendingChunk = chunk;
+ if(self.paused === true) {
+ self.executing = false;
+ return;
+ }
+
+ gstore.currentChunk = self.pendingChunk;
+ self._execute();
+ });
+ }
+};
+
+/**
+ * Pauses this stream, then no farther events will be fired.
+ *
+ * @ignore
+ * @api public
+ */
+ReadStream.prototype.pause = function() {
+ if(!this.executing) {
+ this.paused = true;
+ }
+};
+
+/**
+ * Destroys the stream, then no farther events will be fired.
+ *
+ * @ignore
+ * @api public
+ */
+ReadStream.prototype.destroy = function() {
+ this.readable = false;
+ // Emit close event
+ this.emit("close");
+};
+
+/**
+ * Resumes this stream.
+ *
+ * @ignore
+ * @api public
+ */
+ReadStream.prototype.resume = function() {
+ if(this.paused === false || !this.readable) {
+ return;
+ }
+
+ this.paused = false;
+ var self = this;
+ if(self.pendingChunk != null) {
+ self.currentChunk = self.pendingChunk;
+ process.nextTick(function() {
+ self._execute();
+ });
+ } else {
+ self.readable = false;
+ self.emit("close");
+ }
+};
+
+exports.ReadStream = ReadStream;
diff --git a/node_modules/mongodb/lib/mongodb/index.js b/node_modules/mongodb/lib/mongodb/index.js
new file mode 100644
index 0000000..721e2f3
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/index.js
@@ -0,0 +1,142 @@
+try {
+ exports.BSONPure = require('bson').BSONPure;
+ exports.BSONNative = require('bson').BSONNative;
+} catch(err) {
+ // do nothing
+}
+
+[ 'commands/base_command'
+ , 'commands/db_command'
+ , 'commands/delete_command'
+ , 'commands/get_more_command'
+ , 'commands/insert_command'
+ , 'commands/kill_cursor_command'
+ , 'commands/query_command'
+ , 'commands/update_command'
+ , 'responses/mongo_reply'
+ , 'admin'
+ , 'collection'
+ , 'connection/connection'
+ , 'connection/server'
+ , 'connection/repl_set_servers'
+ , 'cursor'
+ , 'db'
+ , 'gridfs/grid'
+ , 'gridfs/chunk'
+ , 'gridfs/gridstore'].forEach(function (path) {
+ var module = require('./' + path);
+ for (var i in module) {
+ exports[i] = module[i];
+ }
+
+ // Add BSON Classes
+ exports.Binary = require('bson').Binary;
+ exports.Code = require('bson').Code;
+ exports.DBRef = require('bson').DBRef;
+ exports.Double = require('bson').Double;
+ exports.Long = require('bson').Long;
+ exports.MinKey = require('bson').MinKey;
+ exports.MaxKey = require('bson').MaxKey;
+ exports.ObjectID = require('bson').ObjectID;
+ exports.Symbol = require('bson').Symbol;
+ exports.Timestamp = require('bson').Timestamp;
+
+ // Add BSON Parser
+ exports.BSON = require('bson').BSONPure.BSON;
+});
+
+// Exports all the classes for the PURE JS BSON Parser
+exports.pure = function() {
+ var classes = {};
+ // Map all the classes
+ [ 'commands/base_command'
+ , 'commands/db_command'
+ , 'commands/delete_command'
+ , 'commands/get_more_command'
+ , 'commands/insert_command'
+ , 'commands/kill_cursor_command'
+ , 'commands/query_command'
+ , 'commands/update_command'
+ , 'responses/mongo_reply'
+ , 'admin'
+ , 'collection'
+ , 'connection/connection'
+ , 'connection/server'
+ , 'connection/repl_set_servers'
+ , 'cursor'
+ , 'db'
+ , 'gridfs/grid'
+ , 'gridfs/chunk'
+ , 'gridfs/gridstore'].forEach(function (path) {
+ var module = require('./' + path);
+ for (var i in module) {
+ classes[i] = module[i];
+ }
+ });
+
+ // Add BSON Classes
+ classes.Binary = require('bson').Binary;
+ classes.Code = require('bson').Code;
+ classes.DBRef = require('bson').DBRef;
+ classes.Double = require('bson').Double;
+ classes.Long = require('bson').Long;
+ classes.MinKey = require('bson').MinKey;
+ classes.MaxKey = require('bson').MaxKey;
+ classes.ObjectID = require('bson').ObjectID;
+ classes.Symbol = require('bson').Symbol;
+ classes.Timestamp = require('bson').Timestamp;
+
+ // Add BSON Parser
+ classes.BSON = require('bson').BSONPure.BSON;
+
+ // Return classes list
+ return classes;
+}
+
+// Exports all the classes for the PURE JS BSON Parser
+exports.native = function() {
+ var classes = {};
+ // Map all the classes
+ [ 'commands/base_command'
+ , 'commands/db_command'
+ , 'commands/delete_command'
+ , 'commands/get_more_command'
+ , 'commands/insert_command'
+ , 'commands/kill_cursor_command'
+ , 'commands/query_command'
+ , 'commands/update_command'
+ , 'responses/mongo_reply'
+ , 'admin'
+ , 'collection'
+ , 'connection/connection'
+ , 'connection/server'
+ , 'connection/repl_set_servers'
+ , 'cursor'
+ , 'db'
+ , 'gridfs/grid'
+ , 'gridfs/chunk'
+ , 'gridfs/gridstore'].forEach(function (path) {
+ var module = require('./' + path);
+ for (var i in module) {
+ classes[i] = module[i];
+ }
+ });
+
+ // Add BSON Classes
+ classes.Binary = require('bson').Binary;
+ classes.Code = require('bson').Code;
+ classes.DBRef = require('bson').DBRef;
+ classes.Double = require('bson').Double;
+ classes.Long = require('bson').Long;
+ classes.MinKey = require('bson').MinKey;
+ classes.MaxKey = require('bson').MaxKey;
+ classes.ObjectID = require('bson').ObjectID;
+ classes.Symbol = require('bson').Symbol;
+ classes.Timestamp = require('bson').Timestamp;
+
+ // Add BSON Parser
+ classes.BSON = require('bson').BSONNative.BSON;
+
+ // Return classes list
+ return classes;
+}
diff --git a/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js b/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js
new file mode 100644
index 0000000..74396fa
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js
@@ -0,0 +1,131 @@
+var Long = require('bson').Long;
+
+/**
+ Reply message from mongo db
+**/
+var MongoReply = exports.MongoReply = function() {
+ this.documents = [];
+ this.index = 0;
+};
+
+MongoReply.prototype.parseHeader = function(binary_reply, bson) {
+ // Unpack the standard header first
+ this.messageLength = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ this.index = this.index + 4;
+ // Fetch the request id for this reply
+ this.requestId = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ this.index = this.index + 4;
+ // Fetch the id of the request that triggered the response
+ this.responseTo = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ // Skip op-code field
+ this.index = this.index + 4 + 4;
+ // Unpack the reply message
+ this.responseFlag = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ this.index = this.index + 4;
+ // Unpack the cursor id (a 64 bit long integer)
+ var low_bits = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ this.index = this.index + 4;
+ var high_bits = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ this.index = this.index + 4;
+ this.cursorId = new Long(low_bits, high_bits);
+ // Unpack the starting from
+ this.startingFrom = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ this.index = this.index + 4;
+ // Unpack the number of objects returned
+ this.numberReturned = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ this.index = this.index + 4;
+}
+
+MongoReply.prototype.parseBody = function(binary_reply, bson, raw, callback) {
+ raw = raw == null ? false : raw;
+ // Just set a doc limit for deserializing
+ var docLimitSize = 1024*20;
+
+ // If our message length is very long, let's switch to process.nextTick for messages
+ if(this.messageLength > docLimitSize) {
+ var batchSize = this.numberReturned;
+ this.documents = new Array(this.numberReturned);
+
+ // Just walk down until we get a positive number >= 1
+ for(var i = 50; i > 0; i--) {
+ if((this.numberReturned/i) >= 1) {
+ batchSize = i;
+ break;
+ }
+ }
+
+ // Actual main creator of the processFunction setting internal state to control the flow
+ var parseFunction = function(_self, _binary_reply, _batchSize, _numberReturned) {
+ var object_index = 0;
+ // Internal loop process that will use nextTick to ensure we yield some time
+ var processFunction = function() {
+ // Adjust batchSize if we have less results left than batchsize
+ if((_numberReturned - object_index) < _batchSize) {
+ _batchSize = _numberReturned - object_index;
+ }
+
+ // If raw just process the entries
+ if(raw) {
+ // Iterate over the batch
+ for(var i = 0; i < _batchSize; i++) {
+ // Are we done ?
+ if(object_index <= _numberReturned) {
+ // Read the size of the bson object
+ var bsonObjectSize = _binary_reply[_self.index] | _binary_reply[_self.index + 1] << 8 | _binary_reply[_self.index + 2] << 16 | _binary_reply[_self.index + 3] << 24;
+ // If we are storing the raw responses to pipe straight through
+ _self.documents[object_index] = binary_reply.slice(_self.index, _self.index + bsonObjectSize);
+ // Adjust binary index to point to next block of binary bson data
+ _self.index = _self.index + bsonObjectSize;
+ // Update number of docs parsed
+ object_index = object_index + 1;
+ }
+ }
+ } else {
+ // Parse documents
+ _self.index = bson.deserializeStream(binary_reply, _self.index, _batchSize, _self.documents, object_index);
+ // Adjust index
+ object_index = object_index + _batchSize;
+ }
+
+ // If we hav more documents process NextTick
+ if(object_index < _numberReturned) {
+ process.nextTick(processFunction);
+ } else {
+ callback(null);
+ }
+ }
+
+ // Return the process function
+ return processFunction;
+ }(this, binary_reply, batchSize, this.numberReturned)();
+ } else {
+ // Let's unpack all the bson documents, deserialize them and store them
+ for(var object_index = 0; object_index < this.numberReturned; object_index++) {
+ // Read the size of the bson object
+ var bsonObjectSize = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
+ // If we are storing the raw responses to pipe straight through
+ if(raw) {
+ // Deserialize the object and add to the documents array
+ this.documents.push(binary_reply.slice(this.index, this.index + bsonObjectSize));
+ } else {
+ // Deserialize the object and add to the documents array
+ this.documents.push(bson.deserialize(binary_reply.slice(this.index, this.index + bsonObjectSize)));
+ }
+ // Adjust binary index to point to next block of binary bson data
+ this.index = this.index + bsonObjectSize;
+ }
+
+ callback(null);
+ }
+}
+
+MongoReply.prototype.is_error = function(){
+ if(this.documents.length == 1) {
+ return this.documents[0].ok == 1 ? false : true;
+ }
+ return false;
+};
+
+MongoReply.prototype.error_message = function() {
+ return this.documents.length == 1 && this.documents[0].ok == 1 ? '' : this.documents[0].errmsg;
+}; \ No newline at end of file
diff --git a/node_modules/mongodb/lib/mongodb/utils.js b/node_modules/mongodb/lib/mongodb/utils.js
new file mode 100644
index 0000000..07a0ab0
--- /dev/null
+++ b/node_modules/mongodb/lib/mongodb/utils.js
@@ -0,0 +1,74 @@
+/**
+ * Sort functions, Normalize and prepare sort parameters
+ */
+var formatSortValue = exports.formatSortValue = function(sortDirection) {
+ var value = ("" + sortDirection).toLowerCase();
+
+ switch (value) {
+ case 'ascending':
+ case 'asc':
+ case '1':
+ return 1;
+ case 'descending':
+ case 'desc':
+ case '-1':
+ return -1;
+ default:
+ throw new Error("Illegal sort clause, must be of the form "
+ + "[['field1', '(ascending|descending)'], "
+ + "['field2', '(ascending|descending)']]");
+ }
+};
+
+var formattedOrderClause = exports.formattedOrderClause = function(sortValue) {
+ var orderBy = {};
+
+ if (Array.isArray(sortValue)) {
+ for(var i = 0; i < sortValue.length; i++) {
+ if(sortValue[i].constructor == String) {
+ orderBy[sortValue[i]] = 1;
+ } else {
+ orderBy[sortValue[i][0]] = formatSortValue(sortValue[i][1]);
+ }
+ }
+ } else if(Object.prototype.toString.call(sortValue) === '[object Object]') {
+ orderBy = sortValue;
+ } else if (sortValue.constructor == String) {
+ orderBy[sortValue] = 1;
+ } else {
+ throw new Error("Illegal sort clause, must be of the form " +
+ "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]");
+ }
+
+ return orderBy;
+};
+
+exports.encodeInt = function(value) {
+ var buffer = new Buffer(4);
+ buffer[3] = (value >> 24) & 0xff;
+ buffer[2] = (value >> 16) & 0xff;
+ buffer[1] = (value >> 8) & 0xff;
+ buffer[0] = value & 0xff;
+ return buffer;
+}
+
+exports.encodeIntInPlace = function(value, buffer, index) {
+ buffer[index + 3] = (value >> 24) & 0xff;
+ buffer[index + 2] = (value >> 16) & 0xff;
+ buffer[index + 1] = (value >> 8) & 0xff;
+ buffer[index] = value & 0xff;
+}
+
+exports.encodeCString = function(string) {
+ var buf = new Buffer(string, 'utf8');
+ return [buf, new Buffer([0])];
+}
+
+exports.decodeUInt32 = function(array, index) {
+ return array[index] | array[index + 1] << 8 | array[index + 2] << 16 | array[index + 3] << 24;
+}
+
+// Decode the int
+exports.decodeUInt8 = function(array, index) {
+ return array[index];
+}