removing ned for second mongo

This commit is contained in:
2013-01-07 15:37:18 -05:00
parent 362fa3eaa8
commit 915edd69dc
27 changed files with 2841 additions and 2321 deletions

8
app.js
View File

@@ -4,9 +4,9 @@
*/ */
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var mongo = require('mongodb'); var mongo = require('mongoskin');
var BSON = mongo.BSONPure; var BSON = mongo.BSONPure;
var db = require('mongoskin').db('localhost:27017/test'); var db = mongo.db('localhost:27017/test');
var testcollection = db.collection('testcollection'); var testcollection = db.collection('testcollection');
var exercisecollection = db.collection('exercisecollection'); var exercisecollection = db.collection('exercisecollection');
var expressocollection = db.collection('expressocollection'); var expressocollection = db.collection('expressocollection');
@@ -16,8 +16,8 @@ var formidable = require('formidable');
var xml2js = require('xml2js'); var xml2js = require('xml2js');
var parser = new xml2js.Parser(); var parser = new xml2js.Parser();
var dateFormat = require('dateformat'); var dateFormat = require('dateformat');
var nodeGarminConnect = require('./node-garmin-connect'); //var nodeGarminConnect = require('./node-garmin-connect');
var garminClient = new nodeGarminConnect(); //var garminClient = new nodeGarminConnect();
var app = require('http').createServer(function handler(request, response) { var app = require('http').createServer(function handler(request, response) {

14
node_modules/mongoskin/History.md generated vendored
View File

@@ -1,3 +1,17 @@
0.5.0 / 2012-12-29
==================
* fixed unsafe mode warnning log
* Merge pull request #84 from kingpearl/master
* MongoDB 1.2.x support
* Merge pull request #73 from jockster/master
* Merge pull request #75 from voke/patch-1
* Fix typo
* fixed bind() test cases;
* Minor error in readme. Now updated
* Updated readme according to issue #72
0.3.4 / 2011-03-24 0.3.4 / 2011-03-24
* fix global leaks * fix global leaks

40
node_modules/mongoskin/Readme.md generated vendored
View File

@@ -1,6 +1,6 @@
# mongoskin # mongoskin [![Build Status](https://secure.travis-ci.org/kissjs/node-mongoskin.png)](http://travis-ci.org/kissjs/node-mongoskin)
[![Build Status](https://secure.travis-ci.org/kissjs/node-mongoskin.png)](http://travis-ci.org/kissjs/node-mongoskin) ![logo](https://raw.github.com/kissjs/node-mongoskin/master/logo.png)
This project is a wrapper of [node-mongodb-native](https://github.com/mongodb/node-mongodb-native). This project is a wrapper of [node-mongodb-native](https://github.com/mongodb/node-mongodb-native).
The api is same to node-mongodb-native, please see the [document](http://mongodb.github.com/node-mongodb-native/) first. The api is same to node-mongodb-native, please see the [document](http://mongodb.github.com/node-mongodb-native/) first.
@@ -14,11 +14,12 @@ The api is same to node-mongodb-native, please see the [document](http://mongodb
* <del>>= 0.9.8 < 1.0.0</del>: mongodb have bug, it will throw a `TypeError: object is not a function` * <del>>= 0.9.8 < 1.0.0</del>: mongodb have bug, it will throw a `TypeError: object is not a function`
when connection open error. when connection open error.
* 1.0.x * <del>1.0.x</del>
* 1.1.x * <del>1.1.x</del>
* 1.2.x
```bash ```bash
$ make test-version $ make test
``` ```
<a name='index'> <a name='index'>
@@ -129,7 +130,7 @@ mongo.db('localhost:27017/testdb').collection('blog').find().toArray(function (e
Server options and BSON options Server options and BSON options
-------- --------
You can also set `auto_reconnect` options querystring. You can also set `auto_reconnect` options querystring.
And native_parser options will automatically set if native_parser is avariable. And native_parser options will automatically set if native_parser is available.
```js ```js
var mongo = require('mongoskin'); var mongo = require('mongoskin');
@@ -172,6 +173,10 @@ It is very useful if you want to use MVC patterns with nodejs and mongodb.
You can also invoke collection by properties after bind, You can also invoke collection by properties after bind,
it could simplfy your `require`. it could simplfy your `require`.
To keep your code in line with DRY principles, it's possible to create your own
data layer by for example, setting up your own validators and/or default values
inside the MVC methods as shown below in the config example
```js ```js
db.bind('posts', { db.bind('posts', {
findTop10 : function (fn) { findTop10 : function (fn) {
@@ -180,8 +185,26 @@ db.bind('posts', {
removeTagWith : function (tag, fn) { removeTagWith : function (tag, fn) {
this.remove({tags:tag},fn); this.remove({tags:tag},fn);
} }
}
}); });
db.bind('settings', {
getAll: function(user, fn) {
this.find({user: user}).toArray(function(err, settings) {
// We want to set a default currency from within our app instead of storing it
// for every user
settings.currency = (typeof settings.currency !== "undefined") ? settings.currency : 'USD';
fn(err, settings);
});
}
});
db.bind('comments'); db.bind('comments');
db.collection('posts').removeTagWith('delete', function (err, replies) { db.collection('posts').removeTagWith('delete', function (err, replies) {
@@ -245,7 +268,8 @@ var db = mongoskin.db([
'192.168.0.2:27017/?auto_reconnect=true', '192.168.0.2:27017/?auto_reconnect=true',
'192.168.0.3:27017/?auto_reconnect=true' '192.168.0.3:27017/?auto_reconnect=true'
], { ], {
database: 'testdb' database: 'testdb',
safe: true
}, { }, {
connectArbiter: false, connectArbiter: false,
socketOptions: { socketOptions: {
@@ -587,6 +611,8 @@ collection.group([], {}, {"count":0}, "function (obj, prev) { prev.count++; }",
Options: Options:
upsert - true/false (perform upsert operation) upsert - true/false (perform upsert operation)
multi - true/false (update all documents matching spec) multi - true/false (update all documents matching spec)
strict - true/false (perform check if the operation failed, required extra call to db)
Deprecated Options:
safe - true/false (perform check if the operation failed, required extra call to db) safe - true/false (perform check if the operation failed, required extra call to db)
**/ **/
``` ```

View File

@@ -3,7 +3,7 @@ var articles = db.collection('articles');
articles.insert({foo: 'bar', val: 'val1'}, function(err, result) { articles.insert({foo: 'bar', val: 'val1'}, function(err, result) {
console.log(result); console.log(result);
articles.update({foo:'bar'}, {foo: 'bar', val:'val2'}, {safe: true}, function(err, result) { articles.update({foo:'bar'}, {foo: 'bar', val:'val2'}, {strict: true}, function(err, result) {
console.log(result); console.log(result);
articles.find({foo: 'bar'}).toArray(function(err, docs){ articles.find({foo: 'bar'}).toArray(function(err, docs){

0
node_modules/mongoskin/node_modules/mongodb/index.js generated vendored Normal file → Executable file
View File

View File

@@ -21,7 +21,7 @@ function Admin(db) {
* Retrieve the server information for the current * Retrieve the server information for the current
* instance of the db client * instance of the db client
* *
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from buildInfo or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -33,7 +33,7 @@ Admin.prototype.buildInfo = function(callback) {
* Retrieve the server information for the current * Retrieve the server information for the current
* instance of the db client * instance of the db client
* *
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from serverInfo or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api private * @api private
*/ */
@@ -47,7 +47,7 @@ Admin.prototype.serverInfo = function(callback) {
/** /**
* Retrieve this db's server status. * Retrieve this db's server status.
* *
* @param {Function} callback returns the server status. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from serverStatus or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -67,7 +67,7 @@ Admin.prototype.serverStatus = function(callback) {
/** /**
* Retrieve the current profiling Level for MongoDB * Retrieve the current profiling Level for MongoDB
* *
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from profilingLevel or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -92,7 +92,7 @@ Admin.prototype.profilingLevel = function(callback) {
/** /**
* Ping the MongoDB server and retrieve results * Ping the MongoDB server and retrieve results
* *
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from ping or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -109,7 +109,7 @@ Admin.prototype.ping = function(options, callback) {
* *
* @param {String} username The user name for the authentication. * @param {String} username The user name for the authentication.
* @param {String} password The password for the authentication. * @param {String} password The password for the authentication.
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from authenticate or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -123,7 +123,7 @@ Admin.prototype.authenticate = function(username, password, callback) {
* Logout current authenticated user * Logout current authenticated user
* *
* @param {Object} [options] Optional parameters to the command. * @param {Object} [options] Optional parameters to the command.
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from logout or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -143,7 +143,7 @@ Admin.prototype.logout = function(callback) {
* @param {String} username The user name for the authentication. * @param {String} username The user name for the authentication.
* @param {String} password The password for the authentication. * @param {String} password The password for the authentication.
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from addUser or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -167,7 +167,7 @@ Admin.prototype.addUser = function(username, password, options, callback) {
* *
* @param {String} username The user name for the authentication. * @param {String} username The user name for the authentication.
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from removeUser or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -187,7 +187,7 @@ Admin.prototype.removeUser = function(username, options, callback) {
* Set the current profiling level of MongoDB * Set the current profiling level of MongoDB
* *
* @param {String} level The new profiling level (off, slow_only, all) * @param {String} level The new profiling level (off, slow_only, all)
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from setProfilingLevel or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -221,15 +221,13 @@ Admin.prototype.setProfilingLevel = function(level, callback) {
/** /**
* Retrive the current profiling information for MongoDB * Retrive the current profiling information for MongoDB
* *
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from profilingInfo or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
Admin.prototype.profilingInfo = function(callback) { Admin.prototype.profilingInfo = function(callback) {
try { try {
new Cursor(this.db, new Collection(this.db, DbCommand.SYSTEM_PROFILE_COLLECTION), {}, null, null, null new Cursor(this.db, new Collection(this.db, DbCommand.SYSTEM_PROFILE_COLLECTION), {}, {}, {dbName: 'admin'}).toArray(function(err, items) {
, null, null, null, null, null, null, null, null, null, null
, null, null, null, null, null, null, null, null, 'admin').toArray(function(err, items) {
return callback(err, items); return callback(err, items);
}); });
} catch (err) { } catch (err) {
@@ -242,7 +240,7 @@ Admin.prototype.profilingInfo = function(callback) {
* *
* @param {Object} command A command object `{ping:1}`. * @param {Object} command A command object `{ping:1}`.
* @param {Object} [options] Optional parameters to the command. * @param {Object} [options] Optional parameters to the command.
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The command always return the whole result of the command as the second parameter.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -264,7 +262,7 @@ Admin.prototype.command = function(command, options, callback) {
* *
* @param {String} collectionName The name of the collection to validate. * @param {String} collectionName The name of the collection to validate.
* @param {Object} [options] Optional parameters to the command. * @param {Object} [options] Optional parameters to the command.
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from validateCollection or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -304,7 +302,7 @@ Admin.prototype.validateCollection = function(collectionName, options, callback)
/** /**
* List the available databases * List the available databases
* *
* @param {Function} callback Callback function of format `function(err, result) {}`. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from listDatabases or null if an error occured.
* @return {null} Returns no result * @return {null} Returns no result
* @api public * @api public
*/ */
@@ -319,7 +317,7 @@ Admin.prototype.listDatabases = function(callback) {
/** /**
* Get ReplicaSet status * Get ReplicaSet status
* *
* @param {Function} callback returns the replica set status (if available). * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from replSetGetStatus or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */

View File

@@ -67,13 +67,19 @@ function Collection (db, collectionName, pkFactory, options) {
* Inserts a single document or a an array of documents into MongoDB. * Inserts a single document or a an array of documents into MongoDB.
* *
* Options * Options
* - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write
* - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
* - **fsync**, (Boolean, default:false) write waits for fsync before returning
* - **journal**, (Boolean, default:false) write waits for journal sync before returning
* - **continueOnError/keepGoing** {Boolean, default:false}, keep inserting documents even if one document has an error, *mongodb 1.9.1 >*. * - **continueOnError/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. * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document.
*
* Deprecated 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 {Array|Object} docs * @param {Array|Object} docs
* @param {Object} [options] optional options for insert command * @param {Object} [options] optional options for insert command
* @param {Function} [callback] optional callback for the function, must be provided when using `safe` mode * @param {Function} [callback] optional callback for the function, must be provided when using a writeconcern
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -112,12 +118,18 @@ var checkCollectionName = function checkCollectionName (collectionName) {
* Removes documents specified by `selector` from the db. * Removes documents specified by `selector` from the db.
* *
* Options * Options
* - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write
* - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
* - **fsync**, (Boolean, default:false) write waits for fsync before returning
* - **journal**, (Boolean, default:false) write waits for journal sync before returning
* - **single** {Boolean, default:false}, removes the first document found. * - **single** {Boolean, default:false}, removes the first document found.
*
* Deprecated 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} [selector] optional select, no selector is equivalent to removing all documents.
* @param {Object} [options] additional options during remove. * @param {Object} [options] additional options during remove.
* @param {Function} [callback] must be provided if you performing a safe remove * @param {Function} [callback] must be provided if you performing a remove with a writeconcern
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -153,14 +165,9 @@ Collection.prototype.remove = function remove(selector, options, callback) {
, flags); , flags);
var self = this; var self = this;
var errorOptions = options.safe != null ? options.safe : null; var errorOptions = _getWriteConcern(self, options, callback);
errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
errorOptions = errorOptions == null && this.db.safe != null ? this.db.safe : 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 // Execute the command, do not add a callback as it's async
if(errorOptions && errorOptions != false) { if(_hasWriteConcern(errorOptions) && typeof callback == 'function') {
// Insert options // Insert options
var commandOptions = {read:false}; var commandOptions = {read:false};
// If we have safe set set async to false // If we have safe set set async to false
@@ -188,6 +195,8 @@ Collection.prototype.remove = function remove(selector, options, callback) {
callback(null, error[0].n); callback(null, error[0].n);
} }
}); });
} else if(_hasWriteConcern(errorOptions) && callback == null) {
throw new Error("Cannot use a writeConcern without a provided callback");
} else { } else {
var result = this.db._executeRemoveCommand(deleteCommand); var result = this.db._executeRemoveCommand(deleteCommand);
// If no callback just return // If no callback just return
@@ -271,7 +280,7 @@ var insertAll = function insertAll (self, docs, options, callback) {
, dbName + "." + self.collectionName, true, insertFlags); , dbName + "." + self.collectionName, true, insertFlags);
// Add the documents and decorate them with id's if they have none // Add the documents and decorate them with id's if they have none
for (var index = 0, len = docs.length; index < len; ++index) { for(var index = 0, len = docs.length; index < len; ++index) {
var doc = docs[index]; var doc = docs[index];
// Add id to each document if it's not already defined // Add id to each document if it's not already defined
@@ -283,17 +292,11 @@ var insertAll = function insertAll (self, docs, options, callback) {
} }
// Collect errorOptions // Collect errorOptions
var errorOptions = options.safe != null ? options.safe : null; var errorOptions = _getWriteConcern(self, options, callback);
errorOptions = errorOptions == null && self.opts.safe != null ? self.opts.safe : errorOptions;
errorOptions = errorOptions == null && self.db.safe != null ? self.db.safe : 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 // Default command options
var commandOptions = {}; var commandOptions = {};
// If safe is defined check for error message // If safe is defined check for error message
if(errorOptions && errorOptions != false) { if(_hasWriteConcern(errorOptions) && typeof callback == 'function') {
// Insert options // Insert options
commandOptions['read'] = false; commandOptions['read'] = false;
// If we have safe set set async to false // If we have safe set set async to false
@@ -322,8 +325,11 @@ var insertAll = function insertAll (self, docs, options, callback) {
callback(null, docs); callback(null, docs);
} }
}); });
} else if(_hasWriteConcern(errorOptions) && callback == null) {
throw new Error("Cannot use a writeConcern without a provided callback");
} else { } else {
var result = self.db._executeInsertCommand(insertCommand, commandOptions, callback); // Execute the call without a write concern
var result = self.db._executeInsertCommand(insertCommand, commandOptions);
// If no callback just return // If no callback just return
if(!callback) return; if(!callback) return;
// If error return error // If error return error
@@ -340,6 +346,12 @@ var insertAll = function insertAll (self, docs, options, callback) {
* operators and update instead for more efficient operations. * operators and update instead for more efficient operations.
* *
* Options * Options
* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write
* - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
* - **fsync**, (Boolean, default:false) write waits for fsync before returning
* - **journal**, (Boolean, default:false) write waits for journal sync before returning
*
* Deprecated Options
* - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. * - **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} [doc] the document to save
@@ -352,16 +364,15 @@ Collection.prototype.save = function save(doc, options, callback) {
if('function' === typeof options) callback = options, options = null; if('function' === typeof options) callback = options, options = null;
if(options == null) options = {}; if(options == null) options = {};
if(!('function' === typeof callback)) callback = null; 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 // Extract the id, if we have one we need to do a update command
var id = doc['_id']; var id = doc['_id'];
var commandOptions = _getWriteConcern(this, options, callback);
if(id) { if(id) {
this.update({ _id: id }, doc, { upsert: true, safe: errorOptions }, callback); commandOptions.upsert = true;
this.update({ _id: id }, doc, commandOptions, callback);
} else { } else {
this.insert(doc, { safe: errorOptions }, callback && function (err, docs) { this.insert(doc, commandOptions, callback && function (err, docs) {
if (err) return callback(err, null); if (err) return callback(err, null);
if (Array.isArray(docs)) { if (Array.isArray(docs)) {
@@ -377,15 +388,21 @@ Collection.prototype.save = function save(doc, options, callback) {
* Updates documents. * Updates documents.
* *
* Options * Options
* - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write
* - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
* - **fsync**, (Boolean, default:false) write waits for fsync before returning
* - **journal**, (Boolean, default:false) write waits for journal sync before returning
* - **upsert** {Boolean, default:false}, perform an upsert operation. * - **upsert** {Boolean, default:false}, perform an upsert operation.
* - **multi** {Boolean, default:false}, update all documents matching the selector. * - **multi** {Boolean, default:false}, update all documents matching the selector.
* - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document.
*
* Deprecated 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 the query to select the document/documents to be updated * @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} document the fields/vals to be updated, or in the case of an upsert operation, inserted.
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} [callback] must be provided if you performing a safe update * @param {Function} [callback] must be provided if you performing an update with a writeconcern
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -418,21 +435,15 @@ Collection.prototype.update = function update(selector, document, options, callb
var self = this; var self = this;
// Unpack the error options if any // Unpack the error options if any
var errorOptions = (options && options.safe != null) ? options.safe : null; var errorOptions = _getWriteConcern(this, options, callback);
errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions; // If safe is defined check for error message
errorOptions = errorOptions == null && this.db.safe != null ? this.db.safe : errorOptions; if(_hasWriteConcern(errorOptions) && typeof callback == 'function') {
// 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 safe mode or safe both the update and the safe command must happen on the same line
if(errorOptions && errorOptions != false) {
// Insert options // Insert options
var commandOptions = {read:false}; var commandOptions = {read:false};
// If we have safe set set async to false // If we have safe set set async to false
if(errorOptions == null) commandOptions['async'] = true; if(errorOptions == null) commandOptions['async'] = true;
// Set safe option // Set safe option
commandOptions['safe'] = true; commandOptions['safe'] = errorOptions;
// If we have an error option // If we have an error option
if(typeof errorOptions == 'object') { if(typeof errorOptions == 'object') {
var keys = Object.keys(errorOptions); var keys = Object.keys(errorOptions);
@@ -455,6 +466,8 @@ Collection.prototype.update = function update(selector, document, options, callb
callback(null, error[0].n, error[0]); callback(null, error[0].n, error[0]);
} }
}); });
} else if(_hasWriteConcern(errorOptions) && callback == null) {
throw new Error("Cannot use a writeConcern without a provided callback");
} else { } else {
// Execute update // Execute update
var result = this.db._executeUpdateCommand(updateCommand); var result = this.db._executeUpdateCommand(updateCommand);
@@ -478,7 +491,7 @@ Collection.prototype.update = function update(selector, document, options, callb
* @param {String} key key to run distinct against. * @param {String} key key to run distinct against.
* @param {Object} [query] option query to narrow the returned objects. * @param {Object} [query] option query to narrow the returned objects.
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} callback must be provided. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from distinct or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -516,7 +529,7 @@ Collection.prototype.distinct = function distinct(key, query, options, callback)
* *
* @param {Object} [query] query to filter by before performing count. * @param {Object} [query] query to filter by before performing count.
* @param {Object} [options] additional options during count. * @param {Object} [options] additional options during count.
* @param {Function} callback must be provided. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the count method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -567,7 +580,7 @@ Collection.prototype.count = function count (query, options, callback) {
/** /**
* Drop the collection * Drop the collection
* *
* @param {Function} [callback] provide a callback to be notified when command finished executing * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the drop method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -579,16 +592,22 @@ Collection.prototype.drop = function drop(callback) {
* Find and update a document. * Find and update a document.
* *
* Options * Options
* - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write
* - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
* - **fsync**, (Boolean, default:false) write waits for fsync before returning
* - **journal**, (Boolean, default:false) write waits for journal sync before returning
* - **remove** {Boolean, default:false}, set to true to remove the object before returning. * - **remove** {Boolean, default:false}, set to true to remove the object before returning.
* - **upsert** {Boolean, default:false}, perform an upsert operation. * - **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. * - **new** {Boolean, default:false}, set to true if you want to return the modified object rather than the original. Ignored for remove.
*
* Deprecated 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 {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 {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} doc - the fields/vals to be updated
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} [callback] returns results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findAndModify method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -627,9 +646,7 @@ Collection.prototype.findAndModify = function findAndModify (query, sort, doc, o
} }
// Unpack the error options if any // Unpack the error options if any
var errorOptions = (options && options.safe != null) ? options.safe : null; var errorOptions = _getWriteConcern(this, options, callback);
errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
errorOptions = errorOptions == null && this.db.safe != null ? this.db.safe : errorOptions;
// If we have j, w or something else do the getLast Error path // If we have j, w or something else do the getLast Error path
if(errorOptions != null && typeof errorOptions == 'object') { if(errorOptions != null && typeof errorOptions == 'object') {
@@ -676,12 +693,18 @@ Collection.prototype.findAndModify = function findAndModify (query, sort, doc, o
* Find and remove a document * Find and remove a document
* *
* Options * Options
* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write
* - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
* - **fsync**, (Boolean, default:false) write waits for fsync before returning
* - **journal**, (Boolean, default:false) write waits for journal sync before returning
*
* Deprecated Options
* - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. * - **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 {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 {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 {Object} [options] additional options during update.
* @param {Function} [callback] returns results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findAndRemove method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -696,10 +719,11 @@ Collection.prototype.findAndRemove = function(query, sort, options, callback) {
this.findAndModify(query, sort, null, options, callback); this.findAndModify(query, sort, null, options, callback);
} }
var testForFields = {'limit' : 1, 'sort' : 1, 'fields' : 1, 'skip' : 1, 'hint' : 1, 'explain' : 1, 'snapshot' : 1 var testForFields = {
, 'timeout' : 1, 'tailable' : 1, 'batchSize' : 1, 'raw' : 1, 'read' : 1 limit: 1, sort: 1, fields:1, skip: 1, hint: 1, explain: 1, snapshot: 1, timeout: 1, tailable: 1, tailableRetryInterval: 1
, 'returnKey' : 1, 'maxScan' : 1, 'min' : 1, 'max' : 1, 'showDiskLoc' : 1, 'comment' : 1, 'dbName' : 1, 'exhaust': 1 , numberOfRetries: 1, awaitdata: 1, exhaust: 1, batchSize: 1, returnKey: 1, maxScan: 1, min: 1, max: 1, showDiskLoc: 1
, 'tailableRetryInterval': 1}; , comment: 1, raw: 1, readPreference: 1, numberOfRetries: 1, partial: 1, read: 1, dbName: 1
};
/** /**
* Creates a cursor for a query that can be used to iterate over results from MongoDB * Creates a cursor for a query that can be used to iterate over results from MongoDB
@@ -741,7 +765,7 @@ var testForFields = {'limit' : 1, 'sort' : 1, 'fields' : 1, 'skip' : 1, 'hint' :
* *
* @param {Object} query query object to locate the object to modify * @param {Object} query query object to locate the object to modify
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} [callback] optional callback for cursor. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the find method or null if an error occured.
* @return {Cursor} returns a cursor to the query * @return {Cursor} returns a cursor to the query
* @api public * @api public
*/ */
@@ -860,15 +884,9 @@ Collection.prototype.find = function find () {
// callback for backward compatibility // callback for backward compatibility
if(callback) { if(callback) {
// TODO refactor Cursor args // TODO refactor Cursor args
callback(null, new Cursor(this.db, this, selector, fields, o.skip, o.limit callback(null, new Cursor(this.db, this, selector, fields, o));
, 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, o.awaitdata
, o.numberOfRetries, o.dbName, o.tailableRetryInterval, o.exhaust));
} else { } else {
return new Cursor(this.db, this, selector, fields, o.skip, o.limit return new Cursor(this.db, this, selector, fields, o);
, 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, o.awaitdata
, o.numberOfRetries, o.dbName, o.tailableRetryInterval, o.exhaust);
} }
}; };
@@ -941,7 +959,7 @@ var normalizeHintField = function normalizeHintField(hint) {
* *
* @param {Object} query query object to locate the object to modify * @param {Object} query query object to locate the object to modify
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} [callback] optional callback for cursor. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findOne method or null if an error occured.
* @return {Cursor} returns a cursor to the query * @return {Cursor} returns a cursor to the query
* @api public * @api public
*/ */
@@ -951,10 +969,9 @@ Collection.prototype.findOne = function findOne () {
var callback = args.pop(); var callback = args.pop();
var cursor = this.find.apply(this, args).limit(-1).batchSize(1); var cursor = this.find.apply(this, args).limit(-1).batchSize(1);
// Return the item // Return the item
cursor.toArray(function(err, items) { cursor.nextObject(function(err, item) {
if(err != null) return callback(err instanceof Error ? err : self.db.wrap(new Error(err)), null); if(err != null) return callback(err instanceof Error ? err : self.db.wrap(err), null);
if(items.length == 1) return callback(null, items[0]); callback(null, item);
callback(null, null);
}); });
}; };
@@ -962,7 +979,10 @@ Collection.prototype.findOne = function findOne () {
* Creates an index on the collection. * Creates an index on the collection.
* *
* Options * Options
* - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write
* - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
* - **fsync**, (Boolean, default:false) write waits for fsync before returning
* - **journal**, (Boolean, default:false) write waits for journal sync before returning
* - **unique** {Boolean, default:false}, creates an unique index. * - **unique** {Boolean, default:false}, creates an unique index.
* - **sparse** {Boolean, default:false}, creates a sparse index. * - **sparse** {Boolean, default:false}, creates a sparse index.
* - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible.
@@ -972,10 +992,13 @@ Collection.prototype.findOne = function findOne () {
* - **v** {Number}, specify the format version of the indexes. * - **v** {Number}, specify the format version of the indexes.
* - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
* - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
*
* Deprecated 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} fieldOrSpec fieldOrSpec that defines the index. * @param {Object} fieldOrSpec fieldOrSpec that defines the index.
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} callback for results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the createIndex method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -988,13 +1011,7 @@ Collection.prototype.createIndex = function createIndex (fieldOrSpec, options, c
options = options == null ? {} : options; options = options == null ? {} : options;
// Collect errorOptions // Collect errorOptions
var errorOptions = options.safe != null ? options.safe : null; var errorOptions = _getWriteConcern(this, options, callback);
errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
errorOptions = errorOptions == null && this.db.safe != null ? this.db.safe : 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 // Execute create index
this.db.createIndex(this.collectionName, fieldOrSpec, options, callback); this.db.createIndex(this.collectionName, fieldOrSpec, options, callback);
}; };
@@ -1003,7 +1020,10 @@ Collection.prototype.createIndex = function createIndex (fieldOrSpec, options, c
* Ensures that an index exists, if it does not it creates it * Ensures that an index exists, if it does not it creates it
* *
* Options * Options
* - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write
* - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option)
* - **fsync**, (Boolean, default:false) write waits for fsync before returning
* - **journal**, (Boolean, default:false) write waits for journal sync before returning
* - **unique** {Boolean, default:false}, creates an unique index. * - **unique** {Boolean, default:false}, creates an unique index.
* - **sparse** {Boolean, default:false}, creates a sparse index. * - **sparse** {Boolean, default:false}, creates a sparse index.
* - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible.
@@ -1013,10 +1033,13 @@ Collection.prototype.createIndex = function createIndex (fieldOrSpec, options, c
* - **v** {Number}, specify the format version of the indexes. * - **v** {Number}, specify the format version of the indexes.
* - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
* - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
*
* Deprecated 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} fieldOrSpec fieldOrSpec that defines the index. * @param {Object} fieldOrSpec fieldOrSpec that defines the index.
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} callback for results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the ensureIndex method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1031,14 +1054,6 @@ Collection.prototype.ensureIndex = function ensureIndex (fieldOrSpec, options, c
options = {}; 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.safe != null ? this.db.safe : 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 // Execute create index
this.db.ensureIndex(this.collectionName, fieldOrSpec, options, callback); this.db.ensureIndex(this.collectionName, fieldOrSpec, options, callback);
}; };
@@ -1050,7 +1065,7 @@ Collection.prototype.ensureIndex = function ensureIndex (fieldOrSpec, options, c
* - **full** {Boolean, default:false}, returns the full raw index information. * - **full** {Boolean, default:false}, returns the full raw index information.
* *
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} callback returns the index information. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexInformation method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1067,7 +1082,7 @@ Collection.prototype.indexInformation = function indexInformation (options, call
* Drops an index from this collection. * Drops an index from this collection.
* *
* @param {String} name * @param {String} name
* @param {Function} callback returns the results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropIndex method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1078,7 +1093,7 @@ Collection.prototype.dropIndex = function dropIndex (name, callback) {
/** /**
* Drops all indexes from this collection. * Drops all indexes from this collection.
* *
* @param {Function} callback returns the results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropAllIndexes method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1098,7 +1113,7 @@ Collection.prototype.dropAllIndexes = function dropIndexes (callback) {
* Drops all indexes from this collection. * Drops all indexes from this collection.
* *
* @deprecated * @deprecated
* @param {Function} callback returns the results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropIndexes method or null if an error occured.
* @return {null} * @return {null}
* @api private * @api private
*/ */
@@ -1108,7 +1123,7 @@ Collection.prototype.dropIndexes = Collection.prototype.dropAllIndexes;
* Reindex all indexes on the collection * 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. * 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. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the reIndex method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
**/ **/
@@ -1134,7 +1149,7 @@ Collection.prototype.reIndex = function(callback) {
* @param {Function|String} map the mapping function. * @param {Function|String} map the mapping function.
* @param {Function|String} reduce the reduce function. * @param {Function|String} reduce the reduce function.
* @param {Objects} [options] options for the map reduce job. * @param {Objects} [options] options for the map reduce job.
* @param {Function} callback returns the result of the map reduce job, (error, results, [stats]) * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the mapReduce method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1165,7 +1180,11 @@ Collection.prototype.mapReduce = function mapReduce (map, reduce, options, callb
// Add any other options passed in // Add any other options passed in
for (var name in options) { for (var name in options) {
mapCommandHash[name] = options[name]; if ('scope' == name) {
mapCommandHash[name] = processScope(options[name]);
} else {
mapCommandHash[name] = options[name];
}
} }
// Set read preference if we set one // Set read preference if we set one
@@ -1222,6 +1241,30 @@ Collection.prototype.mapReduce = function mapReduce (map, reduce, options, callb
}); });
}; };
/**
* Functions that are passed as scope args must
* be converted to Code instances.
* @ignore
*/
function processScope (scope) {
if (!utils.isObject(scope)) {
return scope;
}
var keys = Object.keys(scope);
var i = keys.length;
var key;
while (i--) {
key = keys[i];
if ('function' == typeof scope[key]) {
scope[key] = new Code(String(scope[key]));
}
}
return scope;
}
/** /**
* Group function helper * Group function helper
* @ignore * @ignore
@@ -1267,7 +1310,7 @@ var groupFunction = function () {
* @param {Function|Code} finalize an optional function to be run on each item in the result set just before the item is returned. * @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 {Boolean} command specify if you wish to run using the internal group command or using eval, default is true.
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} callback returns the results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the group method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1371,7 +1414,7 @@ Collection.prototype.group = function group(keys, condition, initial, reduce, fi
/** /**
* Returns the options of the collection. * Returns the options of the collection.
* *
* @param {Function} callback returns option results. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the options method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1387,7 +1430,7 @@ Collection.prototype.options = function options(callback) {
/** /**
* Returns if the collection is a capped collection * Returns if the collection is a capped collection
* *
* @param {Function} callback returns if collection is capped. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the isCapped method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1405,7 +1448,7 @@ Collection.prototype.isCapped = function isCapped(callback) {
* Checks if one or more indexes exist on the collection * 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 {String|Array} indexNames check if one or more indexes exist on the collection.
* @param {Function} callback returns if the indexes exist. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexExists method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1445,7 +1488,7 @@ Collection.prototype.indexExists = function indexExists(indexes, callback) {
* @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order. * @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 {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 {Objects} [options] options for the map reduce job.
* @param {Function} callback returns matching documents. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the geoNear method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1486,7 +1529,7 @@ Collection.prototype.geoNear = function geoNear(x, y, options, callback) {
* @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order. * @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 {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 {Objects} [options] options for the map reduce job.
* @param {Function} callback returns matching documents. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the geoHaystackSearch method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1515,7 +1558,7 @@ Collection.prototype.geoHaystackSearch = function geoHaystackSearch(x, y, option
/** /**
* Retrieve all the indexes on the collection. * Retrieve all the indexes on the collection.
* *
* @param {Function} callback returns index information. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexes method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1532,7 +1575,7 @@ Collection.prototype.indexes = function indexes(callback) {
* *
* @param {Array} array containing all the aggregation framework commands for the execution. * @param {Array} array containing all the aggregation framework commands for the execution.
* @param {Object} [options] additional options during update. * @param {Object} [options] additional options during update.
* @param {Function} callback returns matching documents. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the aggregate method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1541,8 +1584,10 @@ Collection.prototype.aggregate = function(pipeline, options, callback) {
var args = Array.prototype.slice.call(arguments, 0); var args = Array.prototype.slice.call(arguments, 0);
callback = args.pop(); callback = args.pop();
var self = this; var self = this;
// Get the right options
options = args[args.length - 1].explain ? args.pop() : {} // If we have any of the supported options in the options object
var opts = args[args.length - 1];
options = opts.readPreference || opts.explain ? args.pop() : {}
// Convert operations to an array // Convert operations to an array
if(!Array.isArray(args[0])) { if(!Array.isArray(args[0])) {
@@ -1582,7 +1627,7 @@ Collection.prototype.aggregate = function(pipeline, options, callback) {
* - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST).
* *
* @param {Objects} [options] options for the stats command. * @param {Objects} [options] options for the stats command.
* @param {Function} callback returns statistical information for the collection. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the stats method or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -1617,20 +1662,65 @@ Object.defineProperty(Collection.prototype, "hint", {
} }
}); });
/**
* @ignore
*/
var _hasWriteConcern = function(errorOptions) {
return errorOptions == true
|| errorOptions.w > 0
|| errorOptions.w == 'majority'
|| errorOptions.j == true
|| errorOptions.journal == true
|| errorOptions.fsync == true
}
/**
* @ignore
*/
var _setWriteConcernHash = function(options) {
var finalOptions = {};
if(options.w != null) finalOptions.w = options.w;
if(options.journal == true) finalOptions.j = options.journal;
if(options.j == true) finalOptions.j = options.j;
if(options.fsync == true) finalOptions.fsync = options.fsync;
if(options.wtimeout != null) finalOptions.wtimeout = options.wtimeout;
return finalOptions;
}
/**
* @ignore
*/
var _getWriteConcern = function(self, options, callback) {
// Final options
var finalOptions = {w:1};
// Local options verification
if(options.w != null || typeof options.j == 'boolean' || typeof options.journal == 'boolean' || typeof options.fsync == 'boolean') {
finalOptions = _setWriteConcernHash(options);
} else if(typeof options.safe == "boolean") {
finalOptions = {w: (options.safe ? 1 : 0)};
} else if(options.safe != null && typeof options.safe == 'object') {
finalOptions = _setWriteConcernHash(options.safe);
} else if(self.opts.w != null || typeof self.opts.j == 'boolean' || typeof self.opts.journal == 'boolean' || typeof self.opts.fsync == 'boolean') {
finalOptions = _setWriteConcernHash(self.opts);
} else if(typeof self.opts.safe == "boolean") {
finalOptions = {w: (self.opts.safe ? 1 : 0)};
} else if(self.db.safe.w != null || typeof self.db.safe.j == 'boolean' || typeof self.db.safe.journal == 'boolean' || typeof self.db.safe.fsync == 'boolean') {
finalOptions = _setWriteConcernHash(self.db.safe);
} else if(self.db.options.w != null || typeof self.db.options.j == 'boolean' || typeof self.db.options.journal == 'boolean' || typeof self.db.options.fsync == 'boolean') {
finalOptions = _setWriteConcernHash(self.db.options);
} else if(typeof self.db.safe == "boolean") {
finalOptions = {w: (self.db.safe ? 1 : 0)};
}
// Ensure we don't have an invalid combination of write concerns
if(finalOptions.w < 1
&& (finalOptions.journal == true || finalOptions.j == true || finalOptions.fsync == true)) throw new Error("No acknowlegement using w < 1 cannot be combined with journal:true or fsync:true");
// Return the options
return finalOptions;
}
/** /**
* Expose. * Expose.
*/ */
exports.Collection = Collection; exports.Collection = Collection;

View File

@@ -1,6 +1,7 @@
var QueryCommand = require('./query_command').QueryCommand, var QueryCommand = require('./query_command').QueryCommand,
InsertCommand = require('./insert_command').InsertCommand, InsertCommand = require('./insert_command').InsertCommand,
inherits = require('util').inherits, inherits = require('util').inherits,
utils = require('../utils'),
crypto = require('crypto'); crypto = require('crypto');
/** /**
@@ -107,6 +108,11 @@ DbCommand.createGetLastErrorCommand = function(options, db) {
} }
} }
// Special case for w == 1, remove the w
if(1 == command.w) {
delete command.w;
}
// Execute command // Execute command
return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command, null); return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command, null);
}; };
@@ -127,28 +133,36 @@ DbCommand.createCreateIndexCommand = function(db, collectionName, fieldOrSpec, o
var keys; var keys;
// Get all the fields accordingly // Get all the fields accordingly
if (fieldOrSpec.constructor === String) { // 'type' if('string' == typeof fieldOrSpec) {
// 'type'
indexes.push(fieldOrSpec + '_' + 1); indexes.push(fieldOrSpec + '_' + 1);
fieldHash[fieldOrSpec] = 1; fieldHash[fieldOrSpec] = 1;
} else if (fieldOrSpec.constructor === Array) { // [{location:'2d'}, ...]
} else if(utils.isArray(fieldOrSpec)) {
fieldOrSpec.forEach(function(f) { fieldOrSpec.forEach(function(f) {
if (f.constructor === String) { // [{location:'2d'}, 'type'] if('string' == typeof f) {
// [{location:'2d'}, 'type']
indexes.push(f + '_' + 1); indexes.push(f + '_' + 1);
fieldHash[f] = 1; fieldHash[f] = 1;
} else if (f.constructor === Array) { // [['location', '2d'],['type', 1]] } else if(utils.isArray(f)) {
// [['location', '2d'],['type', 1]]
indexes.push(f[0] + '_' + (f[1] || 1)); indexes.push(f[0] + '_' + (f[1] || 1));
fieldHash[f[0]] = f[1] || 1; fieldHash[f[0]] = f[1] || 1;
} else if (f.constructor === Object) { // [{location:'2d'}, {type:1}] } else if(utils.isObject(f)) {
// [{location:'2d'}, {type:1}]
keys = Object.keys(f); keys = Object.keys(f);
keys.forEach(function(k) { keys.forEach(function(k) {
indexes.push(k + '_' + f[k]); indexes.push(k + '_' + f[k]);
fieldHash[k] = f[k]; fieldHash[k] = f[k];
}); });
} else { } else {
// undefined // undefined (ignore)
} }
}); });
} else if (fieldOrSpec.constructor === Object) { // {location:'2d', type:1}
} else if(utils.isObject(fieldOrSpec)) {
// {location:'2d', type:1}
keys = Object.keys(fieldOrSpec); keys = Object.keys(fieldOrSpec);
keys.forEach(function(key) { keys.forEach(function(key) {
indexes.push(key + '_' + fieldOrSpec[key]); indexes.push(key + '_' + fieldOrSpec[key]);
@@ -157,26 +171,38 @@ DbCommand.createCreateIndexCommand = function(db, collectionName, fieldOrSpec, o
} }
// Generate the index name // Generate the index name
var indexName = typeof options.name == 'string' ? options.name : indexes.join("_"); var indexName = typeof options.name == 'string'
// Build the selector ? options.name
var selector = {'ns':(db.databaseName + "." + collectionName), 'key':fieldHash, 'name':indexName}; : indexes.join("_");
var selector = {
'ns': db.databaseName + "." + collectionName,
'key': fieldHash,
'name': indexName
}
// Ensure we have a correct finalUnique // Ensure we have a correct finalUnique
var finalUnique = options == null || 'object' === typeof options ? false : options; var finalUnique = options == null || 'object' === typeof options
? false
: options;
// Set up options // Set up options
options = options == null || typeof options == 'boolean' ? {} : options; options = options == null || typeof options == 'boolean'
? {}
: options;
// Add all the options // Add all the options
var keys = Object.keys(options); var keys = Object.keys(options);
// Add all the fields to the selector
for(var i = 0; i < keys.length; i++) { for(var i = 0; i < keys.length; i++) {
selector[keys[i]] = options[keys[i]]; selector[keys[i]] = options[keys[i]];
} }
// If we don't have the unique property set on the selector if(selector['unique'] == null)
if(selector['unique'] == null) selector['unique'] = finalUnique; 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); var name = db.databaseName + "." + DbCommand.SYSTEM_INDEX_COLLECTION;
var cmd = new InsertCommand(db, name, false);
return cmd.add(selector);
}; };
DbCommand.logoutCommand = function(db, command_hash, options) { DbCommand.logoutCommand = function(db, command_hash, options) {
@@ -211,4 +237,4 @@ DbCommand.createAdminDbCommandSlaveOk = function(db, command_hash) {
DbCommand.createDbSlaveOkCommand = function(db, command_hash, options) { 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); return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT | QueryCommand.OPTS_SLAVE, 0, -1, command_hash, null, options);
}; };

View File

@@ -89,10 +89,10 @@ Connection.prototype.start = function() {
} }
} else { } else {
// Create new connection instance // Create new connection instance
if(!this.domainSocket) { if(this.domainSocket) {
this.connection = net.createConnection(this.socketOptions.port, this.socketOptions.host); this.connection = net.createConnection(this.socketOptions.host);
} else { } else {
this.connection = net.createConnection(this.socketOptions.host); this.connection = net.createConnection(this.socketOptions.port, this.socketOptions.host);
} }
if(this.logger != null && this.logger.doDebug){ if(this.logger != null && this.logger.doDebug){
this.logger.debug("opened connection", this.socketOptions); this.logger.debug("opened connection", this.socketOptions);
@@ -139,7 +139,7 @@ Connection.prototype.write = function(command, callback) {
if(!this.socketOptions['disableDriverBSONSizeCheck'] && binaryCommand.length > this.maxBsonSize) if(!this.socketOptions['disableDriverBSONSizeCheck'] && binaryCommand.length > this.maxBsonSize)
return callback(new Error("Document exceeds maximal allowed bson size of " + this.maxBsonSize + " bytes")); return callback(new Error("Document exceeds maximal allowed bson size of " + this.maxBsonSize + " bytes"));
if(this.logger != null && this.logger.doDebug) if(this.logger != null && this.logger.doDebug)
this.logger.debug("writing command to mongodb", binaryCommand); this.logger.debug("writing command to mongodb", {binary: binaryCommand, json: command[i]});
var r = this.writeSteam.write(binaryCommand); var r = this.writeSteam.write(binaryCommand);
} }
@@ -149,7 +149,7 @@ Connection.prototype.write = function(command, callback) {
return callback(new Error("Document exceeds maximal allowed bson size of " + this.maxBsonSize + " bytes")); return callback(new Error("Document exceeds maximal allowed bson size of " + this.maxBsonSize + " bytes"));
if(this.logger != null && this.logger.doDebug) if(this.logger != null && this.logger.doDebug)
this.logger.debug("writing command to mongodb", binaryCommand); this.logger.debug("writing command to mongodb", {binary: binaryCommand, json: command});
var r = this.writeSteam.write(binaryCommand); var r = this.writeSteam.write(binaryCommand);
} }

View File

@@ -7,9 +7,13 @@ var utils = require('./connection_utils'),
Connection = require("./connection").Connection; Connection = require("./connection").Connection;
var ConnectionPool = exports.ConnectionPool = function(host, port, poolSize, bson, socketOptions) { 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 + "]"; if(typeof host !== 'string') {
throw new Error("host must be specified [" + host + "]");
}
// Set up event emitter // Set up event emitter
EventEmitter.call(this); EventEmitter.call(this);
// Keep all options for the socket in a specific collection allowing the user to specify the // Keep all options for the socket in a specific collection allowing the user to specify the
// Wished upon socket connection parameters // Wished upon socket connection parameters
this.socketOptions = typeof socketOptions === 'object' ? socketOptions : {}; this.socketOptions = typeof socketOptions === 'object' ? socketOptions : {};
@@ -22,7 +26,11 @@ var ConnectionPool = exports.ConnectionPool = function(host, port, poolSize, bso
this.minPoolSize = Math.floor(this.poolSize / 2) + 1; this.minPoolSize = Math.floor(this.poolSize / 2) + 1;
// Check if the host is a socket // Check if the host is a socket
if(host.match(/^\//)) this.socketOptions.domainSocket = true; if(host.match(/^\//)) {
this.socketOptions.domainSocket = true;
} else if(typeof port !== 'number') {
throw new Error("port must be specified [" + port + "]");
}
// Set default settings for the socket options // Set default settings for the socket options
utils.setIntegerParameter(this.socketOptions, 'timeout', 0); utils.setIntegerParameter(this.socketOptions, 'timeout', 0);
@@ -96,9 +104,8 @@ var _connect = function(_self) {
_self.emit("error", err); _self.emit("error", err);
} }
// Set disconnected connection.close();
_self._poolState = 'disconnected'; _self._poolState = 'disconnected';
// Stop
_self.stop(); _self.stop();
}); });
@@ -120,9 +127,8 @@ var _connect = function(_self) {
if(_self._poolState !== 'disconnected' && _self.listeners("timeout").length > 0) { if(_self._poolState !== 'disconnected' && _self.listeners("timeout").length > 0) {
_self.emit("timeout", err); _self.emit("timeout", err);
} }
// Set disconnected
_self._poolState = 'disconnected'; connection.close();
// Stop
_self.stop(); _self.stop();
}); });

View File

@@ -1,14 +1,14 @@
var Connection = require('./connection').Connection, var Connection = require('./connection').Connection,
ReadPreference = require('./read_preference').ReadPreference, ReadPreference = require('./read_preference').ReadPreference,
DbCommand = require('../commands/db_command').DbCommand, DbCommand = require('../commands/db_command').DbCommand,
MongoReply = require('../responses/mongo_reply').MongoReply, MongoReply = require('../responses/mongo_reply').MongoReply,
debug = require('util').debug, debug = require('util').debug,
EventEmitter = require('events').EventEmitter,
inherits = require('util').inherits, inherits = require('util').inherits,
inspect = require('util').inspect, inspect = require('util').inspect,
Server = require('./server').Server, Server = require('./server').Server,
PingStrategy = require('./strategies/ping_strategy').PingStrategy, PingStrategy = require('./strategies/ping_strategy').PingStrategy,
StatisticsStrategy = require('./strategies/statistics_strategy').StatisticsStrategy; StatisticsStrategy = require('./strategies/statistics_strategy').StatisticsStrategy,
Base = require('./base').Base;
const STATE_STARTING_PHASE_1 = 0; const STATE_STARTING_PHASE_1 = 0;
const STATE_PRIMARY = 1; const STATE_PRIMARY = 1;
@@ -35,6 +35,7 @@ const STATE_ROLLBACK = 9;
* - **strategy** {String, default:null}, selection strategy for reads choose between (ping and statistical, default is round-robin) * - **strategy** {String, default:null}, selection strategy for reads choose between (ping and statistical, default is round-robin)
* - **secondaryAcceptableLatencyMS** {Number, default:15}, sets the range of servers to pick when using NEAREST (lowest ping ms + the latency fence, ex: range of 1 to (1 + 15) ms) * - **secondaryAcceptableLatencyMS** {Number, default:15}, sets the range of servers to pick when using NEAREST (lowest ping ms + the latency fence, ex: range of 1 to (1 + 15) ms)
* - **connectArbiter** {Boolean, default:false}, sets if the driver should connect to arbiters or not. * - **connectArbiter** {Boolean, default:false}, sets if the driver should connect to arbiters or not.
* - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**.
* *
* @class Represents a Replicaset Configuration * @class Represents a Replicaset Configuration
* @param {Array} list of server objects participating in the replicaset. * @param {Array} list of server objects participating in the replicaset.
@@ -48,7 +49,7 @@ var ReplSet = exports.ReplSet = function(servers, options) {
return new ReplSet(servers, options); return new ReplSet(servers, options);
// Set up event emitter // Set up event emitter
EventEmitter.call(this); Base.call(this);
// Ensure no Mongos's // Ensure no Mongos's
for(var i = 0; i < servers.length; i++) { for(var i = 0; i < servers.length; i++) {
@@ -89,6 +90,8 @@ var ReplSet = exports.ReplSet = function(servers, options) {
this._numberOfServersLeftToInitialize = 0; this._numberOfServersLeftToInitialize = 0;
// Do we record server stats or not // Do we record server stats or not
this.recordQueryStats = false; this.recordQueryStats = false;
// Update health try server
this.updateHealthServerTry = 0;
// Get the readPreference // Get the readPreference
var readPreference = this.options['readPreference']; var readPreference = this.options['readPreference'];
@@ -106,6 +109,12 @@ var ReplSet = exports.ReplSet = function(servers, options) {
this._readPreference = null; this._readPreference = null;
} }
// Ensure read_secondary is set correctly
if(!this.readSecondary)
this.readSecondary = this._readPreference == ReadPreference.PRIMARY
|| this._readPreference == false
|| this._readPreference == null ? false : true;
// Strategy for picking a secondary // Strategy for picking a secondary
this.secondaryAcceptableLatencyMS = this.options['secondaryAcceptableLatencyMS'] == null ? 15 : this.options['secondaryAcceptableLatencyMS']; this.secondaryAcceptableLatencyMS = this.options['secondaryAcceptableLatencyMS'] == null ? 15 : this.options['secondaryAcceptableLatencyMS'];
this.strategy = this.options['strategy']; this.strategy = this.options['strategy'];
@@ -115,7 +124,6 @@ var ReplSet = exports.ReplSet = function(servers, options) {
if(this.strategy == 'ping') { if(this.strategy == 'ping') {
// Create a new instance // Create a new instance
this.strategyInstance = new PingStrategy(this, this.secondaryAcceptableLatencyMS); this.strategyInstance = new PingStrategy(this, this.secondaryAcceptableLatencyMS);
this.strategyInstance.start();
} else if(this.strategy == 'statistical') { } else if(this.strategy == 'statistical') {
// Set strategy as statistical // Set strategy as statistical
this.strategyInstance = new StatisticsStrategy(this); this.strategyInstance = new StatisticsStrategy(this);
@@ -174,8 +182,12 @@ var ReplSet = exports.ReplSet = function(servers, options) {
// How often are we checking for new servers in the replicaset // How often are we checking for new servers in the replicaset
this.replicasetStatusCheckInterval = this.options['haInterval'] == null ? 1000 : this.options['haInterval']; this.replicasetStatusCheckInterval = this.options['haInterval'] == null ? 1000 : this.options['haInterval'];
this._replicasetTimeoutId = null; this._replicasetTimeoutId = null;
// Connection timeout // Connection timeout
this._connectTimeoutMS = 1000; this._connectTimeoutMS = this.socketOptions.connectTimeoutMS
? this.socketOptions.connectTimeoutMS
: 1000;
// Current list of servers to test // Current list of servers to test
this.pingCandidateServers = []; this.pingCandidateServers = [];
@@ -186,7 +198,7 @@ var ReplSet = exports.ReplSet = function(servers, options) {
/** /**
* @ignore * @ignore
*/ */
inherits(ReplSet, EventEmitter); inherits(ReplSet, Base);
/** /**
* @ignore * @ignore
@@ -219,8 +231,20 @@ ReplSet.prototype.isMongos = function() {
/** /**
* @ignore * @ignore
*/ */
ReplSet.prototype.isConnected = function() { ReplSet.prototype.isConnected = function(read) {
return this.primary != null && this._state.master != null && this._state.master.isConnected(); if(read == null || read == ReadPreference.PRIMARY || read == false)
return this.primary != null && this._state.master != null && this._state.master.isConnected();
if((read == ReadPreference.PRIMARY_PREFERRED || read == ReadPreference.SECONDARY_PREFERRED || read == ReadPreference.NEAREST)
&& ((this.primary != null && this._state.master != null && this._state.master.isConnected())
|| (this._state && this._state.secondaries && Object.keys(this._state.secondaries).length > 0))) {
return true;
} else if(read == ReadPreference.SECONDARY) {
return this._state && this._state.secondaries && Object.keys(this._state.secondaries).length > 0;
}
// No valid connection return false
return false;
} }
/** /**
@@ -242,25 +266,13 @@ ReplSet.prototype.isPrimary = function(config) {
*/ */
ReplSet.prototype.isReadPrimary = ReplSet.prototype.isPrimary; ReplSet.prototype.isReadPrimary = ReplSet.prototype.isPrimary;
/**
* @ignore
**/
ReplSet.prototype._checkReplicaSet = function() {
if(!this.haEnabled) return false;
var currentTime = new Date().getTime();
if((currentTime - this.lastReplicaSetTime) >= this.replicasetStatusCheckInterval) {
this.lastReplicaSetTime = currentTime;
return true;
} else {
return false;
}
}
/** /**
* @ignore * @ignore
*/ */
ReplSet.prototype.allServerInstances = function() { ReplSet.prototype.allServerInstances = function() {
var self = this; var self = this;
// If no state yet return empty
if(!self._state) return [];
// Close all the servers (concatenate entire list of servers first for ease) // Close all the servers (concatenate entire list of servers first for ease)
var allServers = self._state.master != null ? [self._state.master] : []; var allServers = self._state.master != null ? [self._state.master] : [];
@@ -290,153 +302,274 @@ ReplSet.prototype.allServerInstances = function() {
} }
/** /**
* Enables high availability pings.
*
* @ignore * @ignore
*/ */
var __executeAllCallbacksWithError = function(dbInstance, error) { ReplSet.prototype._enableHA = function () {
var keys = Object.keys(dbInstance._callBackStore._notReplied); var self = this;
// Iterate over all callbacks return check();
for(var i = 0; i < keys.length; i++) {
// Delete info object function ping () {
delete dbInstance._callBackStore._notReplied[keys[i]]; if("disconnected" == self._serverState) return;
// Emit the error
dbInstance._callBackStore.emit(keys[i], error); if(Object.keys(self._state.addresses).length == 0) return;
var selectedServer = self._state.addresses[Object.keys(self._state.addresses)[self.updateHealthServerTry++]];
if(self.updateHealthServerTry >= Object.keys(self._state.addresses).length) self.updateHealthServerTry = 0;
if(selectedServer == null) return check();
// If we have an active db instance
if(self.dbInstances.length > 0) {
var db = self.dbInstances[0];
// Create a new master connection
var _server = new Server(selectedServer.host, selectedServer.port, {
auto_reconnect: false,
returnIsMasterResults: true,
slaveOk: true,
poolSize: 1,
socketOptions: { connectTimeoutMS: self._connectTimeoutMS }
});
// Connect using the new _server connection to not impact the driver
// behavior on any errors we could possibly run into
_server.connect(db, function(err, result, _server) {
if(err) {
if(_server.close) _server.close();
return check();
}
// Create is master command
var cmd = DbCommand.createIsMasterCommand(db);
// Execute is master command
db._executeQueryCommand(cmd, {failFast:true, connection: _server.checkoutWriter()}, function(err, res) {
// Close the connection used
_server.close();
// If error let's set perform another check
if(err) return check();
// Validate the replicaset
self._validateReplicaset(res, db.auths, function() {
check();
});
});
});
}
}
function check () {
self._haTimer = setTimeout(ping, self.replicasetStatusCheckInterval);
} }
} }
/** /**
* @ignore * @ignore
*/ */
ReplSet.prototype._validateReplicaset = function(result, auths) { ReplSet.prototype._validateReplicaset = function(result, auths, cb) {
var self = this; var self = this;
// For each member we need to check if we have a new connection that needs to be established var res = result.documents[0];
var members = result['documents'][0]['members'];
// Get members
var members = Array.isArray(result['documents'][0]['members']) ? result['documents'][0]['members'] : [];
// The total members we check
var serversToConnectList = {};
// Iterate over all the members and see if we need to reconnect // manage master node changes
for(var i = 0, jlen = members.length; i < jlen; i++) { if(res.primary && self._state.master && self._state.master.name != res.primary) {
var member = members[i]; // Delete master record so we can rediscover it
delete self._state.addresses[self._state.master.name];
if(member['health'] != 0 // TODO existing issue? this seems to only work if
&& null == self._state['addresses'][member['name']] // we already have a connection to the new primary.
&& null == serversToConnectList[member['name']]) {
if (member['stateStr'] == 'ARBITER' && self.connectArbiter != true) {
continue;
}
// Split the server string
var parts = member.name.split(/:/);
if(parts.length == 1) {
parts = [parts[0], Connection.DEFAULT_PORT];
}
// Default empty socket options object // Update information on new primary
var socketOptions = {host:parts[0], port:parseInt(parts[1], 10)}; // add as master, remove from secondary
// If a socket option object exists clone it var newMaster = self._state.addresses[res.primary];
if(self.socketOptions != null) { newMaster.isMasterDoc.ismaster = true;
var keys = Object.keys(self.socketOptions); newMaster.isMasterDoc.secondary = false;
for(var k = 0; k < keys.length;k++) socketOptions[keys[i]] = self.socketOptions[keys[i]]; self._state.master = newMaster;
} delete self._state.secondaries[res.primary];
}
// Create a new server instance // discover new hosts
var newServer = new Server(parts[0], parseInt(parts[1], 10), {auto_reconnect:false, 'socketOptions':socketOptions var hosts = [];
, logger:self.logger, ssl:self.ssl, poolSize:self.poolSize});
// Set the replicaset instance
newServer.replicasetInstance = self;
// Add handlers for(var i = 0; i < res.hosts.length; ++i) {
newServer.on("close", _handler("close", self)); var host = res.hosts[i];
newServer.on("error", _handler("error", self)); if (host == res.me) continue;
newServer.on("timeout", _handler("timeout", self)); if (!(self._state.addresses[host] || ~hosts.indexOf(host))) {
// Add to list of server connection target // we dont already have a connection to this host and aren't
serversToConnectList[member['name']] = newServer; // already planning on connecting.
} else if(member['stateStr'] == 'PRIMARY' && self._state.master['name'] != member['name']) { hosts.push(host);
// Delete master record so we can rediscover it
delete self._state['addresses'][self._state.master['name']];
// Update inormation on new primary
var newMaster = self._state.addresses[member['name']];
newMaster.isMasterDoc.ismaster = true;
newMaster.isMasterDoc.secondary = false;
self._state.master = newMaster;
// Remove from secondaries
delete self._state.secondaries[member['name']];
newMaster = null;
} }
} }
// All servers we want to connect to connectTo(hosts, auths, self, cb);
var serverKeys = Object.keys(serversToConnectList); }
// For all remaining servers on the list connect
while(serverKeys.length > 0) { /**
var _serverKey = serverKeys.pop(); * Create connections to all `hosts` firing `cb` after
// Fetch the server * connections are attempted for all `hosts`.
var _server = serversToConnectList[_serverKey]; *
// Add a new server to the total number of servers that need to initialized before we are done * @param {Array} hosts
//var newServerCallback = self.connectionHandler(_server); * @param {Array} [auths]
var newServerCallback = _connectHandler(self, null, _server) * @param {ReplSet} replset
// Connect To the new server * @param {Function} cb
_server.connect(self.db, {returnIsMasterResults: true, eventReceiver:newServer}, function(err, result, _server) { * @ignore
if(err == null && result != null) { */
// Fetch the myState function connectTo (hosts, auths, replset, cb) {
var document = result.documents[0]; var pending = hosts.length;
// Remove from list until if (!pending) return cb();
if(document.ismaster || document.secondary || document.arbiterOnly) {
process.nextTick(function() { for(var i = 0; i < hosts.length; ++i) {
// Apply any auths connectToHost(hosts[i], auths, replset, handle);
if(Array.isArray(auths) && auths.length > 0) { }
// Get number of auths we need to execute
var numberOfAuths = auths.length; function handle () {
// Apply all auths --pending;
for(var i = 0; i < auths.length; i++) { if (0 === pending) cb();
self.db.authenticate(auths[i].username, auths[i].password, {'authdb':auths[i].authdb}, function(err, authenticated) {
numberOfAuths = numberOfAuths - 1;
// If we have no more authentications to replay
if(numberOfAuths == 0) {
newServerCallback(err, result, _server);
}
});
}
} else {
newServerCallback(err, result, _server);
}
});
} else {
_server.close();
}
} else {
_server.close();
}
});
} }
} }
var _handler = function(event, self) { /**
return function(err, server) { * Attempts connection to `host` and authenticates with optional `auth`
// Check if we have a secondary server * for the given `replset` firing `cb` when finished.
if(self._state.master && self._state.master.name == server.name) { *
// Force close * @param {String} host
self.close(); * @param {Array} auths
// Error out all callbacks * @param {ReplSet} replset
__executeAllCallbacksWithError(self.db, err); * @param {Function} cb
} else if(self._state.master * @ignore
&& (self._state.secondaries[server.name] != null */
|| self._state.arbiters[server.name] != null function connectToHost (host, auths, replset, cb) {
|| self._state.passives[server.name] != null)) { var server = createServer(host, replset);
delete self._state.secondaries[server.name]; var options = {
delete self._state.arbiters[server.name]; returnIsMasterResults: true,
delete self._state.passives[server.name]; eventReceiver: server
delete self._state.addresses[server.name]; }
server.connect(replset.db, options, function(err, result) {
var doc = result && result.documents && result.documents[0];
if (err || !doc) {
server.close();
return cb(err, result, server);
} }
// If it's a primary we need to close the set to reconnect if(!(doc.ismaster || doc.secondary || doc.arbiterOnly)) {
if(self._state.master && self._state.master.host == server.host && self._state.master.port == server.port) { server.close();
// If we have app listeners on close event return cb(null, result, server);
if(self.db.listeners(event).length > 0) { }
self.db.emit(event, err);
// if host is an arbiter, disconnect if not configured for it
if(doc.arbiterOnly && !replset.connectArbiter) {
server.close();
return cb(null, result, server);
}
// create handler for successful connections
var handleConnect = _connectHandler(replset, null, server);
function complete () {
handleConnect(err, result);
cb();
}
// authenticate if necessary
if(!(Array.isArray(auths) && auths.length > 0)) {
return complete();
}
var pending = auths.length;
var connections = server.allRawConnections();
var pendingAuthConn = connections.length;
for(var x = 0; x <connections.length; x++) {
var connection = connections[x];
var authDone = false;
for(var i = 0; i < auths.length; i++) {
var auth = auths[i];
var options = { authdb: auth.authdb, connection: connection };
var username = auth.username;
var password = auth.password;
replset.db.authenticate(username, password, options, function() {
--pending;
if(0 === pending) {
authDone = true;
--pendingAuthConn;
if(0 === pendingAuthConn) {
return complete();
}
}
});
} }
} }
});
}
/**
* Creates a new server for the `replset` based on `host`.
*
* @param {String} host - host:port pair (localhost:27017)
* @param {ReplSet} replset - the ReplSet instance
* @return {Server}
* @ignore
*/
function createServer (host, replset) {
// copy existing socket options to new server
var socketOptions = {}
if(replset.socketOptions) {
var keys = Object.keys(replset.socketOptions);
for(var k = 0; k < keys.length; k++) {
socketOptions[keys[k]] = replset.socketOptions[keys[k]];
}
}
var parts = host.split(/:/);
if(1 === parts.length) {
parts[1] = Connection.DEFAULT_PORT;
}
socketOptions.host = parts[0];
socketOptions.port = parseInt(parts[1], 10);
var serverOptions = {
readPreference: replset._readPreference,
socketOptions: socketOptions,
poolSize: replset.poolSize,
logger: replset.logger,
auto_reconnect: false,
ssl: replset.ssl
}
var server = new Server(socketOptions.host, socketOptions.port, serverOptions);
server.replicasetInstance = replset;
server.on("close", _handler("close", replset));
server.on("error", _handler("error", replset));
server.on("timeout", _handler("timeout", replset));
return server;
}
var _handler = function(event, self) {
return function(err, server) {
// console.log("============================== HANDLE EVENT :: " + event)
// console.log("" + server.host + ":" + server.port)
// Remove from all lists
delete self._state.secondaries[server.name];
delete self._state.arbiters[server.name];
delete self._state.passives[server.name];
delete self._state.addresses[server.name];
// Execute all the callbacks with errors
self.__executeAllCallbacksWithError(err);
// If we have app listeners on close event
if(self.db.listeners(event).length > 0) {
self.db.emit(event, err);
}
// If it's the primary close all connections
if(self._state.master
&& self._state.master.host == server.host
&& self._state.master.port == server.port) {
// return self.close();
self._state.master = null;
}
} }
} }
@@ -444,6 +577,7 @@ var _connectHandler = function(self, candidateServers, instanceServer) {
return function(err, result) { return function(err, result) {
// We are disconnected stop attempting reconnect or connect // We are disconnected stop attempting reconnect or connect
if(self._serverState == 'disconnected') return instanceServer.close(); if(self._serverState == 'disconnected') return instanceServer.close();
// If no error handle isMaster // If no error handle isMaster
if(err == null && result.documents[0].hosts != null) { if(err == null && result.documents[0].hosts != null) {
// Fetch the isMaster command result // Fetch the isMaster command result
@@ -479,7 +613,10 @@ var _connectHandler = function(self, candidateServers, instanceServer) {
if (oldServer && oldServer !== instanceServer) oldServer.close(); if (oldServer && oldServer !== instanceServer) oldServer.close();
delete self._state.addresses[userProvidedServerString]; delete self._state.addresses[userProvidedServerString];
if (self._state.addresses[me] && self._state.addresses[me] !== instanceServer) self._state.addresses[me].close(); if (self._state.addresses[me] && self._state.addresses[me] !== instanceServer) {
self._state.addresses[me].close();
}
self._state.addresses[me] = instanceServer; self._state.addresses[me] = instanceServer;
// Let's add the server to our list of server types // Let's add the server to our list of server types
@@ -499,6 +636,7 @@ var _connectHandler = function(self, candidateServers, instanceServer) {
instanceServer.name = me; instanceServer.name = me;
// Add tag info // Add tag info
instanceServer.tags = tags; instanceServer.tags = tags;
// Add the handlers to the instance // Add the handlers to the instance
instanceServer.on("close", _handler("close", self)); instanceServer.on("close", _handler("close", self));
instanceServer.on("error", _handler("error", self)); instanceServer.on("error", _handler("error", self));
@@ -539,19 +677,25 @@ var _connectHandler = function(self, candidateServers, instanceServer) {
// Attempt to connect to the next server // Attempt to connect to the next server
if(Array.isArray(candidateServers) && candidateServers.length > 0) { if(Array.isArray(candidateServers) && candidateServers.length > 0) {
var server = candidateServers.pop(); var server = candidateServers.pop();
// Get server addresses // Get server addresses
var addresses = self._state.addresses; var addresses = self._state.addresses;
// Default empty socket options object // Default empty socket options object
var socketOptions = {}; var socketOptions = {};
// Set fast connect timeout
socketOptions['connectTimeoutMS'] = self._connectTimeoutMS;
// If a socket option object exists clone it // If a socket option object exists clone it
if(self.socketOptions != null && typeof self.socketOptions === 'object') { if(self.socketOptions != null && typeof self.socketOptions === 'object') {
var keys = Object.keys(self.socketOptions); var keys = Object.keys(self.socketOptions);
for(var j = 0; j < keys.length;j++) socketOptions[keys[j]] = self.socketOptions[keys[j]]; for(var j = 0; j < keys.length;j++) socketOptions[keys[j]] = self.socketOptions[keys[j]];
} }
// If ssl is specified // If ssl is specified
if(self.ssl) serverConnections[i].ssl = true; if(self.ssl) server.ssl = true;
// Set fast connect timeout
socketOptions['connectTimeoutMS'] = self._connectTimeoutMS
// Add host information to socket options // Add host information to socket options
socketOptions['host'] = server.host; socketOptions['host'] = server.host;
socketOptions['port'] = server.port; socketOptions['port'] = server.port;
@@ -579,6 +723,10 @@ var _connectHandler = function(self, candidateServers, instanceServer) {
return self.emit("connectionError", return self.emit("connectionError",
new Error("no primary server found in set")) new Error("no primary server found in set"))
} else{ } else{
if (self.strategyInstance) {
self.strategyInstance.start();
}
self.emit("fullsetup", null, self.db, self); self.emit("fullsetup", null, self.db, self);
self.emit("open", null, self.db, self); self.emit("open", null, self.db, self);
} }
@@ -586,6 +734,23 @@ var _connectHandler = function(self, candidateServers, instanceServer) {
} }
} }
/**
* Interval state object constructor
*
* @ignore
*/
ReplSet.State = function ReplSetState () {
this.errorMessages = [];
this.secondaries = {};
this.addresses = {};
this.arbiters = {};
this.passives = {};
this.members = [];
this.errors = {};
this.setName = null;
this.master = null;
}
/** /**
* @ignore * @ignore
*/ */
@@ -597,14 +762,18 @@ ReplSet.prototype.connect = function(parent, options, callback) {
// Ensure it's all closed // Ensure it's all closed
self.close(); self.close();
// Set connecting status // Set connecting status
this.db = parent; this.db = parent;
this._serverState = 'connecting'; this._serverState = 'connecting';
this._callbackList = []; this._callbackList = [];
this._state = {'master':null, 'secondaries':{}, 'arbiters':{}, 'passives':{}
, 'errors':{}, 'addresses':{}, 'setName':null, 'errorMessages':[], 'members':[]}; this._state = new ReplSet.State();
// Ensure parent can do a slave query if it's set // Ensure parent can do a slave query if it's set
parent.slaveOk = this.slaveOk ? this.slaveOk : parent.slaveOk; parent.slaveOk = this.slaveOk
? this.slaveOk
: parent.slaveOk;
// Remove any listeners // Remove any listeners
this.removeAllListeners("fullsetup"); this.removeAllListeners("fullsetup");
@@ -612,11 +781,8 @@ ReplSet.prototype.connect = function(parent, options, callback) {
// Add primary found event handler // Add primary found event handler
this.once("fullsetup", function() { this.once("fullsetup", function() {
// Set state connected self._handleOnFullSetup(parent);
self._serverState = 'connected';
// Emit the fullsetup and open event
parent.emit("open", null, self.db, self);
parent.emit("fullsetup", null, self.db, self);
// Callback // Callback
if(typeof callback == 'function') { if(typeof callback == 'function') {
var internalCallback = callback; var internalCallback = callback;
@@ -640,31 +806,13 @@ ReplSet.prototype.connect = function(parent, options, callback) {
// Get server addresses // Get server addresses
var addresses = this._state.addresses; var addresses = this._state.addresses;
// 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(this.ssl) serverConnections[i].ssl = true;
// Set fast connect timeout
socketOptions['connectTimeoutMS'] = this._connectTimeoutMS
// De-duplicate any servers // De-duplicate any servers
var server; var server, key;
for(var i = 0; i < this.servers.length; i++) { for(var i = 0; i < this.servers.length; i++) {
server = this.servers[i]; server = this.servers[i];
// Add host information to socket options key = server.host + ":" + server.port;
socketOptions['host'] = server.host; if(null == addresses[key]) {
socketOptions['port'] = server.port; addresses[key] = server;
server.socketOptions = socketOptions;
server.replicasetInstance = this;
server.enableRecordQueryStats(this.recordQueryStats);
// If server does not exist set it
if(addresses[server.host + ":" + server.port] == null) {
addresses[server.host + ":" + server.port] = server;
} }
} }
@@ -672,11 +820,45 @@ ReplSet.prototype.connect = function(parent, options, callback) {
var candidateServers = []; var candidateServers = [];
var keys = Object.keys(addresses); var keys = Object.keys(addresses);
for(var i = 0; i < keys.length; i++) { for(var i = 0; i < keys.length; i++) {
candidateServers.push(addresses[keys[i]]); server = addresses[keys[i]];
server.assignReplicaSet(this);
candidateServers.push(server);
} }
// Let's connect to the first one on the list // Let's connect to the first one on the list
server = candidateServers.pop(); server = candidateServers.pop();
server.connect(parent, {returnIsMasterResults: true, eventReceiver:server}, _connectHandler(this, candidateServers, server)); var opts = {
returnIsMasterResults: true,
eventReceiver: server
}
server.connect(parent, opts, _connectHandler(this, candidateServers, server));
}
/**
* Handles the first `fullsetup` event of this ReplSet.
*
* @param {Db} parent
* @ignore
*/
ReplSet.prototype._handleOnFullSetup = function (parent) {
this._serverState = 'connected';
// Emit the fullsetup and open event
parent.emit("open", null, this.db, this);
parent.emit("fullsetup", null, this.db, this);
if(!this.haEnabled) return;
this._enableHA();
}
/**
* Disables high availability pings.
*
* @ignore
*/
ReplSet.prototype._disableHA = function () {
clearTimeout(this._haTimer);
this._haTimer = undefined;
} }
/** /**
@@ -694,26 +876,28 @@ ReplSet.prototype.checkoutWriter = function() {
*/ */
var pickFirstConnectedSecondary = function pickFirstConnectedSecondary(self, tags) { var pickFirstConnectedSecondary = function pickFirstConnectedSecondary(self, tags) {
var keys = Object.keys(self._state.secondaries); var keys = Object.keys(self._state.secondaries);
var connection = null; var connection;
// Find first available reader if any // Find first available reader if any
for(var i = 0; i < keys.length; i++) { for(var i = 0; i < keys.length; i++) {
connection = self._state.secondaries[keys[i]].checkoutReader(); connection = self._state.secondaries[keys[i]].checkoutReader();
if(connection != null) break; if(connection) return connection;
} }
// If we still have a null, read from primary if it's not secondary only // If we still have a null, read from primary if it's not secondary only
if(self._readPreference == ReadPreference.SECONDARY_PREFERRED) { if(self._readPreference == ReadPreference.SECONDARY_PREFERRED) {
connection = self._state.master.checkoutReader(); connection = self._state.master.checkoutReader();
if(connection) return connection;
} }
if(connection == null) { var preferenceName = self._readPreference == ReadPreference.SECONDARY_PREFERRED
var preferenceName = self._readPreference == ReadPreference.SECONDARY_PREFERRED ? 'secondary' : self._readPreference; ? 'secondary'
return new Error("No replica set member available for query with ReadPreference " + preferenceName + " and tags " + JSON.stringify(tags)); : self._readPreference;
}
// Return the connection // console.log("================================================================ pickFirstConnectedSecondary :::: ")
return connection;
return new Error("No replica set member available for query with ReadPreference "
+ preferenceName + " and tags " + JSON.stringify(tags));
} }
/** /**
@@ -769,6 +953,10 @@ var _pickFromTags = function(self, tags) {
*/ */
ReplSet.prototype.checkoutReader = function(readPreference, tags) { ReplSet.prototype.checkoutReader = function(readPreference, tags) {
var connection = null; var connection = null;
// console.log("============================ checkoutReader")
// console.dir(readPreference)
// console.log(arguments.callee.caller.toString())
// If we have a read preference object unpack it // If we have a read preference object unpack it
if(typeof readPreference == 'object' && readPreference['_type'] == 'ReadPreference') { if(typeof readPreference == 'object' && readPreference['_type'] == 'ReadPreference') {
// Validate if the object is using a valid mode // Validate if the object is using a valid mode
@@ -783,6 +971,11 @@ ReplSet.prototype.checkoutReader = function(readPreference, tags) {
// Set up our read Preference, allowing us to override the readPreference // Set up our read Preference, allowing us to override the readPreference
var finalReadPreference = readPreference != null ? readPreference : this._readPreference; var finalReadPreference = readPreference != null ? readPreference : this._readPreference;
finalReadPreference = finalReadPreference == true ? ReadPreference.SECONDARY_PREFERRED : finalReadPreference; finalReadPreference = finalReadPreference == true ? ReadPreference.SECONDARY_PREFERRED : finalReadPreference;
finalReadPreference = finalReadPreference == null ? ReadPreference.PRIMARY : finalReadPreference;
// finalReadPreference = 'primary';
// console.log("============================ finalReadPreference: " + finalReadPreference)
// console.dir(finalReadPreference)
// If we are reading from a primary // If we are reading from a primary
if(finalReadPreference == 'primary') { if(finalReadPreference == 'primary') {
@@ -812,14 +1005,7 @@ ReplSet.prototype.checkoutReader = function(readPreference, tags) {
return new Error("No replica set members available for query"); return new Error("No replica set members available for query");
} }
} else { } else {
// Pick a secondary using round robin connection = _roundRobin(this, tags);
var keys = Object.keys(this._state.secondaries);
this._currentServerChoice = this._currentServerChoice % keys.length;
var key = keys[this._currentServerChoice++];
// Fetch a connectio
connection = this._state.secondaries[key] != null ? this._state.secondaries[key].checkoutReader() : null;
// If connection is null fallback to first available secondary
connection = connection == null ? pickFirstConnectedSecondary(this, tags) : connection;
} }
} else if(finalReadPreference == ReadPreference.PRIMARY_PREFERRED) { } else if(finalReadPreference == ReadPreference.PRIMARY_PREFERRED) {
// Check if there is a primary available and return that if possible // Check if there is a primary available and return that if possible
@@ -835,17 +1021,11 @@ ReplSet.prototype.checkoutReader = function(readPreference, tags) {
return new Error("No replica set members available for query"); return new Error("No replica set members available for query");
} }
} else { } else {
// Pick a secondary using round robin connection = _roundRobin(this, tags);
var keys = Object.keys(this._state.secondaries);
this._currentServerChoice = this._currentServerChoice % keys.length;
var key = keys[this._currentServerChoice++];
// Fetch a connectio
connection = this._state.secondaries[key] != null ? this._state.secondaries[key].checkoutReader() : null;
// If connection is null fallback to first available secondary
connection = connection == null ? pickFirstConnectedSecondary(this, tags) : connection;
} }
} }
} else if(finalReadPreference == ReadPreference.SECONDARY_PREFERRED && tags == null && Object.keys(this._state.secondaries).length == 0) { } else if(finalReadPreference == ReadPreference.SECONDARY_PREFERRED && tags == null && Object.keys(this._state.secondaries).length == 0) {
// console.log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SECONDARY_PREFERRED")
connection = this.checkoutWriter(); connection = this.checkoutWriter();
// If no connection return an error // If no connection return an error
if(connection == null) { if(connection == null) {
@@ -874,6 +1054,7 @@ ReplSet.prototype.checkoutReader = function(readPreference, tags) {
} else if(finalReadPreference == ReadPreference.NEAREST && this.strategyInstance == null) { } else if(finalReadPreference == ReadPreference.NEAREST && this.strategyInstance == null) {
return new Error("A strategy for calculating nearness must be enabled such as ping or statistical"); return new Error("A strategy for calculating nearness must be enabled such as ping or statistical");
} else if(finalReadPreference == ReadPreference.SECONDARY && Object.keys(this._state.secondaries).length == 0) { } else if(finalReadPreference == ReadPreference.SECONDARY && Object.keys(this._state.secondaries).length == 0) {
// console.log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SECONDARY")
if(tags != null && typeof tags == 'object') { if(tags != null && typeof tags == 'object') {
var preferenceName = finalReadPreference == ReadPreference.SECONDARY ? 'secondary' : finalReadPreference; var preferenceName = finalReadPreference == ReadPreference.SECONDARY ? 'secondary' : finalReadPreference;
connection = new Error("No replica set member available for query with ReadPreference " + preferenceName + " and tags " + JSON.stringify(tags)); connection = new Error("No replica set member available for query with ReadPreference " + preferenceName + " and tags " + JSON.stringify(tags));
@@ -888,6 +1069,27 @@ ReplSet.prototype.checkoutReader = function(readPreference, tags) {
return connection; return connection;
} }
/**
* Pick a secondary using round robin
*
* @ignore
*/
function _roundRobin (replset, tags) {
var keys = Object.keys(replset._state.secondaries);
var key = keys[replset._currentServerChoice++ % keys.length];
var conn = null != replset._state.secondaries[key]
? replset._state.secondaries[key].checkoutReader()
: null;
// If connection is null fallback to first available secondary
if (null == conn) {
conn = pickFirstConnectedSecondary(replset, tags);
}
return conn;
}
/** /**
* @ignore * @ignore
*/ */
@@ -899,9 +1101,8 @@ ReplSet.prototype.allRawConnections = function() {
var allMasterConnections = this._state.master.connectionPool.getAllConnections(); var allMasterConnections = this._state.master.connectionPool.getAllConnections();
// Add all connections to list // Add all connections to list
allConnections = allConnections.concat(allMasterConnections); allConnections = allConnections.concat(allMasterConnections);
// If we have read secondary let's add all secondary servers // If we have read secondary let's add all secondary servers
if(this.readSecondary && Object.keys(this._state.secondaries).length > 0) { if(Object.keys(this._state.secondaries).length > 0) {
// Get all the keys // Get all the keys
var keys = Object.keys(this._state.secondaries); var keys = Object.keys(this._state.secondaries);
// For each of the secondaries grab the connections // For each of the secondaries grab the connections

View File

@@ -4,6 +4,8 @@ var Connection = require('./connection').Connection,
MongoReply = require('../responses/mongo_reply').MongoReply, MongoReply = require('../responses/mongo_reply').MongoReply,
ConnectionPool = require('./connection_pool').ConnectionPool, ConnectionPool = require('./connection_pool').ConnectionPool,
EventEmitter = require('events').EventEmitter, EventEmitter = require('events').EventEmitter,
Base = require('./base').Base,
utils = require('../utils'),
inherits = require('util').inherits; inherits = require('util').inherits;
/** /**
@@ -13,7 +15,7 @@ var Connection = require('./connection').Connection,
* - **readPreference** {String, default:null}, set's the read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST) * - **readPreference** {String, default:null}, set's the read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST)
* - **ssl** {Boolean, default:false}, use ssl connection (needs to have a mongod server with ssl support) * - **ssl** {Boolean, default:false}, use ssl connection (needs to have a mongod server with ssl support)
* - **slaveOk** {Boolean, default:false}, legacy option allowing reads from secondary, use **readPrefrence** instead. * - **slaveOk** {Boolean, default:false}, legacy option allowing reads from secondary, use **readPrefrence** instead.
* - **poolSize** {Number, default:1}, number of connections in the connection pool, set to 1 as default for legacy reasons. * - **poolSize** {Number, default:5}, number of connections in the connection pool, set to 5 as default for legacy reasons.
* - **socketOptions** {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number)) * - **socketOptions** {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number))
* - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**. * - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**.
* - **auto_reconnect** {Boolean, default:false}, reconnect on error. * - **auto_reconnect** {Boolean, default:false}, reconnect on error.
@@ -25,10 +27,12 @@ var Connection = require('./connection').Connection,
* @param {Object} [options] optional options for insert command * @param {Object} [options] optional options for insert command
*/ */
function Server(host, port, options) { function Server(host, port, options) {
// Set up event emitter
EventEmitter.call(this);
// Set up Server instance // Set up Server instance
if(!(this instanceof Server)) return new Server(host, port, options); if(!(this instanceof Server)) return new Server(host, port, options);
// Set up event emitter
Base.call(this);
// Ensure correct values // Ensure correct values
if(port != null && typeof port == 'object') { if(port != null && typeof port == 'object') {
options = port; options = port;
@@ -45,16 +49,19 @@ function Server(host, port, options) {
this.poolSize = this.options.poolSize == null ? 5 : this.options.poolSize; this.poolSize = this.options.poolSize == null ? 5 : this.options.poolSize;
this.disableDriverBSONSizeCheck = this.options.disableDriverBSONSizeCheck != null ? this.options.disableDriverBSONSizeCheck : false; this.disableDriverBSONSizeCheck = this.options.disableDriverBSONSizeCheck != null ? this.options.disableDriverBSONSizeCheck : false;
this.ssl = this.options.ssl == null ? false : this.options.ssl; this.ssl = this.options.ssl == null ? false : this.options.ssl;
this.slaveOk = this.options["slave_ok"]; this.slaveOk = this.options["slave_ok"] ? this.options["slave_ok"] : this.options["slaveOk"];
this._used = false; this._used = false;
this.replicasetInstance = null;
// Get the readPreference // Get the readPreference
var readPreference = this.options['readPreference']; var readPreference = this.options['readPreference'];
// If readPreference is an object get the mode string
var validateReadPreference = readPreference != null && typeof readPreference == 'object' ? readPreference.mode : readPreference;
// Read preference setting // Read preference setting
if(readPreference != null) { if(validateReadPreference != null) {
if(readPreference != ReadPreference.PRIMARY && readPreference != ReadPreference.SECONDARY && readPreference != ReadPreference.NEAREST if(validateReadPreference != ReadPreference.PRIMARY && validateReadPreference != ReadPreference.SECONDARY && validateReadPreference != ReadPreference.NEAREST
&& readPreference != ReadPreference.SECONDARY_PREFERRED && readPreference != ReadPreference.PRIMARY_PREFERRED) { && validateReadPreference != ReadPreference.SECONDARY_PREFERRED && validateReadPreference != ReadPreference.PRIMARY_PREFERRED) {
throw new Error("Illegal readPreference mode specified, " + readPreference); throw new Error("Illegal readPreference mode specified, " + validateReadPreference);
} }
// Set read Preference // Set read Preference
@@ -95,8 +102,7 @@ function Server(host, port, options) {
/** /**
* @ignore * @ignore
*/ */
// Inherit simple event emitter inherits(Server, Base);
inherits(Server, EventEmitter);
// //
// Deprecated, USE ReadPreferences class // Deprecated, USE ReadPreferences class
@@ -142,7 +148,7 @@ Server.prototype.close = function(callback) {
// Set server status as disconnected // Set server status as disconnected
this._serverState = 'disconnected'; this._serverState = 'disconnected';
// Peform callback if present // Peform callback if present
if(typeof callback === 'function') callback(); if(typeof callback === 'function') callback(null);
}; };
/** /**
@@ -163,10 +169,46 @@ Server.prototype.allServerInstances = function() {
* @ignore * @ignore
*/ */
Server.prototype.isSetMember = function() { Server.prototype.isSetMember = function() {
return this['replicasetInstance'] != null || this['mongosInstance'] != null; return this.replicasetInstance != null || this.mongosInstance != null;
} }
/** /**
* Assigns a replica set to this `server`.
*
* @param {ReplSet} replset
* @ignore
*/
Server.prototype.assignReplicaSet = function (replset) {
this.replicasetInstance = replset;
this.inheritReplSetOptionsFrom(replset);
this.enableRecordQueryStats(replset.recordQueryStats);
}
/**
* Takes needed options from `replset` and overwrites
* our own options.
*
* @param {ReplSet} replset
* @ignore
*/
Server.prototype.inheritReplSetOptionsFrom = function (replset) {
this.socketOptions = {};
this.socketOptions.connectTimeoutMS = replset._connectTimeoutMS;
if(replset.ssl)
this.socketOptions.ssl = true;
// If a socket option object exists clone it
if(utils.isObject(replset.socketOptions)) {
var keys = Object.keys(replset.socketOptions);
for(var i = 0; i < keys.length; i++)
this.socketOptions[keys[i]] = replset.socketOptions[keys[i]];
}
}
/**
* Opens this server connection.
*
* @ignore * @ignore
*/ */
Server.prototype.connect = function(dbInstance, options, callback) { Server.prototype.connect = function(dbInstance, options, callback) {
@@ -291,8 +333,9 @@ Server.prototype.connect = function(dbInstance, options, callback) {
} }
// The command executed another request, log the handler again under that request id // The command executed another request, log the handler again under that request id
if(mongoReply.requestId > 0 && mongoReply.cursorId.toString() != "0" && callbackInfo.info && callbackInfo.info.exhaust) { if(mongoReply.requestId > 0 && mongoReply.cursorId.toString() != "0"
dbInstance._reRegisterHandler(mongoReply.requestId, callbackInfo); && callbackInfo && callbackInfo.info && callbackInfo.info.exhaust) {
dbInstance._reRegisterHandler(mongoReply.requestId, callbackInfo);
} }
// Only execute callback if we have a caller // Only execute callback if we have a caller
@@ -317,6 +360,8 @@ Server.prototype.connect = function(dbInstance, options, callback) {
// Parse the body // Parse the body
mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) { mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) {
// console.log("+++++++++++++++++++++++++++++++++++++++ recieved message")
// console.dir(message)
if(err != null) { if(err != null) {
// If pool connection is already closed // If pool connection is already closed
if(server._serverState === 'disconnected') return; if(server._serverState === 'disconnected') return;
@@ -342,7 +387,7 @@ Server.prototype.connect = function(dbInstance, options, callback) {
// If we are a single server connection fire errors correctly // If we are a single server connection fire errors correctly
if(!server.isSetMember()) { if(!server.isSetMember()) {
// Fire all callback errors // Fire all callback errors
_fireCallbackErrors(server, new Error("connection closed due to parseError")); server.__executeAllCallbacksWithError(new Error("connection closed due to parseError"));
// Emit error // Emit error
_emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true);
} }
@@ -355,6 +400,7 @@ Server.prototype.connect = function(dbInstance, options, callback) {
// If we have an error let's execute the callback and clean up all other // If we have an error let's execute the callback and clean up all other
// chained commands // chained commands
var firstResult = mongoReply && mongoReply.documents; var firstResult = mongoReply && mongoReply.documents;
// Check for an error, if we have one let's trigger the callback and clean up // Check for an error, if we have one let's trigger the callback and clean up
// The chained callbacks // The chained callbacks
if(firstResult[0].err != null || firstResult[0].errmsg != null) { if(firstResult[0].err != null || firstResult[0].errmsg != null) {
@@ -387,6 +433,8 @@ Server.prototype.connect = function(dbInstance, options, callback) {
} else if(callbackInfo && callbackInfo.callback && callbackInfo.info) { } else if(callbackInfo && callbackInfo.callback && callbackInfo.info) {
// Parse the body // Parse the body
mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) { mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) {
// console.log("+++++++++++++++++++++++++++++++++++++++ recieved message")
// console.dir(message)
if(err != null) { if(err != null) {
// If pool connection is already closed // If pool connection is already closed
if(server._serverState === 'disconnected') return; if(server._serverState === 'disconnected') return;
@@ -412,7 +460,7 @@ Server.prototype.connect = function(dbInstance, options, callback) {
// If we are a single server connection fire errors correctly // If we are a single server connection fire errors correctly
if(!server.isSetMember()) { if(!server.isSetMember()) {
// Fire all callback errors // Fire all callback errors
_fireCallbackErrors(server, new Error("connection closed due to parseError")); server.__executeAllCallbacksWithError(new Error("connection closed due to parseError"));
// Emit error // Emit error
_emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true);
} }
@@ -461,7 +509,7 @@ Server.prototype.connect = function(dbInstance, options, callback) {
// If we are a single server connection fire errors correctly // If we are a single server connection fire errors correctly
if(!server.isSetMember()) { if(!server.isSetMember()) {
// Fire all callback errors // Fire all callback errors
_fireCallbackErrors(server, err); server.__executeAllCallbacksWithError(err);
// Emit error // Emit error
_emitAcrossAllDbInstances(server, eventReceiver, "timeout", err, server, true); _emitAcrossAllDbInstances(server, eventReceiver, "timeout", err, server, true);
} }
@@ -489,7 +537,7 @@ Server.prototype.connect = function(dbInstance, options, callback) {
// If we are a single server connection fire errors correctly // If we are a single server connection fire errors correctly
if(!server.isSetMember()) { if(!server.isSetMember()) {
// Fire all callback errors // Fire all callback errors
_fireCallbackErrors(server, new Error(message && message.err ? message.err : message)); server.__executeAllCallbacksWithError(new Error(message && message.err ? message.err : message));
// Emit error // Emit error
_emitAcrossAllDbInstances(server, eventReceiver, "error", new Error(message && message.err ? message.err : message), server, true); _emitAcrossAllDbInstances(server, eventReceiver, "error", new Error(message && message.err ? message.err : message), server, true);
} }
@@ -517,7 +565,7 @@ Server.prototype.connect = function(dbInstance, options, callback) {
// If we are a single server connection fire errors correctly // If we are a single server connection fire errors correctly
if(!server.isSetMember()) { if(!server.isSetMember()) {
// Fire all callback errors // Fire all callback errors
_fireCallbackErrors(server, new Error("connection closed")); server.__executeAllCallbacksWithError(new Error("connection closed"));
// Emit error // Emit error
_emitAcrossAllDbInstances(server, eventReceiver, "close", server, null, true); _emitAcrossAllDbInstances(server, eventReceiver, "close", server, null, true);
} }
@@ -546,7 +594,7 @@ Server.prototype.connect = function(dbInstance, options, callback) {
// If we are a single server connection fire errors correctly // If we are a single server connection fire errors correctly
if(!server.isSetMember()) { if(!server.isSetMember()) {
// Fire all callback errors // Fire all callback errors
_fireCallbackErrors(server, new Error("connection closed due to parseError")); server.__executeAllCallbacksWithError(new Error("connection closed due to parseError"));
// Emit error // Emit error
_emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true);
} }
@@ -556,45 +604,6 @@ Server.prototype.connect = function(dbInstance, options, callback) {
connectionPool.start(); connectionPool.start();
} }
/**
* Fire all the errors
* @ignore
*/
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]];
// Check if we have a chained command (findAndModify)
if(info && info['chained'] && Array.isArray(info['chained']) && info['chained'].length > 0) {
var chained = info['chained'];
// Only callback once and the last one is the right one
var finalCallback = chained.pop();
if(info.connection.socketOptions.host === server.host && info.connection.socketOptions.port === server.port) {
dbInstance._callBackStore.emit(finalCallback, err, null);
}
// Put back the final callback to ensure we don't call all commands in the chain
chained.push(finalCallback);
// Remove all chained callbacks
for(var i = 0; i < chained.length; i++) {
delete dbInstance._callBackStore._notReplied[chained[i]];
}
} else {
if(info && info.connection.socketOptions.host === server.host && info.connection.socketOptions.port === server.port) {
dbInstance._callBackStore.emit(keys[j], err, null);
}
}
}
}
}
/** /**
* @ignore * @ignore
*/ */
@@ -649,6 +658,8 @@ var canCheckoutWriter = function(self, read) {
* @ignore * @ignore
*/ */
Server.prototype.checkoutWriter = function(read) { Server.prototype.checkoutWriter = function(read) {
// console.log("===================== checkoutWriter :: " + read)
// console.dir(this.isMasterDoc)
if(read == true) return this.connectionPool.checkoutConnection(); if(read == true) return this.connectionPool.checkoutConnection();
// Check if are allowed to do a checkout (if we try to use an arbiter f.ex) // Check if are allowed to do a checkout (if we try to use an arbiter f.ex)
var result = canCheckoutWriter(this, read); var result = canCheckoutWriter(this, read);
@@ -687,6 +698,7 @@ var canCheckoutReader = function(self) {
* @ignore * @ignore
*/ */
Server.prototype.checkoutReader = function() { Server.prototype.checkoutReader = function() {
// console.log("===================== checkoutReader")
// Check if are allowed to do a checkout (if we try to use an arbiter f.ex) // Check if are allowed to do a checkout (if we try to use an arbiter f.ex)
var result = canCheckoutReader(this); var result = canCheckoutReader(this);
// If the result is null check out a writer // If the result is null check out a writer

View File

@@ -144,13 +144,13 @@ PingStrategy.prototype._pingServer = function(callback) {
new function(serverInstance) { new function(serverInstance) {
var options = { poolSize: 1, timeout: 500, auto_reconnect: false }; var options = { poolSize: 1, timeout: 500, auto_reconnect: false };
var server = new Server(serverInstance.host, serverInstance.port, options); var server = new Server(serverInstance.host, serverInstance.port, options);
var db = new self.Db(self.replicaset.db.databaseName, server); var db = new self.Db(self.replicaset.db.databaseName, server, { safe: true });
db.on("error", done); db.on("error", done);
// Open the db instance // Open the db instance
db.open(function(err, _db) { db.open(function(err, _db) {
if(err) return done(_db); if(err) return done(err, _db);
// Startup time of the command // Startup time of the command
var startTime = Date.now(); var startTime = Date.now();
@@ -161,13 +161,13 @@ PingStrategy.prototype._pingServer = function(callback) {
serverInstance.runtimeStats['pingMs'] = Date.now() - startTime; serverInstance.runtimeStats['pingMs'] = Date.now() - startTime;
} }
done(_db); done(null, _db);
}) })
}) })
function done (_db) { function done (err, _db) {
// Close connection // Close connection
_db.close(true); if (_db) _db.close(true);
// Adjust the number of checks // Adjust the number of checks
numberOfEntries--; numberOfEntries--;

View File

@@ -7,7 +7,7 @@ var StatisticsStrategy = exports.StatisticsStrategy = function(replicaset) {
// Starts any needed code // Starts any needed code
StatisticsStrategy.prototype.start = function(callback) { StatisticsStrategy.prototype.start = function(callback) {
callback(null, null); callback && callback(null, null);
} }
StatisticsStrategy.prototype.stop = function(callback) { StatisticsStrategy.prototype.stop = function(callback) {

View File

@@ -11,73 +11,77 @@ var QueryCommand = require('./commands/query_command').QueryCommand,
* using find. This cursor object is unidirectional and cannot traverse backwards. Clients should not be creating a cursor directly, * 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. * but use find to acquire a cursor.
* *
* Options
* - **skip** {Number} skip number of documents to skip.
* - **limit** {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.
* - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc.
* - **hint** {Object}, hint force the query to use a specific index.
* - **explain** {Boolean}, explain return the explaination of the query.
* - **snapshot** {Boolean}, snapshot Snapshot mode assures no duplicates are returned.
* - **timeout** {Boolean}, timeout allow the query to timeout.
* - **tailable** {Boolean}, tailable allow the cursor to be tailable.
* - **awaitdata** {Boolean}, awaitdata allow the cursor to wait for data, only applicable for tailable cursor.
* - **batchSize** {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.
* - **raw** {Boolean}, raw return all query documents as raw buffers (default false).
* - **read** {Boolean}, read specify override of read from source (primary/secondary).
* - **slaveOk** {Boolean}, slaveOk, sets the slaveOk flag on the query wire protocol for secondaries.
* - **returnKey** {Boolean}, returnKey only return the index key.
* - **maxScan** {Number}, maxScan limit the number of items to scan.
* - **min** {Number}, min set index bounds.
* - **max** {Number}, max set index bounds.
* - **showDiskLoc** {Boolean}, showDiskLoc show disk location of results.
* - **comment** {String}, comment you can put a $comment field on a query to make looking in the profiler logs simpler.
* - **numberOfRetries** {Number}, numberOfRetries if using awaidata specifies the number of times to retry on timeout.
* - **dbName** {String}, dbName override the default dbName.
* - **tailableRetryInterval** {Number}, tailableRetryInterval specify the miliseconds between getMores on tailable cursor.
* - **exhaust** {Boolean}, exhaust have the server send all the documents at once as getMore packets.
* - **partial** {Boolean}, partial have the sharded system return a partial result from mongos.
*
* @class Represents a Cursor. * @class Represents a Cursor.
* @param {Db} db the database object to work with. * @param {Db} db the database object to work with.
* @param {Collection} collection the collection to query. * @param {Collection} collection the collection to query.
* @param {Object} selector the query selector. * @param {Object} selector the query selector.
* @param {Object} fields an object containing what fields to include or exclude from objects returned. * @param {Object} fields an object containing what fields to include or exclude from objects returned.
* @param {Number} skip number of documents to skip. * @param {Object} [options] additional options for the collection.
* @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. function Cursor(db, collection, selector, fields, options) {
* @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 {Boolean} awaitdata allow the cursor to wait for data, only applicable for tailable cursor.
* @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.
* @param {Number} numberOfRetries if using awaidata specifies the number of times to retry on timeout.
* @param {String} dbName override the default dbName.
* @param {Number} tailableRetryInterval specify the miliseconds between getMores on tailable cursor.
* @param {Boolean} exhaust have the server send all the documents at once as getMore packets.
* @param {Boolean} partial have the sharded system return a partial result from mongos.
*/
function Cursor(db, collection, selector, fields, skip, limit
, sort, hint, explain, snapshot, timeout, tailable, batchSize, slaveOk, raw, read
, returnKey, maxScan, min, max, showDiskLoc, comment, awaitdata, numberOfRetries, dbName, tailableRetryInterval, exhaust, partial) {
this.db = db; this.db = db;
this.collection = collection; this.collection = collection;
this.selector = selector; this.selector = selector;
this.fields = fields; this.fields = fields;
this.skipValue = skip == null ? 0 : skip; options = !options ? {} : options;
this.limitValue = limit == null ? 0 : limit;
this.sortValue = sort; this.skipValue = options.skip == null ? 0 : options.skip;
this.hint = hint; this.limitValue = options.limit == null ? 0 : options.limit;
this.explainValue = explain; this.sortValue = options.sort;
this.snapshot = snapshot; this.hint = options.hint;
this.timeout = timeout == null ? true : timeout; this.explainValue = options.explain;
this.tailable = tailable; this.snapshot = options.snapshot;
this.awaitdata = awaitdata; this.timeout = options.timeout == null ? true : options.timeout;
this.numberOfRetries = numberOfRetries == null ? 5 : numberOfRetries; this.tailable = options.tailable;
this.awaitdata = options.awaitdata;
this.numberOfRetries = options.numberOfRetries == null ? 5 : options.numberOfRetries;
this.currentNumberOfRetries = this.numberOfRetries; this.currentNumberOfRetries = this.numberOfRetries;
this.batchSizeValue = batchSize == null ? 0 : batchSize; this.batchSizeValue = options.batchSize == null ? 0 : options.batchSize;
this.slaveOk = slaveOk == null ? collection.slaveOk : slaveOk; this.slaveOk = options.slaveOk == null ? collection.slaveOk : options.slaveOk;
this.raw = raw == null ? false : raw; this.raw = options.raw == null ? false : options.raw;
this.read = read == null ? ReadPreference.PRIMARY : read; this.read = options.read == null ? ReadPreference.PRIMARY : options.read;
this.returnKey = returnKey; this.returnKey = options.returnKey;
this.maxScan = maxScan; this.maxScan = options.maxScan;
this.min = min; this.min = options.min;
this.max = max; this.max = options.max;
this.showDiskLoc = showDiskLoc; this.showDiskLoc = options.showDiskLoc;
this.comment = comment; this.comment = options.comment;
this.tailableRetryInterval = tailableRetryInterval || 100; this.tailableRetryInterval = options.tailableRetryInterval || 100;
this.exhaust = exhaust || false; this.exhaust = options.exhaust || false;
this.partial = partial || false; this.partial = options.partial || false;
this.totalNumberOfRecords = 0; this.totalNumberOfRecords = 0;
this.items = []; this.items = [];
this.cursorId = Long.fromInt(0); this.cursorId = Long.fromInt(0);
// This name // This name
this.dbName = dbName; this.dbName = options.dbName;
// State variables for the cursor // State variables for the cursor
this.state = Cursor.INIT; this.state = Cursor.INIT;
@@ -126,7 +130,7 @@ Cursor.prototype.rewind = function() {
* results when this cursor had been previouly accessed. In that case, * results when this cursor had been previouly accessed. In that case,
* cursor.rewind() can be used to reset the cursor. * 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. * @param {Function} callback This will be called after executing this method successfully. The first parameter will contain the Error object if an error occured, or null otherwise. The second parameter will contain an array of BSON deserialized objects as a result of the query.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -168,7 +172,7 @@ Cursor.prototype.toArray = function(callback) {
* at any given time if batch size is specified. Otherwise, the caller is responsible * 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. * 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. * @param {Function} callback this will be called for while iterating every document of the query result. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the document.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -204,7 +208,7 @@ Cursor.prototype.each = function(callback) {
/** /**
* Determines how many result the query for this cursor will return * 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. * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the number of results or null if an error occured.
* @return {null} * @return {null}
* @api public * @api public
*/ */
@@ -293,15 +297,20 @@ Cursor.prototype.limit = function(limit, callback) {
*/ */
Cursor.prototype.setReadPreference = function(readPreference, tags, callback) { Cursor.prototype.setReadPreference = function(readPreference, tags, callback) {
if(typeof tags == 'function') callback = tags; if(typeof tags == 'function') callback = tags;
callback = callback || function() {};
var _mode = readPreference != null && typeof readPreference == 'object' ? readPreference.mode : readPreference;
if(this.queryRun == true || this.state == Cursor.CLOSED) { if(this.queryRun == true || this.state == Cursor.CLOSED) {
if(callback == null) throw new Error("Cannot change read preference on executed query or closed cursor");
callback(new Error("Cannot change read preference on executed query or closed cursor")); callback(new Error("Cannot change read preference on executed query or closed cursor"));
} else if(readPreference == null && readPreference != 'primary' } else if(_mode != null && _mode != 'primary'
&& readPreference != 'secondaryOnly' && readPreference != 'secondary') { && _mode != 'secondaryOnly' && _mode != 'secondary'
callback(new Error("only readPreference of primary, secondary or secondaryOnly supported")); && _mode != 'nearest' && _mode != 'primaryPreferred' && _mode != 'secondaryPreferred') {
if(callback == null) throw new Error("only readPreference of primary, secondary, secondaryPreferred, primaryPreferred or nearest supported");
callback(new Error("only readPreference of primary, secondary, secondaryPreferred, primaryPreferred or nearest supported"));
} else { } else {
this.read = readPreference; this.read = readPreference;
if(callback != null) callback(null, this);
} }
return this; return this;
@@ -497,7 +506,10 @@ Cursor.prototype.nextObject = function(callback) {
return callback(err, null); return callback(err, null);
} }
// Execute command
var commandHandler = function(err, result) { var commandHandler = function(err, result) {
self.state = Cursor.OPEN;
// console.dir(err)
if(err != null && result == null) return callback(err, null); if(err != null && result == null) return callback(err, null);
if(!err && result.documents[0] && result.documents[0]['$err']) { if(!err && result.documents[0] && result.documents[0]['$err']) {
@@ -556,6 +568,12 @@ Cursor.prototype.nextObject = function(callback) {
var getMore = function(self, callback) { var getMore = function(self, callback) {
var limit = 0; var limit = 0;
if(self.state == Cursor.GET_MORE) return callback(null, null);
// Set get more in progress
self.state = Cursor.GET_MORE;
// Set options
if (!self.tailable && self.limitValue > 0) { if (!self.tailable && self.limitValue > 0) {
limit = self.limitValue - self.totalNumberOfRecords; limit = self.limitValue - self.totalNumberOfRecords;
if (limit < 1) { if (limit < 1) {
@@ -563,6 +581,7 @@ var getMore = function(self, callback) {
return; return;
} }
} }
try { try {
var getMoreCommand = new GetMoreCommand( var getMoreCommand = new GetMoreCommand(
self.db self.db
@@ -576,6 +595,9 @@ var getMore = function(self, callback) {
// Execute the command // Execute the command
self.db._executeQueryCommand(getMoreCommand, options, function(err, result) { self.db._executeQueryCommand(getMoreCommand, options, function(err, result) {
// Get more done
self.state = Cursor.OPEN;
try { try {
if(err != null) { if(err != null) {
return callback(err, null); return callback(err, null);
@@ -629,6 +651,9 @@ var getMore = function(self, callback) {
getMoreCommand = null; getMoreCommand = null;
} catch(err) { } catch(err) {
// Get more done
self.state = Cursor.OPEN;
var handleClose = function() { var handleClose = function() {
callback(err, null); callback(err, null);
}; };
@@ -647,11 +672,62 @@ var getMore = function(self, callback) {
*/ */
Cursor.prototype.explain = function(callback) { Cursor.prototype.explain = function(callback) {
var limit = (-1)*Math.abs(this.limitValue); var limit = (-1)*Math.abs(this.limitValue);
// * - **skip** {Number} skip number of documents to skip.
// * - **limit** {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.
// * - **hint** {Object}, hint force the query to use a specific index.
// * - **explain** {Boolean}, explain return the explaination of the query.
// * - **slaveOk** {Boolean}, slaveOk, sets the slaveOk flag on the query wire protocol for secondaries.
// * - **snapshot** {Boolean}, snapshot Snapshot mode assures no duplicates are returned.
// * - **timeout** {Boolean}, timeout allow the query to timeout.
// * - **tailable** {Boolean}, tailable allow the cursor to be tailable.
// * - **awaitdata** {Boolean}, awaitdata allow the cursor to wait for data, only applicable for tailable cursor.
// * - **batchSize** {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.
// * - **raw** {Boolean}, raw return all query documents as raw buffers (default false).
// * - **read** {Boolean}, read specify override of read from source (primary/secondary).
// * - **returnKey** {Boolean}, returnKey only return the index key.
// * - **maxScan** {Number}, maxScan limit the number of items to scan.
// * - **min** {Number}, min set index bounds.
// * - **max** {Number}, max set index bounds.
// * - **showDiskLoc** {Boolean}, showDiskLoc show disk location of results.
// * - **comment** {String}, comment you can put a $comment field on a query to make looking in the profiler logs simpler.
// * - **numberOfRetries** {Number}, numberOfRetries if using awaidata specifies the number of times to retry on timeout.
// * - **dbName** {String}, dbName override the default dbName.
// * - **tailableRetryInterval** {Number}, tailableRetryInterval specify the miliseconds between getMores on tailable cursor.
// * - **exhaust** {Boolean}, exhaust have the server send all the documents at once as getMore packets.
// * - **partial** {Boolean}, partial have the sharded system return a partial result from mongos.
// * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc.
// function Cursor(db, collection, selector, fields, skip, limit
// - , sort, hint, explain, snapshot, timeout, tailable, batchSize, slaveOk, raw, read
// - , returnKey, maxScan, min, max, showDiskLoc, comment, awaitdata, numberOfRetries, dbName, tailableRetry
// Create a new cursor and fetch the plan // Create a new cursor and fetch the plan
var cursor = new Cursor(this.db, this.collection, this.selector, this.fields, this.skipValue, limit var cursor = new Cursor(this.db, this.collection, this.selector, this.fields, {
, this.sortValue, this.hint, true, this.snapshot, this.timeout, this.tailable, this.batchSizeValue skip: this.skipValue
, this.slaveOk, this.raw, this.read, this.returnKey, this.maxScan, this.min, this.max, this.showDiskLoc , limit:limit
, this.comment, this.awaitdata, this.numberOfRetries, this.dbName); , sort: this.sortValue
, hint: this.hint
, explain: true
, snapshot: this.snapshot
, timeout: this.timeout
, tailable: this.tailable
, batchSize: this.batchSizeValue
, slaveOk: this.slaveOk
, raw: this.raw
, read: this.read
, returnKey: this.returnKey
, maxScan: this.maxScan
, min: this.min
, max: this.max
, showDiskLoc: this.showDiskLoc
, comment: this.comment
, awaitdata: this.awaitdata
, numberOfRetries: this.numberOfRetries
, dbName: this.dbName
});
// Fetch the explaination document // Fetch the explaination document
cursor.nextObject(function(err, item) { cursor.nextObject(function(err, item) {
if(err != null) return callback(err, null); if(err != null) return callback(err, null);
@@ -761,7 +837,8 @@ Cursor.prototype.close = function(callback) {
if(this.cursorId instanceof Long && this.cursorId.greaterThan(Long.fromInt(0))) { if(this.cursorId instanceof Long && this.cursorId.greaterThan(Long.fromInt(0))) {
try { try {
var command = new KillCursorCommand(this.db, [this.cursorId]); var command = new KillCursorCommand(this.db, [this.cursorId]);
this.db._executeQueryCommand(command, {read:self.read, raw:self.raw, connection:self.connection}, null); // Added an empty callback to ensure we don't throw any null exceptions
this.db._executeQueryCommand(command, {read:self.read, raw:self.raw, connection:self.connection}, function() {});
} catch(err) {} } catch(err) {}
} }
@@ -811,8 +888,15 @@ Cursor.OPEN = 1;
**/ **/
Cursor.CLOSED = 2; Cursor.CLOSED = 2;
/**
* Cursor performing a get more
*
* @classconstant OPEN
**/
Cursor.GET_MORE = 3;
/** /**
* @ignore * @ignore
* @api private * @api private
*/ */
exports.Cursor = Cursor; exports.Cursor = Cursor;

View File

@@ -70,13 +70,11 @@ CursorStream.prototype._next = function () {
if (this.paused || this._destroyed) return; if (this.paused || this._destroyed) return;
var self = this; var self = this;
// Get the next object
// nextTick is necessary to avoid stack overflows when process.nextTick(function() {
// dealing with large result sets.
process.nextTick(function () {
self._cursor.nextObject(function (err, doc) { self._cursor.nextObject(function (err, doc) {
self._onNextObject(err, doc); self._onNextObject(err, doc);
}); });
}); });
} }
@@ -86,16 +84,16 @@ CursorStream.prototype._next = function () {
* @api private * @api private
*/ */
CursorStream.prototype._onNextObject = function (err, doc) { CursorStream.prototype._onNextObject = function (err, doc) {
if (err) return this.destroy(err); if(err) return this.destroy(err);
// when doc is null we hit the end of the cursor // when doc is null we hit the end of the cursor
if (!doc) { if(!doc && (this._cursor.state == 1 || this._cursor.state == 2)) {
this.emit('end') this.emit('end')
return this.destroy(); return this.destroy();
} else if(doc) {
this.emit('data', doc);
this._next();
} }
this.emit('data', doc);
this._next();
} }
/** /**
@@ -114,8 +112,14 @@ CursorStream.prototype.pause = function () {
*/ */
CursorStream.prototype.resume = function () { CursorStream.prototype.resume = function () {
var self = this; var self = this;
// Don't do anything if we are not paused
if(!this.paused) return;
if(!this._cursor.state == 3) return;
process.nextTick(function() { process.nextTick(function() {
self.paused = false; self.paused = false;
// Only trigger more fetching if the cursor is open
self._next(); self._next();
}) })
} }

File diff suppressed because it is too large Load Diff

View File

@@ -20,9 +20,16 @@ function Grid(db, fsName) {
/** /**
* Puts binary data to the grid * Puts binary data to the grid
* *
* Options
* - **_id** {Any}, unique id for this file
* - **root** {String}, root collection to use. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**.
* - **content_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.
*
* @param {Buffer} data buffer with Binary Data. * @param {Buffer} data buffer with Binary Data.
* @param {Object} [options] the options for the files. * @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. * @param {Function} callback 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} * @return {null}
* @api public * @api public
*/ */
@@ -37,9 +44,11 @@ Grid.prototype.put = function(data, options, callback) {
// Return if we don't have a buffer object as data // 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); if(!(Buffer.isBuffer(data))) return callback(new Error("Data object must be a buffer object"), null);
// Get filename if we are using it // Get filename if we are using it
var filename = options['filename']; var filename = options['filename'] || null;
// Get id if we are using it
var id = options['_id'] || null;
// Create gridstore // Create gridstore
var gridStore = new GridStore(this.db, filename, "w", options); var gridStore = new GridStore(this.db, id, filename, "w", options);
gridStore.open(function(err, gridStore) { gridStore.open(function(err, gridStore) {
if(err) return callback(err, null); if(err) return callback(err, null);
@@ -57,16 +66,14 @@ Grid.prototype.put = function(data, options, callback) {
/** /**
* Get binary data to the grid * Get binary data to the grid
* *
* @param {ObjectID} id ObjectID for file. * @param {Any} id 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. * @param {Function} callback 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} * @return {null}
* @api public * @api public
*/ */
Grid.prototype.get = function(id, callback) { 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 // Create gridstore
var gridStore = new GridStore(this.db, id, "r", {root:this.fsName}); var gridStore = new GridStore(this.db, id, null, "r", {root:this.fsName});
gridStore.open(function(err, gridStore) { gridStore.open(function(err, gridStore) {
if(err) return callback(err, null); if(err) return callback(err, null);
@@ -80,14 +87,12 @@ Grid.prototype.get = function(id, callback) {
/** /**
* Delete file from grid * Delete file from grid
* *
* @param {ObjectID} id ObjectID for file. * @param {Any} id 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. * @param {Function} callback 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} * @return {null}
* @api public * @api public
*/ */
Grid.prototype.delete = function(id, callback) { 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 // Create gridstore
GridStore.unlink(this.db, id, {root:this.fsName}, function(err, result) { GridStore.unlink(this.db, id, {root:this.fsName}, function(err, result) {
if(err) return callback(err, false); if(err) return callback(err, false);

View File

@@ -35,10 +35,10 @@ var REFERENCE_BY_FILENAME = 0,
* *
* @class Represents the GridStore. * @class Represents the GridStore.
* @param {Db} db A database instance to interact with. * @param {Db} db A database instance to interact with.
* @param {ObjectID} id an unique ObjectID for this file * @param {Any} [id] optional unique id for this file
* @param {String} [filename] optional a filename for this file, no unique constrain on the field * @param {String} [filename] optional filename for this file, no unique constrain on the field
* @param {String} mode set the mode for this file. * @param {String} mode set the mode for this file.
* @param {Object} options optional properties to specify. Recognized keys: * @param {Object} options optional properties to specify.
* @return {GridStore} * @return {GridStore}
*/ */
var GridStore = function GridStore(db, id, filename, mode, options) { var GridStore = function GridStore(db, id, filename, mode, options) {
@@ -56,34 +56,52 @@ var GridStore = function GridStore(db, id, filename, mode, options) {
} }
// Handle options // Handle options
if(options == null) options = {}; if(typeof options === 'undefined') options = {};
// Handle mode // Handle mode
if(mode == null) { if(typeof mode === 'undefined') {
mode = filename; mode = filename;
filename = null; filename = undefined;
} else if(typeof mode == 'object') { } else if(typeof mode == 'object') {
options = mode; options = mode;
mode = filename; mode = filename;
filename = null; filename = undefined;
} }
if(id instanceof ObjectID) {
this.referenceBy = REFERENCE_BY_ID;
this.fileId = id;
this.filename = filename;
} else if(typeof filename == 'undefined') {
this.referenceBy = REFERENCE_BY_FILENAME;
this.filename = id;
if (mode.indexOf('w') != null) {
this.fileId = new ObjectID();
}
} else {
this.referenceBy = REFERENCE_BY_ID;
this.fileId = id;
this.filename = filename;
}
/*
// Handle id // Handle id
if(id instanceof ObjectID && (typeof filename == 'string' || filename == null)) { if(id instanceof ObjectID && (typeof filename == 'string' || filename == null)) {
this.referenceBy = 1; this.referenceBy = REFERENCE_BY_ID;
this.fileId = id; this.fileId = id;
this.filename = filename; this.filename = filename;
} else if(!(id instanceof ObjectID) && typeof id == 'string' && mode.indexOf("w") != null) { } else if(!(id instanceof ObjectID) && typeof id == 'string' && mode.indexOf("w") != null) {
this.referenceBy = 0; this.referenceBy = REFERENCE_BY_FILENAME;
this.fileId = new ObjectID(); this.fileId = new ObjectID();
this.filename = id; this.filename = id;
} else if(!(id instanceof ObjectID) && typeof id == 'string' && mode.indexOf("r") != null) { } else if(!(id instanceof ObjectID) && typeof id == 'string' && mode.indexOf("r") != null) {
this.referenceBy = 0; this.referenceBy = REFERENCE_BY_FILENAME;
this.filename = filename; this.filename = id;
} else { } else {
this.referenceBy = 1; this.referenceBy = REFERENCE_BY_ID;
this.fileId = id; this.fileId = id;
this.filename = filename; this.filename = filename;
} }
*/
// Set up the rest // Set up the rest
this.mode = mode == null ? "r" : mode; this.mode = mode == null ? "r" : mode;
@@ -92,8 +110,6 @@ var GridStore = function GridStore(db, id, filename, mode, options) {
this.position = 0; this.position = 0;
// Set default chunk size // Set default chunk size
this.internalChunkSize = this.options['chunkSize'] == null ? Chunk.DEFAULT_CHUNK_SIZE : this.options['chunkSize']; this.internalChunkSize = this.options['chunkSize'] == null ? Chunk.DEFAULT_CHUNK_SIZE : this.options['chunkSize'];
// Previous chunk size
this.previousChunkSize = 0;
} }
/** /**
@@ -198,7 +214,8 @@ var _open = function(self, callback) {
self.length = 0; self.length = 0;
} else { } else {
self.length = 0; self.length = 0;
return error(new Error((self.referenceBy == REFERENCE_BY_ID ? self.fileId.toHexString() : self.filename) + " does not exist", self)); var txtId = self.fileId instanceof ObjectID ? self.fileId.toHexString() : self.fileId;
return error(new Error((self.referenceBy == REFERENCE_BY_ID ? txtId : self.filename) + " does not exist", self));
} }
// Process the mode of the object // Process the mode of the object
@@ -442,19 +459,19 @@ var writeBuffer = function(self, buffer, close, callback) {
* @api private * @api private
*/ */
var buildMongoObject = function(self, callback) { var buildMongoObject = function(self, callback) {
// Keeps the final chunk number // // Keeps the final chunk number
var chunkNumber = 0; // var chunkNumber = 0;
var previousChunkSize = self.previousChunkSize; // var previousChunkSize = 0;
// Get the correct chunk Number, if we have an empty chunk return the previous chunk number // // Get the correct chunk Number, if we have an empty chunk return the previous chunk number
if(null != self.currentChunk && self.currentChunk.chunkNumber > 0 && self.currentChunk.position == 0) { // if(null != self.currentChunk && self.currentChunk.chunkNumber > 0 && self.currentChunk.position == 0) {
chunkNumber = self.currentChunk.chunkNumber - 1; // chunkNumber = self.currentChunk.chunkNumber - 1;
} else { // } else {
chunkNumber = self.currentChunk.chunkNumber; // chunkNumber = self.currentChunk.chunkNumber;
previousChunkSize = self.currentChunk.position; // previousChunkSize = self.currentChunk.position;
} // }
// Calcuate the length // // Calcuate the length
var length = self.currentChunk != null ? (chunkNumber * self.chunkSize + previousChunkSize) : 0; // var length = self.currentChunk != null ? (chunkNumber * self.chunkSize + previousChunkSize) : 0;
var mongoObject = { var mongoObject = {
'_id': self.fileId, '_id': self.fileId,
'filename': self.filename, 'filename': self.filename,
@@ -1381,6 +1398,8 @@ var _read = function _read(self) {
} }
self.resume = function () { self.resume = function () {
if(!self.paused) return;
self.paused = false; self.paused = false;
stream.resume(); stream.resume();
self.readable = stream.readable; self.readable = stream.readable;

View File

@@ -26,7 +26,7 @@ function ReadStream(autoclose, gstore) {
this.finalLength = gstore.length - gstore.position; this.finalLength = gstore.length - gstore.position;
this.completedLength = 0; this.completedLength = 0;
this.currentChunkNumber = 0; this.currentChunkNumber = gstore.currentChunk.chunkNumber;
this.paused = false; this.paused = false;
this.readable = true; this.readable = true;
@@ -35,6 +35,9 @@ function ReadStream(autoclose, gstore) {
// Calculate the number of chunks // Calculate the number of chunks
this.numberOfChunks = Math.ceil(gstore.length/gstore.chunkSize); this.numberOfChunks = Math.ceil(gstore.length/gstore.chunkSize);
// This seek start position inside the current chunk
this.seekStartPosition = gstore.position - (this.currentChunkNumber * gstore.chunkSize);
var self = this; var self = this;
process.nextTick(function() { process.nextTick(function() {
@@ -81,7 +84,18 @@ ReadStream.prototype._execute = function() {
last = true; last = true;
} }
var data = gstore.currentChunk.readSlice(gstore.currentChunk.length()); // Data setup
var data = null;
// Read a slice (with seek set if none)
if(this.seekStartPosition > 0 && (gstore.currentChunk.length() - this.seekStartPosition) > 0) {
data = gstore.currentChunk.readSlice(gstore.currentChunk.length() - this.seekStartPosition);
this.seekStartPosition = 0;
} else {
data = gstore.currentChunk.readSlice(gstore.currentChunk.length());
}
// Return the data
if(data != null && gstore.currentChunk.chunkNumber == self.currentChunkNumber) { if(data != null && gstore.currentChunk.chunkNumber == self.currentChunkNumber) {
self.currentChunkNumber = self.currentChunkNumber + 1; self.currentChunkNumber = self.currentChunkNumber + 1;
self.completedLength += data.length; self.completedLength += data.length;

View File

@@ -6,14 +6,6 @@ try {
} }
[ 'commands/base_command' [ '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' , 'admin'
, 'collection' , 'collection'
, 'connection/read_preference' , 'connection/read_preference'
@@ -21,8 +13,10 @@ try {
, 'connection/server' , 'connection/server'
, 'connection/mongos' , 'connection/mongos'
, 'connection/repl_set' , 'connection/repl_set'
, 'mongo_client'
, 'cursor' , 'cursor'
, 'db' , 'db'
, 'mongo_client'
, 'gridfs/grid' , 'gridfs/grid'
, 'gridfs/chunk' , 'gridfs/chunk'
, 'gridfs/gridstore'].forEach(function (path) { , 'gridfs/gridstore'].forEach(function (path) {
@@ -48,110 +42,28 @@ try {
// Add BSON Parser // Add BSON Parser
exports.BSON = require('bson').BSONPure.BSON; exports.BSON = require('bson').BSONPure.BSON;
}); });
// Exports all the classes for the PURE JS BSON Parser // Get the Db object
exports.pure = function() { var Db = require('./db').Db;
var classes = {}; // Set up the connect function
// Map all the classes var connect = Db.connect;
[ 'commands/base_command' var obj = connect;
, 'commands/db_command' // Map all values to the exports value
, 'commands/delete_command' for(var name in exports) {
, 'commands/get_more_command' obj[name] = exports[name];
, 'commands/insert_command'
, 'commands/kill_cursor_command'
, 'commands/query_command'
, 'commands/update_command'
, 'responses/mongo_reply'
, 'admin'
, 'collection'
, 'connection/read_preference'
, 'connection/connection'
, 'connection/server'
, 'connection/mongos'
, 'connection/repl_set'
, 'cursor'
, 'db'
, 'gridfs/grid'
, 'gridfs/chunk'
, 'gridfs/gridstore'].forEach(function (path) {
var module = require('./' + path);
for (var i in module) {
classes[i] = module[i];
}
});
// backwards compat
classes.ReplSetServers = exports.ReplSet;
// 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 // Add the pure and native backward compatible functions
exports.native = function() { exports.pure = exports.native = function() {
var classes = {}; return obj;
// 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/read_preference'
, 'connection/connection'
, 'connection/server'
, 'connection/mongos'
, 'connection/repl_set'
, '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;
// backwards compat
classes.ReplSetServers = exports.ReplSet;
// Add BSON Parser
classes.BSON = require('bson').BSONNative.BSON;
// Return classes list
return classes;
} }
// Map all values to the exports value
for(var name in exports) {
connect[name] = exports[name];
}
// Set our exports to be the connect function
module.exports = connect;

View File

@@ -72,3 +72,26 @@ exports.decodeUInt32 = function(array, index) {
exports.decodeUInt8 = function(array, index) { exports.decodeUInt8 = function(array, index) {
return array[index]; return array[index];
} }
/**
* Context insensitive type checks
*/
var toString = Object.prototype.toString;
exports.isObject = function (arg) {
return '[object Object]' == toString.call(arg)
}
exports.isArray = function (arg) {
return Array.isArray(arg) ||
'object' == typeof arg && '[object Array]' == toString.call(arg)
}
exports.isDate = function (arg) {
return 'object' == typeof arg && '[object Date]' == toString.call(arg)
}
exports.isRegExp = function (arg) {
return 'object' == typeof arg && '[object RegExp]' == toString.call(arg)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,273 +1,273 @@
//=========================================================================== //===========================================================================
#ifndef BSON_H_ #ifndef BSON_H_
#define BSON_H_ #define BSON_H_
//=========================================================================== //===========================================================================
#define USE_MISALIGNED_MEMORY_ACCESS 1 #define USE_MISALIGNED_MEMORY_ACCESS 1
#include <node.h> #include <node.h>
#include <node_object_wrap.h> #include <node_object_wrap.h>
#include <v8.h> #include <v8.h>
using namespace v8; using namespace v8;
using namespace node; using namespace node;
//=========================================================================== //===========================================================================
enum BsonType enum BsonType
{ {
BSON_TYPE_NUMBER = 1, BSON_TYPE_NUMBER = 1,
BSON_TYPE_STRING = 2, BSON_TYPE_STRING = 2,
BSON_TYPE_OBJECT = 3, BSON_TYPE_OBJECT = 3,
BSON_TYPE_ARRAY = 4, BSON_TYPE_ARRAY = 4,
BSON_TYPE_BINARY = 5, BSON_TYPE_BINARY = 5,
BSON_TYPE_UNDEFINED = 6, BSON_TYPE_UNDEFINED = 6,
BSON_TYPE_OID = 7, BSON_TYPE_OID = 7,
BSON_TYPE_BOOLEAN = 8, BSON_TYPE_BOOLEAN = 8,
BSON_TYPE_DATE = 9, BSON_TYPE_DATE = 9,
BSON_TYPE_NULL = 10, BSON_TYPE_NULL = 10,
BSON_TYPE_REGEXP = 11, BSON_TYPE_REGEXP = 11,
BSON_TYPE_CODE = 13, BSON_TYPE_CODE = 13,
BSON_TYPE_SYMBOL = 14, BSON_TYPE_SYMBOL = 14,
BSON_TYPE_CODE_W_SCOPE = 15, BSON_TYPE_CODE_W_SCOPE = 15,
BSON_TYPE_INT = 16, BSON_TYPE_INT = 16,
BSON_TYPE_TIMESTAMP = 17, BSON_TYPE_TIMESTAMP = 17,
BSON_TYPE_LONG = 18, BSON_TYPE_LONG = 18,
BSON_TYPE_MAX_KEY = 0x7f, BSON_TYPE_MAX_KEY = 0x7f,
BSON_TYPE_MIN_KEY = 0xff BSON_TYPE_MIN_KEY = 0xff
}; };
//=========================================================================== //===========================================================================
template<typename T> class BSONSerializer; template<typename T> class BSONSerializer;
class BSON : public ObjectWrap { class BSON : public ObjectWrap {
public: public:
BSON(); BSON();
~BSON() {} ~BSON() {}
static void Initialize(Handle<Object> target); static void Initialize(Handle<Object> target);
static Handle<Value> BSONDeserializeStream(const Arguments &args); static Handle<Value> BSONDeserializeStream(const Arguments &args);
// JS based objects // JS based objects
static Handle<Value> BSONSerialize(const Arguments &args); static Handle<Value> BSONSerialize(const Arguments &args);
static Handle<Value> BSONDeserialize(const Arguments &args); static Handle<Value> BSONDeserialize(const Arguments &args);
// Calculate size of function // Calculate size of function
static Handle<Value> CalculateObjectSize(const Arguments &args); static Handle<Value> CalculateObjectSize(const Arguments &args);
static Handle<Value> SerializeWithBufferAndIndex(const Arguments &args); static Handle<Value> SerializeWithBufferAndIndex(const Arguments &args);
// Constructor used for creating new BSON objects from C++ // Constructor used for creating new BSON objects from C++
static Persistent<FunctionTemplate> constructor_template; static Persistent<FunctionTemplate> constructor_template;
private: private:
static Handle<Value> New(const Arguments &args); static Handle<Value> New(const Arguments &args);
static Handle<Value> deserialize(BSON *bson, char *data, uint32_t dataLength, uint32_t startIndex, bool is_array_item); static Handle<Value> deserialize(BSON *bson, char *data, uint32_t dataLength, uint32_t startIndex, bool is_array_item);
// BSON type instantiate functions // BSON type instantiate functions
Persistent<Function> longConstructor; Persistent<Function> longConstructor;
Persistent<Function> objectIDConstructor; Persistent<Function> objectIDConstructor;
Persistent<Function> binaryConstructor; Persistent<Function> binaryConstructor;
Persistent<Function> codeConstructor; Persistent<Function> codeConstructor;
Persistent<Function> dbrefConstructor; Persistent<Function> dbrefConstructor;
Persistent<Function> symbolConstructor; Persistent<Function> symbolConstructor;
Persistent<Function> doubleConstructor; Persistent<Function> doubleConstructor;
Persistent<Function> timestampConstructor; Persistent<Function> timestampConstructor;
Persistent<Function> minKeyConstructor; Persistent<Function> minKeyConstructor;
Persistent<Function> maxKeyConstructor; Persistent<Function> maxKeyConstructor;
// Equality Objects // Equality Objects
Persistent<String> longString; Persistent<String> longString;
Persistent<String> objectIDString; Persistent<String> objectIDString;
Persistent<String> binaryString; Persistent<String> binaryString;
Persistent<String> codeString; Persistent<String> codeString;
Persistent<String> dbrefString; Persistent<String> dbrefString;
Persistent<String> symbolString; Persistent<String> symbolString;
Persistent<String> doubleString; Persistent<String> doubleString;
Persistent<String> timestampString; Persistent<String> timestampString;
Persistent<String> minKeyString; Persistent<String> minKeyString;
Persistent<String> maxKeyString; Persistent<String> maxKeyString;
// Equality speed up comparison objects // Equality speed up comparison objects
Persistent<String> _bsontypeString; Persistent<String> _bsontypeString;
Persistent<String> _longLowString; Persistent<String> _longLowString;
Persistent<String> _longHighString; Persistent<String> _longHighString;
Persistent<String> _objectIDidString; Persistent<String> _objectIDidString;
Persistent<String> _binaryPositionString; Persistent<String> _binaryPositionString;
Persistent<String> _binarySubTypeString; Persistent<String> _binarySubTypeString;
Persistent<String> _binaryBufferString; Persistent<String> _binaryBufferString;
Persistent<String> _doubleValueString; Persistent<String> _doubleValueString;
Persistent<String> _symbolValueString; Persistent<String> _symbolValueString;
Persistent<String> _dbRefRefString; Persistent<String> _dbRefRefString;
Persistent<String> _dbRefIdRefString; Persistent<String> _dbRefIdRefString;
Persistent<String> _dbRefDbRefString; Persistent<String> _dbRefDbRefString;
Persistent<String> _dbRefNamespaceString; Persistent<String> _dbRefNamespaceString;
Persistent<String> _dbRefDbString; Persistent<String> _dbRefDbString;
Persistent<String> _dbRefOidString; Persistent<String> _dbRefOidString;
Persistent<String> _codeCodeString; Persistent<String> _codeCodeString;
Persistent<String> _codeScopeString; Persistent<String> _codeScopeString;
Persistent<String> _toBSONString; Persistent<String> _toBSONString;
Local<Object> GetSerializeObject(const Handle<Value>& object); Local<Object> GetSerializeObject(const Handle<Value>& object);
template<typename T> friend class BSONSerializer; template<typename T> friend class BSONSerializer;
friend class BSONDeserializer; friend class BSONDeserializer;
}; };
//=========================================================================== //===========================================================================
class CountStream class CountStream
{ {
public: public:
CountStream() : count(0) { } CountStream() : count(0) { }
void WriteByte(int value) { ++count; } void WriteByte(int value) { ++count; }
void WriteByte(const Handle<Object>&, const Handle<String>&) { ++count; } void WriteByte(const Handle<Object>&, const Handle<String>&) { ++count; }
void WriteBool(const Handle<Value>& value) { ++count; } void WriteBool(const Handle<Value>& value) { ++count; }
void WriteInt32(int32_t value) { count += 4; } void WriteInt32(int32_t value) { count += 4; }
void WriteInt32(const Handle<Value>& value) { count += 4; } void WriteInt32(const Handle<Value>& value) { count += 4; }
void WriteInt32(const Handle<Object>& object, const Handle<String>& key) { count += 4; } void WriteInt32(const Handle<Object>& object, const Handle<String>& key) { count += 4; }
void WriteInt64(int64_t value) { count += 8; } void WriteInt64(int64_t value) { count += 8; }
void WriteInt64(const Handle<Value>& value) { count += 8; } void WriteInt64(const Handle<Value>& value) { count += 8; }
void WriteDouble(double value) { count += 8; } void WriteDouble(double value) { count += 8; }
void WriteDouble(const Handle<Value>& value) { count += 8; } void WriteDouble(const Handle<Value>& value) { count += 8; }
void WriteDouble(const Handle<Object>&, const Handle<String>&) { count += 8; } void WriteDouble(const Handle<Object>&, const Handle<String>&) { count += 8; }
void WriteUInt32String(uint32_t name) { char buffer[32]; count += sprintf(buffer, "%u", name) + 1; } void WriteUInt32String(uint32_t name) { char buffer[32]; count += sprintf(buffer, "%u", name) + 1; }
void WriteLengthPrefixedString(const Local<String>& value) { count += value->Utf8Length()+5; } void WriteLengthPrefixedString(const Local<String>& value) { count += value->Utf8Length()+5; }
void WriteObjectId(const Handle<Object>& object, const Handle<String>& key) { count += 12; } void WriteObjectId(const Handle<Object>& object, const Handle<String>& key) { count += 12; }
void WriteString(const Local<String>& value) { count += value->Utf8Length() + 1; } // This returns the number of bytes exclusive of the NULL terminator void WriteString(const Local<String>& value) { count += value->Utf8Length() + 1; } // This returns the number of bytes exclusive of the NULL terminator
void WriteData(const char* data, size_t length) { count += length; } void WriteData(const char* data, size_t length) { count += length; }
void* BeginWriteType() { ++count; return NULL; } void* BeginWriteType() { ++count; return NULL; }
void CommitType(void*, BsonType) { } void CommitType(void*, BsonType) { }
void* BeginWriteSize() { count += 4; return NULL; } void* BeginWriteSize() { count += 4; return NULL; }
void CommitSize(void*) { } void CommitSize(void*) { }
size_t GetSerializeSize() const { return count; } size_t GetSerializeSize() const { return count; }
// Do nothing. CheckKey is implemented for DataStream // Do nothing. CheckKey is implemented for DataStream
void CheckKey(const Local<String>&) { } void CheckKey(const Local<String>&) { }
private: private:
size_t count; size_t count;
}; };
class DataStream class DataStream
{ {
public: public:
DataStream(char* aDestinationBuffer) : destinationBuffer(aDestinationBuffer), p(aDestinationBuffer) { } DataStream(char* aDestinationBuffer) : destinationBuffer(aDestinationBuffer), p(aDestinationBuffer) { }
void WriteByte(int value) { *p++ = value; } void WriteByte(int value) { *p++ = value; }
void WriteByte(const Handle<Object>& object, const Handle<String>& key) { *p++ = object->Get(key)->Int32Value(); } void WriteByte(const Handle<Object>& object, const Handle<String>& key) { *p++ = object->Get(key)->Int32Value(); }
#if USE_MISALIGNED_MEMORY_ACCESS #if USE_MISALIGNED_MEMORY_ACCESS
void WriteInt32(int32_t value) { *reinterpret_cast<int32_t*>(p) = value; p += 4; } void WriteInt32(int32_t value) { *reinterpret_cast<int32_t*>(p) = value; p += 4; }
void WriteInt64(int64_t value) { *reinterpret_cast<int64_t*>(p) = value; p += 8; } void WriteInt64(int64_t value) { *reinterpret_cast<int64_t*>(p) = value; p += 8; }
void WriteDouble(double value) { *reinterpret_cast<double*>(p) = value; p += 8; } void WriteDouble(double value) { *reinterpret_cast<double*>(p) = value; p += 8; }
#else #else
void WriteInt32(int32_t value) { memcpy(p, &value, 4); p += 4; } void WriteInt32(int32_t value) { memcpy(p, &value, 4); p += 4; }
void WriteInt64(int64_t value) { memcpy(p, &value, 8); p += 8; } void WriteInt64(int64_t value) { memcpy(p, &value, 8); p += 8; }
void WriteDouble(double value) { memcpy(p, &value, 8); p += 8; } void WriteDouble(double value) { memcpy(p, &value, 8); p += 8; }
#endif #endif
void WriteBool(const Handle<Value>& value) { WriteByte(value->BooleanValue() ? 1 : 0); } void WriteBool(const Handle<Value>& value) { WriteByte(value->BooleanValue() ? 1 : 0); }
void WriteInt32(const Handle<Value>& value) { WriteInt32(value->Int32Value()); } void WriteInt32(const Handle<Value>& value) { WriteInt32(value->Int32Value()); }
void WriteInt32(const Handle<Object>& object, const Handle<String>& key) { WriteInt32(object->Get(key)); } void WriteInt32(const Handle<Object>& object, const Handle<String>& key) { WriteInt32(object->Get(key)); }
void WriteInt64(const Handle<Value>& value) { WriteInt64(value->IntegerValue()); } void WriteInt64(const Handle<Value>& value) { WriteInt64(value->IntegerValue()); }
void WriteDouble(const Handle<Value>& value) { WriteDouble(value->NumberValue()); } void WriteDouble(const Handle<Value>& value) { WriteDouble(value->NumberValue()); }
void WriteDouble(const Handle<Object>& object, const Handle<String>& key) { WriteDouble(object->Get(key)); } void WriteDouble(const Handle<Object>& object, const Handle<String>& key) { WriteDouble(object->Get(key)); }
void WriteUInt32String(uint32_t name) { p += sprintf(p, "%u", name) + 1; } void WriteUInt32String(uint32_t name) { p += sprintf(p, "%u", name) + 1; }
void WriteLengthPrefixedString(const Local<String>& value) { WriteInt32(value->Utf8Length()+1); WriteString(value); } void WriteLengthPrefixedString(const Local<String>& value) { WriteInt32(value->Utf8Length()+1); WriteString(value); }
void WriteObjectId(const Handle<Object>& object, const Handle<String>& key); void WriteObjectId(const Handle<Object>& object, const Handle<String>& key);
void WriteString(const Local<String>& value) { p += value->WriteUtf8(p); } // This returns the number of bytes inclusive of the NULL terminator. void WriteString(const Local<String>& value) { p += value->WriteUtf8(p); } // This returns the number of bytes inclusive of the NULL terminator.
void WriteData(const char* data, size_t length) { memcpy(p, data, length); p += length; } void WriteData(const char* data, size_t length) { memcpy(p, data, length); p += length; }
void* BeginWriteType() { void* returnValue = p; p++; return returnValue; } void* BeginWriteType() { void* returnValue = p; p++; return returnValue; }
void CommitType(void* beginPoint, BsonType value) { *reinterpret_cast<unsigned char*>(beginPoint) = value; } void CommitType(void* beginPoint, BsonType value) { *reinterpret_cast<unsigned char*>(beginPoint) = value; }
void* BeginWriteSize() { void* returnValue = p; p += 4; return returnValue; } void* BeginWriteSize() { void* returnValue = p; p += 4; return returnValue; }
#if USE_MISALIGNED_MEMORY_ACCESS #if USE_MISALIGNED_MEMORY_ACCESS
void CommitSize(void* beginPoint) { *reinterpret_cast<int32_t*>(beginPoint) = (int32_t) (p - (char*) beginPoint); } void CommitSize(void* beginPoint) { *reinterpret_cast<int32_t*>(beginPoint) = (int32_t) (p - (char*) beginPoint); }
#else #else
void CommitSize(void* beginPoint) { int32_t value = (int32_t) (p - (char*) beginPoint); memcpy(beginPoint, &value, 4); } void CommitSize(void* beginPoint) { int32_t value = (int32_t) (p - (char*) beginPoint); memcpy(beginPoint, &value, 4); }
#endif #endif
size_t GetSerializeSize() const { return p - destinationBuffer; } size_t GetSerializeSize() const { return p - destinationBuffer; }
void CheckKey(const Local<String>& keyName); void CheckKey(const Local<String>& keyName);
protected: protected:
char *const destinationBuffer; // base, never changes char *const destinationBuffer; // base, never changes
char* p; // cursor into buffer char* p; // cursor into buffer
}; };
template<typename T> class BSONSerializer : public T template<typename T> class BSONSerializer : public T
{ {
private: private:
typedef T Inherited; typedef T Inherited;
public: public:
BSONSerializer(BSON* aBson, bool aCheckKeys, bool aSerializeFunctions) : Inherited(), checkKeys(aCheckKeys), serializeFunctions(aSerializeFunctions), bson(aBson) { } BSONSerializer(BSON* aBson, bool aCheckKeys, bool aSerializeFunctions) : Inherited(), checkKeys(aCheckKeys), serializeFunctions(aSerializeFunctions), bson(aBson) { }
BSONSerializer(BSON* aBson, bool aCheckKeys, bool aSerializeFunctions, char* parentParam) : Inherited(parentParam), checkKeys(aCheckKeys), serializeFunctions(aSerializeFunctions), bson(aBson) { } BSONSerializer(BSON* aBson, bool aCheckKeys, bool aSerializeFunctions, char* parentParam) : Inherited(parentParam), checkKeys(aCheckKeys), serializeFunctions(aSerializeFunctions), bson(aBson) { }
void SerializeDocument(const Handle<Value>& value); void SerializeDocument(const Handle<Value>& value);
void SerializeArray(const Handle<Value>& value); void SerializeArray(const Handle<Value>& value);
void SerializeValue(void* typeLocation, const Handle<Value>& value); void SerializeValue(void* typeLocation, const Handle<Value>& value);
private: private:
bool checkKeys; bool checkKeys;
bool serializeFunctions; bool serializeFunctions;
BSON* bson; BSON* bson;
}; };
//=========================================================================== //===========================================================================
class BSONDeserializer class BSONDeserializer
{ {
public: public:
BSONDeserializer(BSON* aBson, char* data, size_t length); BSONDeserializer(BSON* aBson, char* data, size_t length);
BSONDeserializer(BSONDeserializer& parentSerializer, size_t length); BSONDeserializer(BSONDeserializer& parentSerializer, size_t length);
Handle<Value> DeserializeDocument(); Handle<Value> DeserializeDocument();
bool HasMoreData() const { return p < pEnd; } bool HasMoreData() const { return p < pEnd; }
Local<String> ReadCString(); Local<String> ReadCString();
uint32_t ReadIntegerString(); uint32_t ReadIntegerString();
int32_t ReadRegexOptions(); int32_t ReadRegexOptions();
Local<String> ReadString(); Local<String> ReadString();
Local<String> ReadObjectId(); Local<String> ReadObjectId();
unsigned char ReadByte() { return *reinterpret_cast<unsigned char*>(p++); } unsigned char ReadByte() { return *reinterpret_cast<unsigned char*>(p++); }
#if USE_MISALIGNED_MEMORY_ACCESS #if USE_MISALIGNED_MEMORY_ACCESS
int32_t ReadInt32() { int32_t returnValue = *reinterpret_cast<int32_t*>(p); p += 4; return returnValue; } int32_t ReadInt32() { int32_t returnValue = *reinterpret_cast<int32_t*>(p); p += 4; return returnValue; }
uint32_t ReadUInt32() { uint32_t returnValue = *reinterpret_cast<uint32_t*>(p); p += 4; return returnValue; } uint32_t ReadUInt32() { uint32_t returnValue = *reinterpret_cast<uint32_t*>(p); p += 4; return returnValue; }
int64_t ReadInt64() { int64_t returnValue = *reinterpret_cast<int64_t*>(p); p += 8; return returnValue; } int64_t ReadInt64() { int64_t returnValue = *reinterpret_cast<int64_t*>(p); p += 8; return returnValue; }
double ReadDouble() { double returnValue = *reinterpret_cast<double*>(p); p += 8; return returnValue; } double ReadDouble() { double returnValue = *reinterpret_cast<double*>(p); p += 8; return returnValue; }
#else #else
int32_t ReadInt32() { int32_t returnValue; memcpy(&returnValue, p, 4); p += 4; return returnValue; } int32_t ReadInt32() { int32_t returnValue; memcpy(&returnValue, p, 4); p += 4; return returnValue; }
uint32_t ReadUInt32() { uint32_t returnValue; memcpy(&returnValue, p, 4); p += 4; return returnValue; } uint32_t ReadUInt32() { uint32_t returnValue; memcpy(&returnValue, p, 4); p += 4; return returnValue; }
int64_t ReadInt64() { int64_t returnValue; memcpy(&returnValue, p, 8); p += 8; return returnValue; } int64_t ReadInt64() { int64_t returnValue; memcpy(&returnValue, p, 8); p += 8; return returnValue; }
double ReadDouble() { double returnValue; memcpy(&returnValue, p, 8); p += 8; return returnValue; } double ReadDouble() { double returnValue; memcpy(&returnValue, p, 8); p += 8; return returnValue; }
#endif #endif
size_t GetSerializeSize() const { return p - pStart; } size_t GetSerializeSize() const { return p - pStart; }
private: private:
Handle<Value> DeserializeArray(); Handle<Value> DeserializeArray();
Handle<Value> DeserializeValue(BsonType type); Handle<Value> DeserializeValue(BsonType type);
Handle<Value> DeserializeDocumentInternal(); Handle<Value> DeserializeDocumentInternal();
Handle<Value> DeserializeArrayInternal(); Handle<Value> DeserializeArrayInternal();
BSON* bson; BSON* bson;
char* const pStart; char* const pStart;
char* p; char* p;
char* const pEnd; char* const pEnd;
}; };
//=========================================================================== //===========================================================================
#endif // BSON_H_ #endif // BSON_H_
//=========================================================================== //===========================================================================

View File

@@ -48,11 +48,8 @@
"dependencies": {}, "dependencies": {},
"optionalDependencies": {}, "optionalDependencies": {},
"_engineSupported": true, "_engineSupported": true,
"_npmVersion": "1.1.21", "_npmVersion": "1.1.24",
"_nodeVersion": "v0.6.18", "_nodeVersion": "v0.6.19",
"_defaultsLoaded": true, "_defaultsLoaded": true,
"dist": {
"shasum": "d6954ee604c768436a1956ad487ea2bd571910da"
},
"_from": "bson@0.1.5" "_from": "bson@0.1.5"
} }

17
node_modules/mongoskin/node_modules/mongodb/package.json generated vendored Normal file → Executable file
View File

@@ -7,7 +7,7 @@
"driver", "driver",
"db" "db"
], ],
"version": "1.1.11", "version": "1.2.8",
"author": { "author": {
"name": "Christian Amor Kvalheim", "name": "Christian Amor Kvalheim",
"email": "christkv@gmail.com" "email": "christkv@gmail.com"
@@ -165,6 +165,9 @@
}, },
{ {
"name": "Roman Shtylman" "name": "Roman Shtylman"
},
{
"name": "Matt Self"
} }
], ],
"repository": { "repository": {
@@ -193,6 +196,7 @@
"native": false "native": false
}, },
"main": "./lib/mongodb/index", "main": "./lib/mongodb/index",
"homepage": "http://mongodb.github.com/node-mongodb-native/",
"directories": { "directories": {
"lib": "./lib/mongodb" "lib": "./lib/mongodb"
}, },
@@ -208,14 +212,11 @@
"url": "http://www.apache.org/licenses/LICENSE-2.0" "url": "http://www.apache.org/licenses/LICENSE-2.0"
} }
], ],
"_id": "mongodb@1.1.11", "_id": "mongodb@1.2.8",
"optionalDependencies": {}, "optionalDependencies": {},
"_engineSupported": true, "_engineSupported": true,
"_npmVersion": "1.1.21", "_npmVersion": "1.1.24",
"_nodeVersion": "v0.6.18", "_nodeVersion": "v0.6.19",
"_defaultsLoaded": true, "_defaultsLoaded": true,
"dist": { "_from": "mongodb@1.2.x"
"shasum": "51f813dad01721d585f3588f4233920d23d5169e"
},
"_from": "mongodb@< 1.2.0"
} }

16
node_modules/mongoskin/package.json generated vendored
View File

@@ -1,7 +1,7 @@
{ {
"name": "mongoskin", "name": "mongoskin",
"description": "The future layer above node-mongodb-native", "description": "The future layer above node-mongodb-native",
"version": "0.4.4", "version": "0.5.0",
"author": { "author": {
"name": "Gui Lin", "name": "Gui Lin",
"email": "guileen@gmail.com" "email": "guileen@gmail.com"
@@ -24,21 +24,22 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
}, },
"dependencies": { "dependencies": {
"mongodb": "< 1.2.0" "mongodb": "1.2.x"
}, },
"devDependencies": { "devDependencies": {
"mocha": "*", "mocha": "*",
"jscover": "*",
"should": "*" "should": "*"
}, },
"scripts": { "scripts": {
"test": "make test-version" "test": "make test"
}, },
"directories": { "directories": {
"example": "./examples", "example": "./examples",
"lib": "./lib/mongoskin" "lib": "./lib/mongoskin"
}, },
"license": "MIT", "license": "MIT",
"_id": "mongoskin@0.4.4", "_id": "mongoskin@0.5.0",
"contributors": [ "contributors": [
{ {
"name": "Gui Lin", "name": "Gui Lin",
@@ -103,11 +104,8 @@
], ],
"optionalDependencies": {}, "optionalDependencies": {},
"_engineSupported": true, "_engineSupported": true,
"_npmVersion": "1.1.21", "_npmVersion": "1.1.24",
"_nodeVersion": "v0.6.18", "_nodeVersion": "v0.6.19",
"_defaultsLoaded": true, "_defaultsLoaded": true,
"dist": {
"shasum": "dec69b0cbcbcb30f3327e1fc26f6f29ad35781bc"
},
"_from": "mongoskin@>= 0.0.1" "_from": "mongoskin@>= 0.0.1"
} }