diff --git a/config.js b/config.js
index f4acdd6..b157a60 100644
--- a/config.js
+++ b/config.js
@@ -1,6 +1,6 @@
module.exports = {
'secret': 'secretfortoken',
- 'database': 'mongodb://localhost/thoughts'
+ 'database': 'mongodb://userdb:userdb@ds013366.mlab.com:13366/thoughts'
};
diff --git a/controllers/userController.js b/controllers/userController.js
index ec8e6e4..3969b0d 100644
--- a/controllers/userController.js
+++ b/controllers/userController.js
@@ -53,7 +53,7 @@ exports.findUserByUsername = function(req, res) {
//POST - Insert a new TVShow in the DB
exports.addUser = function(req, res) {
console.log('POST new user, name: ' + req.body.username);
- console.log(req.body);
+ //console.log(req.body);
var user = new userModel({
username: req.body.username,
diff --git a/node_modules/jsonwebtoken/CHANGELOG.md b/node_modules/jsonwebtoken/CHANGELOG.md
index d175213..a3710f8 100644
--- a/node_modules/jsonwebtoken/CHANGELOG.md
+++ b/node_modules/jsonwebtoken/CHANGELOG.md
@@ -4,6 +4,25 @@
All notable changes to this project will be documented in this file starting from version **v4.0.0**.
This project adheres to [Semantic Versioning](http://semver.org/).
+## 7.1.7 - 2016-07-29
+
+ - Use lodash.once instead of unlicensed/unmaintained cb ([3ac95ad93ef3068a64e03d8d14deff231b1ed529](https://github.com/auth0/node-jsonwebtoken/commit/3ac95ad93ef3068a64e03d8d14deff231b1ed529))
+
+## 7.1.6 - 2016-07-15
+
+ - fix issue with buffer payload. closes #216 ([6b50ff324b4dfd2cb0e49b666f14a6672d015b22](https://github.com/auth0/node-jsonwebtoken/commit/6b50ff324b4dfd2cb0e49b666f14a6672d015b22)), closes [#216](https://github.com/auth0/node-jsonwebtoken/issues/216)
+
+
+## 7.1.5 - 2016-07-15
+
+ - update jws in package.json ([b6260951eefc68aae5f4ede359210761f901ff7a](https://github.com/auth0/node-jsonwebtoken/commit/b6260951eefc68aae5f4ede359210761f901ff7a))
+
+
+## 7.1.4 - 2016-07-14
+
+ - add redundant test ([bece8816096f324511c3efcb8db0e64b75d757a1](https://github.com/auth0/node-jsonwebtoken/commit/bece8816096f324511c3efcb8db0e64b75d757a1))
+ - fix an issue of double callback on error ([758ca5eeca2f1b06c32c9fce70642bf488b2e52b](https://github.com/auth0/node-jsonwebtoken/commit/758ca5eeca2f1b06c32c9fce70642bf488b2e52b))
+
## 7.1.2 - 2016-07-12
- do not stringify the payload when signing async - closes #224 ([084f537d3dfbcef2bea411cc0a1515899cc8aa21](https://github.com/auth0/node-jsonwebtoken/commit/084f537d3dfbcef2bea411cc0a1515899cc8aa21)), closes [#224](https://github.com/auth0/node-jsonwebtoken/issues/224)
diff --git a/node_modules/jsonwebtoken/node_modules/cb/.npmignore b/node_modules/jsonwebtoken/node_modules/cb/.npmignore
deleted file mode 100644
index 496fe9b..0000000
--- a/node_modules/jsonwebtoken/node_modules/cb/.npmignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.DS_Store
-node_modules
\ No newline at end of file
diff --git a/node_modules/jsonwebtoken/node_modules/cb/Makefile b/node_modules/jsonwebtoken/node_modules/cb/Makefile
deleted file mode 100644
index 745ab2c..0000000
--- a/node_modules/jsonwebtoken/node_modules/cb/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-.PHONY: test
-
-MOCHA = ./node_modules/mocha/bin/mocha
-
-test:
- $(MOCHA) -R list
\ No newline at end of file
diff --git a/node_modules/jsonwebtoken/node_modules/cb/README.md b/node_modules/jsonwebtoken/node_modules/cb/README.md
deleted file mode 100644
index a6ff558..0000000
--- a/node_modules/jsonwebtoken/node_modules/cb/README.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# cb()
-
-A minimal node.js utility for handling common (but often overlooked) callback scenarios.
-
-##Features
-
- * `.timeout()`: Simple callback timeouts
- * `.error()`: Explicit error handling
- * `.once()`: Once-and-only-once callback semantics
- * Guaranteed asynchronous callback execution (protects against code that breaks this assumption)
-
-## Installation
-
- $ npm install cb
-
-## Usage
-
-### Basic Usage
-
-The most basic usage of `cb` consists of passing in your own function reference. In this example, `cb` will do nothing other
-than insure the once-and-only-once, asynchronous invocation of the callback.
-
- doAsync(cb(function(err, res) {
- console.log(res);
- }));
-
-### Timeout Handling
-
-Timeouts are specified through the `.timeout()` method, and are specified in milliseconds. If a timeout does occur, the error
-passed to the callback will be an instance of `cb.TimeoutError`.
-
- doReallySlowAsync(cb(function(err, res) {
- assert(err instanceof cb.TimeoutError);
- }).timeout(50));
-
-*Note: once a timeout has occured, any tardy attempts to invoke the callback will be ignored.*
-
-### Explicit Error Handling
-
-In situations where it is convenient to separate the code that runs on success or failure, this can easily be accomplished
-with `.error()`. If an 'errback' handler has been provided to `.error()`, then it is assumed that the error-first parameter
-to the success handler is no longer required. To illustrate,
-
- doAsync(cb(function(err, res) {
- if (err) {
- console.error(err);
- } else {
- console.log(res);
- }
- }));
-
-Can be rewritten as:
-
- doAsync(cb(console.log).error(console.error));
-
-### Force Once-and-only-once Callback Execution
-
-Sometimes it's necessary to ensure that a callback is invoked once, and no more. Once-and-only-once execution semantics can be
-enforced by using `.once()`.
-
- function runTwice(callback) {
- process.nextTick(function() {
- callback();
- callback();
- });
- }
-
- runTwice(cb(function() {
- console.log('I will only run once');
- }).once());
-
-*Note: technically, `.once()` simply enforces at-most-once semantics. However, when combined with `.timeout()`, once-and-only-once
-is achieved.*
-
-### Combining Features
-
-The `cb` API is fully chainable, and any arrangement of the features is valid. For example:
-
- doAsync(cb(console.log).error(console.error).timeout(50).once());
-
-## Running the Tests
-
- $ make test
-
-## License
-
-The MIT License (MIT)
-
-Copyright (c) 2012 Jeremy Martin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/jsonwebtoken/node_modules/cb/lib/cb.js b/node_modules/jsonwebtoken/node_modules/cb/lib/cb.js
deleted file mode 100644
index 001522a..0000000
--- a/node_modules/jsonwebtoken/node_modules/cb/lib/cb.js
+++ /dev/null
@@ -1,39 +0,0 @@
-module.exports = function(callback) {
-
- var cb = function() {
- if (timedout || (once && count)) return;
- count += 1;
- tid && clearTimeout(tid);
-
- var args = Array.prototype.slice.call(arguments);
- process.nextTick(function() {
- if (!errback) return callback.apply(this, args);
- args[0] ? errback(args[0]) : callback.apply(this, args.slice(1));
- });
-
- }, count = 0, once = false, timedout = false, errback, tid;
-
- cb.timeout = function(ms) {
- tid && clearTimeout(tid);
- tid = setTimeout(function() {
- cb(new TimeoutError(ms));
- timedout = true;
- }, ms);
- return cb;
- };
-
- cb.error = function(func) { errback = func; return cb; };
-
- cb.once = function() { once = true; return cb; };
-
- return cb;
-
-};
-
-var TimeoutError = module.exports.TimeoutError = function TimeoutError(ms) {
- this.message = 'Specified timeout of ' + ms + 'ms was reached';
- Error.captureStackTrace(this, this.constructor);
-};
-TimeoutError.prototype = new Error;
-TimeoutError.prototype.constructor = TimeoutError;
-TimeoutError.prototype.name = 'TimeoutError';
\ No newline at end of file
diff --git a/node_modules/jsonwebtoken/node_modules/cb/package.json b/node_modules/jsonwebtoken/node_modules/cb/package.json
deleted file mode 100644
index 61d3812..0000000
--- a/node_modules/jsonwebtoken/node_modules/cb/package.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "author": {
- "name": "Jeremy Martin",
- "email": "jmar777@gmail.com",
- "url": "http://twitter.com/jmar777"
- },
- "name": "cb",
- "description": "Super simple callback mechanism with support for timeouts and explicit error handling",
- "version": "0.1.0",
- "repository": {
- "type": "git",
- "url": "git://github.com/jmar777/cb.git"
- },
- "main": "lib/cb.js",
- "devDependencies": {
- "mocha": ">=0.3.6"
- },
- "engines": {
- "node": ">=0.6.0"
- },
- "_npmUser": {
- "name": "jmar777",
- "email": "jmar777@gmail.com"
- },
- "_id": "cb@0.1.0",
- "dependencies": {},
- "optionalDependencies": {},
- "_engineSupported": true,
- "_npmVersion": "1.1.0-2",
- "_nodeVersion": "v0.6.8",
- "_defaultsLoaded": true,
- "dist": {
- "shasum": "26f7e740f2808facc83cef7b20392e4d881b5203",
- "tarball": "https://registry.npmjs.org/cb/-/cb-0.1.0.tgz"
- },
- "maintainers": [
- {
- "name": "jmar777",
- "email": "jmar777@gmail.com"
- }
- ],
- "directories": {},
- "_shasum": "26f7e740f2808facc83cef7b20392e4d881b5203",
- "_resolved": "https://registry.npmjs.org/cb/-/cb-0.1.0.tgz",
- "_from": "cb@>=0.1.0 <0.2.0"
-}
diff --git a/node_modules/jsonwebtoken/node_modules/cb/test/tests.js b/node_modules/jsonwebtoken/node_modules/cb/test/tests.js
deleted file mode 100644
index b4ac904..0000000
--- a/node_modules/jsonwebtoken/node_modules/cb/test/tests.js
+++ /dev/null
@@ -1,125 +0,0 @@
-var assert = require('assert'),
- cb = require('../');
-
-function invokeAsync(callback) {
- setTimeout(function() {
- callback(null, 'foo');
- }, 100);
-}
-
-function invokeAsyncError(callback) {
- setTimeout(function() {
- callback(new Error());
- }, 100);
-}
-
-function invokeAsyncTwice(callback) {
- setTimeout(function() {
- callback(null, 'foo');
- callback(null, 'foo');
- }, 100);
-}
-
-describe('cb(callback)', function() {
-
- it('should invoke the provided callback', function(done) {
- invokeAsync(cb(function(err, res) {
- assert.strictEqual(res, 'foo');
- done();
- }));
- });
-
- it('shouldn\'t mess with errors', function(done) {
- invokeAsyncError(cb(function(err, res) {
- assert(err);
- done();
- }));
- });
-
- it('should allow multiple executions', function(done) {
- var count = 0;
- invokeAsyncTwice(cb(function(err, res) {
- count++;
- if (count === 2) done();
- }));
- });
-
-});
-
-describe('cb(callback).timeout(ms)', function() {
-
- it('should complete successfully within timeout period', function(done) {
- invokeAsync(cb(function(err, res) {
- assert.strictEqual(res, 'foo');
- done();
- }).timeout(200));
- });
-
- it('should complete with an error after timeout period', function(done) {
- invokeAsync(cb(function(err, res) {
- assert(err);
- done();
- }).timeout(50));
- });
-
- it('error resulting from a timeout should be instanceof cb.TimeoutError', function(done) {
- invokeAsync(cb(function(err, res) {
- assert(err instanceof cb.TimeoutError);
- done();
- }).timeout(50));
- });
-
-});
-
-describe('cb(callback).error(errback)', function() {
-
- it('should skip the err argument when invoking callback', function(done) {
- invokeAsync(cb(function(res) {
- assert.strictEqual(res, 'foo');
- done();
- }).error(assert.ifError));
- });
-
- it('should pass errors to provided errback', function(done) {
- invokeAsyncError(cb(function(res) {
- throw new Error('should not be invoked');
- }).error(function(err) {
- assert(err);
- done();
- }));
- });
-
-});
-
-describe('cb(callback).error(errback).timeout(ms)', function() {
-
- it('should skip the err argument when invoking callback', function(done) {
- invokeAsync(cb(function(res) {
- assert.strictEqual(res, 'foo');
- done();
- }).error(assert.ifError).timeout(200));
- });
-
- it('should pass timeout error to provided errback', function(done) {
- invokeAsyncError(cb(function(res) {
- throw new Error('should not be invoked');
- }).error(function(err) {
- assert(err);
- done();
- }).timeout(50));
- });
-
-});
-
-describe('cb(callback).once()', function() {
-
- it('should allow multiple executions', function(done) {
- var count = 0;
- invokeAsyncTwice(cb(function(err, res) {
- count++;
- assert.notEqual(count, 2);
- setTimeout(done, 100);
- }).once());
- });
-
-});
diff --git a/node_modules/jsonwebtoken/node_modules/joi/node_modules/isemail/package.json b/node_modules/jsonwebtoken/node_modules/joi/node_modules/isemail/package.json
index fd528b9..74958ba 100644
--- a/node_modules/jsonwebtoken/node_modules/joi/node_modules/isemail/package.json
+++ b/node_modules/jsonwebtoken/node_modules/joi/node_modules/isemail/package.json
@@ -58,5 +58,6 @@
}
],
"directories": {},
- "_resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz"
+ "_resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/joi/node_modules/moment/package.json b/node_modules/jsonwebtoken/node_modules/joi/node_modules/moment/package.json
index ac85a48..8e36507 100644
--- a/node_modules/jsonwebtoken/node_modules/joi/node_modules/moment/package.json
+++ b/node_modules/jsonwebtoken/node_modules/joi/node_modules/moment/package.json
@@ -144,5 +144,6 @@
"tmp": "tmp/moment-2.14.1.tgz_1467614674915_0.32715084473602474"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/moment/-/moment-2.14.1.tgz"
+ "_resolved": "https://registry.npmjs.org/moment/-/moment-2.14.1.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/joi/node_modules/topo/package.json b/node_modules/jsonwebtoken/node_modules/joi/node_modules/topo/package.json
index 3493042..6fc5bb7 100644
--- a/node_modules/jsonwebtoken/node_modules/joi/node_modules/topo/package.json
+++ b/node_modules/jsonwebtoken/node_modules/joi/node_modules/topo/package.json
@@ -4,7 +4,7 @@
"version": "1.1.0",
"repository": {
"type": "git",
- "url": "git://github.com/hapijs/topo"
+ "url": "git://github.com/hapijs/topo.git"
},
"main": "lib/index.js",
"keywords": [
@@ -56,5 +56,6 @@
"tarball": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz"
+ "_resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/joi/package.json b/node_modules/jsonwebtoken/node_modules/joi/package.json
index 1df1312..637e324 100644
--- a/node_modules/jsonwebtoken/node_modules/joi/package.json
+++ b/node_modules/jsonwebtoken/node_modules/joi/package.json
@@ -63,5 +63,6 @@
}
],
"directories": {},
- "_resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz"
+ "_resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/node_modules/camelcase/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/node_modules/camelcase/package.json
index df84fbc..21cb210 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/node_modules/camelcase/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/node_modules/camelcase/package.json
@@ -5,7 +5,7 @@
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/sindresorhus/camelcase"
+ "url": "git+https://github.com/sindresorhus/camelcase.git"
},
"author": {
"name": "Sindre Sorhus",
@@ -63,5 +63,6 @@
}
],
"directories": {},
- "_resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
+ "_resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/node_modules/map-obj/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/node_modules/map-obj/package.json
index 9de7392..85697d9 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/node_modules/map-obj/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/node_modules/map-obj/package.json
@@ -5,7 +5,7 @@
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/sindresorhus/map-obj"
+ "url": "git+https://github.com/sindresorhus/map-obj.git"
},
"author": {
"name": "Sindre Sorhus",
@@ -61,5 +61,6 @@
}
],
"directories": {},
- "_resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz"
+ "_resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/package.json
index 2a12c8a..5617d4c 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/camelcase-keys/package.json
@@ -5,7 +5,7 @@
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/sindresorhus/camelcase-keys"
+ "url": "git+https://github.com/sindresorhus/camelcase-keys.git"
},
"author": {
"name": "Sindre Sorhus",
@@ -76,5 +76,6 @@
"tarball": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz"
+ "_resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/indent-string/node_modules/get-stdin/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/indent-string/node_modules/get-stdin/package.json
index fc5e9b2..46eeb71 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/indent-string/node_modules/get-stdin/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/indent-string/node_modules/get-stdin/package.json
@@ -5,7 +5,7 @@
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/sindresorhus/get-stdin"
+ "url": "git+https://github.com/sindresorhus/get-stdin.git"
},
"author": {
"name": "Sindre Sorhus",
@@ -59,5 +59,6 @@
"tarball": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz"
+ "_resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/indent-string/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/indent-string/package.json
index 3aa998b..3a1e56a 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/indent-string/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/indent-string/package.json
@@ -5,7 +5,7 @@
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/sindresorhus/indent-string"
+ "url": "git+https://github.com/sindresorhus/indent-string.git"
},
"author": {
"name": "Sindre Sorhus",
@@ -68,5 +68,6 @@
}
],
"directories": {},
- "_resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz"
+ "_resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/object-assign/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/object-assign/package.json
index 731be3b..7a89a68 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/object-assign/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/node_modules/object-assign/package.json
@@ -5,7 +5,7 @@
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/sindresorhus/object-assign"
+ "url": "git+https://github.com/sindresorhus/object-assign.git"
},
"author": {
"name": "Sindre Sorhus",
@@ -62,5 +62,6 @@
"tarball": "https://registry.npmjs.org/object-assign/-/object-assign-1.0.0.tgz"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/object-assign/-/object-assign-1.0.0.tgz"
+ "_resolved": "https://registry.npmjs.org/object-assign/-/object-assign-1.0.0.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/package.json
index 4ddb47a..d1d2eb2 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/node_modules/meow/package.json
@@ -5,7 +5,7 @@
"license": "MIT",
"repository": {
"type": "git",
- "url": "https://github.com/sindresorhus/meow"
+ "url": "git+https://github.com/sindresorhus/meow.git"
},
"author": {
"name": "Sindre Sorhus",
@@ -63,5 +63,6 @@
"tarball": "https://registry.npmjs.org/meow/-/meow-2.0.0.tgz"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/meow/-/meow-2.0.0.tgz"
+ "_resolved": "https://registry.npmjs.org/meow/-/meow-2.0.0.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/package.json
index bf1aca6..effdbf7 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/base64url/package.json
@@ -57,5 +57,6 @@
"tmp": "tmp/base64url-1.0.6.tgz_1455309394658_0.6706331633031368"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/base64url/-/base64url-1.0.6.tgz"
+ "_resolved": "https://registry.npmjs.org/base64url/-/base64url-1.0.6.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/buffer-equal-constant-time/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/buffer-equal-constant-time/package.json
index b35d217..1aacd0d 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/buffer-equal-constant-time/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/buffer-equal-constant-time/package.json
@@ -8,7 +8,7 @@
},
"repository": {
"type": "git",
- "url": "git@github.com:goinstant/buffer-equal-constant-time.git"
+ "url": "git+ssh://git@github.com/goinstant/buffer-equal-constant-time.git"
},
"keywords": [
"buffer",
@@ -47,5 +47,6 @@
],
"directories": {},
"_shasum": "f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819",
- "_resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz"
+ "_resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "homepage": "https://github.com/goinstant/buffer-equal-constant-time#readme"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/ecdsa-sig-formatter/node_modules/base64-url/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/ecdsa-sig-formatter/node_modules/base64-url/package.json
index 5f08990..7dfca92 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/ecdsa-sig-formatter/node_modules/base64-url/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/ecdsa-sig-formatter/node_modules/base64-url/package.json
@@ -69,5 +69,6 @@
"tmp": "tmp/base64-url-1.3.2.tgz_1468593630635_0.8208693880587816"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.3.2.tgz"
+ "_resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.3.2.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/ecdsa-sig-formatter/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/ecdsa-sig-formatter/package.json
index af365ee..c2fa6cf 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/ecdsa-sig-formatter/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/node_modules/ecdsa-sig-formatter/package.json
@@ -69,5 +69,6 @@
"tmp": "tmp/ecdsa-sig-formatter-1.0.7.tgz_1466263566774_0.3799667169805616"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.7.tgz"
+ "_resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.7.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/package.json b/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/package.json
index 5053ed7..1ebbdee 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/node_modules/jwa/package.json
@@ -70,5 +70,6 @@
"host": "packages-5-east.internal.npmjs.com",
"tmp": "tmp/jwa-1.1.3.tgz_1455809964709_0.6556255409959704"
},
- "_resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.3.tgz"
+ "_resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.3.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/node_modules/jws/package.json b/node_modules/jsonwebtoken/node_modules/jws/package.json
index 86c17d1..7447c05 100644
--- a/node_modules/jsonwebtoken/node_modules/jws/package.json
+++ b/node_modules/jsonwebtoken/node_modules/jws/package.json
@@ -67,5 +67,6 @@
"host": "packages-6-west.internal.npmjs.com",
"tmp": "tmp/jws-3.1.3.tgz_1455809983684_0.8235816163942218"
},
- "_resolved": "https://registry.npmjs.org/jws/-/jws-3.1.3.tgz"
+ "_resolved": "https://registry.npmjs.org/jws/-/jws-3.1.3.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE b/node_modules/jsonwebtoken/node_modules/lodash.once/LICENSE
similarity index 52%
rename from node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE
rename to node_modules/jsonwebtoken/node_modules/lodash.once/LICENSE
index 7c435ba..e0c69d5 100644
--- a/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE
+++ b/node_modules/jsonwebtoken/node_modules/lodash.once/LICENSE
@@ -1,4 +1,16 @@
-Copyright (c) 2008-2011 Pivotal Labs
+Copyright jQuery Foundation and other contributors
+
+Based on Underscore.js, copyright Jeremy Ashkenas,
+DocumentCloud and Investigative Reporters & Editors
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/lodash/lodash
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -18,3 +30,18 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+Copyright and related rights for sample code are waived via CC0. Sample
+code is defined as all source code displayed within the prose of the
+documentation.
+
+CC0: http://creativecommons.org/publicdomain/zero/1.0/
+
+====
+
+Files located in the node_modules and vendor directories are externally
+maintained libraries used by this software which have their own
+licenses; we recommend you read them, as their terms may differ from the
+terms above.
diff --git a/node_modules/jsonwebtoken/node_modules/lodash.once/README.md b/node_modules/jsonwebtoken/node_modules/lodash.once/README.md
new file mode 100644
index 0000000..c4a2f16
--- /dev/null
+++ b/node_modules/jsonwebtoken/node_modules/lodash.once/README.md
@@ -0,0 +1,18 @@
+# lodash.once v4.1.1
+
+The [lodash](https://lodash.com/) method `_.once` exported as a [Node.js](https://nodejs.org/) module.
+
+## Installation
+
+Using npm:
+```bash
+$ {sudo -H} npm i -g npm
+$ npm i --save lodash.once
+```
+
+In Node.js:
+```js
+var once = require('lodash.once');
+```
+
+See the [documentation](https://lodash.com/docs#once) or [package source](https://github.com/lodash/lodash/blob/4.1.1-npm-packages/lodash.once) for more details.
diff --git a/node_modules/jsonwebtoken/node_modules/lodash.once/index.js b/node_modules/jsonwebtoken/node_modules/lodash.once/index.js
new file mode 100644
index 0000000..414ceb3
--- /dev/null
+++ b/node_modules/jsonwebtoken/node_modules/lodash.once/index.js
@@ -0,0 +1,294 @@
+/**
+ * lodash (Custom Build)
+ * Build: `lodash modularize exports="npm" -o ./`
+ * Copyright jQuery Foundation and other contributors
+ * Released under MIT license
+ * Based on Underscore.js 1.8.3
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ */
+
+/** Used as the `TypeError` message for "Functions" methods. */
+var FUNC_ERROR_TEXT = 'Expected a function';
+
+/** Used as references for various `Number` constants. */
+var INFINITY = 1 / 0,
+ MAX_INTEGER = 1.7976931348623157e+308,
+ NAN = 0 / 0;
+
+/** `Object#toString` result references. */
+var symbolTag = '[object Symbol]';
+
+/** Used to match leading and trailing whitespace. */
+var reTrim = /^\s+|\s+$/g;
+
+/** Used to detect bad signed hexadecimal string values. */
+var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+
+/** Used to detect binary string values. */
+var reIsBinary = /^0b[01]+$/i;
+
+/** Used to detect octal string values. */
+var reIsOctal = /^0o[0-7]+$/i;
+
+/** Built-in method references without a dependency on `root`. */
+var freeParseInt = parseInt;
+
+/** Used for built-in method references. */
+var objectProto = Object.prototype;
+
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var objectToString = objectProto.toString;
+
+/**
+ * Creates a function that invokes `func`, with the `this` binding and arguments
+ * of the created function, while it's called less than `n` times. Subsequent
+ * calls to the created function return the result of the last `func` invocation.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Function
+ * @param {number} n The number of calls at which `func` is no longer invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * jQuery(element).on('click', _.before(5, addContactToList));
+ * // => Allows adding up to 4 contacts to the list.
+ */
+function before(n, func) {
+ var result;
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ n = toInteger(n);
+ return function() {
+ if (--n > 0) {
+ result = func.apply(this, arguments);
+ }
+ if (n <= 1) {
+ func = undefined;
+ }
+ return result;
+ };
+}
+
+/**
+ * Creates a function that is restricted to invoking `func` once. Repeat calls
+ * to the function return the value of the first invocation. The `func` is
+ * invoked with the `this` binding and arguments of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // => `createApplication` is invoked once
+ */
+function once(func) {
+ return before(2, func);
+}
+
+/**
+ * Checks if `value` is the
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(_.noop);
+ * // => true
+ *
+ * _.isObject(null);
+ * // => false
+ */
+function isObject(value) {
+ var type = typeof value;
+ return !!value && (type == 'object' || type == 'function');
+}
+
+/**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @example
+ *
+ * _.isObjectLike({});
+ * // => true
+ *
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isObjectLike(_.noop);
+ * // => false
+ *
+ * _.isObjectLike(null);
+ * // => false
+ */
+function isObjectLike(value) {
+ return !!value && typeof value == 'object';
+}
+
+/**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+function isSymbol(value) {
+ return typeof value == 'symbol' ||
+ (isObjectLike(value) && objectToString.call(value) == symbolTag);
+}
+
+/**
+ * Converts `value` to a finite number.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.12.0
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted number.
+ * @example
+ *
+ * _.toFinite(3.2);
+ * // => 3.2
+ *
+ * _.toFinite(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toFinite(Infinity);
+ * // => 1.7976931348623157e+308
+ *
+ * _.toFinite('3.2');
+ * // => 3.2
+ */
+function toFinite(value) {
+ if (!value) {
+ return value === 0 ? value : 0;
+ }
+ value = toNumber(value);
+ if (value === INFINITY || value === -INFINITY) {
+ var sign = (value < 0 ? -1 : 1);
+ return sign * MAX_INTEGER;
+ }
+ return value === value ? value : 0;
+}
+
+/**
+ * Converts `value` to an integer.
+ *
+ * **Note:** This method is loosely based on
+ * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted integer.
+ * @example
+ *
+ * _.toInteger(3.2);
+ * // => 3
+ *
+ * _.toInteger(Number.MIN_VALUE);
+ * // => 0
+ *
+ * _.toInteger(Infinity);
+ * // => 1.7976931348623157e+308
+ *
+ * _.toInteger('3.2');
+ * // => 3
+ */
+function toInteger(value) {
+ var result = toFinite(value),
+ remainder = result % 1;
+
+ return result === result ? (remainder ? result - remainder : result) : 0;
+}
+
+/**
+ * Converts `value` to a number.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
+ * @example
+ *
+ * _.toNumber(3.2);
+ * // => 3.2
+ *
+ * _.toNumber(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toNumber(Infinity);
+ * // => Infinity
+ *
+ * _.toNumber('3.2');
+ * // => 3.2
+ */
+function toNumber(value) {
+ if (typeof value == 'number') {
+ return value;
+ }
+ if (isSymbol(value)) {
+ return NAN;
+ }
+ if (isObject(value)) {
+ var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+ value = isObject(other) ? (other + '') : other;
+ }
+ if (typeof value != 'string') {
+ return value === 0 ? value : +value;
+ }
+ value = value.replace(reTrim, '');
+ var isBinary = reIsBinary.test(value);
+ return (isBinary || reIsOctal.test(value))
+ ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+ : (reIsBadHex.test(value) ? NAN : +value);
+}
+
+module.exports = once;
diff --git a/node_modules/jsonwebtoken/node_modules/lodash.once/package.json b/node_modules/jsonwebtoken/node_modules/lodash.once/package.json
new file mode 100644
index 0000000..b6ded01
--- /dev/null
+++ b/node_modules/jsonwebtoken/node_modules/lodash.once/package.json
@@ -0,0 +1,78 @@
+{
+ "name": "lodash.once",
+ "version": "4.1.1",
+ "description": "The lodash method `_.once` exported as a module.",
+ "homepage": "https://lodash.com/",
+ "icon": "https://lodash.com/icon.svg",
+ "license": "MIT",
+ "keywords": [
+ "lodash-modularized",
+ "once"
+ ],
+ "author": {
+ "name": "John-David Dalton",
+ "email": "john.david.dalton@gmail.com",
+ "url": "http://allyoucanleet.com/"
+ },
+ "contributors": [
+ {
+ "name": "John-David Dalton",
+ "email": "john.david.dalton@gmail.com",
+ "url": "http://allyoucanleet.com/"
+ },
+ {
+ "name": "Blaine Bublitz",
+ "email": "blaine.bublitz@gmail.com",
+ "url": "https://github.com/phated"
+ },
+ {
+ "name": "Mathias Bynens",
+ "email": "mathias@qiwi.be",
+ "url": "https://mathiasbynens.be/"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/lodash/lodash.git"
+ },
+ "scripts": {
+ "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\""
+ },
+ "bugs": {
+ "url": "https://github.com/lodash/lodash/issues"
+ },
+ "_id": "lodash.once@4.1.1",
+ "_shasum": "0dd3971213c7c56df880977d504c88fb471a97ac",
+ "_from": "lodash.once@>=4.0.0 <5.0.0",
+ "_npmVersion": "2.15.10",
+ "_nodeVersion": "4.4.7",
+ "_npmUser": {
+ "name": "jdalton",
+ "email": "john.david.dalton@gmail.com"
+ },
+ "dist": {
+ "shasum": "0dd3971213c7c56df880977d504c88fb471a97ac",
+ "tarball": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "jdalton",
+ "email": "john.david.dalton@gmail.com"
+ },
+ {
+ "name": "mathias",
+ "email": "mathias@qiwi.be"
+ },
+ {
+ "name": "phated",
+ "email": "blaine@iceddev.com"
+ }
+ ],
+ "_npmOperationalInternal": {
+ "host": "packages-12-west.internal.npmjs.com",
+ "tmp": "tmp/lodash.once-4.1.1.tgz_1471110166870_0.09848929662257433"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/node_modules/jsonwebtoken/package.json b/node_modules/jsonwebtoken/package.json
index da0b2a7..d6a7b86 100644
--- a/node_modules/jsonwebtoken/package.json
+++ b/node_modules/jsonwebtoken/package.json
@@ -1,6 +1,6 @@
{
"name": "jsonwebtoken",
- "version": "7.1.6",
+ "version": "7.1.9",
"description": "JSON Web Token implementation (symmetric and asymmetric)",
"main": "index.js",
"scripts": {
@@ -21,9 +21,9 @@
"url": "https://github.com/auth0/node-jsonwebtoken/issues"
},
"dependencies": {
- "cb": "^0.1.0",
"joi": "^6.10.1",
"jws": "^3.1.3",
+ "lodash.once": "^4.0.0",
"ms": "^0.7.1",
"xtend": "^4.0.1"
},
@@ -38,10 +38,10 @@
"npm": ">=1.4.28",
"node": ">=0.12"
},
- "gitHead": "ae360b56792a875e16cefa8ff4103b87b4a2e386",
+ "gitHead": "cc0f4d67b649110a035db3df9265f05db269a15a",
"homepage": "https://github.com/auth0/node-jsonwebtoken#readme",
- "_id": "jsonwebtoken@7.1.6",
- "_shasum": "2ea9557af144311148f53093cfeec69e1e048abc",
+ "_id": "jsonwebtoken@7.1.9",
+ "_shasum": "847804e5258bec5a9499a8dc4a5e7a3bae08d58a",
"_from": "jsonwebtoken@latest",
"_npmVersion": "2.15.1",
"_nodeVersion": "4.4.3",
@@ -50,8 +50,8 @@
"email": "jfromaniello@gmail.com"
},
"dist": {
- "shasum": "2ea9557af144311148f53093cfeec69e1e048abc",
- "tarball": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.1.6.tgz"
+ "shasum": "847804e5258bec5a9499a8dc4a5e7a3bae08d58a",
+ "tarball": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.1.9.tgz"
},
"maintainers": [
{
@@ -84,9 +84,10 @@
}
],
"_npmOperationalInternal": {
- "host": "packages-16-east.internal.npmjs.com",
- "tmp": "tmp/jsonwebtoken-7.1.6.tgz_1468585972042_0.5801840056665242"
+ "host": "packages-12-west.internal.npmjs.com",
+ "tmp": "tmp/jsonwebtoken-7.1.9.tgz_1470932603683_0.2585906428284943"
},
"directories": {},
- "_resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.1.6.tgz"
+ "_resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.1.9.tgz",
+ "readme": "ERROR: No README data found!"
}
diff --git a/node_modules/jsonwebtoken/sign.js b/node_modules/jsonwebtoken/sign.js
index e5af705..cfdfdab 100644
--- a/node_modules/jsonwebtoken/sign.js
+++ b/node_modules/jsonwebtoken/sign.js
@@ -2,7 +2,7 @@ var Joi = require('joi');
var timespan = require('./lib/timespan');
var xtend = require('xtend');
var jws = require('jws');
-var cb = require('cb');
+var once = require('lodash.once');
var sign_options_schema = Joi.object().keys({
expiresIn: [Joi.number().integer(), Joi.string()],
@@ -129,7 +129,7 @@ module.exports = function (payload, secretOrPrivateKey, options, callback) {
var encoding = options.encoding || 'utf8';
if (typeof callback === 'function') {
- callback = callback && cb(callback).once();
+ callback = callback && once(callback);
jws.createSign({
header: header,
diff --git a/node_modules/mongoose/.eslintignore b/node_modules/mongoose/.eslintignore
new file mode 100644
index 0000000..235bdcd
--- /dev/null
+++ b/node_modules/mongoose/.eslintignore
@@ -0,0 +1,3 @@
+docs/
+bin/
+test/triage/
diff --git a/node_modules/mongoose/.eslintrc.yml b/node_modules/mongoose/.eslintrc.yml
new file mode 100644
index 0000000..013d2ec
--- /dev/null
+++ b/node_modules/mongoose/.eslintrc.yml
@@ -0,0 +1,31 @@
+extends: 'eslint:recommended'
+
+env:
+ node: true
+ mocha: true
+
+rules:
+ comma-style: error
+ consistent-this:
+ - error
+ - _this
+ indent:
+ - error
+ - 2
+ - SwitchCase: 1
+ VariableDeclarator: 2
+ keyword-spacing: error
+ no-console: off
+ no-multi-spaces: error
+ no-spaced-func: error
+ no-trailing-spaces: error
+ quotes:
+ - error
+ - single
+ semi: error
+ space-before-blocks: error
+ space-before-function-paren:
+ - error
+ - never
+ space-infix-ops: error
+ space-unary-ops: error
diff --git a/node_modules/mongoose/.npmignore b/node_modules/mongoose/.npmignore
index df5ea94..377d63f 100644
--- a/node_modules/mongoose/.npmignore
+++ b/node_modules/mongoose/.npmignore
@@ -11,3 +11,6 @@ Makefile
CNAME
index.html
index.jade
+bin/
+karma.*.js
+format_deps.js
diff --git a/node_modules/mongoose/.travis.yml b/node_modules/mongoose/.travis.yml
index ab51641..b9348aa 100644
--- a/node_modules/mongoose/.travis.yml
+++ b/node_modules/mongoose/.travis.yml
@@ -1,7 +1,14 @@
language: node_js
+sudo: false
node_js:
- - 0.6
- - 0.8
- - 0.10
-services:
- - mongodb
+ - "6"
+ - "5"
+ - "4"
+ - "0.12"
+ - "0.10"
+ - "iojs"
+before_script:
+ - wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.6.11.tgz
+ - tar -zxvf mongodb-linux-x86_64-2.6.11.tgz
+ - mkdir -p ./data/db
+ - ./mongodb-linux-x86_64-2.6.11/bin/mongod --fork --nopreallocj --dbpath ./data/db --syslog --port 27017
diff --git a/node_modules/mongoose/CONTRIBUTING.md b/node_modules/mongoose/CONTRIBUTING.md
index daddb4a..904aeef 100644
--- a/node_modules/mongoose/CONTRIBUTING.md
+++ b/node_modules/mongoose/CONTRIBUTING.md
@@ -1,60 +1,56 @@
## Contributing to Mongoose
-### STOP!
-
-If you have a question about Mongoose (not a bug report) please post it to either [StackOverflow](http://stackoverflow.com/questions/tagged/mongoose), our [Google Group](http://groups.google.com/group/mongoose-orm), or on the #mongoosejs irc channel on freenode.
+If you have a question about Mongoose (not a bug report) please post it to either [StackOverflow](http://stackoverflow.com/questions/tagged/mongoose), or on [Gitter](https://gitter.im/Automattic/mongoose?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
### Reporting bugs
-- Before opening a new issue, look for existing [issues](https://github.com/learnboost/mongoose/issues) to avoid duplication. If the issue does not yet exist, [create one](https://github.com/learnboost/mongoose/issues/new).
- - Please describe the issue you are experiencing, along with any associated stack trace.
- - Please post code that reproduces the issue, the version of mongoose, node version, and mongodb version.
- - _The source of this project is written in javascript, not coffeescript, therefore your bug reports should be written in javascript_.
- - In general, adding a "+1" comment to an existing issue does little to help get it resolved. A better way is to submit a well documented pull request with clean code and passing tests.
+- Before opening a new issue, look for existing [issues](https://github.com/Automattic/mongoose/issues) to avoid duplication. If the issue does not yet exist, [create one](https://github.com/Automattic/mongoose/issues/new).
+ - Please post any relevant code samples, preferably a standalone script that
+ reproduces your issue. Do **not** describe your issue in prose, show your
+ code.
+ - If the bug involves an error, please post the stack trace.
+ - Please post the version of mongoose and mongodb that you're using.
+ - Please write bug reports in JavaScript (ES5 or ES2015), not coffeescript, typescript, etc.
### Requesting new features
- Before opening a new issue, look for existing [issues](https://github.com/learnboost/mongoose/issues) to avoid duplication. If the issue does not yet exist, [create one](https://github.com/learnboost/mongoose/issues/new).
- Please describe a use case for it
- it would be ideal to include test cases as well
-- In general, adding a "+1" comment to an existing issue does little to help get it resolved. A better way is to submit a well documented pull request with clean code and passing tests.
### Fixing bugs / Adding features
- Before starting to write code, look for existing [issues](https://github.com/learnboost/mongoose/issues). That way you avoid working on something that might not be of interest or that has been addressed already in a different branch. You can create a new issue [here](https://github.com/learnboost/mongoose/issues/new).
- - _The source of this project is written in javascript, not coffeescript, therefore your bug reports should be written in javascript_.
-- Fork the [repo](https://github.com/learnboost/mongoose) _or_ for small documentation changes, navigate to the source on github and click the [Edit](https://github.com/blog/844-forking-with-the-edit-button) button.
+ - _The source of this project is written in javascript, not coffeescript, therefore your bug reports should be written in javascript_.
+- Fork the [repo](https://github.com/Automattic/mongoose) _or_ for small documentation changes, navigate to the source on github and click the [Edit](https://github.com/blog/844-forking-with-the-edit-button) button.
- Follow the general coding style of the rest of the project:
- 2 space tabs
- no trailing whitespace
- - comma first
- - inline documentation for new methods, class members, etc
- - 1 space between conditionals/functions, and their parenthesis and curly braces
+ - inline documentation for new methods, class members, etc.
+ - 1 space between conditionals, no space before function parenthesis
- `if (..) {`
- `for (..) {`
- `while (..) {`
- - `function (err) {`
-- Write tests and make sure they pass (tests are in the [test](https://github.com/LearnBoost/mongoose/tree/master/test) directory).
+ - `function(err) {`
+- Write tests and make sure they pass (tests are in the [test](https://github.com/Automattic/mongoose/tree/master/test) directory).
### Running the tests
- Open a terminal and navigate to the root of the project
- execute `npm install` to install the necessary dependencies
-- execute `make test` to run the tests (we're using [mocha](http://visionmedia.github.com/mocha/))
- - or to execute a single test `T="-g 'some regexp that matches the test description'" make test`
- - any mocha flags can be specified with T="..."
+- start a mongodb instance on port 27017 if one isn't running already. `mongod --dbpath --port 27017`
+- execute `npm test` to run the tests (we're using [mocha](http://mochajs.org/))
+ - or to execute a single test `npm test -- -g 'some regexp that matches the test description'`
+ - any mocha flags can be specified with `-- `
+ - For example, you can use `npm test -- -R spec` to use the spec reporter, rather than the dot reporter (by default, the test output looks like a bunch of dots)
### Documentation
-To contribute to the [API documentation](http://mongoosejs.com/docs/api.html) just make your changes to the inline documentation of the appropriate [source code](https://github.com/LearnBoost/mongoose/tree/master/lib) in the master branch and submit a [pull request](https://help.github.com/articles/using-pull-requests/). You might also use the github [Edit](https://github.com/blog/844-forking-with-the-edit-button) button.
+To contribute to the [API documentation](http://mongoosejs.com/docs/api.html) just make your changes to the inline documentation of the appropriate [source code](https://github.com/Automattic/mongoose/tree/master/lib) in the master branch and submit a [pull request](https://help.github.com/articles/using-pull-requests/). You might also use the github [Edit](https://github.com/blog/844-forking-with-the-edit-button) button.
-To contribute to the [guide](http://mongoosejs.com/docs/guide.html) or [quick start](http://mongoosejs.com/docs/index.html) docs, make your changes to the appropriate `.jade` files in the [docs](https://github.com/LearnBoost/mongoose/tree/master/docs) directory of the master branch and submit a pull request. Again, the [Edit](https://github.com/blog/844-forking-with-the-edit-button) button might work for you here.
+To contribute to the [guide](http://mongoosejs.com/docs/guide.html) or [quick start](http://mongoosejs.com/docs/index.html) docs, make your changes to the appropriate `.jade` files in the [docs](https://github.com/Automattic/mongoose/tree/master/docs) directory of the master branch and submit a pull request. Again, the [Edit](https://github.com/blog/844-forking-with-the-edit-button) button might work for you here.
-If you'd like to preview your documentation changes, first commit your changes to your local master branch, then execute `make docs` from the project root, which switches to the gh-pages branch, merges from master, and builds all the static pages for you. Now execute `node server.js` from the project root which will launch a local webserver where you can browse the documentation site locally. If all looks good, submit a [pull request](https://help.github.com/articles/using-pull-requests/) to the master branch with your changes.
+If you'd like to preview your documentation changes, first commit your changes to your local master branch, then execute `make docs` from the project root, which switches to the gh-pages branch, merges from the master branch and builds all the static pages for you. Now execute `node static.js` from the project root which will launch a local webserver where you can browse the documentation site locally. If all looks good, submit a [pull request](https://help.github.com/articles/using-pull-requests/) to the master branch with your changes.
### Plugins website
-The [plugins](http://plugins.mongoosejs.com/) site is also an [open source project](https://github.com/aheckmann/mongooseplugins) that you can get involved with. Feel free to fork and improve it as well!
-
-### Sharing your projects
-
-All are welcome to share their creations which use mongoose on our [tumbler](http://mongoosejs.tumblr.com/). Just fill out the [simple submission form](http://mongoosejs.tumblr.com/submit).
+The [plugins](http://plugins.mongoosejs.io/) site is also an [open source project](https://github.com/vkarpov15/mongooseplugins) that you can get involved with. Feel free to fork and improve it as well!
diff --git a/node_modules/mongoose/History.md b/node_modules/mongoose/History.md
index 768bb63..98e3bb2 100644
--- a/node_modules/mongoose/History.md
+++ b/node_modules/mongoose/History.md
@@ -1,3 +1,1439 @@
+4.5.9 / 2016-08-14
+==================
+ * docs: add mixed schema doc for Object literal #4400 [Kikobeats](https://github.com/Kikobeats)
+ * fix(query): cast $geoWithin and convert mongoose objects to POJOs before casting #4392
+ * fix(schematype): dont cast defaults without parent doc #4390
+ * fix(query): disallow passing empty string to findOne() #4378
+ * fix(document): set single nested doc isNew correctly #4369
+ * fix(types): checks field name correctly with nested arrays and populate #4365
+ * fix(drivers): make debug output copy-pastable into mongodb shell #4352
+ * fix(services): run update validators on nested paths #4332
+ * fix(model): handle typeKey with discriminators #4339
+ * fix(query): apply timestamps to child schemas when explicitly specified in update #4049
+ * fix(schema): set prefix as nested path with add() #1730
+
+4.5.8 / 2016-08-01
+==================
+ * fix(model): make changing the discriminator key cause a cast error #4374
+ * fix(query): pass projection fields to cursor #4371 #4342 [Corei13](https://github.com/Corei13)
+ * fix(document): support multiple paths for isModified #4370 [adambuczynski](https://github.com/adambuczynski)
+ * fix(querycursor): always cast fields before returning cursor #4355
+ * fix(query): support projection as alias for fields in findOneAndUpdate #4315
+ * fix(schema): treat index false + unique false as no index #4304
+ * fix(types): dont mark single nested subpath as modified if whole doc already modified #4224
+
+4.5.7 / 2016-07-25
+==================
+ * fix(document): ensure no unhandled rejections if callback specified for save #4364
+
+4.5.6 / 2016-07-23
+==================
+ * fix(schema): don't overwrite createdAt if it isn't selected #4351 [tusbar](https://github.com/tusbar)
+ * docs(api): fix link to populate() and add a new one from depopulate() #4345 [Delapouite](https://github.com/Delapouite)
+ * fix(types): ownerDocument() works properly with single nested docs #4344 [vichle](https://github.com/vichle)
+ * fix(populate): dont use findOne when justOne option set #4329
+ * fix(document): dont trigger .then() deprecated warning when calling doc.remove() #4291
+ * docs(connection): add promiseLibrary option #4280
+ * fix(plugins): apply global plugins to subschemas #4271
+ * fix(model): ensure `ensureIndex()` never calls back in the same tick #4246
+ * docs(schema): improve post hook docs on schema #4238
+
+4.5.5 / 2016-07-18
+==================
+ * fix(document): handle setting root to empty obj if minimize false #4337
+ * fix: downgrade to mongodb 2.1.18 #4335 #4334 #4328 #4323
+ * perf(types): remove defineProperty usage in documentarray #4333
+ * fix(query): correctly pass model in .toConstructor() #4318
+ * fix(services): avoid double-validating mixed types with update validators #4305
+ * docs(middleware): add docs describing error handling middleware #4229
+ * fix(types): throw correct error when invalidating doc array #3602
+
+4.5.4 / 2016-07-11
+==================
+ * fix(types): fix removing embedded documents #4309 [RoCat](https://github.com/RoCat)
+ * docs: various docs improvements #4302 #4294 [simonxca](https://github.com/simonxca)
+ * fix: upgrade mongodb -> 2.1.21 #4295 #4202 [RoCat](https://github.com/RoCat)
+ * fix(populate): convert single result to array for virtual populate because of lean #4288
+ * fix(populate): handle empty results for populate virtuals properly #4285 #4284
+ * fix(query): dont cast $inc to number if type is long #4283
+ * fix(types): allow setting single nested doc to null #4281
+ * fix(populate): handle deeply nested virtual populate #4278
+ * fix(document): allow setting empty obj if strict mode is false #4274
+ * fix(aggregate): allow passing obj to .unwind() #4239
+ * docs(document): add return statements to transform examples #1963
+
+4.5.3 / 2016-06-30
+==================
+ * fix(query): pass correct options to QueryCursor #4277 #4266
+ * fix(querycursor): handle lean option correctly #4276 [gchudnov](https://github.com/gchudnov)
+ * fix(document): fix error handling when no error occurred #4275
+ * fix(error): use strict mode for version error #4272
+ * docs(populate): fix crashing compilation for populate.jade #4267
+ * fix(populate): support `justOne` option for populate virtuals #4263
+ * fix(populate): ensure model param gets used for populate virtuals #4261 #4243
+ * fix(querycursor): add ability to properly close the cursor #4258
+ * docs(model): correct link to Document #4250
+ * docs(populate): correct path for refPath populate #4240
+ * fix(document): support validator.isEmail as validator #4064
+
+4.5.2 / 2016-06-24
+==================
+ * fix(connection): add checks for collection presence for `onOpen` and `onClose` #4259 [nodkz](https://github.com/nodkz)
+ * fix(cast): allow strings for $type operator #4256
+ * fix(querycursor): support lean() #4255 [pyramation](https://github.com/pyramation)
+ * fix(aggregate): allow setting noCursorTimeout option #4241
+ * fix(document): handle undefined for Array.pull #4222 [Sebmaster](https://github.com/Sebmaster)
+ * fix(connection): ensure promise.catch() catches initial connection error #4135
+ * fix(document): show additional context for VersionError #2633
+
+4.5.1 / 2016-06-18
+==================
+ * fix(model): ensure wrapped insertMany() returns a promise #4237
+ * fix(populate): dont overwrite populateVirtuals when populating multiple paths #4234
+ * docs(model): clarify relationship between create() and save() #4233
+ * fix(types): handle option param in subdoc remove() #4231 [tdebarochez](https://github.com/tdebarochez)
+ * fix(document): dedupe modified paths #4226 #4223 [adambuczynski](https://github.com/adambuczynski)
+ * fix(model): don't modify user-provided options object #4221
+ * fix(document): handle setting nested path to empty object #4218 #4182
+ * fix(document): clean subpaths when removing single nested #4216
+ * fix(document): don't force transform on subdocs with inspect #4213
+ * fix(error): allow setting .messages object #4207
+
+4.5.0 / 2016-06-13
+==================
+ * feat(query): added Query.prototype.catch() #4215 #4173 [adambuczynski](https://github.com/adambuczynski)
+ * feat(query): add Query.prototype.cursor() as a .stream() alternative #4117 #3637 #1907
+ * feat(document): add markUnmodified() function #4092 [vincentcr](https://github.com/vincentcr)
+ * feat(aggregate): convert aggregate object to a thenable #3995 #3946 [megagon](https://github.com/megagon)
+ * perf(types): remove defineProperties call for array (**Note:** Because of this, a mongoose array will no longer `assert.deepEqual()` a plain old JS array) #3886
+ * feat(model): add hooks for insertMany() #3846
+ * feat(schema): add support for custom query methods #3740 #2372
+ * feat(drivers): emit error on 'serverClosed' because that means that reconnect failed #3615
+ * feat(model): emit error event when callback throws exception #3499
+ * feat(model): inherit options from discriminator base schema #3414 #1818
+ * feat(populate): expose mongoose-populate-virtuals inspired populate API #2562
+ * feat(document): trigger remove hooks on subdocs when removing parent #2348
+ * feat(schema): add support for express-style error handling middleware #2284
+ * fix(model): disallow setting discriminator key #2041
+ * feat(schema): add support for nested arrays #1361
+
+4.4.20 / 2016-06-05
+===================
+ * docs: clarify command buffering when using driver directly #4195
+ * fix(promise): correct broken mpromise .catch() #4189
+ * fix(document): clean modified subpaths when set path to empty obj #4182
+ * fix(query): support minDistance with query casting and `.near()` #4179
+ * fix(model): remove unnecessary .save() promise #4177
+ * fix(schema): cast all valid ObjectId strings to object ids #3365
+ * docs: remove unclear "unsafe" term in query docs #3282
+
+4.4.19 / 2016-05-21
+===================
+ * fix(model): handle insertMany if timestamps not set #4171
+
+4.4.18 / 2016-05-21
+===================
+ * docs: add missing period #4170 [gitname](https://github.com/gitname)
+ * docs: change build badge to svg #4158 [a0viedo](https://github.com/a0viedo)
+ * fix(model): update timestamps when setting `createdAt` #4155
+ * fix(utils): make sure to require in document properly #4152
+ * fix(model): throw overwrite error when discriminator name conflicts #4148
+
+4.4.17 / 2016-05-13
+===================
+ * docs: remove repetition in QueryStream docs #4147 [hugoabonizio](https://github.com/hugoabonizio)
+ * fix(document): dont double-validate doc array elements #4145
+ * fix(document): call required function with correct scope #4142 [JedWatson](https://github.com/JedWatson)
+
+4.4.16 / 2016-05-09
+===================
+ * refactor(document): use function reference #4133 [dciccale](https://github.com/dciccale)
+ * docs(querystream): clarify `destroy()` and close event #4126 [AnthonyCC](https://github.com/AnthonyCC)
+ * test: make before hook fail fast if it can't connect #4121
+ * docs: add description of CastError constructor params #4120
+ * fix(schematype): ensure single embedded defaults have $parent #4115
+ * fix(document): mark nested paths for validation #4111
+ * fix(schema): make sure element is always a subdoc in doc array validation #3816
+
+4.4.15 / 2016-05-06
+===================
+ * fix(schema): support overwriting array default #4109
+ * fix(populate): assign values when resolving each populate #4104
+ * fix(aggregate): dont send async option to server #4101
+ * fix(model): ensure isNew set to false after insertMany #4099
+ * fix(connection): emit on error if listeners and no callback #4098
+ * fix(document): treat required fn that returns false as required: false #4094
+
+4.4.14 / 2016-04-27
+===================
+ * fix: upgrade mongodb -> 2.1.18 #4102
+ * feat(connection): allow setting mongos as a uri query param #4093 #4035 [burtonjc](https://github.com/burtonjc)
+ * fix(populate): make sure to use correct assignment order for each model #4073
+ * fix(schema): add complete set of geospatial operators for single embedded subdocs #4014
+
+3.8.40 / 2016-04-24
+===================
+ * upgraded; mquery -> 1.10.0 #3989
+
+4.4.13 / 2016-04-21
+===================
+ * docs: add docs favicons #4082 [robertjustjones](https://github.com/robertjustjones)
+ * docs(model): correct Model.remove() return value #4075 [Jokero](https://github.com/Jokero)
+ * fix(query): add $geoWithin query casting for single embedded docs #4044
+ * fix(schema): handle setting trim option to falsy #4042
+ * fix(query): handle setDefaultsOnInsert with empty update #3835
+
+4.4.12 / 2016-04-08
+===================
+ * docs(query): document context option for update and findOneAndUpdate #4055
+ * docs(query): correct link to $geoWithin docs #4050
+ * fix(project): upgrade to mongodb driver 2.1.16 #4048 [schmalliso](https://github.com/schmalliso)
+ * docs(validation): fix validation docs #4028
+ * fix(types): improve .id() check for document arrays #4011
+ * fix(query): remove premature return when using $rename #3171
+ * docs(connection): clarify relationship between models and connections #2157
+
+4.4.11 / 2016-04-03
+===================
+ * fix: upgrade to mongodb driver 2.1.14 #4036 #4030 #3945
+ * fix(connection): allow connecting with { mongos: true } to handle query params #4032 [burtonjc](https://github.com/burtonjc)
+ * docs(connection): add autoIndex example #4026 [tilleps](https://github.com/tilleps)
+ * fix(query): handle passRawResult option when zero results #4023
+ * fix(populate): clone options before modifying #4022
+ * docs: add missing whitespace #4019 [chenxsan](https://github.com/chenxsan)
+ * chore: upgrade to ESLint 2.4.0 #4015 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix(types): single nested subdocs get ids by default #4008
+ * chore(project): add dependency status badge #4007 [Maheshkumar-Kakade](http://github.com/Maheshkumar-Kakade)
+ * fix: make sure timestamps don't trigger unnecessary updates #4005 #3991 [tommarien](https://github.com/tommarien)
+ * fix(document): inspect inherits schema options #4001
+ * fix(populate): don't mark populated path as modified if setting to object w/ same id #3992
+ * fix(document): support kind argument to invalidate #3965
+
+4.4.10 / 2016-03-24
+===================
+ * fix(document): copy isNew when copying a document #3982
+ * fix(document): don't override defaults with undefined keys #3981
+ * fix(populate): merge multiple deep populate options for the same path #3974
+
+4.4.9 / 2016-03-22
+==================
+ * fix: upgrade mongodb -> 2.1.10 re https://jira.mongodb.org/browse/NODE-679 #4010
+ * docs: add syntax highlighting for acquit examples #3975
+
+4.4.8 / 2016-03-18
+==================
+ * docs(aggregate): clarify promises #3990 [megagon](https://github.com/megagon)
+ * fix: upgrade mquery -> 1.10 #3988 [matskiv](https://github.com/matskiv)
+ * feat(connection): 'all' event for repl sets #3986 [xizhibei](https://github.com/xizhibei)
+ * docs(types): clarify Array.pull #3985 [seriousManual](https://github.com/seriousManual)
+ * feat(query): support array syntax for .sort() via mquery 1.9 #3980
+ * fix(populate): support > 3 level nested populate #3973
+ * fix: MongooseThenable exposes connection correctly #3972
+ * docs(connection): add note about reconnectTries and reconnectInterval #3969
+ * feat(document): invalidate returns the new validationError #3964
+ * fix(query): .eq() as shorthand for .equals #3953 [Fonger](https://github.com/Fonger)
+ * docs(connection): clarify connection string vs passed options #3941
+ * docs(query): select option for findOneAndUpdate #3933
+ * fix(error): ValidationError.properties no longer enumerable #3925
+ * docs(validation): clarify how required validators work with nested schemas #3915
+ * fix: upgrade mongodb driver -> 2.1.8 to make partial index errors more sane #3864
+
+4.4.7 / 2016-03-11
+==================
+ * fix(query): stop infinite recursion caused by merging a mongoose buffer #3961
+ * fix(populate): handle deep populate array -> array #3954
+ * fix(schema): allow setting timestamps with .set() #3952 #3951 #3907 [Fonger](https://github.com/Fonger)
+ * fix: MongooseThenable doesn't overwrite constructors #3940
+ * fix(schema): don't cast boolean to date #3935
+ * fix(drivers): support sslValidate in connection string #3929
+ * fix(types): correct markModified() for single nested subdocs #3910
+ * fix(drivers): catch and report any errors that occur in driver methods #3906
+ * fix(populate): get subpopulate model correctly when array under nested #3904
+ * fix(document): allow fields named 'pre' and 'post' #3902
+ * docs(query): clarify runValidators and setDefaultsOnInsert options #3892
+ * docs(validation): show how to use custom required messages in schema #2616
+
+4.4.6 / 2016-03-02
+==================
+ * fix: upgrade mongodb driver to 2.1.7 #3938
+ * docs: fix plugins link #3917 #3909 [fbertone](https://github.com/fbertone)
+ * fix(query): sort+select with count works #3914
+ * fix(query): improve mergeUpdate's ability to handle nested docs #3890
+
+4.4.5 / 2016-02-24
+==================
+ * fix(query): ability to select a length field (upgrade to mquery 1.7.0) #3903
+ * fix: include nested CastError as reason for array CastError #3897 [kotarou3](https://github.com/kotarou3)
+ * fix(schema): check for doc existence before taking fields #3889
+ * feat(schema): useNestedStrict option to take nested strict mode for update #3883
+ * docs(validation): clarify relationship between required and checkRequired #3822
+ * docs(populate): dynamic reference docs #3809
+ * docs: expand dropdown when clicking on file name #3807
+ * docs: plugins.mongoosejs.io is up #3127
+ * fix(schema): ability to add a virtual with same name as removed path #2398
+
+4.4.4 / 2016-02-17
+==================
+ * fix(schema): handle field selection when casting single nested subdocs #3880
+ * fix(populate): populating using base model with multiple child models in result #3878
+ * fix: ability to properly use return value of `mongoose.connect()` #3874
+ * fix(populate): dont hydrate populated subdoc if lean option set #3873
+ * fix(connection): dont re-auth if already connected with useDb #3871
+ * docs: cover how to set underlying driver's promise lib #3869
+ * fix(document): handle conflicting names in validation errors with subdocs #3867
+ * fix(populate): set undefined instead of null consistently when populate couldn't find results #3859
+ * docs: link to `execPopulate()` in `doc.populate()` docs #3836
+ * docs(plugin): link to the `mongoose.plugin()` function #3732
+
+4.4.3 / 2016-02-09
+==================
+ * fix: upgrade to mongodb 2.1.6 to remove kerberos log output #3861 #3860 [cartuchogl](https://github.com/cartuchogl)
+ * fix: require('mongoose') is no longer a pseudo-promise #3856
+ * fix(query): update casting for single nested docs #3820
+ * fix(populate): deep populating multiple paths with same options #3808
+ * docs(middleware): clarify save/validate hook order #1149
+
+4.4.2 / 2016-02-05
+==================
+ * fix(aggregate): handle calling .cursor() with no options #3855
+ * fix: upgrade mongodb driver to 2.1.5 for GridFS memory leak fix #3854
+ * docs: fix schematype.html conflict #3853 #3850 #3843
+ * fix(model): bluebird unhandled rejection with ensureIndexes() on init #3837
+ * docs: autoIndex option for createConnection #3805
+
+4.4.1 / 2016-02-03
+==================
+ * fix: linting broke some cases where we use `== null` as shorthand #3852
+ * docs: fix up schematype.html conflict #3848 #3843 [mynameiscoffey](https://github.com/mynameiscoffey)
+ * fix: backwards breaking change with `.connect()` return value #3847
+ * docs: downgrade dox and highlight.js to fix docs build #3845
+ * docs: clean up typo #3842 [Flash-](https://github.com/Flash-)
+ * fix(document): storeShard handles undefined values #3841
+ * chore: more linting #3838 [TrejGun](https://github.com/TrejGun)
+ * fix(schema): handle `text: true` as a way to declare a text index #3824
+
+4.4.0 / 2016-02-02
+==================
+ * docs: fix expireAfterSeconds index option name #3831 [Flash-](https://github.com/Flash-)
+ * chore: run lint after test #3829 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * chore: use power-assert instead of assert #3828 [TrejGun](https://github.com/TrejGun)
+ * chore: stricter lint #3827 [TrejGun](https://github.com/TrejGun)
+ * feat(types): casting moment to date #3813 [TrejGun](https://github.com/TrejGun)
+ * chore: comma-last lint for test folder #3810 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix: upgrade async mpath, mpromise, muri, and sliced #3801 [TrejGun](https://github.com/TrejGun)
+ * fix(query): geo queries now return proper ES2015 promises #3800 [TrejGun](https://github.com/TrejGun)
+ * perf(types): use `Object.defineProperties()` for array #3799 [TrejGun](https://github.com/TrejGun)
+ * fix(model): mapReduce, ensureIndexes, remove, and save properly return ES2015 promises #3795 #3628 #3595 [TrejGun](https://github.com/TrejGun)
+ * docs: fixed dates in History.md #3791 [Jokero](https://github.com/Jokero)
+ * feat: connect, open, openSet, and disconnect return ES2015 promises #3790 #3622 [TrejGun](https://github.com/TrejGun)
+ * feat: custom type for int32 via mongoose-int32 npm package #3652 #3102
+ * feat: basic custom schema type API #995
+ * feat(model): `insertMany()` for more performant bulk inserts #723
+
+4.3.7 / 2016-01-23
+==================
+ * docs: grammar fix in timestamps docs #3786 [zclancy](https://github.com/zclancy)
+ * fix(document): setting nested populated docs #3783 [slamuu](https://github.com/slamuu)
+ * fix(document): don't call post save hooks twice for pushed docs #3780
+ * fix(model): handle `_id=0` correctly #3776
+ * docs(middleware): async post hooks #3770
+ * docs: remove confusing sentence #3765 [marcusmellis89](https://github.com/marcusmellis89)
+
+3.8.39 / 2016-01-15
+===================
+ * fixed; casting a number to a buffer #3764
+ * fixed; enumerating virtual property with nested objects #3743 [kusold](https://github.com/kusold)
+
+4.3.6 / 2016-01-15
+==================
+ * fix(types): casting a number to a buffer #3764
+ * fix: add "listener" to reserved keywords #3759
+ * chore: upgrade uglify #3757 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix: broken execPopulate() in 4.3.5 #3755 #3753
+ * fix: ability to remove() a single embedded doc #3754
+ * style: comma-last in test folder #3751 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * docs: clarify versionKey option #3747
+ * fix: improve colorization for arrays #3744 [TrejGun](https://github.com/TrejGun)
+ * fix: webpack build #3713
+
+4.3.5 / 2016-01-09
+==================
+ * fix(query): throw when 4th parameter to update not a function #3741 [kasselTrankos](https://github.com/kasselTrankos)
+ * fix(document): separate error type for setting an object to a primitive #3735
+ * fix(populate): Model.populate returns ES6 promise #3734
+ * fix(drivers): re-register event handlers after manual reconnect #3729
+ * docs: broken links #3727
+ * fix(validation): update validators run array validation #3724
+ * docs: clarify the need to use markModified with in-place date ops #3722
+ * fix(document): mark correct path as populated when manually populating array #3721
+ * fix(aggregate): support for array pipeline argument to append #3718 [dbkup](https://github.com/dbkup)
+ * docs: clarify `.connect()` callback #3705
+ * fix(schema): properly validate nested single nested docs #3702
+ * fix(types): handle setting documentarray of wrong type #3701
+ * docs: broken links #3700
+ * fix(drivers): debug output properly displays '0' #3689
+
+3.8.38 / 2016-01-07
+===================
+ * fixed; aggregate.append an array #3730 [dbkup](https://github.com/dbkup)
+
+4.3.4 / 2015-12-23
+==================
+ * fix: upgrade mongodb driver to 2.1.2 for repl set error #3712 [sansmischevia](https://github.com/sansmischevia)
+ * docs: validation docs typo #3709 [ivanmaeder](https://github.com/ivanmaeder)
+ * style: remove unused variables #3708 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix(schema): duck-typing for schemas #3703 [mgcrea](https://github.com/mgcrea)
+ * docs: connection sample code issue #3697
+ * fix(schema): duck-typing for schemas #3693 [mgcrea](https://github.com/mgcrea)
+ * docs: clarify id schema option #3638
+
+4.3.3 / 2015-12-18
+==================
+ * fix(connection): properly support 'replSet' as well as 'replset' #3688 [taxilian](https://github.com/taxilian)
+ * fix(document): single nested doc pre hooks called before nested doc array #3687 [aliatsis](https://github.com/aliatsis)
+
+4.3.2 / 2015-12-17
+==================
+ * fix(document): .set() into single nested schemas #3686
+ * fix(connection): support 'replSet' as well as 'replset' option #3685
+ * fix(document): bluebird unhandled rejection when validating doc arrays #3681
+ * fix(document): hooks for doc arrays in single nested schemas #3680
+ * fix(document): post hooks for single nested schemas #3679
+ * fix: remove unused npm module #3674 [sybarite](https://github.com/sybarite)
+ * fix(model): don't swallow exceptions in nested doc save callback #3671
+ * docs: update keepAlive info #3667 [ChrisZieba](https://github.com/ChrisZieba)
+ * fix(document): strict 'throw' throws a specific mongoose error #3662
+ * fix: flakey test #3332
+ * fix(query): more robust check for RegExp #2969
+
+4.3.1 / 2015-12-11
+==================
+ * feat(aggregate): `.sample()` helper #3665
+ * fix(query): bitwise query operators with buffers #3663
+ * docs(migration): clarify `new` option and findByIdAndUpdate #3661
+
+4.3.0 / 2015-12-09
+==================
+ * feat(query): support for mongodb 3.2 bitwise query operators #3660
+ * style: use comma-last style consistently #3657 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * feat: upgrade mongodb driver to 2.1.0 for full MongoDB 3.2 support #3656
+ * feat(aggregate): `.lookup()` helper #3532
+
+4.2.10 / 2015-12-08
+===================
+ * fixed; upgraded marked #3653 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * docs; cross-db populate #3648
+ * docs; update mocha URL #3646 [ojhaujjwal](https://github.com/ojhaujjwal)
+ * fixed; call close callback asynchronously #3645
+ * docs; virtuals.html issue #3644 [Psarna94](https://github.com/Psarna94)
+ * fixed; single embedded doc casting on init #3642
+ * docs; validation docs improvements #3640
+
+4.2.9 / 2015-12-02
+==================
+ * docs; defaults docs #3625
+ * fix; nested numeric keys causing an embedded document crash #3623
+ * fix; apply path getters before virtual getters #3618
+ * fix; casting for arrays in single nested schemas #3616
+
+4.2.8 / 2015-11-25
+==================
+ * docs; clean up README links #3612 [ReadmeCritic](https://github.com/ReadmeCritic)
+ * fix; ESLint improvements #3605 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fix; assigning single nested subdocs #3601
+ * docs; describe custom logging functions in `mongoose.set()` docs #3557
+
+4.2.7 / 2015-11-20
+==================
+ * fixed; readPreference connection string option #3600
+ * fixed; pulling from manually populated arrays #3598 #3579
+ * docs; FAQ about OverwriteModelError #3597 [stcruy](https://github.com/stcruy)
+ * fixed; setting single embedded schemas to null #3596
+ * fixed; indexes for single embedded schemas #3594
+ * docs; clarify projection for `findOne()` #3593 [gunar](https://github.com/gunar)
+ * fixed; .ownerDocument() method on single embedded schemas #3589
+ * fixed; properly throw casterror for query on single embedded schema #3580
+ * upgraded; mongodb driver -> 2.0.49 for reconnect issue fix #3481
+
+4.2.6 / 2015-11-16
+==================
+ * fixed; ability to manually populate an array #3575
+ * docs; clarify `isAsync` parameter to hooks #3573
+ * fixed; use captureStackTrace if possible instead #3571
+ * fixed; crash with buffer and update validators #3565 [johnpeb](https://github.com/johnpeb)
+ * fixed; update casting with operators overwrite: true #3564
+ * fixed; validation with single embedded docs #3562
+ * fixed; inline docs inherit parents $type key #3560
+ * docs; bad grammar in populate docs #3559 [amaurymedeiros](https://github.com/amaurymedeiros)
+ * fixed; properly handle populate option for find() #2321
+
+3.8.37 / 2015-11-16
+===================
+ * fixed; use retainKeyOrder for cloning update op #3572
+
+4.2.5 / 2015-11-09
+==================
+ * fixed; handle setting fields in pre update hooks with exec #3549
+ * upgraded; ESLint #3547 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; bluebird unhandled rejections with cast errors and .exec #3543
+ * fixed; min/max validators handling undefined #3539
+ * fixed; standalone mongos connections #3537
+ * fixed; call `.toObject()` when setting a single nested doc #3535
+ * fixed; single nested docs now have methods #3534
+ * fixed; single nested docs with .create() #3533 #3521 [tusbar](https://github.com/tusbar)
+ * docs; deep populate docs #3528
+ * fixed; deep populate schema ref handling #3507
+ * upgraded; mongodb driver -> 2.0.48 for sort overflow issue #3493
+ * docs; clarify default ids for discriminators #3482
+ * fixed; properly support .update(doc) #3221
+
+4.2.4 / 2015-11-02
+==================
+ * fixed; upgraded `ms` package for security vulnerability #3524 [fhemberger](https://github.com/fhemberger)
+ * fixed; ESlint rules #3517 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * docs; typo in aggregation docs #3513 [rafakato](https://github.com/rafakato)
+ * fixed; add `dontThrowCastError` option to .update() for promises #3512
+ * fixed; don't double-cast buffers in node 4.x #3510 #3496
+ * fixed; population with single embedded schemas #3501
+ * fixed; pre('set') hooks work properly #3479
+ * docs; promises guide #3441
+
+4.2.3 / 2015-10-26
+==================
+ * docs; remove unreferenced function in middleware.jade #3506
+ * fixed; handling auth with no username/password #3500 #3498 #3484 [mleanos](https://github.com/mleanos)
+ * fixed; more ESlint rules #3491 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; swallowing exceptions in save callback #3478
+ * docs; fixed broken links in subdocs guide #3477
+ * fixed; casting booleans to numbers #3475
+ * fixed; report CastError for subdoc arrays in findOneAndUpdate #3468
+ * fixed; geoNear returns ES6 promise #3458
+
+4.2.2 / 2015-10-22
+==================
+ * fixed; go back to old pluralization code #3490
+
+4.2.1 / 2015-10-22
+==================
+ * fixed; pluralization issues #3492 [ChristianMurphy](https://github.com/ChristianMurphy)
+
+4.2.0 / 2015-10-22
+==================
+ * added; support for skipVersioning for document arrays #3467 [chazmo03](https://github.com/chazmo03)
+ * added; ability to customize schema 'type' key #3459 #3245
+ * fixed; writeConcern for index builds #3455
+ * added; emit event when individual index build starts #3440 [objectiveSee](https://github.com/objectiveSee)
+ * added; 'context' option for update validators #3430
+ * refactor; pluralization now in separate pluralize-mongoose npm module #3415 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * added; customizable error validation messages #3406 [geronime](https://github.com/geronime)
+ * added; support for passing 'minimize' option to update #3381
+ * added; ability to customize debug logging format #3261
+ * added; baseModelName property for discriminator models #3202
+ * added; 'emitIndexErrors' option #3174
+ * added; 'async' option for aggregation cursor to support buffering #3160
+ * added; ability to skip validation for individual save() calls #2981
+ * added; single embedded schema support #2689 #585
+ * added; depopulate function #2509
+
+4.1.12 / 2015-10-19
+===================
+ * docs; use readPreference instead of slaveOk for Query.setOptions docs #3471 [buunguyen](https://github.com/buunguyen)
+ * fixed; more helpful error when regexp contains null bytes #3456
+ * fixed; x509 auth issue #3454 [NoxHarmonium](https://github.com/NoxHarmonium)
+
+3.8.36 / 2015-10-18
+===================
+ * fixed; Make array props non-enumerable #3461 [boblauer](https://github.com/boblauer)
+
+4.1.11 / 2015-10-12
+===================
+ * fixed; update timestamps for update() if they're enabled #3450 [isayme](https://github.com/isayme)
+ * fixed; unit test error on node 0.10 #3449 [isayme](https://github.com/isayme)
+ * docs; timestamp option docs #3448 [isayme](https://github.com/isayme)
+ * docs; fix unexpected indent #3443 [isayme](https://github.com/isayme)
+ * fixed; use ES6 promises for Model.prototype.remove() #3442
+ * fixed; don't use unused 'safe' option for index builds #3439
+ * fixed; elemMatch casting bug #3437 #3435 [DefinitelyCarter](https://github.com/DefinitelyCarter)
+ * docs; schema.index docs #3434
+ * fixed; exceptions in save() callback getting swallowed on mongodb 2.4 #3371
+
+4.1.10 / 2015-10-05
+===================
+ * docs; improve virtuals docs to explain virtuals schema option #3433 [zoyaH](https://github.com/zoyaH)
+ * docs; MongoDB server version compatibility guide #3427
+ * docs; clarify that findById and findByIdAndUpdate fire hooks #3422
+ * docs; clean up Model.save() docs #3420
+ * fixed; properly handle projection with just id #3407 #3412
+ * fixed; infinite loop when database document is corrupted #3405
+ * docs; clarify remove middleware #3388
+
+4.1.9 / 2015-09-28
+==================
+ * docs; minlength and maxlength string validation docs #3368 #3413 [cosmosgenius](https://github.com/cosmosgenius)
+ * fixed; linting for infix operators #3397 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; proper casting for $all #3394
+ * fixed; unhandled rejection warnings with .create() #3391
+ * docs; clarify update validators on paths that aren't explicitly set #3386
+ * docs; custom validator examples #2778
+
+4.1.8 / 2015-09-21
+==================
+ * docs; fixed typo in example #3390 [kmctown](https://github.com/kmctown)
+ * fixed; error in toObject() #3387 [guumaster](https://github.com/guumaster)
+ * fixed; handling for casting null dates #3383 [alexmingoia](https://github.com/alexmingoia)
+ * fixed; passing composite ids to `findByIdAndUpdate` #3380
+ * fixed; linting #3376 #3375 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; added NodeJS v4 to Travis #3374 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; casting $elemMatch inside of $not #3373 [gaguirre](https://github.com/gaguirre)
+ * fixed; handle case where $slice is 0 #3369
+ * fixed; avoid running getters if path is populated #3357
+ * fixed; cast documents to objects when setting to a nested path #3346
+
+4.1.7 / 2015-09-14
+==================
+ * docs; typos in SchemaType documentation #3367 [jasson15](https://github.com/jasson15)
+ * fixed; MONGOOSE_DRIVER_PATH env variable again #3360
+ * docs; added validateSync docs #3353
+ * fixed; set findOne op synchronously in query #3344
+ * fixed; handling for `.pull()` on a documentarray without an id #3341
+ * fixed; use natural order for cloning update conditions #3338
+ * fixed; issue with strict mode casting for mixed type updates #3337
+
+4.1.6 / 2015-09-08
+==================
+ * fixed; MONGOOSE_DRIVER_PATH env variable #3345 [g13013](https://github.com/g13013)
+ * docs; global autoIndex option #3335 [albertorestifo](https://github.com/albertorestifo)
+ * docs; model documentation typos #3330
+ * fixed; report reason for CastError #3320
+ * fixed; .populate() no longer returns true after re-assigning #3308
+ * fixed; discriminators with aggregation geoNear #3304
+ * docs; discriminator docs #2743
+
+4.1.5 / 2015-09-01
+==================
+ * fixed; document.remove() removing all docs #3326 #3325
+ * fixed; connect() checks for rs_name in options #3299
+ * docs; examples for schema.set() #3288
+ * fixed; checkKeys issue with bluebird #3286 [gregthegeek](https://github.com/gregthegeek)
+
+4.1.4 / 2015-08-31
+==================
+ * fixed; ability to set strict: false for update #3305
+ * fixed; .create() properly uses ES6 promises #3297
+ * fixed; pre hooks on nested subdocs #3291 #3284 [aliatsis](https://github.com/aliatsis)
+ * docs; remove unclear text in .remove() docs #3282
+ * fixed; pre hooks called twice for 3rd-level nested doc #3281
+ * fixed; nested transforms #3279
+ * upgraded; mquery -> 1.6.3 #3278 #3272
+ * fixed; don't swallow callback errors by default #3273 #3222
+ * fixed; properly get nested paths from nested schemas #3265
+ * fixed; remove() with id undefined deleting all docs #3260 [thanpolas](https://github.com/thanpolas)
+ * fixed; handling for non-numeric projections #3256
+ * fixed; findById with id undefined returning first doc #3255
+ * fixed; use retainKeyOrder for update #3215
+ * added; passRawResult option to findOneAndUpdate for backwards compat #3173
+
+4.1.3 / 2015-08-16
+==================
+ * fixed; getUpdate() in pre update hooks #3520 [gregthegeek](https://github.com/gregthegeek)
+ * fixed; handleArray() ensures arg is an array #3238 [jloveridge](https://github.com/jloveridge)
+ * fixed; refresh required path cache when recreating docs #3199
+ * fixed; $ operator on unwind aggregation helper #3197
+ * fixed; findOneAndUpdate() properly returns raw result as third arg to callback #3173
+ * fixed; querystream with dynamic refs #3108
+
+3.8.35 / 2015-08-14
+===================
+ * fixed; handling for minimize on nested objects #2930
+ * fixed; don't crash when schema.path.options undefined #1824
+
+4.1.2 / 2015-08-10
+==================
+ * fixed; better handling for Jade templates #3241 [kbadk](https://github.com/kbadk)
+ * added; ESlint trailing spaces #3234 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * added; ESlint #3191 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; properly emit event on disconnect #3183
+ * fixed; copy options properly using Query.toConstructor() #3176
+ * fixed; setMaxListeners() issue in browser build #3170
+ * fixed; node driver -> 2.0.40 to not store undefined keys as null #3169
+ * fixed; update validators handle positional operator #3167
+ * fixed; handle $all + $elemMatch query casting #3163
+ * fixed; post save hooks don't swallow extra args #3155
+ * docs; spelling mistake in index.jade #3154
+ * fixed; don't crash when toObject() has no fields #3130
+ * fixed; apply toObject() recursively for find and update queries #3086 [naoina](https://github.com/naoina)
+
+4.1.1 / 2015-08-03
+==================
+ * fixed; aggregate exec() crash with no callback #3212 #3198 [jpgarcia](https://github.com/jpgarcia)
+ * fixed; pre init hooks now properly synchronous #3207 [burtonjc](https://github.com/burtonjc)
+ * fixed; updateValidators doesn't flatten dates #3206 #3194 [victorkohl](https://github.com/victorkohl)
+ * fixed; default fields don't make document dirty between saves #3205 [burtonjc](https://github.com/burtonjc)
+ * fixed; save passes 0 as numAffected rather than undefined when no change #3195 [burtonjc](https://github.com/burtonjc)
+ * fixed; better handling for positional operator in update #3185
+ * fixed; use Travis containers #3181 [ChristianMurphy](https://github.com/ChristianMurphy)
+ * fixed; leaked variable #3180 [ChristianMurphy](https://github.com/ChristianMurphy)
+
+4.1.0 / 2015-07-24
+==================
+ * added; `schema.queue()` now public #3193
+ * added; raw result as third parameter to findOneAndX callback #3173
+ * added; ability to run validateSync() on only certain fields #3153
+ * added; subPopulate #3103 [timbur](https://github.com/timbur)
+ * added; $isDefault function on documents #3077
+ * added; additional properties for built-in validator messages #3063 [KLicheR](https://github.com/KLicheR)
+ * added; getQuery() and getUpdate() functions for Query #3013
+ * added; export DocumentProvider #2996
+ * added; ability to remove path from schema #2993 [JohnnyEstilles](https://github.com/JohnnyEstilles)
+ * added; .explain() helper for aggregate #2714
+ * added; ability to specify which ES6-compatible promises library mongoose uses #2688
+ * added; export Aggregate #1910
+
+4.0.8 / 2015-07-20
+==================
+ * fixed; assignment with document arrays #3178 [rosston](https://github.com/rosston)
+ * docs; remove duplicate paragraph #3164 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; improve findOneAndXYZ parameter descriptions #3159 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; add findOneAndRemove to list of supported middleware #3158
+ * docs; clarify ensureIndex #3156
+ * fixed; refuse to save/remove document without id #3118
+ * fixed; hooks next() no longer accidentally returns promise #3104
+ * fixed; strict mode for findOneAndUpdate #2947
+ * added; .min.js.gz file for browser component #2806
+
+3.8.34 / 2015-07-20
+===================
+ * fixed; allow using $rename #3171
+ * fixed; no longer modifies update arguments #3008
+
+4.0.7 / 2015-07-11
+==================
+ * fixed; documentarray id method when using object id #3157 [siboulet](https://github.com/siboulet)
+ * docs; improve findById docs #3147
+ * fixed; update validators handle null properly #3136 [odeke-em](https://github.com/odeke-em)
+ * docs; jsdoc syntax errors #3128 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; fix typo #3126 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; proper formatting in queries.jade #3121 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * docs; correct example for string maxlength validator #3111 [rhmeeuwisse](https://github.com/rhmeeuwisse)
+ * fixed; setDefaultsOnInsert with arrays #3107
+ * docs; LearnBoost -> Automattic in package.json #3099
+ * docs; pre update hook example #3094 [danpe](https://github.com/danpe)
+ * docs; clarify query middleware example #3051
+ * fixed; ValidationErrors in strict mode #3046
+ * fixed; set findOneAndUpdate properties before hooks run #3024
+
+3.8.33 / 2015-07-10
+===================
+ * upgraded; node driver -> 1.4.38
+ * fixed; dont crash when `match` validator undefined
+
+4.0.6 / 2015-06-21
+==================
+ * upgraded; node driver -> 2.0.34 #3087
+ * fixed; apply setters on addToSet, etc #3067 [victorkohl](https://github.com/victorkohl)
+ * fixed; missing semicolons #3065 [sokolikp](https://github.com/sokolikp)
+ * fixed; proper handling for async doc hooks #3062 [gregthegeek](https://github.com/gregthegeek)
+ * fixed; dont set failed populate field to null if other docs are successfully populated #3055 [eloytoro](https://github.com/eloytoro)
+ * fixed; setDefaultsOnInsert with document arrays #3034 [taxilian](https://github.com/taxilian)
+ * fixed; setters fired on array items #3032
+ * fixed; stop validateSync() on first error #3025 [victorkohl](https://github.com/victorkohl)
+ * docs; improve query docs #3016
+ * fixed; always exclude _id when its deselected #3010
+ * fixed; enum validator kind property #3009
+ * fixed; mquery collection names #3005
+ * docs; clarify mongos option #3000
+ * docs; clarify that query builder has a .then() #2995
+ * fixed; race condition in dynamic ref #2992
+
+3.8.31 / 2015-06-20
+===================
+ * fixed; properly handle text search with discriminators and $meta #2166
+
+4.0.5 / 2015-06-05
+==================
+ * fixed; ObjectIds and buffers when mongodb driver is a sibling dependency #3050 #3048 #3040 #3031 #3020 #2988 #2951
+ * fixed; warn user when 'increment' is used in schema #3039
+ * fixed; setDefaultsOnInsert with array in schema #3035
+ * fixed; dont use default Object toString to cast to string #3030
+ * added; npm badge #3020 [odeke-em](https://github.com/odeke-em)
+ * fixed; proper handling for calling .set() with a subdoc #2782
+ * fixed; dont throw cast error when using $rename on non-string path #1845
+
+3.8.30 / 2015-06-05
+===================
+ * fixed; enable users to set all options with tailable() #2883
+
+4.0.4 / 2015-05-28
+==================
+ * docs; findAndModify new parameter correct default value #3012 [JonForest](https://github.com/JonForest)
+ * docs; clarify pluralization rules #2999 [anonmily](https://github.com/anonmily)
+ * fix; discriminators with schema methods #2978
+ * fix; make `isModified` a schema reserved keyword #2975
+ * fix; properly fire setters when initializing path with object #2943
+ * fix; can use `setDefaultsOnInsert` without specifying `runValidators` #2938
+ * fix; always set validation errors `kind` property #2885
+ * upgraded; node driver -> 2.0.33 #2865
+
+3.8.29 / 2015-05-27
+===================
+ * fixed; Handle JSON.stringify properly for nested docs #2990
+
+4.0.3 / 2015-05-13
+==================
+ * upgraded; mquery -> 1.5.1 #2983
+ * docs; clarify context for query middleware #2974
+ * docs; fix missing type -> kind rename in History.md #2961
+ * fixed; broken ReadPreference include on Heroku #2957
+ * docs; correct form for cursor aggregate option #2955
+ * fixed; sync post hooks now properly called after function #2949 #2925
+ * fixed; fix sub-doc validate() function #2929
+ * upgraded; node driver -> 2.0.30 #2926
+ * docs; retainKeyOrder for save() #2924
+ * docs; fix broken class names #2913
+ * fixed; error when using node-clone on a doc #2909
+ * fixed; no more hard references to bson #2908 #2906
+ * fixed; dont overwrite array values #2907 [naoina](https://github.com/naoina)
+ * fixed; use readPreference=primary for findOneAndUpdate #2899 #2823
+ * docs; clarify that update validators only run on $set and $unset #2889
+ * fixed; set kind consistently for built-in validators #2885
+ * docs; single field populated documents #2884
+ * fixed; nested objects are now enumerable #2880 [toblerpwn](https://github.com/toblerpwn)
+ * fixed; properly populate field when ref, lean, stream used together #2841
+ * docs; fixed migration guide jade error #2807
+
+3.8.28 / 2015-05-12
+===================
+ * fixed; proper handling for toJSON options #2910
+ * fixed; dont attach virtuals to embedded docs in update() #2046
+
+4.0.2 / 2015-04-23
+==================
+ * fixed; error thrown when calling .validate() on subdoc not in an array #2902
+ * fixed; rename define() to play nice with webpack #2900 [jspears](https://github.com/jspears)
+ * fixed; pre validate called twice with discriminators #2892
+ * fixed; .inspect() on mongoose.Types #2875
+ * docs; correct callback params for Model.update #2872
+ * fixed; setDefaultsOnInsert now works when runValidators not specified #2870
+ * fixed; Document now wraps EventEmitter.addListener #2867
+ * fixed; call non-hook functions in schema queue #2856
+ * fixed; statics can be mocked out for tests #2848 [ninelb](https://github.com/ninelb)
+ * upgraded; mquery 1.4.0 for bluebird bug fix #2846
+ * fixed; required validators run first #2843
+ * docs; improved docs for new option to findAndMody #2838
+ * docs; populate example now uses correct field #2837 [swilliams](https://github.com/swilliams)
+ * fixed; pre validate changes causing VersionError #2835
+ * fixed; get path from correct place when setting CastError #2832
+ * docs; improve docs for Model.update() function signature #2827 [irnc](https://github.com/irnc)
+ * fixed; populating discriminators #2825 [chetverikov](https://github.com/chetverikov)
+ * fixed; discriminators with nested schemas #2821
+ * fixed; CastErrors with embedded docs #2819
+ * fixed; post save hook context #2816
+ * docs; 3.8.x -> 4.x migration guide #2807
+ * fixed; proper _distinct copying for query #2765 [cdelauder](https://github.com/cdelauder)
+
+3.8.27 / 2015-04-22
+===================
+ * fixed; dont duplicate db calls on Q.ninvoke() #2864
+ * fixed; Model.find arguments naming in docs #2828
+ * fixed; Support ipv6 in connection strings #2298
+
+3.8.26 / 2015-04-07
+===================
+ * fixed; TypeError when setting date to undefined #2833
+ * fixed; handle CastError properly in distinct() with no callback #2786
+ * fixed; broken links in queries docs #2779
+ * fixed; dont mark buffer as modified when setting type initially #2738
+ * fixed; dont crash when using slice with populate #1934
+
+4.0.1 / 2015-03-28
+==================
+ * fixed; properly handle empty cast doc in update() with promises #2796
+ * fixed; unstable warning #2794
+ * fixed; findAndModify docs now show new option is false by default #2793
+
+4.0.0 / 2015-03-25
+==================
+ * fixed; on-the-fly schema docs typo #2783 [artiifix](https://github.com/artiifix)
+ * fixed; cast error validation handling #2775 #2766 #2678
+ * fixed; discriminators with populate() #2773 #2719 [chetverikov](https://github.com/chetverikov)
+ * fixed; increment now a reserved path #2709
+ * fixed; avoid sending duplicate object ids in populate() #2683
+ * upgraded; mongodb to 2.0.24 to properly emit reconnect event multiple times #2656
+
+4.0.0-rc4 / 2015-03-14
+======================
+ * fixed; toObject virtuals schema option handled properly #2751
+ * fixed; update validators work on document arrays #2733
+ * fixed; check for cast errors on $set #2729
+ * fixed; instance field set for all schema types #2727 [csdco](https://github.com/csdco)
+ * fixed; dont run other validators if required fails #2725
+ * fixed; custom getters execute on ref paths #2610
+ * fixed; save defaults if they were set when doc was loaded from db #2558
+ * fixed; pre validate now runs before pre save #2462
+ * fixed; no longer throws errors with --use_strict #2281
+
+3.8.25 / 2015-03-13
+===================
+ * fixed; debug output reverses order of aggregation keys #2759
+ * fixed; $eq is a valid query selector in 3.0 #2752
+ * fixed; upgraded node driver to 1.4.32 for handling non-numeric poolSize #2682
+ * fixed; update() with overwrite sets _id for nested docs #2658
+ * fixed; casting for operators in $elemMatch #2199
+
+4.0.0-rc3 / 2015-02-28
+======================
+ * fixed; update() pre hooks run before validators #2706
+ * fixed; setters not called on arrays of refs #2698 [brandom](https://github.com/brandom)
+ * fixed; use node driver 2.0.18 for nodejs 0.12 support #2685
+ * fixed; comments reference file that no longer exists #2681
+ * fixed; populated() returns _id of manually populated doc #2678
+ * added; ability to exclude version key in toObject() #2675
+ * fixed; dont allow setting nested path to a string #2592
+ * fixed; can cast objects with _id field to ObjectIds #2581
+ * fixed; on-the-fly schema getters #2360
+ * added; strict option for findOneAndUpdate() #1967
+
+3.8.24 / 2015-02-25
+===================
+ * fixed; properly apply child schema transforms #2691
+ * fixed; make copy of findOneAndUpdate options before modifying #2687
+ * fixed; apply defaults when parent path is selected #2670 #2629
+ * fixed; properly get ref property for nested paths #2665
+ * fixed; node driver makes copy of authenticate options before modifying them #2619
+ * fixed; dont block process exit when auth fails #2599
+ * fixed; remove redundant clone in update() #2537
+
+4.0.0-rc2 / 2015-02-10
+======================
+ * added; io.js to travis build
+ * removed; browser build dependencies not installed by default
+ * added; dynamic refpaths #2640 [chetverikov](https://github.com/chetverikov)
+ * fixed; dont call child schema transforms on parent #2639 [chetverikov](https://github.com/chetverikov)
+ * fixed; get rid of remove option if new is set in findAndModify #2598
+ * fixed; aggregate all document array validation errors #2589
+ * fixed; custom setters called when setting value to undefined #1892
+
+3.8.23 / 2015-02-06
+===================
+ * fixed; unset opts.remove when upsert is true #2519
+ * fixed; array saved as object when path is object in array #2442
+ * fixed; inline transforms #2440
+ * fixed; check for callback in count() #2204
+ * fixed; documentation for selecting fields #1534
+
+4.0.0-rc1 / 2015-02-01
+======================
+ * fixed; use driver 2.0.14
+ * changed; use transform: true by default #2245
+
+4.0.0-rc0 / 2015-01-31
+===================
+ * fixed; wrong order for distinct() params #2628
+ * fixed; handling no query argument to remove() #2627
+ * fixed; createModel and discriminators #2623 [ashaffer](https://github.com/ashaffer)
+ * added; pre('count') middleware #2621
+ * fixed; double validation calls on document arrays #2618
+ * added; validate() catches cast errors #2611
+ * fixed; respect replicaSet parameter in connection string #2609
+ * added; can explicitly exclude paths from versioning #2576 [csabapalfi](https://github.com/csabapalfi)
+ * upgraded; driver to 2.0.15 #2552
+ * fixed; save() handles errors more gracefully in ES6 #2371
+ * fixed; undefined is now a valid argument to findOneAndUpdate #2272
+ * changed; `new` option to findAndModify ops is false by default #2262
+
+3.8.22 / 2015-01-24
+===================
+ * upgraded; node-mongodb-native to 1.4.28 #2587 [Climax777](https://github.com/Climax777)
+ * added; additional documentation for validators #2449
+ * fixed; stack overflow when creating massive arrays #2423
+ * fixed; undefined is a valid id for queries #2411
+ * fixed; properly create nested schema index when same schema used twice #2322
+ * added; link to plugin generator in docs #2085 [huei90](https://github.com/huei90)
+ * fixed; optional arguments documentation for findOne() #1971 [nachinius](https://github.com/nachinius)
+
+3.9.7 / 2014-12-19
+===================
+ * added; proper cursors for aggregate #2539 [changyy](https://github.com/changyy)
+ * added; min/max built-in validators for dates #2531 [bshamblen](https://github.com/bshamblen)
+ * fixed; save and validate are now reserved keywords #2380
+ * added; basic documentation for browser component #2256
+ * added; find and findOne hooks (query middleware) #2138
+ * fixed; throw a DivergentArrayError when saving positional operator queries #2031
+ * added; ability to use options as a document property #1416
+ * fixed; document no longer inherits from event emitter and so domain and _events are no longer reserved #1351
+ * removed; setProfiling #1349
+
+3.8.21 / 2014-12-18
+===================
+ * fixed; syntax in index.jade #2517 [elderbas](https://github.com/elderbas)
+ * fixed; writable statics #2510 #2528
+ * fixed; overwrite and explicit $set casting #2515
+
+3.9.6 / 2014-12-05
+===================
+ * added; correctly run validators on each element of array when entire array is modified #661 #1227
+ * added; castErrors in validation #1013 [jondavidjohn](https://github.com/jondavidjohn)
+ * added; specify text indexes in schema fields #1401 [sr527](https://github.com/sr527)
+ * added; ability to set field with validators to undefined #1594 [alabid](https://github.com/alabid)
+ * added; .create() returns an array when passed an array #1746 [alabid](https://github.com/alabid)
+ * added; test suite and docs for use with co and yield #2177 #2474
+ * fixed; subdocument toObject() transforms #2447 [chmanie](https://github.com/chmanie)
+ * fixed; Model.create() with save errors #2484
+ * added; pass options to .save() and .remove() #2494 [jondavidjohn](https://github.com/jondavidjohn)
+
+3.8.20 / 2014-12-01
+===================
+ * fixed; recursive readPref #2490 [kjvalencik](https://github.com/kjvalencik)
+ * fixed; make sure to copy parameters to update() before modifying #2406 [alabid](https://github.com/alabid)
+ * fixed; unclear documentation about query callbacks #2319
+ * fixed; setting a schema-less field to an empty object #2314 [alabid](https://github.com/alabid)
+ * fixed; registering statics and methods for discriminators #2167 [alabid](https://github.com/alabid)
+
+3.9.5 / 2014-11-10
+===================
+ * added; ability to disable autoIndex on a per-connection basis #1875 [sr527](https://github.com/sr527)
+ * fixed; `geoNear()` no longer enforces legacy coordinate pairs - supports GeoJSON #1987 [alabid](https://github.com/alabid)
+ * fixed; browser component works when minified with mangled variable names #2302
+ * fixed; `doc.errors` now cleared before `validate()` called #2302
+ * added; `execPopulate()` function to make `doc.populate()` compatible with promises #2317
+ * fixed; `count()` no longer throws an error when used with `sort()` #2374
+ * fixed; `save()` no longer recursively calls `save()` on populated fields #2418
+
+3.8.19 / 2014-11-09
+===================
+ * fixed; make sure to not override subdoc _ids on find #2276 [alabid](https://github.com/alabid)
+ * fixed; exception when comparing two documents when one lacks _id #2333 [slawo](https://github.com/slawo)
+ * fixed; getters for properties with non-strict schemas #2439 [alabid](https://github.com/alabid)
+ * fixed; inconsistent URI format in docs #2414 [sr527](https://github.com/sr527)
+
+3.9.4 / 2014-10-25
+==================
+ * fixed; statics no longer can be overwritten #2343 [nkcmr](https://github.com/chetverikov)
+ * added; ability to set single populated paths to documents #1530
+ * added; setDefaultsOnInsert and runValidator options for findOneAndUpdate() #860
+
+3.8.18 / 2014-10-22
+==================
+ * fixed; Dont use all toObject options in save #2340 [chetverikov](https://github.com/chetverikov)
+
+3.9.3 / 2014-10-01
+=================
+ * added; support for virtuals that return objects #2294
+ * added; ability to manually hydrate POJOs into mongoose objects #2292
+ * added; setDefaultsOnInsert and runValidator options for update() #860
+
+3.8.17 / 2014-09-29
+==================
+ * fixed; use schema options retainKeyOrder in save() #2274
+ * fixed; fix skip in populate when limit is set #2252
+ * fixed; fix stack overflow when passing MongooseArray to findAndModify #2214
+ * fixed; optimize .length usage in populate #2289
+
+3.9.2 / 2014-09-08
+==================
+ * added; test coverage for browser component #2255
+ * added; in-order execution of validators #2243
+ * added; custom fields for validators #2132
+ * removed; exception thrown when find() used with count() #1950
+
+3.8.16 / 2014-09-08
+==================
+ * fixed; properly remove modified array paths if array has been overwritten #1638
+ * fixed; key check errors #1884
+ * fixed; make sure populate on an array always returns a Mongoose array #2214
+ * fixed; SSL connections with node 0.11 #2234
+ * fixed; return sensible strings for promise errors #2239
+
+3.9.1 / 2014-08-17
+==================
+ * added; alpha version of browser-side schema validation #2254
+ * added; support passing a function to schemas `required` field #2247
+ * added; support for setting updatedAt and createdAt timestamps #2227
+ * added; document.validate() returns a promise #2131
+
+3.8.15 / 2014-08-17
+==================
+ * fixed; Replica set connection string example in docs #2246
+ * fixed; bubble up parseError event #2229
+ * fixed; removed buggy populate cache #2176
+ * fixed; dont $inc versionKey if its being $set #1933
+ * fixed; cast $or and $and in $pull #1932
+ * fixed; properly cast to schema in stream() #1862
+ * fixed; memory leak in nested objects #1565 #2211 [devongovett](https://github.com/devongovett)
+
+3.8.14 / 2014-07-26
+==================
+ * fixed; stringifying MongooseArray shows nested arrays #2002
+ * fixed; use populated doc schema in toObject and toJSON by default #2035
+ * fixed; dont crash on arrays containing null #2140
+ * fixed; model.update w/ upsert has same return values on .exec and promise #2143
+ * fixed; better handling for populate limit with multiple documents #2151
+ * fixed; dont prevent users from adding weights to text index #2183
+ * fixed; helper for aggregation cursor #2187
+ * updated; node-mongodb-native to 1.4.7
+
+3.8.13 / 2014-07-15
+==================
+ * fixed; memory leak with isNew events #2159
+ * fixed; docs for overwrite option for update() #2144
+ * fixed; storeShard() handles dates properly #2127
+ * fixed; sub-doc changes not getting persisted to db after save #2082
+ * fixed; populate with _id: 0 actually removes _id instead of setting to undefined #2123
+ * fixed; save versionKey on findOneAndUpdate w/ upsert #2122
+ * fixed; fix typo in 2.8 docs #2120 [shakirullahi](https://github.com/shakirullahi)
+ * fixed; support maxTimeMs #2102 [yuukinajima](https://github.com/yuukinajima)
+ * fixed; support $currentDate #2019
+ * fixed; $addToSet handles objects without _ids properly #1973
+ * fixed; dont crash on invalid nearSphere query #1874
+
+3.8.12 / 2014-05-30
+==================
+ * fixed; single-server reconnect event fires #1672
+ * fixed; sub-docs not saved when pushed into populated array #1794
+ * fixed; .set() sometimes converts embedded docs to pojos #1954 [archangel-irk](https://github.com/archangel-irk)
+ * fixed; sub-doc changes not getting persisted to db after save #2082
+ * fixed; custom getter might cause mongoose to mistakenly think a path is dirty #2100 [pgherveou](https://github.com/pgherveou)
+ * fixed; chainable helper for allowDiskUse option in aggregation #2114
+
+3.9.0 (unstable) / 2014-05-22
+==================
+ * changed; added `domain` to reserved keywords #1338 #2052 [antoinepairet](https://github.com/antoinepairet)
+ * added; asynchronous post hooks #1977 #2081 [chopachom](https://github.com/chopachom) [JasonGhent](https://github.com/JasonGhent)
+ * added; using model for population, cross-db populate [mihai-chiorean](https://github.com/mihai-chiorean)
+ * added; can define a type for schema validators
+ * added; `doc.remove()` returns a promise #1619 [refack](https://github.com/refack)
+ * added; internal promises for hooks, pre-save hooks run in parallel #1732 [refack](https://github.com/refack)
+ * fixed; geoSearch hanging when no results returned #1846 [ghartnett](https://github.com/ghartnett)
+ * fixed; do not set .type property on ValidationError, use .kind instead #1323
+
+3.8.11 / 2014-05-22
+==================
+ * updated; node-mongodb-native to 1.4.5
+ * reverted; #2052, fixes #2097
+
+3.8.10 / 2014-05-20
+==================
+
+ * updated; node-mongodb-native to 1.4.4
+ * fixed; _.isEqual false negatives bug in js-bson #2070
+ * fixed; missing check for schema.options #2014
+ * fixed; missing support for $position #2024
+ * fixed; options object corruption #2049
+ * fixed; improvements to virtuals docs #2055
+ * fixed; added `domain` to reserved keywords #2052 #1338
+
+3.8.9 / 2014-05-08
+==================
+
+ * updated; mquery to 0.7.0
+ * updated; node-mongodb-native to 1.4.3
+ * fixed; $near failing against MongoDB 2.6
+ * fixed; relying on .options() to determine if collection exists
+ * fixed; $out aggregate helper
+ * fixed; all test failures against MongoDB 2.6.1, with caveat #2065
+
+3.8.8 / 2014-02-22
+==================
+
+ * fixed; saving Buffers #1914
+ * updated; expose connection states for user-land #1926 [yorkie](https://github.com/yorkie)
+ * updated; mquery to 0.5.3
+ * updated; added get / set to reserved path list #1903 [tstrimple](https://github.com/tstrimple)
+ * docs; README code highlighting, syntax fixes #1930 [IonicaBizau](https://github.com/IonicaBizau)
+ * docs; fixes link in the doc at #1925 [kapeels](https://github.com/kapeels)
+ * docs; add a missed word 'hook' for the description of the post-hook api #1924 [ipoval](https://github.com/ipoval)
+
+3.8.7 / 2014-02-09
+==================
+
+ * fixed; sending safe/read options in Query#exec #1895
+ * fixed; findOneAnd..() with sort #1887
+
+3.8.6 / 2014-01-30
+==================
+
+ * fixed; setting readPreferences #1895
+
+3.8.5 / 2014-01-23
+==================
+
+ * fixed; ssl setting when using URI #1882
+ * fixed; findByIdAndUpdate now respects the overwrite option #1809 [owenallenaz](https://github.com/owenallenaz)
+
+3.8.4 / 2014-01-07
+==================
+
+ * updated; mongodb driver to 1.3.23
+ * updated; mquery to 0.4.1
+ * updated; mpromise to 0.4.3
+ * fixed; discriminators now work when selecting fields #1820 [daemon1981](https://github.com/daemon1981)
+ * fixed; geoSearch with no results timeout #1846 [ghartnett](https://github.com/ghartnett)
+ * fixed; infitite recursion in ValidationError #1834 [chetverikov](https://github.com/chetverikov)
+
+3.8.3 / 2013-12-17
+==================
+
+ * fixed; setting empty array with model.update #1838
+ * docs; fix url
+
+3.8.2 / 2013-12-14
+==================
+
+ * fixed; enum validation of multiple values #1778 [heroicyang](https://github.com/heroicyang)
+ * fixed; global var leak #1803
+ * fixed; post remove now fires on subdocs #1810
+ * fixed; no longer set default empty array for geospatial-indexed fields #1668 [shirish87](https://github.com/shirish87)
+ * fixed; model.stream() not hydrating discriminators correctly #1792 [j](https://github.com/j)
+ * docs: Stablility -> Stability [nikmartin](https://github.com/nikmartin)
+ * tests; improve shard error handling
+
+3.8.1 / 2013-11-19
+==================
+
+ * fixed; mishandling of Dates with minimize/getters #1764
+ * fixed; Normalize bugs.email, so `npm` will shut up #1769 [refack](https://github.com/refack)
+ * docs; Improve the grammar where "lets us" was used #1777 [alexyoung](https://github.com/alexyoung)
+ * docs; Fix some grammatical issues in the documentation #1777 [alexyoung](https://github.com/alexyoung)
+ * docs; fix Query api exposure
+ * docs; fix return description
+ * docs; Added Notes on findAndUpdate() #1750 [sstadelman](https://github.com/sstadelman)
+ * docs; Update version number in README #1762 [Fodi69](https://github.com/Fodi69)
+
+3.8.0 / 2013-10-31
+==================
+
+ * updated; warn when using an unstable version
+ * updated; error message returned in doc.save() #1595
+ * updated; mongodb driver to 1.3.19 (fix error swallowing behavior)
+ * updated; mquery to 0.3.2
+ * updated; mocha to 1.12.0
+ * updated; mpromise 0.3.0
+ * updated; sliced 0.0.5
+ * removed; mongoose.Error.DocumentError (never used)
+ * removed; namedscope (undocumented and broken) #679 #642 #455 #379
+ * changed; no longer offically supporting node 0.6.x
+ * changed; query.within getter is now a method -> query.within()
+ * changed; query.intersects getter is now a method -> query.intersects()
+ * added; custom error msgs for built-in validators #747
+ * added; discriminator support #1647 #1003 [j](https://github.com/j)
+ * added; support disabled collection name pluralization #1350 #1707 [refack](https://github.com/refack)
+ * added; support for GeoJSON to Query#near [ebensing](https://github.com/ebensing)
+ * added; stand-alone base query support - query.toConstructor() [ebensing](https://github.com/ebensing)
+ * added; promise support to geoSearch #1614 [ebensing](https://github.com/ebensing)
+ * added; promise support for geoNear #1614 [ebensing](https://github.com/ebensing)
+ * added; connection.useDb() #1124 [ebensing](https://github.com/ebensing)
+ * added; promise support to model.mapReduce()
+ * added; promise support to model.ensureIndexes()
+ * added; promise support to model.populate()
+ * added; benchmarks [ebensing](https://github.com/ebensing)
+ * added; publicly exposed connection states #1585
+ * added; $geoWithin support #1529 $1455 [ebensing](https://github.com/ebensing)
+ * added; query method chain validation
+ * added; model.update `overwrite` option
+ * added; model.geoNear() support #1563 [ebensing](https://github.com/ebensing)
+ * added; model.geoSearch() support #1560 [ebensing](https://github.com/ebensing)
+ * added; MongooseBuffer#subtype()
+ * added; model.create() now returns a promise #1340
+ * added; support for `awaitdata` query option
+ * added; pass the doc to doc.remove() callback #1419 [JoeWagner](https://github.com/JoeWagner)
+ * added; aggregation query builder #1404 [njoyard](https://github.com/njoyard)
+ * fixed; document.toObject when using `minimize` and `getters` options #1607 [JedWatson](https://github.com/JedWatson)
+ * fixed; Mixed types can now be required #1722 [Reggino](https://github.com/Reggino)
+ * fixed; do not pluralize model names not ending with letters #1703 [refack](https://github.com/refack)
+ * fixed; repopulating modified populated paths #1697
+ * fixed; doc.equals() when _id option is set to false #1687
+ * fixed; strict mode warnings #1686
+ * fixed; $near GeoJSON casting #1683
+ * fixed; nearSphere GeoJSON query builder
+ * fixed; population field selection w/ strings #1669
+ * fixed; setters not firing on null values #1445 [ebensing](https://github.com/ebensing)
+ * fixed; handle another versioning edge case #1520
+ * fixed; excluding subdocument fields #1280 [ebensing](https://github.com/ebensing)
+ * fixed; allow array properties to be set to null with findOneAndUpdate [aheuermann](https://github.com/aheuermann)
+ * fixed; subdocuments now use own toJSON opts #1376 [ebensing](https://github.com/ebensing)
+ * fixed; model#geoNear fulfills promise when results empty #1658 [ebensing](https://github.com/ebensing)
+ * fixed; utils.merge no longer overrides props and methods #1655 [j](https://github.com/j)
+ * fixed; subdocuments now use their own transform #1412 [ebensing](https://github.com/ebensing)
+ * fixed; model.remove() removes only what is necessary #1649
+ * fixed; update() now only runs with cb or explicit true #1644
+ * fixed; casting ref docs on creation #1606 [ebensing](https://github.com/ebensing)
+ * fixed; model.update "overwrite" option works as documented
+ * fixed; query#remove() works as documented
+ * fixed; "limit" correctly applies to individual items on population #1490 [ebensing](https://github.com/ebensing)
+ * fixed; issue with positional operator on ref docs #1572 [ebensing](https://github.com/ebensing)
+ * fixed; benchmarks to actually output valid json
+ * deprecated; promise#addBack (use promise#onResolve)
+ * deprecated; promise#complete (use promise#fulfill)
+ * deprecated; promise#addCallback (use promise#onFulFill)
+ * deprecated; promise#addErrback (use promise#onReject)
+ * deprecated; query.nearSphere() (use query.near)
+ * deprecated; query.center() (use query.circle)
+ * deprecated; query.centerSphere() (use query.circle)
+ * deprecated; query#slaveOk (use query#read)
+ * docs; custom validator messages
+ * docs; 10gen -> MongoDB
+ * docs; add Date method caveats #1598
+ * docs; more validation details
+ * docs; state which branch is stable/unstable
+ * docs; mention that middleware does not run on Models
+ * docs; promise.fulfill()
+ * docs; fix readme spelling #1483 [yorchopolis](https://github.com/yorchopolis)
+ * docs; fixed up the README and examples [ebensing](https://github.com/ebensing)
+ * website; add "show code" for properties
+ * website; move "show code" links down
+ * website; update guide
+ * website; add unstable docs
+ * website; many improvements
+ * website; fix copyright #1439
+ * website; server.js -> static.js #1546 [nikmartin](https://github.com/nikmartin)
+ * tests; refactor 1703
+ * tests; add test generator
+ * tests; validate formatMessage() throws
+ * tests; add script for continuously running tests
+ * tests; fixed versioning tests
+ * tests; race conditions in tests
+ * tests; added for nested and/or queries
+ * tests; close some test connections
+ * tests; validate db contents
+ * tests; remove .only
+ * tests; close some test connections
+ * tests; validate db contents
+ * tests; remove .only
+ * tests; replace deprecated method names
+ * tests; convert id to string
+ * tests; fix sharding tests for MongoDB 2.4.5
+ * tests; now 4-5 seconds faster
+ * tests; fix race condition
+ * make; suppress warning msg in test
+ * benchmarks; updated for pull requests
+ * examples; improved and expanded [ebensing](https://github.com/ebensing)
+
+3.7.4 (unstable) / 2013-10-01
+=============================
+
+ * updated; mquery to 0.3.2
+ * removed; mongoose.Error.DocumentError (never used)
+ * added; custom error msgs for built-in validators #747
+ * added; discriminator support #1647 #1003 [j](https://github.com/j)
+ * added; support disabled collection name pluralization #1350 #1707 [refack](https://github.com/refack)
+ * fixed; do not pluralize model names not ending with letters #1703 [refack](https://github.com/refack)
+ * fixed; repopulating modified populated paths #1697
+ * fixed; doc.equals() when _id option is set to false #1687
+ * fixed; strict mode warnings #1686
+ * fixed; $near GeoJSON casting #1683
+ * fixed; nearSphere GeoJSON query builder
+ * fixed; population field selection w/ strings #1669
+ * docs; custom validator messages
+ * docs; 10gen -> MongoDB
+ * docs; add Date method caveats #1598
+ * docs; more validation details
+ * website; add "show code" for properties
+ * website; move "show code" links down
+ * tests; refactor 1703
+ * tests; add test generator
+ * tests; validate formatMessage() throws
+
+3.7.3 (unstable) / 2013-08-22
+=============================
+
+ * updated; warn when using an unstable version
+ * updated; mquery to 0.3.1
+ * updated; mocha to 1.12.0
+ * updated; mongodb driver to 1.3.19 (fix error swallowing behavior)
+ * changed; no longer offically supporting node 0.6.x
+ * added; support for GeoJSON to Query#near [ebensing](https://github.com/ebensing)
+ * added; stand-alone base query support - query.toConstructor() [ebensing](https://github.com/ebensing)
+ * added; promise support to geoSearch #1614 [ebensing](https://github.com/ebensing)
+ * added; promise support for geoNear #1614 [ebensing](https://github.com/ebensing)
+ * fixed; setters not firing on null values #1445 [ebensing](https://github.com/ebensing)
+ * fixed; handle another versioning edge case #1520
+ * fixed; excluding subdocument fields #1280 [ebensing](https://github.com/ebensing)
+ * fixed; allow array properties to be set to null with findOneAndUpdate [aheuermann](https://github.com/aheuermann)
+ * fixed; subdocuments now use own toJSON opts #1376 [ebensing](https://github.com/ebensing)
+ * fixed; model#geoNear fulfills promise when results empty #1658 [ebensing](https://github.com/ebensing)
+ * fixed; utils.merge no longer overrides props and methods #1655 [j](https://github.com/j)
+ * fixed; subdocuments now use their own transform #1412 [ebensing](https://github.com/ebensing)
+ * make; suppress warning msg in test
+ * docs; state which branch is stable/unstable
+ * docs; mention that middleware does not run on Models
+ * tests; add script for continuously running tests
+ * tests; fixed versioning tests
+ * benchmarks; updated for pull requests
+
+3.7.2 (unstable) / 2013-08-15
+==================
+
+ * fixed; model.remove() removes only what is necessary #1649
+ * fixed; update() now only runs with cb or explicit true #1644
+ * tests; race conditions in tests
+ * website; update guide
+
+3.7.1 (unstable) / 2013-08-13
+=============================
+
+ * updated; driver to 1.3.18 (fixes memory leak)
+ * added; connection.useDb() #1124 [ebensing](https://github.com/ebensing)
+ * added; promise support to model.mapReduce()
+ * added; promise support to model.ensureIndexes()
+ * added; promise support to model.populate()
+ * fixed; casting ref docs on creation #1606 [ebensing](https://github.com/ebensing)
+ * fixed; model.update "overwrite" option works as documented
+ * fixed; query#remove() works as documented
+ * fixed; "limit" correctly applies to individual items on population #1490 [ebensing](https://github.com/ebensing)
+ * fixed; issue with positional operator on ref docs #1572 [ebensing](https://github.com/ebensing)
+ * fixed; benchmarks to actually output valid json
+ * tests; added for nested and/or queries
+ * tests; close some test connections
+ * tests; validate db contents
+ * tests; remove .only
+ * tests; close some test connections
+ * tests; validate db contents
+ * tests; remove .only
+ * tests; replace deprecated method names
+ * tests; convert id to string
+ * docs; promise.fulfill()
+
+3.7.0 (unstable) / 2013-08-05
+===================
+
+ * changed; query.within getter is now a method -> query.within()
+ * changed; query.intersects getter is now a method -> query.intersects()
+ * deprecated; promise#addBack (use promise#onResolve)
+ * deprecated; promise#complete (use promise#fulfill)
+ * deprecated; promise#addCallback (use promise#onFulFill)
+ * deprecated; promise#addErrback (use promise#onReject)
+ * deprecated; query.nearSphere() (use query.near)
+ * deprecated; query.center() (use query.circle)
+ * deprecated; query.centerSphere() (use query.circle)
+ * deprecated; query#slaveOk (use query#read)
+ * removed; namedscope (undocumented and broken) #679 #642 #455 #379
+ * added; benchmarks [ebensing](https://github.com/ebensing)
+ * added; publicly exposed connection states #1585
+ * added; $geoWithin support #1529 $1455 [ebensing](https://github.com/ebensing)
+ * added; query method chain validation
+ * added; model.update `overwrite` option
+ * added; model.geoNear() support #1563 [ebensing](https://github.com/ebensing)
+ * added; model.geoSearch() support #1560 [ebensing](https://github.com/ebensing)
+ * added; MongooseBuffer#subtype()
+ * added; model.create() now returns a promise #1340
+ * added; support for `awaitdata` query option
+ * added; pass the doc to doc.remove() callback #1419 [JoeWagner](https://github.com/JoeWagner)
+ * added; aggregation query builder #1404 [njoyard](https://github.com/njoyard)
+ * updated; integrate mquery #1562 [ebensing](https://github.com/ebensing)
+ * updated; error msg in doc.save() #1595
+ * updated; bump driver to 1.3.15
+ * updated; mpromise 0.3.0
+ * updated; sliced 0.0.5
+ * tests; fix sharding tests for MongoDB 2.4.5
+ * tests; now 4-5 seconds faster
+ * tests; fix race condition
+ * docs; fix readme spelling #1483 [yorchopolis](https://github.com/yorchopolis)
+ * docs; fixed up the README and examples [ebensing](https://github.com/ebensing)
+ * website; add unstable docs
+ * website; many improvements
+ * website; fix copyright #1439
+ * website; server.js -> static.js #1546 [nikmartin](https://github.com/nikmartin)
+ * examples; improved and expanded [ebensing](https://github.com/ebensing)
3.6.20 (stable) / 2013-09-23
===================
@@ -1448,7 +2884,7 @@
1.3.1 / 2011-04-27
===================
- * fixed; setting a property on an embedded array no longer overwrites entire array (GH-310)
+ * fixed; setting a property on an embedded array no longer overwrites entire array (GH-310)
* fixed; setting nested properties works when sibling prop is named "type"
* fixed; isModified is now much finer grained when .set() is used (GH-323)
* fixed; mongoose.model() and connection.model() now return the Model (GH-308, GH-305)
@@ -1557,7 +2993,7 @@
* Changed; Make sure to only ensure indexes while connected
-1.1.9 / 2011-03-2
+1.1.9 / 2011-03-2
==================
* Fixed; Mixed can now default to empty arrays
@@ -1615,7 +3051,7 @@
* Restored Query#exec and added notion of default operation [brian]
* Fixed ValidatorError messages [brian]
-1.1.1 / 2011-03-01
+1.1.1 / 2011-03-01
==================
* Added SchemaType String `lowercase`, `uppercase`, `trim`.
@@ -1650,28 +3086,28 @@
* Fixed embedded documents saving.
-1.0.12 / 2011-02-14
+1.0.12 / 2011-02-14
===================
* Minor refactorings [brian]
-1.0.11 / 2011-02-14
+1.0.11 / 2011-02-14
===================
* Query refactor and $ne, $slice, $or, $size, $elemMatch, $nin, $exists support [brian]
* Named scopes sugar [brian]
-1.0.10 / 2011-02-11
+1.0.10 / 2011-02-11
===================
* Updated node-mongodb-native driver [thanks John Allen]
-1.0.9 / 2011-02-09
+1.0.9 / 2011-02-09
==================
* Fixed single member arrays as defaults [brian]
-1.0.8 / 2011-02-09
+1.0.8 / 2011-02-09
==================
* Fixed for collection-level buffering of commands [gitfy]
@@ -1680,7 +3116,7 @@
* Fixed clash of accessors in getters/setters [eirikurn]
* Improved `Model#save` promise handling
-1.0.7 / 2011-02-05
+1.0.7 / 2011-02-05
==================
* Fixed memory leak warnings for test suite on 0.3
@@ -1689,14 +3125,14 @@
* Fixed default value for Array types (fixes GH-210). [brian]
* Fixed example code.
-1.0.6 / 2011-02-03
+1.0.6 / 2011-02-03
==================
* Fixed `post` middleware
* Fixed; it's now possible to instantiate a model even when one of the paths maps
to an undefined value [brian]
-1.0.5 / 2011-02-02
+1.0.5 / 2011-02-02
==================
* Fixed; combo $push and $pushAll auto-converts into a $pushAll [brian]
@@ -1710,20 +3146,20 @@
* Added tests that show that Schemas with nested Arrays don't apply defaults
[brian]
-1.0.4 / 2011-02-02
+1.0.4 / 2011-02-02
==================
* Added MongooseNumber#toString
* Added MongooseNumber unit tests
-1.0.3 / 2011-02-02
+1.0.3 / 2011-02-02
==================
* Make sure safe mode works with Model#save
* Changed Schema options: safe mode is now the default
* Updated node-mongodb-native to HEAD
-1.0.2 / 2011-02-02
+1.0.2 / 2011-02-02
==================
* Added a Model.create shortcut for creating documents. [brian]
@@ -1731,7 +3167,7 @@
null value. [brian]
* Fixed Schema with more than 2 nested levels. [brian]
-1.0.1 / 2011-02-02
+1.0.1 / 2011-02-02
==================
* Improved `MongooseNumber`, works almost like the native except for `typeof`
diff --git a/node_modules/mongoose/README.md b/node_modules/mongoose/README.md
index d87fbc9..9bda25d 100644
--- a/node_modules/mongoose/README.md
+++ b/node_modules/mongoose/README.md
@@ -1,69 +1,84 @@
-## What's Mongoose?
+# Mongoose
-Mongoose is a [MongoDB](http://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment.
+Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment.
+
+[![Build Status](https://api.travis-ci.org/Automattic/mongoose.svg?branch=master)](https://travis-ci.org/Automattic/mongoose)
+[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Automattic/mongoose?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![NPM version](https://badge.fury.io/js/mongoose.svg)](http://badge.fury.io/js/mongoose)
+[![Dependency Status](https://gemnasium.com/Automattic/mongoose.svg)](https://gemnasium.com/Automattic/mongoose)
## Documentation
[mongoosejs.com](http://mongoosejs.com/)
-## Try it live
-
-
## Support
- [Stack Overflow](http://stackoverflow.com/questions/tagged/mongoose)
- - [bug reports](https://github.com/learnboost/mongoose/issues/)
+ - [bug reports](https://github.com/Automattic/mongoose/issues/)
- [help forum](http://groups.google.com/group/mongoose-orm)
- - [MongoDB support](http://www.mongodb.org/display/DOCS/Technical+Support)
- - (irc) #mongoosejs on freenode
+ - [MongoDB support](https://docs.mongodb.org/manual/support/)
+ - [Mongoose Slack Channel](https://mongoosejs.slack.com/)
-## Installation
+## Plugins
-First install [node.js](http://nodejs.org/) and [mongodb](http://www.mongodb.org/downloads).
+Check out the [plugins search site](http://plugins.mongoosejs.io/) to see hundreds of related modules from the community.
- $ npm install mongoose
+Build your own Mongoose plugin through [generator-mongoose-plugin](https://github.com/huei90/generator-mongoose-plugin).
-## Plugins
+## Contributors
-Check out the [plugins search site](http://plugins.mongoosejs.com/) to see hundreds of related modules from the community.
+View all 100+ [contributors](https://github.com/Automattic/mongoose/graphs/contributors). Stand up and be counted as a [contributor](https://github.com/Automattic/mongoose/blob/master/CONTRIBUTING.md) too!
-## Contributors
+## Live Examples
+
+
+## Installation
+
+First install [node.js](http://nodejs.org/) and [mongodb](https://www.mongodb.org/downloads). Then:
-View all 90+ [contributors](https://github.com/learnboost/mongoose/graphs/contributors).
+```sh
+$ npm install mongoose
+```
-## Get Involved
+## Stability
-Stand up and be counted as a [contributor](https://github.com/LearnBoost/mongoose/blob/master/CONTRIBUTING.md) too!
+The current stable branch is [master](https://github.com/Automattic/mongoose/tree/master). The [3.8.x](https://github.com/Automattic/mongoose/tree/3.8.x) branch contains legacy support for the 3.x release series, which is no longer under active development as of September 2015. The [3.8.x docs](http://mongoosejs.com/docs/3.8.x/) are still available.
## Overview
### Connecting to MongoDB
-First, we need to define a connection. If your app uses only one database, you should use `mongose.connect`. If you need to create additional connections, use `mongoose.createConnection`.
+First, we need to define a connection. If your app uses only one database, you should use `mongoose.connect`. If you need to create additional connections, use `mongoose.createConnection`.
Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters `host, database, port, options`.
- var mongoose = require('mongoose');
+```js
+var mongoose = require('mongoose');
- mongoose.connect('mongodb://localhost/my_database');
+mongoose.connect('mongodb://localhost/my_database');
+```
Once connected, the `open` event is fired on the `Connection` instance. If you're using `mongoose.connect`, the `Connection` is `mongoose.connection`. Otherwise, `mongoose.createConnection` return value is a `Connection`.
+**Note:** _If the local connection fails then try using 127.0.0.1 instead of localhost. Sometimes issues may arise when the local hostname has been changed._
+
**Important!** Mongoose buffers all the commands until it's connected to the database. This means that you don't have to wait until it connects to MongoDB in order to define models, run queries, etc.
### Defining a Model
-Models are defined through the `Schema` interface.
+Models are defined through the `Schema` interface.
- var Schema = mongoose.Schema
- , ObjectId = Schema.ObjectId;
+```js
+var Schema = mongoose.Schema,
+ ObjectId = Schema.ObjectId;
- var BlogPost = new Schema({
- author : ObjectId
- , title : String
- , body : String
- , date : Date
- });
+var BlogPost = new Schema({
+ author : ObjectId,
+ title : String,
+ body : String,
+ date : Date
+});
+```
Aside from defining the structure of your documents and the types of data you're storing, a Schema handles the definition of:
@@ -80,24 +95,26 @@ Aside from defining the structure of your documents and the types of data you're
The following example shows some of these features:
- var Comment = new Schema({
- name : { type: String, default: 'hahaha' }
- , age : { type: Number, min: 18, index: true }
- , bio : { type: String, match: /[a-z]/ }
- , date : { type: Date, default: Date.now }
- , buff : Buffer
- });
-
- // a setter
- Comment.path('name').set(function (v) {
- return capitalize(v);
- });
-
- // middleware
- Comment.pre('save', function (next) {
- notify(this.get('email'));
- next();
- });
+```js
+var Comment = new Schema({
+ name: { type: String, default: 'hahaha' },
+ age: { type: Number, min: 18, index: true },
+ bio: { type: String, match: /[a-z]/ },
+ date: { type: Date, default: Date.now },
+ buff: Buffer
+});
+
+// a setter
+Comment.path('name').set(function (v) {
+ return capitalize(v);
+});
+
+// middleware
+Comment.pre('save', function (next) {
+ notify(this.get('email'));
+ next();
+});
+```
Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup.
@@ -105,77 +122,102 @@ Take a look at the example in `examples/schema.js` for an end-to-end example of
Once we define a model through `mongoose.model('ModelName', mySchema)`, we can access it through the same function
- var myModel = mongoose.model('ModelName');
+```js
+var myModel = mongoose.model('ModelName');
+```
Or just do it all at once
- var MyModel = mongoose.model('ModelName', mySchema);
+```js
+var MyModel = mongoose.model('ModelName', mySchema);
+```
-We can then instantiate it, and save it:
+The first argument is the _singular_ name of the collection your model is for. **Mongoose automatically looks for the _plural_ version of your model name.** For example, if you use
- var instance = new MyModel();
- instance.my.key = 'hello';
- instance.save(function (err) {
- //
- });
+```js
+var MyModel = mongoose.model('Ticket', mySchema);
+```
+
+Then Mongoose will create the model for your __tickets__ collection, not your __ticket__ collection.
+
+Once we have our model, we can then instantiate it, and save it:
+
+```js
+var instance = new MyModel();
+instance.my.key = 'hello';
+instance.save(function (err) {
+ //
+});
+```
Or we can find documents from the same collection
- MyModel.find({}, function (err, docs) {
- // docs.forEach
- });
+```js
+MyModel.find({}, function (err, docs) {
+ // docs.forEach
+});
+```
You can also `findOne`, `findById`, `update`, etc. For more details check out [the docs](http://mongoosejs.com/docs/queries.html).
**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this case access your model through the connection you created:
- var conn = mongoose.createConnection('your connection string');
- var MyModel = conn.model('ModelName', schema);
- var m = new MyModel;
- m.save() // works
+```js
+var conn = mongoose.createConnection('your connection string'),
+ MyModel = conn.model('ModelName', schema),
+ m = new MyModel;
+m.save(); // works
+```
- vs
+vs
- var conn = mongoose.createConnection('your connection string');
- var MyModel = mongoose.model('ModelName', schema);
- var m = new MyModel;
- m.save() // does not work b/c the default connection object was never connected
+```js
+var conn = mongoose.createConnection('your connection string'),
+ MyModel = mongoose.model('ModelName', schema),
+ m = new MyModel;
+m.save(); // does not work b/c the default connection object was never connected
+```
### Embedded Documents
In the first example snippet, we defined a key in the Schema that looks like:
- comments: [Comments]
+```
+comments: [Comment]
+```
-Where `Comments` is a `Schema` we created. This means that creating embedded documents is as simple as:
+Where `Comment` is a `Schema` we created. This means that creating embedded documents is as simple as:
- // retrieve my model
- var BlogPost = mongoose.model('BlogPost');
+```js
+// retrieve my model
+var BlogPost = mongoose.model('BlogPost');
- // create a blog post
- var post = new BlogPost();
+// create a blog post
+var post = new BlogPost();
- // create a comment
- post.comments.push({ title: 'My comment' });
+// create a comment
+post.comments.push({ title: 'My comment' });
- post.save(function (err) {
- if (!err) console.log('Success!');
- });
+post.save(function (err) {
+ if (!err) console.log('Success!');
+});
+```
The same goes for removing them:
- BlogPost.findById(myId, function (err, post) {
- if (!err) {
- post.comments[0].remove();
- post.save(function (err) {
- // do something
- });
- }
+```js
+BlogPost.findById(myId, function (err, post) {
+ if (!err) {
+ post.comments[0].remove();
+ post.save(function (err) {
+ // do something
});
+ }
+});
+```
Embedded documents enjoy all the same features as your models. Defaults, validators, middleware. Whenever an error occurs, it's bubbled to the `save()` error callback, so error handling is a snap!
-Mongoose interacts with your embedded documents in arrays _atomically_, out of the box.
### Middleware
@@ -187,63 +229,73 @@ You can intercept method arguments via middleware.
For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to a new value:
- schema.pre('set', function (next, path, val, typel) {
- // `this` is the current Document
- this.emit('set', path, val);
+```js
+schema.pre('set', function (next, path, val, typel) {
+ // `this` is the current Document
+ this.emit('set', path, val);
- // Pass control to the next pre
- next();
- });
+ // Pass control to the next pre
+ next();
+});
+```
Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments. To do so, just pass the new values to `next`:
- .pre(method, function firstPre (next, methodArg1, methodArg2) {
- // Mutate methodArg1
- next("altered-" + methodArg1.toString(), methodArg2);
- })
-
- // pre declaration is chainable
- .pre(method, function secondPre (next, methodArg1, methodArg2) {
- console.log(methodArg1);
- // => 'altered-originalValOfMethodArg1'
-
- console.log(methodArg2);
- // => 'originalValOfMethodArg2'
-
- // Passing no arguments to `next` automatically passes along the current argument values
- // i.e., the following `next()` is equivalent to `next(methodArg1, methodArg2)`
- // and also equivalent to, with the example method arg
- // values, `next('altered-originalValOfMethodArg1', 'originalValOfMethodArg2')`
- next();
- })
+```js
+.pre(method, function firstPre (next, methodArg1, methodArg2) {
+ // Mutate methodArg1
+ next("altered-" + methodArg1.toString(), methodArg2);
+});
-#### Schema gotcha
+// pre declaration is chainable
+.pre(method, function secondPre (next, methodArg1, methodArg2) {
+ console.log(methodArg1);
+ // => 'altered-originalValOfMethodArg1'
-`type`, when used in a schema has special meaning within Mongoose. If your schema requires using `type` as a nested property you must use object notation:
+ console.log(methodArg2);
+ // => 'originalValOfMethodArg2'
- new Schema({
- broken: { type: Boolean }
- , asset : {
- name: String
- , type: String // uh oh, it broke. asset will be interpreted as String
- }
- });
+ // Passing no arguments to `next` automatically passes along the current argument values
+ // i.e., the following `next()` is equivalent to `next(methodArg1, methodArg2)`
+ // and also equivalent to, with the example method arg
+ // values, `next('altered-originalValOfMethodArg1', 'originalValOfMethodArg2')`
+ next();
+});
+```
- new Schema({
- works: { type: Boolean }
- , asset : {
- name: String
- , type: { type: String } // works. asset is an object with a type property
- }
- });
+#### Schema gotcha
-### Driver access
+`type`, when used in a schema has special meaning within Mongoose. If your schema requires using `type` as a nested property you must use object notation:
-The driver being used defaults to [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) and is directly accessible through `YourModel.collection`. **Note**: using the driver directly bypasses all Mongoose power-tools like validation, getters, setters, hooks, etc.
+```js
+new Schema({
+ broken: { type: Boolean },
+ asset : {
+ name: String,
+ type: String // uh oh, it broke. asset will be interpreted as String
+ }
+});
+
+new Schema({
+ works: { type: Boolean },
+ asset: {
+ name: String,
+ type: { type: String } // works. asset is an object with a type property
+ }
+});
+```
+
+### Driver Access
+
+Mongoose is built on top of the [official MongoDB Node.js driver](https://github.com/mongodb/node-mongodb-native). Each mongoose model keeps a reference to a [native MongoDB driver collection](http://mongodb.github.io/node-mongodb-native/2.1/api/Collection.html). The collection object can be accessed using `YourModel.collection`. However, using the collection object directly bypasses all mongoose features, including hooks, validation, etc. The one
+notable exception that `YourModel.collection` still buffers
+commands. As such, `YourModel.collection.find()` will **not**
+return a cursor.
## API Docs
-Find the API docs [here](http://mongoosejs.com/docs/api.html), generated by [dox](http://github.com/visionmedia/dox).
+Find the API docs [here](http://mongoosejs.com/docs/api.html), generated using [dox](https://github.com/tj/dox)
+and [acquit](https://github.com/vkarpov15/acquit).
## License
diff --git a/node_modules/mongoose/examples/README.md b/node_modules/mongoose/examples/README.md
index 89728d9..cb32898 100644
--- a/node_modules/mongoose/examples/README.md
+++ b/node_modules/mongoose/examples/README.md
@@ -1,40 +1,39 @@
-
This directory contains runnable sample mongoose programs.
To run:
- first install [Node.js](http://nodejs.org/)
+ - from the root of the project, execute `npm install -d`
+ - in the example directory, run `npm install -d`
- from the command line, execute: `node example.js`, replacing "example.js" with the name of a program.
Goal is to show:
-- global schemas
-- GeoJSON schemas / use (with crs)
-- text search
-- storing schemas as json
-- lean querires
-- statics
+- ~~global schemas~~
+- ~~GeoJSON schemas / use (with crs)~~
+- text search (once MongoDB removes the "Experimental/beta" label)
+- ~~lean queries~~
+- ~~statics~~
- methods and statics on subdocs
- custom types
-- querybuilder
-- promises
-- express + mongoose
+- ~~querybuilder~~
+- ~~promises~~
- accessing driver collection, db
-- connecting to replica sets
+- ~~connecting to replica sets~~
- connecting to sharded clusters
- enabling a fail fast mode
- on the fly schemas
- storing files
-- map reduce
-- aggregation
+- ~~map reduce~~
+- ~~aggregation~~
- advanced hooks
- using $elemMatch to return a subset of an array
- query casting
- upserts
- pagination
- express + mongoose session handling
-- group by (use aggregation)
+- ~~group by (use aggregation)~~
- authentication
- schema migration techniques
- converting documents to plain objects (show transforms)
diff --git a/node_modules/mongoose/examples/aggregate/aggregate.js b/node_modules/mongoose/examples/aggregate/aggregate.js
new file mode 100644
index 0000000..793c8cb
--- /dev/null
+++ b/node_modules/mongoose/examples/aggregate/aggregate.js
@@ -0,0 +1,103 @@
+
+// import async to make control flow simplier
+var async = require('async');
+
+// import the rest of the normal stuff
+var mongoose = require('../../lib');
+
+require('./person.js')();
+
+var Person = mongoose.model('Person');
+
+// define some dummy data
+var data = [
+ {
+ name: 'bill',
+ age: 25,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 25)),
+ gender: 'Male',
+ likes: ['movies', 'games', 'dogs']
+ },
+ {
+ name: 'mary',
+ age: 30,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 30)),
+ gender: 'Female',
+ likes: ['movies', 'birds', 'cats']
+ },
+ {
+ name: 'bob',
+ age: 21,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 21)),
+ gender: 'Male',
+ likes: ['tv', 'games', 'rabbits']
+ },
+ {
+ name: 'lilly',
+ age: 26,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 26)),
+ gender: 'Female',
+ likes: ['books', 'cats', 'dogs']
+ },
+ {
+ name: 'alucard',
+ age: 1000,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 1000)),
+ gender: 'Male',
+ likes: ['glasses', 'wine', 'the night']
+ }
+];
+
+
+mongoose.connect('mongodb://localhost/persons', function(err) {
+ if (err) throw err;
+
+ // create all of the dummy people
+ async.each(data, function(item, cb) {
+ Person.create(item, cb);
+ }, function(err) {
+ if (err) {
+ // handle error
+ }
+
+ // run an aggregate query that will get all of the people who like a given
+ // item. To see the full documentation on ways to use the aggregate
+ // framework, see http://docs.mongodb.org/manual/core/aggregation/
+ Person.aggregate(
+ // select the fields we want to deal with
+ {$project: {name: 1, likes: 1}},
+ // unwind 'likes', which will create a document for each like
+ {$unwind: '$likes'},
+ // group everything by the like and then add each name with that like to
+ // the set for the like
+ {$group: {
+ _id: {likes: '$likes'},
+ likers: {$addToSet: '$name'}
+ }},
+ function(err, result) {
+ if (err) throw err;
+ console.log(result);
+ /* [
+ { _id: { likes: 'the night' }, likers: [ 'alucard' ] },
+ { _id: { likes: 'wine' }, likers: [ 'alucard' ] },
+ { _id: { likes: 'books' }, likers: [ 'lilly' ] },
+ { _id: { likes: 'glasses' }, likers: [ 'alucard' ] },
+ { _id: { likes: 'birds' }, likers: [ 'mary' ] },
+ { _id: { likes: 'rabbits' }, likers: [ 'bob' ] },
+ { _id: { likes: 'cats' }, likers: [ 'lilly', 'mary' ] },
+ { _id: { likes: 'dogs' }, likers: [ 'lilly', 'bill' ] },
+ { _id: { likes: 'tv' }, likers: [ 'bob' ] },
+ { _id: { likes: 'games' }, likers: [ 'bob', 'bill' ] },
+ { _id: { likes: 'movies' }, likers: [ 'mary', 'bill' ] }
+ ] */
+
+ cleanup();
+ });
+ });
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/aggregate/package.json b/node_modules/mongoose/examples/aggregate/package.json
new file mode 100644
index 0000000..53ed2e1
--- /dev/null
+++ b/node_modules/mongoose/examples/aggregate/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "aggregate-example",
+ "private": "true",
+ "version": "0.0.0",
+ "description": "deps for aggregate example",
+ "main": "aggregate.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": { "async": "*" },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
diff --git a/node_modules/mongoose/examples/aggregate/person.js b/node_modules/mongoose/examples/aggregate/person.js
new file mode 100644
index 0000000..607bb07
--- /dev/null
+++ b/node_modules/mongoose/examples/aggregate/person.js
@@ -0,0 +1,17 @@
+
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date,
+ gender: String,
+ likes: [String]
+ });
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/doc-methods.js b/node_modules/mongoose/examples/doc-methods.js
index 373a46b..1f648b9 100644
--- a/node_modules/mongoose/examples/doc-methods.js
+++ b/node_modules/mongoose/examples/doc-methods.js
@@ -1,5 +1,5 @@
-var mongoose = require('mongoose')
+var mongoose = require('mongoose');
var Schema = mongoose.Schema;
console.log('Running mongoose version %s', mongoose.version);
@@ -9,17 +9,24 @@ console.log('Running mongoose version %s', mongoose.version);
*/
var CharacterSchema = Schema({
- name: { type: String, required: true }
- , health: { type: Number, min: 0, max: 100 }
-})
+ name: {
+ type: String,
+ required: true
+ },
+ health: {
+ type: Number,
+ min: 0,
+ max: 100
+ }
+});
/**
* Methods
*/
-CharacterSchema.methods.attack = function () {
+CharacterSchema.methods.attack = function() {
console.log('%s is attacking', this.name);
-}
+};
/**
* Character model
@@ -32,39 +39,39 @@ var Character = mongoose.model('Character', CharacterSchema);
* the default port (27017)
*/
-var dbname = 'mongoose-example-doc-methods-' + ((Math.random()*10000)|0);
+var dbname = 'mongoose-example-doc-methods-' + ((Math.random() * 10000) | 0);
var uri = 'mongodb://localhost/' + dbname;
console.log('connecting to %s', uri);
-mongoose.connect(uri, function (err) {
+mongoose.connect(uri, function(err) {
// if we failed to connect, abort
if (err) throw err;
// we connected ok
example();
-})
+});
/**
* Use case
*/
-function example () {
- Character.create({ name: 'Link', health: 100 }, function (err, link) {
+function example() {
+ Character.create({name: 'Link', health: 100}, function(err, link) {
if (err) return done(err);
console.log('found', link);
link.attack(); // 'Link is attacking'
done();
- })
+ });
}
/**
* Clean up
*/
-function done (err) {
+function done(err) {
if (err) console.error(err);
- mongoose.connection.db.dropDatabase(function () {
+ mongoose.connection.db.dropDatabase(function() {
mongoose.disconnect();
- })
+ });
}
diff --git a/node_modules/mongoose/examples/express/README.md b/node_modules/mongoose/examples/express/README.md
new file mode 100644
index 0000000..7ba07b8
--- /dev/null
+++ b/node_modules/mongoose/examples/express/README.md
@@ -0,0 +1 @@
+Mongoose + Express examples
diff --git a/node_modules/mongoose/examples/express/connection-sharing/README.md b/node_modules/mongoose/examples/express/connection-sharing/README.md
new file mode 100644
index 0000000..fc709a3
--- /dev/null
+++ b/node_modules/mongoose/examples/express/connection-sharing/README.md
@@ -0,0 +1,6 @@
+
+To run:
+
+- Execute `npm install` from this directory
+- Execute `node app.js`
+- Navigate to `localhost:8000`
diff --git a/node_modules/mongoose/examples/express/connection-sharing/app.js b/node_modules/mongoose/examples/express/connection-sharing/app.js
new file mode 100644
index 0000000..8fb2fee
--- /dev/null
+++ b/node_modules/mongoose/examples/express/connection-sharing/app.js
@@ -0,0 +1,17 @@
+
+var express = require('express');
+var mongoose = require('../../../lib');
+
+var uri = 'mongodb://localhost/mongoose-shared-connection';
+global.db = mongoose.createConnection(uri);
+
+var routes = require('./routes');
+
+var app = express();
+app.get('/', routes.home);
+app.get('/insert', routes.insert);
+app.get('/name', routes.modelName);
+
+app.listen(8000, function() {
+ console.log('listening on http://localhost:8000');
+});
diff --git a/node_modules/mongoose/examples/express/connection-sharing/modelA.js b/node_modules/mongoose/examples/express/connection-sharing/modelA.js
new file mode 100644
index 0000000..78f9ff6
--- /dev/null
+++ b/node_modules/mongoose/examples/express/connection-sharing/modelA.js
@@ -0,0 +1,5 @@
+var Schema = require('../../../lib').Schema;
+var mySchema = Schema({name: String});
+
+/* global db */
+module.exports = db.model('MyModel', mySchema);
diff --git a/node_modules/mongoose/examples/express/connection-sharing/package.json b/node_modules/mongoose/examples/express/connection-sharing/package.json
new file mode 100644
index 0000000..e326165
--- /dev/null
+++ b/node_modules/mongoose/examples/express/connection-sharing/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "connection-sharing",
+ "private": "true",
+ "version": "0.0.0",
+ "description": "ERROR: No README.md file found!",
+ "main": "app.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": { "express": "3.1.1" },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
diff --git a/node_modules/mongoose/examples/express/connection-sharing/routes.js b/node_modules/mongoose/examples/express/connection-sharing/routes.js
new file mode 100644
index 0000000..35e4f8f
--- /dev/null
+++ b/node_modules/mongoose/examples/express/connection-sharing/routes.js
@@ -0,0 +1,20 @@
+
+var model = require('./modelA');
+
+exports.home = function(req, res, next) {
+ model.find(function(err, docs) {
+ if (err) return next(err);
+ res.send(docs);
+ });
+};
+
+exports.modelName = function(req, res) {
+ res.send('my model name is ' + model.modelName);
+};
+
+exports.insert = function(req, res, next) {
+ model.create({name: 'inserting ' + Date.now()}, function(err, doc) {
+ if (err) return next(err);
+ res.send(doc);
+ });
+};
diff --git a/node_modules/mongoose/examples/geospatial/geoJSONSchema.js b/node_modules/mongoose/examples/geospatial/geoJSONSchema.js
new file mode 100644
index 0000000..f950dea
--- /dev/null
+++ b/node_modules/mongoose/examples/geospatial/geoJSONSchema.js
@@ -0,0 +1,22 @@
+
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ // NOTE : This object must conform *precisely* to the geoJSON specification
+ // you cannot embed a geoJSON doc inside a model or anything like that- IT
+ // MUST BE VANILLA
+ var LocationObject = new Schema({
+ loc: {
+ type: {type: String},
+ coordinates: []
+ }
+ });
+ // define the index
+ LocationObject.index({loc: '2dsphere'});
+
+ mongoose.model('Location', LocationObject);
+};
diff --git a/node_modules/mongoose/examples/geospatial/geoJSONexample.js b/node_modules/mongoose/examples/geospatial/geoJSONexample.js
new file mode 100644
index 0000000..8e5dd2b
--- /dev/null
+++ b/node_modules/mongoose/examples/geospatial/geoJSONexample.js
@@ -0,0 +1,56 @@
+// import async to make control flow simplier
+var async = require('async');
+
+// import the rest of the normal stuff
+var mongoose = require('../../lib');
+
+require('./geoJSONSchema.js')();
+
+var Location = mongoose.model('Location');
+
+// define some dummy data
+// note: the type can be Point, LineString, or Polygon
+var data = [
+ {loc: {type: 'Point', coordinates: [-20.0, 5.0]}},
+ {loc: {type: 'Point', coordinates: [6.0, 10.0]}},
+ {loc: {type: 'Point', coordinates: [34.0, -50.0]}},
+ {loc: {type: 'Point', coordinates: [-100.0, 70.0]}},
+ {loc: {type: 'Point', coordinates: [38.0, 38.0]}}
+];
+
+
+mongoose.connect('mongodb://localhost/locations', function(err) {
+ if (err) {
+ throw err;
+ }
+
+ Location.on('index', function(err) {
+ if (err) {
+ throw err;
+ }
+ // create all of the dummy locations
+ async.each(data, function(item, cb) {
+ Location.create(item, cb);
+ }, function(err) {
+ if (err) {
+ throw err;
+ }
+ // create the location we want to search for
+ var coords = {type: 'Point', coordinates: [-5, 5]};
+ // search for it
+ Location.find({loc: {$near: coords}}).limit(1).exec(function(err, res) {
+ if (err) {
+ throw err;
+ }
+ console.log('Closest to %s is %s', JSON.stringify(coords), res);
+ cleanup();
+ });
+ });
+ });
+});
+
+function cleanup() {
+ Location.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/geospatial/geospatial.js b/node_modules/mongoose/examples/geospatial/geospatial.js
new file mode 100644
index 0000000..3ff8c0b
--- /dev/null
+++ b/node_modules/mongoose/examples/geospatial/geospatial.js
@@ -0,0 +1,100 @@
+// import async to make control flow simplier
+var async = require('async');
+
+// import the rest of the normal stuff
+var mongoose = require('../../lib');
+
+require('./person.js')();
+
+var Person = mongoose.model('Person');
+
+// define some dummy data
+var data = [
+ {
+ name: 'bill',
+ age: 25,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 25)),
+ gender: 'Male',
+ likes: ['movies', 'games', 'dogs'],
+ loc: [0, 0]
+ },
+ {
+ name: 'mary',
+ age: 30,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 30)),
+ gender: 'Female',
+ likes: ['movies', 'birds', 'cats'],
+ loc: [1, 1]
+ },
+ {
+ name: 'bob',
+ age: 21,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 21)),
+ gender: 'Male',
+ likes: ['tv', 'games', 'rabbits'],
+ loc: [3, 3]
+ },
+ {
+ name: 'lilly',
+ age: 26,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 26)),
+ gender: 'Female',
+ likes: ['books', 'cats', 'dogs'],
+ loc: [6, 6]
+ },
+ {
+ name: 'alucard',
+ age: 1000,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 1000)),
+ gender: 'Male',
+ likes: ['glasses', 'wine', 'the night'],
+ loc: [10, 10]
+ }
+];
+
+
+mongoose.connect('mongodb://localhost/persons', function(err) {
+ if (err) {
+ throw err;
+ }
+
+ // create all of the dummy people
+ async.each(data, function(item, cb) {
+ Person.create(item, cb);
+ }, function(err) {
+ if (err) {
+ // handler error
+ }
+
+ // let's find the closest person to bob
+ Person.find({name: 'bob'}, function(err, res) {
+ if (err) {
+ throw err;
+ }
+
+ res[0].findClosest(function(err, closest) {
+ if (err) {
+ throw err;
+ }
+
+ console.log('%s is closest to %s', res[0].name, closest);
+
+
+ // we can also just query straight off of the model. For more
+ // information about geospatial queries and indexes, see
+ // http://docs.mongodb.org/manual/applications/geospatial-indexes/
+ var coords = [7, 7];
+ Person.find({loc: {$nearSphere: coords}}).limit(1).exec(function(err, res) {
+ console.log('Closest to %s is %s', coords, res);
+ cleanup();
+ });
+ });
+ });
+ });
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/geospatial/package.json b/node_modules/mongoose/examples/geospatial/package.json
new file mode 100644
index 0000000..75c2a0e
--- /dev/null
+++ b/node_modules/mongoose/examples/geospatial/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "geospatial-example",
+ "private": "true",
+ "version": "0.0.0",
+ "description": "deps for geospatial example",
+ "main": "geospatial.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": { "async": "*" },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
diff --git a/node_modules/mongoose/examples/geospatial/person.js b/node_modules/mongoose/examples/geospatial/person.js
new file mode 100644
index 0000000..e816637
--- /dev/null
+++ b/node_modules/mongoose/examples/geospatial/person.js
@@ -0,0 +1,27 @@
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date,
+ gender: String,
+ likes: [String],
+ // define the geospatial field
+ loc: {type: [Number], index: '2d'}
+ });
+
+ // define a method to find the closest person
+ PersonSchema.methods.findClosest = function(cb) {
+ return this.model('Person').find({
+ loc: {$nearSphere: this.loc},
+ name: {$ne: this.name}
+ }).limit(1).exec(cb);
+ };
+
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/globalschemas/gs_example.js b/node_modules/mongoose/examples/globalschemas/gs_example.js
new file mode 100644
index 0000000..af9ff11
--- /dev/null
+++ b/node_modules/mongoose/examples/globalschemas/gs_example.js
@@ -0,0 +1,47 @@
+var mongoose = require('../../lib');
+
+
+// import the global schema, this can be done in any file that needs the model
+require('./person.js')();
+
+// grab the person model object
+var Person = mongoose.model('Person');
+
+// connect to a server to do a quick write / read example
+
+mongoose.connect('mongodb://localhost/persons', function(err) {
+ if (err) {
+ throw err;
+ }
+
+ Person.create({
+ name: 'bill',
+ age: 25,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 25))
+ }, function(err, bill) {
+ if (err) {
+ throw err;
+ }
+ console.log('People added to db: %s', bill.toString());
+ Person.find({}, function(err, people) {
+ if (err) {
+ throw err;
+ }
+
+ people.forEach(function(person) {
+ console.log('People in the db: %s', person.toString());
+ });
+
+ // make sure to clean things up after we're done
+ setTimeout(function() {
+ cleanup();
+ }, 2000);
+ });
+ });
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/globalschemas/person.js b/node_modules/mongoose/examples/globalschemas/person.js
new file mode 100644
index 0000000..39ae725
--- /dev/null
+++ b/node_modules/mongoose/examples/globalschemas/person.js
@@ -0,0 +1,14 @@
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date
+ });
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/lean/lean.js b/node_modules/mongoose/examples/lean/lean.js
new file mode 100644
index 0000000..763367f
--- /dev/null
+++ b/node_modules/mongoose/examples/lean/lean.js
@@ -0,0 +1,84 @@
+
+// import async to make control flow simplier
+var async = require('async');
+
+// import the rest of the normal stuff
+var mongoose = require('../../lib');
+
+require('./person.js')();
+
+var Person = mongoose.model('Person');
+
+// define some dummy data
+var data = [
+ {
+ name: 'bill',
+ age: 25,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 25)),
+ gender: 'Male',
+ likes: ['movies', 'games', 'dogs']
+ },
+ {
+ name: 'mary',
+ age: 30,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 30)),
+ gender: 'Female',
+ likes: ['movies', 'birds', 'cats']
+ },
+ {
+ name: 'bob',
+ age: 21,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 21)),
+ gender: 'Male',
+ likes: ['tv', 'games', 'rabbits']
+ },
+ {
+ name: 'lilly',
+ age: 26,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 26)),
+ gender: 'Female',
+ likes: ['books', 'cats', 'dogs']
+ },
+ {
+ name: 'alucard',
+ age: 1000,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 1000)),
+ gender: 'Male',
+ likes: ['glasses', 'wine', 'the night']
+ }
+];
+
+
+mongoose.connect('mongodb://localhost/persons', function(err) {
+ if (err) throw err;
+
+ // create all of the dummy people
+ async.each(data, function(item, cb) {
+ Person.create(item, cb);
+ }, function(err) {
+ if (err) {
+ // handle error
+ }
+
+ // lean queries return just plain javascript objects, not
+ // MongooseDocuments. This makes them good for high performance read
+ // situations
+
+ // when using .lean() the default is true, but you can explicitly set the
+ // value by passing in a boolean value. IE. .lean(false)
+ var q = Person.find({age: {$lt: 1000}}).sort('age').limit(2).lean();
+ q.exec(function(err, results) {
+ if (err) throw err;
+ console.log('Are the results MongooseDocuments?: %s', results[0] instanceof mongoose.Document);
+
+ console.log(results);
+ cleanup();
+ });
+ });
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/lean/package.json b/node_modules/mongoose/examples/lean/package.json
new file mode 100644
index 0000000..6ee511d
--- /dev/null
+++ b/node_modules/mongoose/examples/lean/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "lean-example",
+ "private": "true",
+ "version": "0.0.0",
+ "description": "deps for lean example",
+ "main": "lean.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": { "async": "*" },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
diff --git a/node_modules/mongoose/examples/lean/person.js b/node_modules/mongoose/examples/lean/person.js
new file mode 100644
index 0000000..b0bd2ed
--- /dev/null
+++ b/node_modules/mongoose/examples/lean/person.js
@@ -0,0 +1,16 @@
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date,
+ gender: String,
+ likes: [String]
+ });
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/mapreduce/mapreduce.js b/node_modules/mongoose/examples/mapreduce/mapreduce.js
new file mode 100644
index 0000000..6d67fbf
--- /dev/null
+++ b/node_modules/mongoose/examples/mapreduce/mapreduce.js
@@ -0,0 +1,100 @@
+// import async to make control flow simplier
+var async = require('async');
+
+// import the rest of the normal stuff
+var mongoose = require('../../lib');
+
+require('./person.js')();
+
+var Person = mongoose.model('Person');
+
+// define some dummy data
+var data = [
+ {
+ name: 'bill',
+ age: 25,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 25)),
+ gender: 'Male'
+ },
+ {
+ name: 'mary',
+ age: 30,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 30)),
+ gender: 'Female'
+ },
+ {
+ name: 'bob',
+ age: 21,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 21)),
+ gender: 'Male'
+ },
+ {
+ name: 'lilly',
+ age: 26,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 26)),
+ gender: 'Female'
+ },
+ {
+ name: 'alucard',
+ age: 1000,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 1000)),
+ gender: 'Male'
+ }
+];
+
+
+mongoose.connect('mongodb://localhost/persons', function(err) {
+ if (err) throw err;
+
+ // create all of the dummy people
+ async.each(data, function(item, cb) {
+ Person.create(item, cb);
+ }, function(err) {
+ if (err) {
+ // handle error
+ }
+
+ // alright, simple map reduce example. We will find the total ages of each
+ // gender
+
+ // create the options object
+ var o = {};
+
+ o.map = function() {
+ // in this function, 'this' refers to the current document being
+ // processed. Return the (gender, age) tuple using
+ /* global emit */
+ emit(this.gender, this.age);
+ };
+
+ // the reduce function receives the array of ages that are grouped by the
+ // id, which in this case is the gender
+ o.reduce = function(id, ages) {
+ return Array.sum(ages);
+ };
+
+ // other options that can be specified
+
+ // o.query = { age : { $lt : 1000 }}; // the query object
+ // o.limit = 3; // max number of documents
+ // o.keeptemp = true; // default is false, specifies whether to keep temp data
+ // o.finalize = someFunc; // function called after reduce
+ // o.scope = {}; // the scope variable exposed to map/reduce/finalize
+ // o.jsMode = true; // default is false, force execution to stay in JS
+ o.verbose = true; // default is false, provide stats on the job
+ // o.out = {}; // objects to specify where output goes, by default is
+ // returned, but can also be stored in a new collection
+ // see: http://mongoosejs.com/docs/api.html#model_Model.mapReduce
+ Person.mapReduce(o, function(err, results, stats) {
+ console.log('map reduce took %d ms', stats.processtime);
+ console.log(results);
+ cleanup();
+ });
+ });
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/mapreduce/package.json b/node_modules/mongoose/examples/mapreduce/package.json
new file mode 100644
index 0000000..4240068
--- /dev/null
+++ b/node_modules/mongoose/examples/mapreduce/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "map-reduce-example",
+ "private": "true",
+ "version": "0.0.0",
+ "description": "deps for map reduce example",
+ "main": "mapreduce.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": { "async": "*" },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
diff --git a/node_modules/mongoose/examples/mapreduce/person.js b/node_modules/mongoose/examples/mapreduce/person.js
new file mode 100644
index 0000000..9e2f084
--- /dev/null
+++ b/node_modules/mongoose/examples/mapreduce/person.js
@@ -0,0 +1,16 @@
+
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date,
+ gender: String
+ });
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/population-basic.js b/node_modules/mongoose/examples/population-basic.js
deleted file mode 100644
index c1d6a70..0000000
--- a/node_modules/mongoose/examples/population-basic.js
+++ /dev/null
@@ -1,95 +0,0 @@
-
-var mongoose = require('mongoose')
-var Schema = mongoose.Schema;
-
-console.log('Running mongoose version %s', mongoose.version);
-
-/**
- * Console schema
- */
-
-var consoleSchema = Schema({
- name: String
- , manufacturer: String
- , released: Date
-})
-var Console = mongoose.model('Console', consoleSchema);
-
-/**
- * Game schema
- */
-
-var gameSchema = Schema({
- name: String
- , developer: String
- , released: Date
- , consoles: [{ type: Schema.Types.ObjectId, ref: 'Console' }]
-})
-var Game = mongoose.model('Game', gameSchema);
-
-/**
- * Connect to the console database on localhost with
- * the default port (27017)
- */
-
-mongoose.connect('mongodb://localhost/console', function (err) {
- // if we failed to connect, abort
- if (err) throw err;
-
- // we connected ok
- createData();
-})
-
-/**
- * Data generation
- */
-
-function createData () {
- Console.create({
- name: 'Nintendo 64'
- , manufacturer: 'Nintendo'
- , released: 'September 29, 1996'
- }, function (err, nintendo64) {
- if (err) return done(err);
-
- Game.create({
- name: 'Legend of Zelda: Ocarina of Time'
- , developer: 'Nintendo'
- , released: new Date('November 21, 1998')
- , consoles: [nintendo64]
- }, function (err) {
- if (err) return done(err);
- example();
- })
- })
-}
-
-/**
- * Population
- */
-
-function example () {
- Game
- .findOne({ name: /^Legend of Zelda/ })
- .populate('consoles')
- .exec(function (err, ocinara) {
- if (err) return done(err);
-
- console.log(
- '"%s" was released for the %s on %s'
- , ocinara.name
- , ocinara.consoles[0].name
- , ocinara.released.toLocaleDateString());
-
- done();
- })
-}
-
-function done (err) {
- if (err) console.error(err);
- Console.remove(function () {
- Game.remove(function () {
- mongoose.disconnect();
- })
- })
-}
diff --git a/node_modules/mongoose/examples/population-of-existing-doc.js b/node_modules/mongoose/examples/population-of-existing-doc.js
deleted file mode 100644
index 6d48fdc..0000000
--- a/node_modules/mongoose/examples/population-of-existing-doc.js
+++ /dev/null
@@ -1,101 +0,0 @@
-
-var mongoose = require('mongoose')
-var Schema = mongoose.Schema;
-
-console.log('Running mongoose version %s', mongoose.version);
-
-/**
- * Console schema
- */
-
-var consoleSchema = Schema({
- name: String
- , manufacturer: String
- , released: Date
-})
-var Console = mongoose.model('Console', consoleSchema);
-
-/**
- * Game schema
- */
-
-var gameSchema = Schema({
- name: String
- , developer: String
- , released: Date
- , consoles: [{ type: Schema.Types.ObjectId, ref: 'Console' }]
-})
-var Game = mongoose.model('Game', gameSchema);
-
-/**
- * Connect to the console database on localhost with
- * the default port (27017)
- */
-
-mongoose.connect('mongodb://localhost/console', function (err) {
- // if we failed to connect, abort
- if (err) throw err;
-
- // we connected ok
- createData();
-})
-
-/**
- * Data generation
- */
-
-function createData () {
- Console.create({
- name: 'Nintendo 64'
- , manufacturer: 'Nintendo'
- , released: 'September 29, 1996'
- }, function (err, nintendo64) {
- if (err) return done(err);
-
- Game.create({
- name: 'Legend of Zelda: Ocarina of Time'
- , developer: 'Nintendo'
- , released: new Date('November 21, 1998')
- , consoles: [nintendo64]
- }, function (err) {
- if (err) return done(err);
- example();
- })
- })
-}
-
-/**
- * Population
- */
-
-function example () {
- Game
- .findOne({ name: /^Legend of Zelda/ })
- .exec(function (err, ocinara) {
- if (err) return done(err);
-
- console.log('"%s" console _id: %s', ocinara.name, ocinara.consoles[0]);
-
- // population of existing document
- ocinara.populate('consoles', function (err) {
- if (err) return done(err);
-
- console.log(
- '"%s" was released for the %s on %s'
- , ocinara.name
- , ocinara.consoles[0].name
- , ocinara.released.toLocaleDateString());
-
- done();
- })
- })
-}
-
-function done (err) {
- if (err) console.error(err);
- Console.remove(function () {
- Game.remove(function () {
- mongoose.disconnect();
- })
- })
-}
diff --git a/node_modules/mongoose/examples/population-of-multiple-existing-docs.js b/node_modules/mongoose/examples/population-of-multiple-existing-docs.js
deleted file mode 100644
index 6d918ff..0000000
--- a/node_modules/mongoose/examples/population-of-multiple-existing-docs.js
+++ /dev/null
@@ -1,112 +0,0 @@
-
-var mongoose = require('mongoose')
-var Schema = mongoose.Schema;
-
-console.log('Running mongoose version %s', mongoose.version);
-
-/**
- * Console schema
- */
-
-var consoleSchema = Schema({
- name: String
- , manufacturer: String
- , released: Date
-})
-var Console = mongoose.model('Console', consoleSchema);
-
-/**
- * Game schema
- */
-
-var gameSchema = Schema({
- name: String
- , developer: String
- , released: Date
- , consoles: [{ type: Schema.Types.ObjectId, ref: 'Console' }]
-})
-var Game = mongoose.model('Game', gameSchema);
-
-/**
- * Connect to the console database on localhost with
- * the default port (27017)
- */
-
-mongoose.connect('mongodb://localhost/console', function (err) {
- // if we failed to connect, abort
- if (err) throw err;
-
- // we connected ok
- createData();
-})
-
-/**
- * Data generation
- */
-
-function createData () {
- Console.create({
- name: 'Nintendo 64'
- , manufacturer: 'Nintendo'
- , released: 'September 29, 1996'
- }, {
- name: 'Super Nintendo'
- , manufacturer: 'Nintendo'
- , released: 'August 23, 1991'
- }, function (err, nintendo64, superNintendo) {
- if (err) return done(err);
-
- Game.create({
- name: 'Legend of Zelda: Ocarina of Time'
- , developer: 'Nintendo'
- , released: new Date('November 21, 1998')
- , consoles: [nintendo64]
- }, {
- name: 'Mario Kart'
- , developer: 'Nintendo'
- , released: 'September 1, 1992'
- , consoles: [superNintendo]
- }, function (err) {
- if (err) return done(err);
- example();
- })
- })
-}
-
-/**
- * Population
- */
-
-function example () {
- Game
- .find({})
- .exec(function (err, games) {
- if (err) return done(err);
-
- console.log('found %d games', games.length);
-
- var options = { path: 'consoles', select: 'name released -_id' };
- Game.populate(games, options, function (err, games) {
- if (err) return done(err);
-
- games.forEach(function (game) {
- console.log(
- '"%s" was released for the %s on %s'
- , game.name
- , game.consoles[0].name
- , game.released.toLocaleDateString());
- })
-
- done()
- })
- })
-}
-
-function done (err) {
- if (err) console.error(err);
- Console.remove(function () {
- Game.remove(function () {
- mongoose.disconnect();
- })
- })
-}
diff --git a/node_modules/mongoose/examples/population-options.js b/node_modules/mongoose/examples/population-options.js
deleted file mode 100644
index 7387b3b..0000000
--- a/node_modules/mongoose/examples/population-options.js
+++ /dev/null
@@ -1,124 +0,0 @@
-
-var mongoose = require('mongoose')
-var Schema = mongoose.Schema;
-
-console.log('Running mongoose version %s', mongoose.version);
-
-/**
- * Console schema
- */
-
-var consoleSchema = Schema({
- name: String
- , manufacturer: String
- , released: Date
-})
-var Console = mongoose.model('Console', consoleSchema);
-
-/**
- * Game schema
- */
-
-var gameSchema = Schema({
- name: String
- , developer: String
- , released: Date
- , consoles: [{ type: Schema.Types.ObjectId, ref: 'Console' }]
-})
-var Game = mongoose.model('Game', gameSchema);
-
-/**
- * Connect to the console database on localhost with
- * the default port (27017)
- */
-
-mongoose.connect('mongodb://localhost/console', function (err) {
- // if we failed to connect, abort
- if (err) throw err;
-
- // we connected ok
- createData();
-})
-
-/**
- * Data generation
- */
-
-function createData () {
- Console.create({
- name: 'Nintendo 64'
- , manufacturer: 'Nintendo'
- , released: 'September 29, 1996'
- }, {
- name: 'Super Nintendo'
- , manufacturer: 'Nintendo'
- , released: 'August 23, 1991'
- }, {
- name: 'XBOX 360'
- , manufacturer: 'Microsoft'
- , released: 'November 22, 2005'
- }, function (err, nintendo64, superNintendo, xbox360) {
- if (err) return done(err);
-
- Game.create({
- name: 'Legend of Zelda: Ocarina of Time'
- , developer: 'Nintendo'
- , released: new Date('November 21, 1998')
- , consoles: [nintendo64]
- }, {
- name: 'Mario Kart'
- , developer: 'Nintendo'
- , released: 'September 1, 1992'
- , consoles: [superNintendo]
- }, {
- name: 'Perfect Dark Zero'
- , developer: 'Rare'
- , released: 'November 17, 2005'
- , consoles: [xbox360]
- }, function (err) {
- if (err) return done(err);
- example();
- })
- })
-}
-
-/**
- * Population
- */
-
-function example () {
- Game
- .find({})
- .populate({
- path: 'consoles'
- , match: { manufacturer: 'Nintendo' }
- , select: 'name'
- , options: { comment: 'population' }
- })
- .exec(function (err, games) {
- if (err) return done(err);
-
- games.forEach(function (game) {
- console.log(
- '"%s" was released for the %s on %s'
- , game.name
- , game.consoles.length ? game.consoles[0].name : '??'
- , game.released.toLocaleDateString());
- })
-
- return done();
- })
-}
-
-/**
- * Clean up
- */
-
-function done (err) {
- if (err) console.error(err);
- Console.remove(function () {
- Game.remove(function () {
- mongoose.disconnect();
- })
- })
-}
diff --git a/node_modules/mongoose/examples/population-plain-objects.js b/node_modules/mongoose/examples/population-plain-objects.js
deleted file mode 100644
index 50fc9fa..0000000
--- a/node_modules/mongoose/examples/population-plain-objects.js
+++ /dev/null
@@ -1,96 +0,0 @@
-
-var mongoose = require('mongoose')
-var Schema = mongoose.Schema;
-
-console.log('Running mongoose version %s', mongoose.version);
-
-/**
- * Console schema
- */
-
-var consoleSchema = Schema({
- name: String
- , manufacturer: String
- , released: Date
-})
-var Console = mongoose.model('Console', consoleSchema);
-
-/**
- * Game schema
- */
-
-var gameSchema = Schema({
- name: String
- , developer: String
- , released: Date
- , consoles: [{ type: Schema.Types.ObjectId, ref: 'Console' }]
-})
-var Game = mongoose.model('Game', gameSchema);
-
-/**
- * Connect to the console database on localhost with
- * the default port (27017)
- */
-
-mongoose.connect('mongodb://localhost/console', function (err) {
- // if we failed to connect, abort
- if (err) throw err;
-
- // we connected ok
- createData();
-})
-
-/**
- * Data generation
- */
-
-function createData () {
- Console.create({
- name: 'Nintendo 64'
- , manufacturer: 'Nintendo'
- , released: 'September 29, 1996'
- }, function (err, nintendo64) {
- if (err) return done(err);
-
- Game.create({
- name: 'Legend of Zelda: Ocarina of Time'
- , developer: 'Nintendo'
- , released: new Date('November 21, 1998')
- , consoles: [nintendo64]
- }, function (err) {
- if (err) return done(err);
- example();
- })
- })
-}
-
-/**
- * Population
- */
-
-function example () {
- Game
- .findOne({ name: /^Legend of Zelda/ })
- .populate('consoles')
- .lean() // just return plain objects, not documents wrapped by mongoose
- .exec(function (err, ocinara) {
- if (err) return done(err);
-
- console.log(
- '"%s" was released for the %s on %s'
- , ocinara.name
- , ocinara.consoles[0].name
- , ocinara.released.toLocaleDateString());
-
- done();
- })
-}
-
-function done (err) {
- if (err) console.error(err);
- Console.remove(function () {
- Game.remove(function () {
- mongoose.disconnect();
- })
- })
-}
diff --git a/node_modules/mongoose/examples/population-across-three-collections.js b/node_modules/mongoose/examples/population/population-across-three-collections.js
similarity index 83%
rename from node_modules/mongoose/examples/population-across-three-collections.js
rename to node_modules/mongoose/examples/population/population-across-three-collections.js
index 4bec928..fe6de72 100644
--- a/node_modules/mongoose/examples/population-across-three-collections.js
+++ b/node_modules/mongoose/examples/population/population-across-three-collections.js
@@ -1,6 +1,6 @@
-var assert = require('assert')
-var mongoose = require('../');
+var assert = require('assert');
+var mongoose = require('../../lib');
var Schema = mongoose.Schema;
var ObjectId = mongoose.Types.ObjectId;
@@ -8,7 +8,7 @@ var ObjectId = mongoose.Types.ObjectId;
* Connect to the db
*/
-var dbname = 'testing_populateAdInfinitum_' + require('../lib/utils').random()
+var dbname = 'testing_populateAdInfinitum_' + require('../../lib/utils').random();
mongoose.connect('localhost', dbname);
mongoose.connection.on('error', function() {
console.error('connection error', arguments);
@@ -34,7 +34,7 @@ var blogpost = Schema({
type: Schema.ObjectId,
ref: 'User'
}
-})
+});
var BlogPost = mongoose.model('BlogPost', blogpost);
/**
@@ -42,7 +42,6 @@ var BlogPost = mongoose.model('BlogPost', blogpost);
*/
mongoose.connection.on('open', function() {
-
/**
* Generate data
*/
@@ -71,7 +70,7 @@ mongoose.connection.on('open', function() {
friends: [userIds[0], userIds[1], userIds[2]]
});
- User.create(users, function(err, docs) {
+ User.create(users, function(err) {
assert.ifError(err);
var blogposts = [];
@@ -79,19 +78,19 @@ mongoose.connection.on('open', function() {
title: 'blog 1',
tags: ['fun', 'cool'],
author: userIds[3]
- })
+ });
blogposts.push({
title: 'blog 2',
tags: ['cool'],
author: userIds[1]
- })
+ });
blogposts.push({
title: 'blog 3',
tags: ['fun', 'odd'],
author: userIds[2]
- })
+ });
- BlogPost.create(blogposts, function(err, docs) {
+ BlogPost.create(blogposts, function(err) {
assert.ifError(err);
/**
@@ -99,7 +98,7 @@ mongoose.connection.on('open', function() {
*/
BlogPost
- .find({ tags: 'fun' })
+ .find({tags: 'fun'})
.lean()
.populate('author')
.exec(function(err, docs) {
@@ -112,19 +111,19 @@ mongoose.connection.on('open', function() {
var opts = {
path: 'author.friends',
select: 'name',
- options: { limit: 2 }
- }
+ options: {limit: 2}
+ };
BlogPost.populate(docs, opts, function(err, docs) {
assert.ifError(err);
console.log('populated');
- var s = require('util').inspect(docs, { depth: null })
+ var s = require('util').inspect(docs, {depth: null, colors: true});
console.log(s);
done();
- })
- })
- })
- })
+ });
+ });
+ });
+ });
});
function done(err) {
diff --git a/node_modules/mongoose/examples/population/population-basic.js b/node_modules/mongoose/examples/population/population-basic.js
new file mode 100644
index 0000000..252cc53
--- /dev/null
+++ b/node_modules/mongoose/examples/population/population-basic.js
@@ -0,0 +1,103 @@
+
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+console.log('Running mongoose version %s', mongoose.version);
+
+/**
+ * Console schema
+ */
+
+var consoleSchema = Schema({
+ name: String,
+ manufacturer: String,
+ released: Date
+});
+var Console = mongoose.model('Console', consoleSchema);
+
+/**
+ * Game schema
+ */
+
+var gameSchema = Schema({
+ name: String,
+ developer: String,
+ released: Date,
+ consoles: [{
+ type: Schema.Types.ObjectId,
+ ref: 'Console'
+ }]
+});
+var Game = mongoose.model('Game', gameSchema);
+
+/**
+ * Connect to the console database on localhost with
+ * the default port (27017)
+ */
+
+mongoose.connect('mongodb://localhost/console', function(err) {
+ // if we failed to connect, abort
+ if (err) throw err;
+
+ // we connected ok
+ createData();
+});
+
+/**
+ * Data generation
+ */
+
+function createData() {
+ Console.create(
+ {
+ name: 'Nintendo 64',
+ manufacturer: 'Nintendo',
+ released: 'September 29, 1996'
+ },
+ function(err, nintendo64) {
+ if (err) return done(err);
+
+ Game.create({
+ name: 'Legend of Zelda: Ocarina of Time',
+ developer: 'Nintendo',
+ released: new Date('November 21, 1998'),
+ consoles: [nintendo64]
+ },
+ function(err) {
+ if (err) return done(err);
+ example();
+ });
+ }
+ );
+}
+
+/**
+ * Population
+ */
+
+function example() {
+ Game
+ .findOne({name: /^Legend of Zelda/})
+ .populate('consoles')
+ .exec(function(err, ocinara) {
+ if (err) return done(err);
+
+ console.log(
+ '"%s" was released for the %s on %s',
+ ocinara.name,
+ ocinara.consoles[0].name,
+ ocinara.released.toLocaleDateString()
+ );
+
+ done();
+ });
+}
+
+function done(err) {
+ if (err) console.error(err);
+ Console.remove(function() {
+ Game.remove(function() {
+ mongoose.disconnect();
+ });
+ });
+}
diff --git a/node_modules/mongoose/examples/population/population-of-existing-doc.js b/node_modules/mongoose/examples/population/population-of-existing-doc.js
new file mode 100644
index 0000000..c7eadfe
--- /dev/null
+++ b/node_modules/mongoose/examples/population/population-of-existing-doc.js
@@ -0,0 +1,109 @@
+
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+console.log('Running mongoose version %s', mongoose.version);
+
+/**
+ * Console schema
+ */
+
+var consoleSchema = Schema({
+ name: String,
+ manufacturer: String,
+ released: Date
+});
+var Console = mongoose.model('Console', consoleSchema);
+
+/**
+ * Game schema
+ */
+
+var gameSchema = Schema({
+ name: String,
+ developer: String,
+ released: Date,
+ consoles: [{
+ type: Schema.Types.ObjectId,
+ ref: 'Console'
+ }]
+});
+var Game = mongoose.model('Game', gameSchema);
+
+/**
+ * Connect to the console database on localhost with
+ * the default port (27017)
+ */
+
+mongoose.connect('mongodb://localhost/console', function(err) {
+ // if we failed to connect, abort
+ if (err) throw err;
+
+ // we connected ok
+ createData();
+});
+
+/**
+ * Data generation
+ */
+
+function createData() {
+ Console.create(
+ {
+ name: 'Nintendo 64',
+ manufacturer: 'Nintendo',
+ released: 'September 29, 1996'
+ },
+ function(err, nintendo64) {
+ if (err) return done(err);
+
+ Game.create({
+ name: 'Legend of Zelda: Ocarina of Time',
+ developer: 'Nintendo',
+ released: new Date('November 21, 1998'),
+ consoles: [nintendo64]
+ },
+ function(err) {
+ if (err) return done(err);
+ example();
+ });
+ }
+ );
+}
+
+/**
+ * Population
+ */
+
+function example() {
+ Game
+ .findOne({name: /^Legend of Zelda/})
+ .exec(function(err, ocinara) {
+ if (err) return done(err);
+
+ console.log('"%s" console _id: %s', ocinara.name, ocinara.consoles[0]);
+
+ // population of existing document
+ ocinara.populate('consoles', function(err) {
+ if (err) return done(err);
+
+ console.log(
+ '"%s" was released for the %s on %s',
+ ocinara.name,
+ ocinara.consoles[0].name,
+ ocinara.released.toLocaleDateString()
+ );
+
+ done();
+ });
+ });
+}
+
+function done(err) {
+ if (err) console.error(err);
+ Console.remove(function() {
+ Game.remove(function() {
+ mongoose.disconnect();
+ });
+ });
+}
diff --git a/node_modules/mongoose/examples/population/population-of-multiple-existing-docs.js b/node_modules/mongoose/examples/population/population-of-multiple-existing-docs.js
new file mode 100644
index 0000000..61b4e85
--- /dev/null
+++ b/node_modules/mongoose/examples/population/population-of-multiple-existing-docs.js
@@ -0,0 +1,124 @@
+
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+console.log('Running mongoose version %s', mongoose.version);
+
+/**
+ * Console schema
+ */
+
+var consoleSchema = Schema({
+ name: String,
+ manufacturer: String,
+ released: Date
+});
+var Console = mongoose.model('Console', consoleSchema);
+
+/**
+ * Game schema
+ */
+
+var gameSchema = Schema({
+ name: String,
+ developer: String,
+ released: Date,
+ consoles: [{
+ type: Schema.Types.ObjectId,
+ ref: 'Console'
+ }]
+});
+var Game = mongoose.model('Game', gameSchema);
+
+/**
+ * Connect to the console database on localhost with
+ * the default port (27017)
+ */
+
+mongoose.connect('mongodb://localhost/console', function(err) {
+ // if we failed to connect, abort
+ if (err) throw err;
+
+ // we connected ok
+ createData();
+});
+
+/**
+ * Data generation
+ */
+
+function createData() {
+ Console.create(
+ {
+ name: 'Nintendo 64',
+ manufacturer: 'Nintendo',
+ released: 'September 29, 1996'
+ },
+ {
+ name: 'Super Nintendo',
+ manufacturer: 'Nintendo',
+ released: 'August 23, 1991'
+ },
+ function(err, nintendo64, superNintendo) {
+ if (err) return done(err);
+
+ Game.create(
+ {
+ name: 'Legend of Zelda: Ocarina of Time',
+ developer: 'Nintendo',
+ released: new Date('November 21, 1998'),
+ consoles: [nintendo64]
+ },
+ {
+ name: 'Mario Kart',
+ developer: 'Nintendo',
+ released: 'September 1, 1992',
+ consoles: [superNintendo]
+ },
+ function(err) {
+ if (err) return done(err);
+ example();
+ }
+ );
+ }
+ );
+}
+
+/**
+ * Population
+ */
+
+function example() {
+ Game
+ .find({})
+ .exec(function(err, games) {
+ if (err) return done(err);
+
+ console.log('found %d games', games.length);
+
+ var options = {path: 'consoles', select: 'name released -_id'};
+ Game.populate(games, options, function(err, games) {
+ if (err) return done(err);
+
+ games.forEach(function(game) {
+ console.log(
+ '"%s" was released for the %s on %s',
+ game.name,
+ game.consoles[0].name,
+ game.released.toLocaleDateString()
+ );
+ });
+
+ done();
+ });
+ });
+}
+
+function done(err) {
+ if (err) console.error(err);
+ Console.remove(function() {
+ Game.remove(function() {
+ mongoose.disconnect();
+ });
+ });
+}
diff --git a/node_modules/mongoose/examples/population/population-options.js b/node_modules/mongoose/examples/population/population-options.js
new file mode 100644
index 0000000..59cfd1e
--- /dev/null
+++ b/node_modules/mongoose/examples/population/population-options.js
@@ -0,0 +1,138 @@
+
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+console.log('Running mongoose version %s', mongoose.version);
+
+/**
+ * Console schema
+ */
+
+var consoleSchema = Schema({
+ name: String,
+ manufacturer: String,
+ released: Date
+});
+var Console = mongoose.model('Console', consoleSchema);
+
+/**
+ * Game schema
+ */
+
+var gameSchema = Schema({
+ name: String,
+ developer: String,
+ released: Date,
+ consoles: [{
+ type: Schema.Types.ObjectId,
+ ref: 'Console'
+ }]
+});
+var Game = mongoose.model('Game', gameSchema);
+
+/**
+ * Connect to the console database on localhost with
+ * the default port (27017)
+ */
+
+mongoose.connect('mongodb://localhost/console', function(err) {
+ // if we failed to connect, abort
+ if (err) throw err;
+
+ // we connected ok
+ createData();
+});
+
+/**
+ * Data generation
+ */
+
+function createData() {
+ Console.create(
+ {
+ name: 'Nintendo 64',
+ manufacturer: 'Nintendo',
+ released: 'September 29, 1996'
+ },
+ {
+ name: 'Super Nintendo',
+ manufacturer: 'Nintendo',
+ released: 'August 23, 1991'
+ },
+ {
+ name: 'XBOX 360',
+ manufacturer: 'Microsoft',
+ released: 'November 22, 2005'
+ },
+ function(err, nintendo64, superNintendo, xbox360) {
+ if (err) return done(err);
+
+ Game.create(
+ {
+ name: 'Legend of Zelda: Ocarina of Time',
+ developer: 'Nintendo',
+ released: new Date('November 21, 1998'),
+ consoles: [nintendo64]
+ },
+ {
+ name: 'Mario Kart',
+ developer: 'Nintendo',
+ released: 'September 1, 1992',
+ consoles: [superNintendo]
+ },
+ {
+ name: 'Perfect Dark Zero',
+ developer: 'Rare',
+ released: 'November 17, 2005',
+ consoles: [xbox360]
+ },
+ function(err) {
+ if (err) return done(err);
+ example();
+ }
+ );
+ }
+ );
+}
+
+/**
+ * Population
+ */
+
+function example() {
+ Game
+ .find({})
+ .populate({
+ path: 'consoles',
+ match: {manufacturer: 'Nintendo'},
+ select: 'name',
+ options: {comment: 'population'}
+ })
+ .exec(function(err, games) {
+ if (err) return done(err);
+
+ games.forEach(function(game) {
+ console.log(
+ '"%s" was released for the %s on %s',
+ game.name,
+ game.consoles.length ? game.consoles[0].name : '??',
+ game.released.toLocaleDateString()
+ );
+ });
+
+ return done();
+ });
+}
+
+/**
+ * Clean up
+ */
+
+function done(err) {
+ if (err) console.error(err);
+ Console.remove(function() {
+ Game.remove(function() {
+ mongoose.disconnect();
+ });
+ });
+}
diff --git a/node_modules/mongoose/examples/population/population-plain-objects.js b/node_modules/mongoose/examples/population/population-plain-objects.js
new file mode 100644
index 0000000..ba849dc
--- /dev/null
+++ b/node_modules/mongoose/examples/population/population-plain-objects.js
@@ -0,0 +1,106 @@
+
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+console.log('Running mongoose version %s', mongoose.version);
+
+/**
+ * Console schema
+ */
+
+var consoleSchema = Schema({
+ name: String,
+ manufacturer: String,
+ released: Date
+});
+var Console = mongoose.model('Console', consoleSchema);
+
+/**
+ * Game schema
+ */
+
+var gameSchema = Schema({
+ name: String,
+ developer: String,
+ released: Date,
+ consoles: [{
+ type: Schema.Types.ObjectId,
+ ref: 'Console'
+ }]
+});
+var Game = mongoose.model('Game', gameSchema);
+
+/**
+ * Connect to the console database on localhost with
+ * the default port (27017)
+ */
+
+mongoose.connect('mongodb://localhost/console', function(err) {
+ // if we failed to connect, abort
+ if (err) throw err;
+
+ // we connected ok
+ createData();
+});
+
+/**
+ * Data generation
+ */
+
+function createData() {
+ Console.create(
+ {
+ name: 'Nintendo 64',
+ manufacturer: 'Nintendo',
+ released: 'September 29, 1996'
+ },
+ function(err, nintendo64) {
+ if (err) return done(err);
+
+ Game.create(
+ {
+ name: 'Legend of Zelda: Ocarina of Time',
+ developer: 'Nintendo',
+ released: new Date('November 21, 1998'),
+ consoles: [nintendo64]
+ },
+ function(err) {
+ if (err) return done(err);
+ example();
+ }
+ );
+ }
+ );
+}
+
+/**
+ * Population
+ */
+
+function example() {
+ Game
+ .findOne({name: /^Legend of Zelda/})
+ .populate('consoles')
+ .lean() // just return plain objects, not documents wrapped by mongoose
+ .exec(function(err, ocinara) {
+ if (err) return done(err);
+
+ console.log(
+ '"%s" was released for the %s on %s',
+ ocinara.name,
+ ocinara.consoles[0].name,
+ ocinara.released.toLocaleDateString()
+ );
+
+ done();
+ });
+}
+
+function done(err) {
+ if (err) console.error(err);
+ Console.remove(function() {
+ Game.remove(function() {
+ mongoose.disconnect();
+ });
+ });
+}
diff --git a/node_modules/mongoose/examples/promises/package.json b/node_modules/mongoose/examples/promises/package.json
new file mode 100644
index 0000000..1983250
--- /dev/null
+++ b/node_modules/mongoose/examples/promises/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "promise-example",
+ "private": "true",
+ "version": "0.0.0",
+ "description": "deps for promise example",
+ "main": "promise.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": { "async": "*" },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
diff --git a/node_modules/mongoose/examples/promises/person.js b/node_modules/mongoose/examples/promises/person.js
new file mode 100644
index 0000000..40e2bf1
--- /dev/null
+++ b/node_modules/mongoose/examples/promises/person.js
@@ -0,0 +1,15 @@
+
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date
+ });
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/promises/promise.js b/node_modules/mongoose/examples/promises/promise.js
new file mode 100644
index 0000000..3f17206
--- /dev/null
+++ b/node_modules/mongoose/examples/promises/promise.js
@@ -0,0 +1,94 @@
+// import async to make control flow simplier
+var async = require('async');
+
+// import the rest of the normal stuff
+var mongoose = require('../../lib');
+
+require('./person.js')();
+
+var Person = mongoose.model('Person');
+
+// define some dummy data
+var data = [
+ {
+ name: 'bill',
+ age: 25,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 25))
+ },
+ {
+ name: 'mary',
+ age: 30,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 30))
+ },
+ {
+ name: 'bob',
+ age: 21,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 21))
+ },
+ {
+ name: 'lilly',
+ age: 26,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 26))
+ },
+ {
+ name: 'alucard',
+ age: 1000,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 1000))
+ }
+];
+
+
+mongoose.connect('mongodb://localhost/persons', function(err) {
+ if (err) {
+ throw err;
+ }
+
+ // create all of the dummy people
+ async.each(data, function(item, cb) {
+ Person.create(item, cb);
+ }, function(err) {
+ if (err) {
+ // handle error
+ }
+
+ // create a promise (get one from the query builder)
+ var prom = Person.find({age: {$lt: 1000}}).exec();
+
+ // add a callback on the promise. This will be called on both error and
+ // complete
+ prom.addBack(function() {
+ console.log('completed');
+ });
+
+ // add a callback that is only called on complete (success) events
+ prom.addCallback(function() {
+ console.log('Successful Completion!');
+ });
+
+ // add a callback that is only called on err (rejected) events
+ prom.addErrback(function() {
+ console.log('Fail Boat');
+ });
+
+ // you can chain things just like in the promise/A+ spec
+ // note: each then() is returning a new promise, so the above methods
+ // that we defined will all fire after the initial promise is fulfilled
+ prom.then(function(people) {
+ // just getting the stuff for the next query
+ var ids = people.map(function(p) {
+ return p._id;
+ });
+
+ // return the next promise
+ return Person.find({_id: {$nin: ids}}).exec();
+ }).then(function(oldest) {
+ console.log('Oldest person is: %s', oldest);
+ }).then(cleanup);
+ });
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/querybuilder/package.json b/node_modules/mongoose/examples/querybuilder/package.json
new file mode 100644
index 0000000..1a3450a
--- /dev/null
+++ b/node_modules/mongoose/examples/querybuilder/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "query-builder-example",
+ "private": "true",
+ "version": "0.0.0",
+ "description": "deps for query builder example",
+ "main": "querybuilder.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": { "async": "*" },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
diff --git a/node_modules/mongoose/examples/querybuilder/person.js b/node_modules/mongoose/examples/querybuilder/person.js
new file mode 100644
index 0000000..40e2bf1
--- /dev/null
+++ b/node_modules/mongoose/examples/querybuilder/person.js
@@ -0,0 +1,15 @@
+
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date
+ });
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/querybuilder/querybuilder.js b/node_modules/mongoose/examples/querybuilder/querybuilder.js
new file mode 100644
index 0000000..6f8645b
--- /dev/null
+++ b/node_modules/mongoose/examples/querybuilder/querybuilder.js
@@ -0,0 +1,79 @@
+
+// import async to make control flow simplier
+var async = require('async');
+
+// import the rest of the normal stuff
+var mongoose = require('../../lib');
+
+require('./person.js')();
+
+var Person = mongoose.model('Person');
+
+// define some dummy data
+var data = [
+ {
+ name: 'bill',
+ age: 25,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 25))
+ },
+ {
+ name: 'mary',
+ age: 30,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 30))
+ },
+ {
+ name: 'bob',
+ age: 21,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 21))
+ },
+ {
+ name: 'lilly',
+ age: 26,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 26))
+ },
+ {
+ name: 'alucard',
+ age: 1000,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 1000))
+ }
+];
+
+
+mongoose.connect('mongodb://localhost/persons', function(err) {
+ if (err) throw err;
+
+ // create all of the dummy people
+ async.each(data, function(item, cb) {
+ Person.create(item, cb);
+ }, function(err) {
+ if (err) throw err;
+
+ // when querying data, instead of providing a callback, you can instead
+ // leave that off and get a query object returned
+ var query = Person.find({age: {$lt: 1000}});
+
+ // this allows you to continue applying modifiers to it
+ query.sort('birthday');
+ query.select('name');
+
+ // you can chain them together as well
+ // a full list of methods can be found:
+ // http://mongoosejs.com/docs/api.html#query-js
+ query.where('age').gt(21);
+
+ // finally, when ready to execute the query, call the exec() function
+ query.exec(function(err, results) {
+ if (err) throw err;
+
+ console.log(results);
+
+ cleanup();
+ });
+ });
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/replicasets/package.json b/node_modules/mongoose/examples/replicasets/package.json
new file mode 100644
index 0000000..927dfd2
--- /dev/null
+++ b/node_modules/mongoose/examples/replicasets/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "replica-set-example",
+ "private": "true",
+ "version": "0.0.0",
+ "description": "deps for replica set example",
+ "main": "querybuilder.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": { "async": "*" },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
diff --git a/node_modules/mongoose/examples/replicasets/person.js b/node_modules/mongoose/examples/replicasets/person.js
new file mode 100644
index 0000000..40e2bf1
--- /dev/null
+++ b/node_modules/mongoose/examples/replicasets/person.js
@@ -0,0 +1,15 @@
+
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date
+ });
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/replicasets/replica-sets.js b/node_modules/mongoose/examples/replicasets/replica-sets.js
new file mode 100644
index 0000000..9ebac1b
--- /dev/null
+++ b/node_modules/mongoose/examples/replicasets/replica-sets.js
@@ -0,0 +1,71 @@
+
+// import async to make control flow simplier
+var async = require('async');
+
+// import the rest of the normal stuff
+var mongoose = require('../../lib');
+
+require('./person.js')();
+
+var Person = mongoose.model('Person');
+
+// define some dummy data
+var data = [
+ {
+ name: 'bill',
+ age: 25,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 25))
+ },
+ {
+ name: 'mary',
+ age: 30,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 30))
+ },
+ {
+ name: 'bob',
+ age: 21,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 21))
+ },
+ {
+ name: 'lilly',
+ age: 26,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 26))
+ },
+ {
+ name: 'alucard',
+ age: 1000,
+ birthday: new Date().setFullYear((new Date().getFullYear() - 1000))
+ }
+];
+
+
+// to connect to a replica set, pass in the comma delimited uri and optionally
+// any connection options such as the rs_name.
+var opts = {
+ replSet: {rs_name: 'rs0'}
+};
+mongoose.connect('mongodb://localhost:27018/persons,localhost:27019,localhost:27020', opts, function(err) {
+ if (err) throw err;
+
+ // create all of the dummy people
+ async.each(data, function(item, cb) {
+ Person.create(item, cb);
+ }, function(err) {
+ if (err) {
+ // handle error
+ }
+
+ // create and delete some data
+ var prom = Person.find({age: {$lt: 1000}}).exec();
+
+ prom.then(function(people) {
+ console.log('young people: %s', people);
+ }).then(cleanup);
+ });
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/examples/schema.js b/node_modules/mongoose/examples/schema.js
deleted file mode 100644
index d108d05..0000000
--- a/node_modules/mongoose/examples/schema.js
+++ /dev/null
@@ -1,102 +0,0 @@
-
-/**
- * Module dependencies.
- */
-
-var mongoose = require('mongoose')
- , Schema = mongoose.Schema;
-
-/**
- * Schema definition
- */
-
-// recursive embedded-document schema
-
-var Comment = new Schema();
-
-Comment.add({
- title : { type: String, index: true }
- , date : Date
- , body : String
- , comments : [Comment]
-});
-
-var BlogPost = new Schema({
- title : { type: String, index: true }
- , slug : { type: String, lowercase: true, trim: true }
- , date : Date
- , buf : Buffer
- , comments : [Comment]
- , creator : Schema.ObjectId
-});
-
-var Person = new Schema({
- name: {
- first: String
- , last : String
- }
- , email: { type: String, required: true, index: { unique: true, sparse: true } }
- , alive: Boolean
-});
-
-/**
- * Accessing a specific schema type by key
- */
-
-BlogPost.path('date')
-.default(function(){
- return new Date()
- })
-.set(function(v){
- return v == 'now' ? new Date() : v;
- });
-
-/**
- * Pre hook.
- */
-
-BlogPost.pre('save', function(next, done){
- emailAuthor(done); // some async function
- next();
-});
-
-/**
- * Methods
- */
-
-BlogPost.methods.findCreator = function (callback) {
- return this.db.model('Person').findById(this.creator, callback);
-}
-
-BlogPost.statics.findByTitle = function (title, callback) {
- return this.find({ title: title }, callback);
-}
-
-BlogPost.methods.expressiveQuery = function (creator, date, callback) {
- return this.find('creator', creator).where('date').gte(date).run(callback);
-}
-
-/**
- * Plugins
- */
-
-function slugGenerator (options){
- options = options || {};
- var key = options.key || 'title';
-
- return function slugGenerator(schema){
- schema.path(key).set(function(v){
- this.slug = v.toLowerCase().replace(/[^a-z0-9]/g, '').replace(/-+/g, '');
- return v;
- });
- };
-};
-
-BlogPost.plugin(slugGenerator());
-
-/**
- * Define model.
- */
-
-mongoose.model('BlogPost', BlogPost);
-mongoose.model('Person', Person);
diff --git a/node_modules/mongoose/examples/schema/schema.js b/node_modules/mongoose/examples/schema/schema.js
new file mode 100644
index 0000000..5bc99ae
--- /dev/null
+++ b/node_modules/mongoose/examples/schema/schema.js
@@ -0,0 +1,119 @@
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('../../lib'),
+ Schema = mongoose.Schema;
+
+/**
+ * Schema definition
+ */
+
+// recursive embedded-document schema
+
+var Comment = new Schema();
+
+Comment.add({
+ title: {
+ type: String,
+ index: true
+ },
+ date: Date,
+ body: String,
+ comments: [Comment]
+});
+
+var BlogPost = new Schema({
+ title: {
+ type: String,
+ index: true
+ },
+ slug: {
+ type: String,
+ lowercase: true,
+ trim: true
+ },
+ date: Date,
+ buf: Buffer,
+ comments: [Comment],
+ creator: Schema.ObjectId
+});
+
+var Person = new Schema({
+ name: {
+ first: String,
+ last: String
+ },
+ email: {
+ type: String,
+ required: true,
+ index: {
+ unique: true,
+ sparse: true
+ }
+ },
+ alive: Boolean
+});
+
+/**
+ * Accessing a specific schema type by key
+ */
+
+BlogPost.path('date')
+.default(function() {
+ return new Date();
+})
+.set(function(v) {
+ return v === 'now' ? new Date() : v;
+});
+
+/**
+ * Pre hook.
+ */
+
+BlogPost.pre('save', function(next, done) {
+ /* global emailAuthor */
+ emailAuthor(done); // some async function
+ next();
+});
+
+/**
+ * Methods
+ */
+
+BlogPost.methods.findCreator = function(callback) {
+ return this.db.model('Person').findById(this.creator, callback);
+};
+
+BlogPost.statics.findByTitle = function(title, callback) {
+ return this.find({title: title}, callback);
+};
+
+BlogPost.methods.expressiveQuery = function(creator, date, callback) {
+ return this.find('creator', creator).where('date').gte(date).run(callback);
+};
+
+/**
+ * Plugins
+ */
+
+function slugGenerator(options) {
+ options = options || {};
+ var key = options.key || 'title';
+
+ return function slugGenerator(schema) {
+ schema.path(key).set(function(v) {
+ this.slug = v.toLowerCase().replace(/[^a-z0-9]/g, '').replace(/-+/g, '');
+ return v;
+ });
+ };
+}
+
+BlogPost.plugin(slugGenerator());
+
+/**
+ * Define model.
+ */
+
+mongoose.model('BlogPost', BlogPost);
+mongoose.model('Person', Person);
diff --git a/node_modules/mongoose/examples/schema/storing-schemas-as-json/index.js b/node_modules/mongoose/examples/schema/storing-schemas-as-json/index.js
new file mode 100644
index 0000000..284b478
--- /dev/null
+++ b/node_modules/mongoose/examples/schema/storing-schemas-as-json/index.js
@@ -0,0 +1,27 @@
+
+// modules
+var mongoose = require('../../../lib');
+var Schema = mongoose.Schema;
+
+// parse json
+var raw = require('./schema.json');
+
+// create a schema
+var timeSignatureSchema = Schema(raw);
+
+// compile the model
+var TimeSignature = mongoose.model('TimeSignatures', timeSignatureSchema);
+
+// create a TimeSignature document
+var threeFour = new TimeSignature({
+ count: 3,
+ unit: 4,
+ description: '3/4',
+ additive: false,
+ created: new Date,
+ links: ['http://en.wikipedia.org/wiki/Time_signature'],
+ user_id: '518d31a0ef32bbfa853a9814'
+});
+
+// print its description
+console.log(threeFour);
diff --git a/node_modules/mongoose/examples/schema/storing-schemas-as-json/schema.json b/node_modules/mongoose/examples/schema/storing-schemas-as-json/schema.json
new file mode 100644
index 0000000..5afc626
--- /dev/null
+++ b/node_modules/mongoose/examples/schema/storing-schemas-as-json/schema.json
@@ -0,0 +1,9 @@
+{
+ "count": "number",
+ "unit": "number",
+ "description": "string",
+ "links": ["string"],
+ "created": "date",
+ "additive": "boolean",
+ "user_id": "ObjectId"
+}
diff --git a/node_modules/mongoose/examples/statics/person.js b/node_modules/mongoose/examples/statics/person.js
new file mode 100644
index 0000000..a93b8c6
--- /dev/null
+++ b/node_modules/mongoose/examples/statics/person.js
@@ -0,0 +1,20 @@
+// import the necessary modules
+var mongoose = require('../../lib');
+var Schema = mongoose.Schema;
+
+// create an export function to encapsulate the model creation
+module.exports = function() {
+ // define schema
+ var PersonSchema = new Schema({
+ name: String,
+ age: Number,
+ birthday: Date
+ });
+
+ // define a static
+ PersonSchema.statics.findPersonByName = function(name, cb) {
+ this.find({name: new RegExp(name, 'i')}, cb);
+ };
+
+ mongoose.model('Person', PersonSchema);
+};
diff --git a/node_modules/mongoose/examples/statics/statics.js b/node_modules/mongoose/examples/statics/statics.js
new file mode 100644
index 0000000..610b2aa
--- /dev/null
+++ b/node_modules/mongoose/examples/statics/statics.js
@@ -0,0 +1,41 @@
+var mongoose = require('../../lib');
+
+
+// import the schema
+require('./person.js')();
+
+// grab the person model object
+var Person = mongoose.model('Person');
+
+// connect to a server to do a quick write / read example
+
+mongoose.connect('mongodb://localhost/persons', function(err) {
+ if (err) {
+ throw err;
+ }
+
+ Person.create({name: 'bill', age: 25, birthday: new Date().setFullYear((new Date().getFullYear() - 25))},
+ function(err, bill) {
+ if (err) {
+ throw err;
+ }
+ console.log('People added to db: %s', bill.toString());
+
+ // using the static
+ Person.findPersonByName('bill', function(err, result) {
+ if (err) {
+ throw err;
+ }
+
+ console.log(result);
+ cleanup();
+ });
+ }
+ );
+});
+
+function cleanup() {
+ Person.remove(function() {
+ mongoose.disconnect();
+ });
+}
diff --git a/node_modules/mongoose/lib/ES6Promise.js b/node_modules/mongoose/lib/ES6Promise.js
new file mode 100644
index 0000000..13371ac
--- /dev/null
+++ b/node_modules/mongoose/lib/ES6Promise.js
@@ -0,0 +1,26 @@
+/**
+ * ES6 Promise wrapper constructor.
+ *
+ * Promises are returned from executed queries. Example:
+ *
+ * var query = Candy.find({ bar: true });
+ * var promise = query.exec();
+ *
+ * DEPRECATED. Mongoose 5.0 will use native promises by default (or bluebird,
+ * if native promises are not present) but still
+ * support plugging in your own ES6-compatible promises library. Mongoose 5.0
+ * will **not** support mpromise.
+ *
+ * @param {Function} fn a function which will be called when the promise is resolved that accepts `fn(err, ...){}` as signature
+ * @api public
+ */
+
+function ES6Promise() {
+ throw new Error('Can\'t use ES6 promise with mpromise style constructor');
+}
+
+ES6Promise.use = function(Promise) {
+ ES6Promise.ES6 = Promise;
+};
+
+module.exports = ES6Promise;
diff --git a/node_modules/mongoose/lib/aggregate.js b/node_modules/mongoose/lib/aggregate.js
new file mode 100644
index 0000000..bb24e88
--- /dev/null
+++ b/node_modules/mongoose/lib/aggregate.js
@@ -0,0 +1,685 @@
+/*!
+ * Module dependencies
+ */
+
+var util = require('util');
+var utils = require('./utils');
+var PromiseProvider = require('./promise_provider');
+var Query = require('./query');
+var read = Query.prototype.read;
+
+/**
+ * Aggregate constructor used for building aggregation pipelines.
+ *
+ * ####Example:
+ *
+ * new Aggregate();
+ * new Aggregate({ $project: { a: 1, b: 1 } });
+ * new Aggregate({ $project: { a: 1, b: 1 } }, { $skip: 5 });
+ * new Aggregate([{ $project: { a: 1, b: 1 } }, { $skip: 5 }]);
+ *
+ * Returned when calling Model.aggregate().
+ *
+ * ####Example:
+ *
+ * Model
+ * .aggregate({ $match: { age: { $gte: 21 }}})
+ * .unwind('tags')
+ * .exec(callback)
+ *
+ * ####Note:
+ *
+ * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
+ * - Requires MongoDB >= 2.1
+ * - Mongoose does **not** cast pipeline stages. `new Aggregate({ $match: { _id: '00000000000000000000000a' } });` will not work unless `_id` is a string in the database. Use `new Aggregate({ $match: { _id: mongoose.Types.ObjectId('00000000000000000000000a') } });` instead.
+ *
+ * @see MongoDB http://docs.mongodb.org/manual/applications/aggregation/
+ * @see driver http://mongodb.github.com/node-mongodb-native/api-generated/collection.html#aggregate
+ * @param {Object|Array} [ops] aggregation operator(s) or operator array
+ * @api public
+ */
+
+function Aggregate() {
+ this._pipeline = [];
+ this._model = undefined;
+ this.options = undefined;
+
+ if (arguments.length === 1 && util.isArray(arguments[0])) {
+ this.append.apply(this, arguments[0]);
+ } else {
+ this.append.apply(this, arguments);
+ }
+}
+
+/**
+ * Binds this aggregate to a model.
+ *
+ * @param {Model} model the model to which the aggregate is to be bound
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.model = function(model) {
+ this._model = model;
+ return this;
+};
+
+/**
+ * Appends new operators to this aggregate pipeline
+ *
+ * ####Examples:
+ *
+ * aggregate.append({ $project: { field: 1 }}, { $limit: 2 });
+ *
+ * // or pass an array
+ * var pipeline = [{ $match: { daw: 'Logic Audio X' }} ];
+ * aggregate.append(pipeline);
+ *
+ * @param {Object} ops operator(s) to append
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.append = function() {
+ var args = (arguments.length === 1 && util.isArray(arguments[0]))
+ ? arguments[0]
+ : utils.args(arguments);
+
+ if (!args.every(isOperator)) {
+ throw new Error('Arguments must be aggregate pipeline operators');
+ }
+
+ this._pipeline = this._pipeline.concat(args);
+
+ return this;
+};
+
+/**
+ * Appends a new $project operator to this aggregate pipeline.
+ *
+ * Mongoose query [selection syntax](#query_Query-select) is also supported.
+ *
+ * ####Examples:
+ *
+ * // include a, include b, exclude _id
+ * aggregate.project("a b -_id");
+ *
+ * // or you may use object notation, useful when
+ * // you have keys already prefixed with a "-"
+ * aggregate.project({a: 1, b: 1, _id: 0});
+ *
+ * // reshaping documents
+ * aggregate.project({
+ * newField: '$b.nested'
+ * , plusTen: { $add: ['$val', 10]}
+ * , sub: {
+ * name: '$a'
+ * }
+ * })
+ *
+ * // etc
+ * aggregate.project({ salary_k: { $divide: [ "$salary", 1000 ] } });
+ *
+ * @param {Object|String} arg field specification
+ * @see projection http://docs.mongodb.org/manual/reference/aggregation/project/
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.project = function(arg) {
+ var fields = {};
+
+ if (typeof arg === 'object' && !util.isArray(arg)) {
+ Object.keys(arg).forEach(function(field) {
+ fields[field] = arg[field];
+ });
+ } else if (arguments.length === 1 && typeof arg === 'string') {
+ arg.split(/\s+/).forEach(function(field) {
+ if (!field) {
+ return;
+ }
+ var include = field[0] === '-' ? 0 : 1;
+ if (include === 0) {
+ field = field.substring(1);
+ }
+ fields[field] = include;
+ });
+ } else {
+ throw new Error('Invalid project() argument. Must be string or object');
+ }
+
+ return this.append({$project: fields});
+};
+
+/**
+ * Appends a new custom $group operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.group({ _id: "$department" });
+ *
+ * @see $group http://docs.mongodb.org/manual/reference/aggregation/group/
+ * @method group
+ * @memberOf Aggregate
+ * @param {Object} arg $group operator contents
+ * @return {Aggregate}
+ * @api public
+ */
+
+/**
+ * Appends a new custom $match operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.match({ department: { $in: [ "sales", "engineering" } } });
+ *
+ * @see $match http://docs.mongodb.org/manual/reference/aggregation/match/
+ * @method match
+ * @memberOf Aggregate
+ * @param {Object} arg $match operator contents
+ * @return {Aggregate}
+ * @api public
+ */
+
+/**
+ * Appends a new $skip operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.skip(10);
+ *
+ * @see $skip http://docs.mongodb.org/manual/reference/aggregation/skip/
+ * @method skip
+ * @memberOf Aggregate
+ * @param {Number} num number of records to skip before next stage
+ * @return {Aggregate}
+ * @api public
+ */
+
+/**
+ * Appends a new $limit operator to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.limit(10);
+ *
+ * @see $limit http://docs.mongodb.org/manual/reference/aggregation/limit/
+ * @method limit
+ * @memberOf Aggregate
+ * @param {Number} num maximum number of records to pass to the next stage
+ * @return {Aggregate}
+ * @api public
+ */
+
+/**
+ * Appends a new $geoNear operator to this aggregate pipeline.
+ *
+ * ####NOTE:
+ *
+ * **MUST** be used as the first operator in the pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.near({
+ * near: [40.724, -73.997],
+ * distanceField: "dist.calculated", // required
+ * maxDistance: 0.008,
+ * query: { type: "public" },
+ * includeLocs: "dist.location",
+ * uniqueDocs: true,
+ * num: 5
+ * });
+ *
+ * @see $geoNear http://docs.mongodb.org/manual/reference/aggregation/geoNear/
+ * @method near
+ * @memberOf Aggregate
+ * @param {Object} parameters
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.near = function(arg) {
+ var op = {};
+ op.$geoNear = arg;
+ return this.append(op);
+};
+
+/*!
+ * define methods
+ */
+
+'group match skip limit out'.split(' ').forEach(function($operator) {
+ Aggregate.prototype[$operator] = function(arg) {
+ var op = {};
+ op['$' + $operator] = arg;
+ return this.append(op);
+ };
+});
+
+/**
+ * Appends new custom $unwind operator(s) to this aggregate pipeline.
+ *
+ * Note that the `$unwind` operator requires the path name to start with '$'.
+ * Mongoose will prepend '$' if the specified field doesn't start '$'.
+ *
+ * ####Examples:
+ *
+ * aggregate.unwind("tags");
+ * aggregate.unwind("a", "b", "c");
+ *
+ * @see $unwind http://docs.mongodb.org/manual/reference/aggregation/unwind/
+ * @param {String} fields the field(s) to unwind
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.unwind = function() {
+ var args = utils.args(arguments);
+
+ var res = [];
+ for (var i = 0; i < args.length; ++i) {
+ var arg = args[i];
+ if (arg && typeof arg === 'object') {
+ res.push({ $unwind: arg });
+ } else if (typeof arg === 'string') {
+ res.push({
+ $unwind: (arg && arg.charAt(0) === '$') ? arg : '$' + arg
+ });
+ } else {
+ throw new Error('Invalid arg "' + arg + '" to unwind(), ' +
+ 'must be string or object');
+ }
+ }
+
+ return this.append.apply(this, res);
+};
+
+/**
+ * Appends new custom $lookup operator(s) to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.lookup({ from: 'users', localField: 'userId', foreignField: '_id', as: 'users' });
+ *
+ * @see $lookup https://docs.mongodb.org/manual/reference/operator/aggregation/lookup/#pipe._S_lookup
+ * @param {Object} options to $lookup as described in the above link
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.lookup = function(options) {
+ return this.append({$lookup: options});
+};
+
+/**
+ * Appends new custom $sample operator(s) to this aggregate pipeline.
+ *
+ * ####Examples:
+ *
+ * aggregate.sample(3); // Add a pipeline that picks 3 random documents
+ *
+ * @see $sample https://docs.mongodb.org/manual/reference/operator/aggregation/sample/#pipe._S_sample
+ * @param {Number} size number of random documents to pick
+ * @return {Aggregate}
+ * @api public
+ */
+
+Aggregate.prototype.sample = function(size) {
+ return this.append({$sample: {size: size}});
+};
+
+/**
+ * Appends a new $sort operator to this aggregate pipeline.
+ *
+ * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
+ *
+ * If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending.
+ *
+ * ####Examples:
+ *
+ * // these are equivalent
+ * aggregate.sort({ field: 'asc', test: -1 });
+ * aggregate.sort('field -test');
+ *
+ * @see $sort http://docs.mongodb.org/manual/reference/aggregation/sort/
+ * @param {Object|String} arg
+ * @return {Aggregate} this
+ * @api public
+ */
+
+Aggregate.prototype.sort = function(arg) {
+ // TODO refactor to reuse the query builder logic
+
+ var sort = {};
+
+ if (arg.constructor.name === 'Object') {
+ var desc = ['desc', 'descending', -1];
+ Object.keys(arg).forEach(function(field) {
+ sort[field] = desc.indexOf(arg[field]) === -1 ? 1 : -1;
+ });
+ } else if (arguments.length === 1 && typeof arg === 'string') {
+ arg.split(/\s+/).forEach(function(field) {
+ if (!field) {
+ return;
+ }
+ var ascend = field[0] === '-' ? -1 : 1;
+ if (ascend === -1) {
+ field = field.substring(1);
+ }
+ sort[field] = ascend;
+ });
+ } else {
+ throw new TypeError('Invalid sort() argument. Must be a string or object.');
+ }
+
+ return this.append({$sort: sort});
+};
+
+/**
+ * Sets the readPreference option for the aggregation query.
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).read('primaryPreferred').exec(callback)
+ *
+ * @param {String} pref one of the listed preference options or their aliases
+ * @param {Array} [tags] optional tags for this query
+ * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
+ * @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences
+ */
+
+Aggregate.prototype.read = function(pref, tags) {
+ if (!this.options) {
+ this.options = {};
+ }
+ read.call(this, pref, tags);
+ return this;
+};
+
+/**
+ * Execute the aggregation with explain
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).explain(callback)
+ *
+ * @param {Function} callback
+ * @return {Promise}
+ */
+
+Aggregate.prototype.explain = function(callback) {
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve, reject) {
+ if (!_this._pipeline.length) {
+ var err = new Error('Aggregate has empty pipeline');
+ if (callback) {
+ callback(err);
+ }
+ reject(err);
+ return;
+ }
+
+ prepareDiscriminatorPipeline(_this);
+
+ _this._model
+ .collection
+ .aggregate(_this._pipeline, _this.options || {})
+ .explain(function(error, result) {
+ if (error) {
+ if (callback) {
+ callback(error);
+ }
+ reject(error);
+ return;
+ }
+
+ if (callback) {
+ callback(null, result);
+ }
+ resolve(result);
+ });
+ });
+};
+
+/**
+ * Sets the allowDiskUse option for the aggregation query (ignored for < 2.6.0)
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).allowDiskUse(true).exec(callback)
+ *
+ * @param {Boolean} value Should tell server it can use hard drive to store data during aggregation.
+ * @param {Array} [tags] optional tags for this query
+ * @see mongodb http://docs.mongodb.org/manual/reference/command/aggregate/
+ */
+
+Aggregate.prototype.allowDiskUse = function(value) {
+ if (!this.options) {
+ this.options = {};
+ }
+ this.options.allowDiskUse = value;
+ return this;
+};
+
+/**
+ * Sets the cursor option option for the aggregation query (ignored for < 2.6.0).
+ * Note the different syntax below: .exec() returns a cursor object, and no callback
+ * is necessary.
+ *
+ * ####Example:
+ *
+ * var cursor = Model.aggregate(..).cursor({ batchSize: 1000 }).exec();
+ * cursor.each(function(error, doc) {
+ * // use doc
+ * });
+ *
+ * @param {Object} options set the cursor batch size
+ * @see mongodb http://mongodb.github.io/node-mongodb-native/2.0/api/AggregationCursor.html
+ */
+
+Aggregate.prototype.cursor = function(options) {
+ if (!this.options) {
+ this.options = {};
+ }
+ this.options.cursor = options || {};
+ return this;
+};
+
+/**
+ * Adds a [cursor flag](http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#addCursorFlag)
+ *
+ * ####Example:
+ *
+ * var cursor = Model.aggregate(..).cursor({ batchSize: 1000 }).exec();
+ * cursor.each(function(error, doc) {
+ * // use doc
+ * });
+ *
+ * @param {String} flag
+ * @param {Boolean} value
+ * @see mongodb http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#addCursorFlag
+ */
+
+Aggregate.prototype.addCursorFlag = function(flag, value) {
+ if (!this.options) {
+ this.options = {};
+ }
+ this.options[flag] = value;
+ return this;
+};
+
+/**
+ * Executes the aggregate pipeline on the currently bound Model.
+ *
+ * ####Example:
+ *
+ * aggregate.exec(callback);
+ *
+ * // Because a promise is returned, the `callback` is optional.
+ * var promise = aggregate.exec();
+ * promise.then(..);
+ *
+ * @see Promise #promise_Promise
+ * @param {Function} [callback]
+ * @return {Promise}
+ * @api public
+ */
+
+Aggregate.prototype.exec = function(callback) {
+ if (!this._model) {
+ throw new Error('Aggregate not bound to any Model');
+ }
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ var options = utils.clone(this.options);
+
+ if (options && options.cursor) {
+ if (options.cursor.async) {
+ delete options.cursor.async;
+ return new Promise.ES6(function(resolve) {
+ if (!_this._model.collection.buffer) {
+ process.nextTick(function() {
+ var cursor = _this._model.collection.
+ aggregate(_this._pipeline, options || {});
+ resolve(cursor);
+ callback && callback(null, cursor);
+ });
+ return;
+ }
+ _this._model.collection.emitter.once('queue', function() {
+ var cursor = _this._model.collection.
+ aggregate(_this._pipeline, options || {});
+ resolve(cursor);
+ callback && callback(null, cursor);
+ });
+ });
+ }
+ return this._model.collection.
+ aggregate(this._pipeline, this.options || {});
+ }
+
+ return new Promise.ES6(function(resolve, reject) {
+ if (!_this._pipeline.length) {
+ var err = new Error('Aggregate has empty pipeline');
+ if (callback) {
+ callback(err);
+ }
+ reject(err);
+ return;
+ }
+
+ prepareDiscriminatorPipeline(_this);
+
+ _this._model
+ .collection
+ .aggregate(_this._pipeline, _this.options || {}, function(error, result) {
+ if (error) {
+ if (callback) {
+ callback(error);
+ }
+ reject(error);
+ return;
+ }
+
+ if (callback) {
+ callback(null, result);
+ }
+ resolve(result);
+ });
+ });
+};
+
+/**
+ * Provides promise for aggregate.
+ *
+ * ####Example:
+ *
+ * Model.aggregate(..).then(successCallback, errorCallback);
+ *
+ * @see Promise #promise_Promise
+ * @param {Function} [resolve] successCallback
+ * @param {Function} [reject] errorCallback
+ * @return {Promise}
+ */
+Aggregate.prototype.then = function(resolve, reject) {
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ var promise = new Promise.ES6(function(success, error) {
+ _this.exec(function(err, val) {
+ if (err) error(err);
+ else success(val);
+ });
+ });
+ return promise.then(resolve, reject);
+};
+
+/*!
+ * Helpers
+ */
+
+/**
+ * Checks whether an object is likely a pipeline operator
+ *
+ * @param {Object} obj object to check
+ * @return {Boolean}
+ * @api private
+ */
+
+function isOperator(obj) {
+ var k;
+
+ if (typeof obj !== 'object') {
+ return false;
+ }
+
+ k = Object.keys(obj);
+
+ return k.length === 1 && k
+ .some(function(key) {
+ return key[0] === '$';
+ });
+}
+
+/*!
+ * Adds the appropriate `$match` pipeline step to the top of an aggregate's
+ * pipeline, should it's model is a non-root discriminator type. This is
+ * analogous to the `prepareDiscriminatorCriteria` function in `lib/query.js`.
+ *
+ * @param {Aggregate} aggregate Aggregate to prepare
+ */
+
+function prepareDiscriminatorPipeline(aggregate) {
+ var schema = aggregate._model.schema,
+ discriminatorMapping = schema && schema.discriminatorMapping;
+
+ if (discriminatorMapping && !discriminatorMapping.isRoot) {
+ var originalPipeline = aggregate._pipeline,
+ discriminatorKey = discriminatorMapping.key,
+ discriminatorValue = discriminatorMapping.value;
+
+ // If the first pipeline stage is a match and it doesn't specify a `__t`
+ // key, add the discriminator key to it. This allows for potential
+ // aggregation query optimizations not to be disturbed by this feature.
+ if (originalPipeline[0] && originalPipeline[0].$match && !originalPipeline[0].$match[discriminatorKey]) {
+ originalPipeline[0].$match[discriminatorKey] = discriminatorValue;
+ // `originalPipeline` is a ref, so there's no need for
+ // aggregate._pipeline = originalPipeline
+ } else if (originalPipeline[0] && originalPipeline[0].$geoNear) {
+ originalPipeline[0].$geoNear.query =
+ originalPipeline[0].$geoNear.query || {};
+ originalPipeline[0].$geoNear.query[discriminatorKey] = discriminatorValue;
+ } else {
+ var match = {};
+ match[discriminatorKey] = discriminatorValue;
+ aggregate._pipeline = [{$match: match}].concat(originalPipeline);
+ }
+ }
+}
+
+
+/*!
+ * Exports
+ */
+
+module.exports = Aggregate;
diff --git a/node_modules/mongoose/lib/browser.js b/node_modules/mongoose/lib/browser.js
new file mode 100644
index 0000000..74a7b6e
--- /dev/null
+++ b/node_modules/mongoose/lib/browser.js
@@ -0,0 +1,99 @@
+/* eslint-env browser */
+
+/**
+ * The [MongooseError](#error_MongooseError) constructor.
+ *
+ * @method Error
+ * @api public
+ */
+
+exports.Error = require('./error');
+
+/**
+ * The Mongoose [Schema](#schema_Schema) constructor
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var Schema = mongoose.Schema;
+ * var CatSchema = new Schema(..);
+ *
+ * @method Schema
+ * @api public
+ */
+
+exports.Schema = require('./schema');
+
+/**
+ * The various Mongoose Types.
+ *
+ * ####Example:
+ *
+ * var mongoose = require('mongoose');
+ * var array = mongoose.Types.Array;
+ *
+ * ####Types:
+ *
+ * - [ObjectId](#types-objectid-js)
+ * - [Buffer](#types-buffer-js)
+ * - [SubDocument](#types-embedded-js)
+ * - [Array](#types-array-js)
+ * - [DocumentArray](#types-documentarray-js)
+ *
+ * Using this exposed access to the `ObjectId` type, we can construct ids on demand.
+ *
+ * var ObjectId = mongoose.Types.ObjectId;
+ * var id1 = new ObjectId;
+ *
+ * @property Types
+ * @api public
+ */
+exports.Types = require('./types');
+
+/**
+ * The Mongoose [VirtualType](#virtualtype_VirtualType) constructor
+ *
+ * @method VirtualType
+ * @api public
+ */
+exports.VirtualType = require('./virtualtype');
+
+/**
+ * The various Mongoose SchemaTypes.
+ *
+ * ####Note:
+ *
+ * _Alias of mongoose.Schema.Types for backwards compatibility._
+ *
+ * @property SchemaTypes
+ * @see Schema.SchemaTypes #schema_Schema.Types
+ * @api public
+ */
+
+exports.SchemaType = require('./schematype.js');
+
+/**
+ * Internal utils
+ *
+ * @property utils
+ * @api private
+ */
+
+exports.utils = require('./utils.js');
+
+/**
+ * The Mongoose browser [Document](#document-js) constructor.
+ *
+ * @method Document
+ * @api public
+ */
+exports.Document = require('./document_provider.js')();
+
+/*!
+ * Module exports.
+ */
+
+if (typeof window !== 'undefined') {
+ window.mongoose = module.exports;
+ window.Buffer = Buffer;
+}
diff --git a/node_modules/mongoose/lib/browserDocument.js b/node_modules/mongoose/lib/browserDocument.js
new file mode 100644
index 0000000..6c87d19
--- /dev/null
+++ b/node_modules/mongoose/lib/browserDocument.js
@@ -0,0 +1,103 @@
+/*!
+ * Module dependencies.
+ */
+
+var NodeJSDocument = require('./document'),
+ EventEmitter = require('events').EventEmitter,
+ MongooseError = require('./error'),
+ Schema = require('./schema'),
+ ObjectId = require('./types/objectid'),
+ utils = require('./utils'),
+ ValidationError = MongooseError.ValidationError,
+ InternalCache = require('./internal');
+
+/**
+ * Document constructor.
+ *
+ * @param {Object} obj the values to set
+ * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
+ * @param {Boolean} [skipId] bool, should we auto create an ObjectId _id
+ * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
+ * @event `init`: Emitted on a document after it has was retrieved from the db and fully hydrated by Mongoose.
+ * @event `save`: Emitted when the document is successfully saved
+ * @api private
+ */
+
+function Document(obj, schema, fields, skipId, skipInit) {
+ if (!(this instanceof Document)) {
+ return new Document(obj, schema, fields, skipId, skipInit);
+ }
+
+
+ if (utils.isObject(schema) && !schema.instanceOfSchema) {
+ schema = new Schema(schema);
+ }
+
+ // When creating EmbeddedDocument, it already has the schema and he doesn't need the _id
+ schema = this.schema || schema;
+
+ // Generate ObjectId if it is missing, but it requires a scheme
+ if (!this.schema && schema.options._id) {
+ obj = obj || {};
+
+ if (obj._id === undefined) {
+ obj._id = new ObjectId();
+ }
+ }
+
+ if (!schema) {
+ throw new MongooseError.MissingSchemaError();
+ }
+
+ this.$__setSchema(schema);
+
+ this.$__ = new InternalCache;
+ this.$__.emitter = new EventEmitter();
+ this.isNew = true;
+ this.errors = undefined;
+
+ // var schema = this.schema;
+
+ if (typeof fields === 'boolean') {
+ this.$__.strictMode = fields;
+ fields = undefined;
+ } else {
+ this.$__.strictMode = this.schema.options && this.schema.options.strict;
+ this.$__.selected = fields;
+ }
+
+ var required = this.schema.requiredPaths();
+ for (var i = 0; i < required.length; ++i) {
+ this.$__.activePaths.require(required[i]);
+ }
+
+ this.$__.emitter.setMaxListeners(0);
+ this._doc = this.$__buildDoc(obj, fields, skipId);
+
+ if (!skipInit && obj) {
+ this.init(obj);
+ }
+
+ this.$__registerHooksFromSchema();
+
+ // apply methods
+ for (var m in schema.methods) {
+ this[m] = schema.methods[m];
+ }
+ // apply statics
+ for (var s in schema.statics) {
+ this[s] = schema.statics[s];
+ }
+}
+
+/*!
+ * Inherit from the NodeJS document
+ */
+Document.prototype = Object.create(NodeJSDocument.prototype);
+Document.prototype.constructor = Document;
+
+/*!
+ * Module exports.
+ */
+Document.ValidationError = ValidationError;
+module.exports = exports = Document;
diff --git a/node_modules/mongoose/lib/cast.js b/node_modules/mongoose/lib/cast.js
new file mode 100644
index 0000000..0cf67d8
--- /dev/null
+++ b/node_modules/mongoose/lib/cast.js
@@ -0,0 +1,238 @@
+/*!
+ * Module dependencies.
+ */
+
+var utils = require('./utils');
+var Types = require('./schema/index');
+
+var ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
+
+/**
+ * Handles internal casting for queries
+ *
+ * @param {Schema} schema
+ * @param {Object} obj Object to cast
+ * @api private
+ */
+module.exports = function cast(schema, obj) {
+ var paths = Object.keys(obj),
+ i = paths.length,
+ any$conditionals,
+ schematype,
+ nested,
+ path,
+ type,
+ val;
+
+ while (i--) {
+ path = paths[i];
+ val = obj[path];
+
+ if (path === '$or' || path === '$nor' || path === '$and') {
+ var k = val.length;
+
+ while (k--) {
+ val[k] = cast(schema, val[k]);
+ }
+ } else if (path === '$where') {
+ type = typeof val;
+
+ if (type !== 'string' && type !== 'function') {
+ throw new Error('Must have a string or function for $where');
+ }
+
+ if (type === 'function') {
+ obj[path] = val.toString();
+ }
+
+ continue;
+ } else if (path === '$elemMatch') {
+ val = cast(schema, val);
+ } else {
+ if (!schema) {
+ // no casting for Mixed types
+ continue;
+ }
+
+ schematype = schema.path(path);
+
+ if (!schematype) {
+ // Handle potential embedded array queries
+ var split = path.split('.'),
+ j = split.length,
+ pathFirstHalf,
+ pathLastHalf,
+ remainingConds;
+
+ // Find the part of the var path that is a path of the Schema
+ while (j--) {
+ pathFirstHalf = split.slice(0, j).join('.');
+ schematype = schema.path(pathFirstHalf);
+ if (schematype) {
+ break;
+ }
+ }
+
+ // If a substring of the input path resolves to an actual real path...
+ if (schematype) {
+ // Apply the casting; similar code for $elemMatch in schema/array.js
+ if (schematype.caster && schematype.caster.schema) {
+ remainingConds = {};
+ pathLastHalf = split.slice(j).join('.');
+ remainingConds[pathLastHalf] = val;
+ obj[path] = cast(schematype.caster.schema, remainingConds)[pathLastHalf];
+ } else {
+ obj[path] = val;
+ }
+ continue;
+ }
+
+ if (utils.isObject(val)) {
+ // handle geo schemas that use object notation
+ // { loc: { long: Number, lat: Number }
+
+ var geo = '';
+ if (val.$near) {
+ geo = '$near';
+ } else if (val.$nearSphere) {
+ geo = '$nearSphere';
+ } else if (val.$within) {
+ geo = '$within';
+ } else if (val.$geoIntersects) {
+ geo = '$geoIntersects';
+ } else if (val.$geoWithin) {
+ geo = '$geoWithin';
+ }
+
+ if (!geo) {
+ continue;
+ }
+
+ var numbertype = new Types.Number('__QueryCasting__');
+ var value = val[geo];
+
+ if (val.$maxDistance != null) {
+ val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
+ }
+ if (val.$minDistance != null) {
+ val.$minDistance = numbertype.castForQuery(val.$minDistance);
+ }
+
+ if (geo === '$within') {
+ var withinType = value.$center
+ || value.$centerSphere
+ || value.$box
+ || value.$polygon;
+
+ if (!withinType) {
+ throw new Error('Bad $within paramater: ' + JSON.stringify(val));
+ }
+
+ value = withinType;
+ } else if (geo === '$near' &&
+ typeof value.type === 'string' && Array.isArray(value.coordinates)) {
+ // geojson; cast the coordinates
+ value = value.coordinates;
+ } else if ((geo === '$near' || geo === '$nearSphere' || geo === '$geoIntersects') &&
+ value.$geometry && typeof value.$geometry.type === 'string' &&
+ Array.isArray(value.$geometry.coordinates)) {
+ if (value.$maxDistance != null) {
+ value.$maxDistance = numbertype.castForQuery(value.$maxDistance);
+ }
+ if (value.$minDistance != null) {
+ value.$minDistance = numbertype.castForQuery(value.$minDistance);
+ }
+ if (utils.isMongooseObject(value.$geometry)) {
+ value.$geometry = value.$geometry.toObject({ virtuals: false });
+ }
+ value = value.$geometry.coordinates;
+ } else if (geo === '$geoWithin') {
+ if (!value.$geometry) {
+ throw new Error('$geoWithin must specify $geometry');
+ }
+ if (utils.isMongooseObject(value.$geometry)) {
+ value.$geometry = value.$geometry.toObject({ virtuals: false });
+ }
+ var geoWithinType = value.$geometry.type;
+ if (ALLOWED_GEOWITHIN_GEOJSON_TYPES.indexOf(geoWithinType) === -1) {
+ throw new Error('Invalid geoJSON type for $geoWithin "' +
+ geoWithinType + '", must be "Polygon" or "MultiPolygon"');
+ }
+ value = value.$geometry.coordinates;
+ }
+
+ _cast(value, numbertype);
+ }
+ } else if (val === null || val === undefined) {
+ obj[path] = null;
+ continue;
+ } else if (val.constructor.name === 'Object') {
+ any$conditionals = Object.keys(val).some(function(k) {
+ return k.charAt(0) === '$' && k !== '$id' && k !== '$ref';
+ });
+
+ if (!any$conditionals) {
+ obj[path] = schematype.castForQuery(val);
+ } else {
+ var ks = Object.keys(val),
+ $cond;
+
+ k = ks.length;
+
+ while (k--) {
+ $cond = ks[k];
+ nested = val[$cond];
+
+ if ($cond === '$exists') {
+ if (typeof nested !== 'boolean') {
+ throw new Error('$exists parameter must be Boolean');
+ }
+ continue;
+ }
+
+ if ($cond === '$type') {
+ if (typeof nested !== 'number' && typeof nested !== 'string') {
+ throw new Error('$type parameter must be number or string');
+ }
+ continue;
+ }
+
+ if ($cond === '$not') {
+ cast(schema, nested);
+ } else {
+ val[$cond] = schematype.castForQuery($cond, nested);
+ }
+ }
+ }
+ } else {
+ obj[path] = schematype.castForQuery(val);
+ }
+ }
+ }
+
+ return obj;
+};
+
+function _cast(val, numbertype) {
+ if (Array.isArray(val)) {
+ val.forEach(function(item, i) {
+ if (Array.isArray(item) || utils.isObject(item)) {
+ return _cast(item, numbertype);
+ }
+ val[i] = numbertype.castForQuery(item);
+ });
+ } else {
+ var nearKeys = Object.keys(val);
+ var nearLen = nearKeys.length;
+ while (nearLen--) {
+ var nkey = nearKeys[nearLen];
+ var item = val[nkey];
+ if (Array.isArray(item) || utils.isObject(item)) {
+ _cast(item, numbertype);
+ val[nkey] = item;
+ } else {
+ val[nkey] = numbertype.castForQuery(item);
+ }
+ }
+ }
+}
diff --git a/node_modules/mongoose/lib/collection.js b/node_modules/mongoose/lib/collection.js
index 1c38286..3aee0aa 100644
--- a/node_modules/mongoose/lib/collection.js
+++ b/node_modules/mongoose/lib/collection.js
@@ -1,9 +1,9 @@
-
/*!
* Module dependencies.
*/
-var STATES = require('./connectionstate')
+var EventEmitter = require('events').EventEmitter;
+var STATES = require('./connectionstate');
/**
* Abstract Collection constructor
@@ -16,28 +16,34 @@ var STATES = require('./connectionstate')
* @api public
*/
-function Collection (name, conn, opts) {
- if (undefined === opts) opts = {};
- if (undefined === opts.capped) opts.capped = {};
+function Collection(name, conn, opts) {
+ if (opts === void 0) {
+ opts = {};
+ }
+ if (opts.capped === void 0) {
+ opts.capped = {};
+ }
opts.bufferCommands = undefined === opts.bufferCommands
- ? true
- : opts.bufferCommands;
+ ? true
+ : opts.bufferCommands;
- if ('number' == typeof opts.capped) {
- opts.capped = { size: opts.capped };
+ if (typeof opts.capped === 'number') {
+ opts.capped = {size: opts.capped};
}
this.opts = opts;
this.name = name;
+ this.collectionName = name;
this.conn = conn;
this.queue = [];
this.buffer = this.opts.bufferCommands;
+ this.emitter = new EventEmitter();
- if (STATES.connected == this.conn.readyState) {
+ if (STATES.connected === this.conn.readyState) {
this.onOpen();
}
-};
+}
/**
* The collection name
@@ -48,6 +54,15 @@ function Collection (name, conn, opts) {
Collection.prototype.name;
+/**
+ * The collection name
+ *
+ * @api public
+ * @property collectionName
+ */
+
+Collection.prototype.collectionName;
+
/**
* The Connection instance
*
@@ -63,10 +78,9 @@ Collection.prototype.conn;
* @api private
*/
-Collection.prototype.onOpen = function () {
- var self = this;
+Collection.prototype.onOpen = function() {
this.buffer = false;
- self.doQueue();
+ this.doQueue();
};
/**
@@ -75,7 +89,7 @@ Collection.prototype.onOpen = function () {
* @api private
*/
-Collection.prototype.onClose = function () {
+Collection.prototype.onClose = function() {
if (this.opts.bufferCommands) {
this.buffer = true;
}
@@ -90,7 +104,7 @@ Collection.prototype.onClose = function () {
* @api private
*/
-Collection.prototype.addQueue = function (name, args) {
+Collection.prototype.addQueue = function(name, args) {
this.queue.push([name, args]);
return this;
};
@@ -101,11 +115,15 @@ Collection.prototype.addQueue = function (name, args) {
* @api private
*/
-Collection.prototype.doQueue = function () {
- for (var i = 0, l = this.queue.length; i < l; i++){
+Collection.prototype.doQueue = function() {
+ for (var i = 0, l = this.queue.length; i < l; i++) {
this[this.queue[i][0]].apply(this, this.queue[i][1]);
}
this.queue = [];
+ var _this = this;
+ process.nextTick(function() {
+ _this.emitter.emit('queue');
+ });
return this;
};
@@ -113,7 +131,7 @@ Collection.prototype.doQueue = function () {
* Abstract method that drivers must implement.
*/
-Collection.prototype.ensureIndex = function(){
+Collection.prototype.ensureIndex = function() {
throw new Error('Collection#ensureIndex unimplemented by driver');
};
@@ -121,7 +139,7 @@ Collection.prototype.ensureIndex = function(){
* Abstract method that drivers must implement.
*/
-Collection.prototype.findAndModify = function(){
+Collection.prototype.findAndModify = function() {
throw new Error('Collection#findAndModify unimplemented by driver');
};
@@ -129,7 +147,7 @@ Collection.prototype.findAndModify = function(){
* Abstract method that drivers must implement.
*/
-Collection.prototype.findOne = function(){
+Collection.prototype.findOne = function() {
throw new Error('Collection#findOne unimplemented by driver');
};
@@ -137,7 +155,7 @@ Collection.prototype.findOne = function(){
* Abstract method that drivers must implement.
*/
-Collection.prototype.find = function(){
+Collection.prototype.find = function() {
throw new Error('Collection#find unimplemented by driver');
};
@@ -145,7 +163,7 @@ Collection.prototype.find = function(){
* Abstract method that drivers must implement.
*/
-Collection.prototype.insert = function(){
+Collection.prototype.insert = function() {
throw new Error('Collection#insert unimplemented by driver');
};
@@ -153,7 +171,7 @@ Collection.prototype.insert = function(){
* Abstract method that drivers must implement.
*/
-Collection.prototype.save = function(){
+Collection.prototype.save = function() {
throw new Error('Collection#save unimplemented by driver');
};
@@ -161,7 +179,7 @@ Collection.prototype.save = function(){
* Abstract method that drivers must implement.
*/
-Collection.prototype.update = function(){
+Collection.prototype.update = function() {
throw new Error('Collection#update unimplemented by driver');
};
@@ -169,7 +187,7 @@ Collection.prototype.update = function(){
* Abstract method that drivers must implement.
*/
-Collection.prototype.getIndexes = function(){
+Collection.prototype.getIndexes = function() {
throw new Error('Collection#getIndexes unimplemented by driver');
};
@@ -177,7 +195,7 @@ Collection.prototype.getIndexes = function(){
* Abstract method that drivers must implement.
*/
-Collection.prototype.mapReduce = function(){
+Collection.prototype.mapReduce = function() {
throw new Error('Collection#mapReduce unimplemented by driver');
};
diff --git a/node_modules/mongoose/lib/connection.js b/node_modules/mongoose/lib/connection.js
index 5cf1361..154aeb5 100644
--- a/node_modules/mongoose/lib/connection.js
+++ b/node_modules/mongoose/lib/connection.js
@@ -2,17 +2,15 @@
* Module dependencies.
*/
-var url = require('url')
- , utils = require('./utils')
- , EventEmitter = require('events').EventEmitter
- , driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native'
- , Model = require('./model')
- , Schema = require('./schema')
- , Collection = require(driver + '/collection')
- , STATES = require('./connectionstate')
- , MongooseError = require('./error')
- , assert =require('assert')
- , muri = require('muri')
+var utils = require('./utils');
+var EventEmitter = require('events').EventEmitter;
+var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
+var Schema = require('./schema');
+var Collection = require(driver + '/collection');
+var STATES = require('./connectionstate');
+var MongooseError = require('./error');
+var muri = require('muri');
+var PromiseProvider = require('./promise_provider');
/*!
* Protocol prefix regexp.
@@ -22,6 +20,16 @@ var url = require('url')
var rgxProtocol = /^(?:.)+:\/\//;
+/*!
+ * A list of authentication mechanisms that don't require a password for authentication.
+ * This is used by the authMechanismDoesNotRequirePassword method.
+ *
+ * @api private
+ */
+var authMechanismsWhichDontRequirePassword = [
+ 'MONGODB-X509'
+];
+
/**
* Connection constructor
*
@@ -37,14 +45,16 @@ var rgxProtocol = /^(?:.)+:\/\//;
* @event `close`: Emitted after we `disconnected` and `onClose` executed on all of this connections models.
* @event `reconnected`: Emitted after we `connected` and subsequently `disconnected`, followed by successfully another successfull connection.
* @event `error`: Emitted when an error occurs on this connection.
- * @event `fullsetup`: Emitted in a replica-set scenario, when all nodes specified in the connection string are connected.
+ * @event `fullsetup`: Emitted in a replica-set scenario, when primary and at least one seconaries specified in the connection string are connected.
+ * @event `all`: Emitted in a replica-set scenario, when all nodes specified in the connection string are connected.
* @api public
*/
-function Connection (base) {
+function Connection(base) {
this.base = base;
this.collections = {};
this.models = {};
+ this.config = {autoIndex: true};
this.replica = false;
this.hosts = null;
this.host = null;
@@ -53,10 +63,11 @@ function Connection (base) {
this.pass = null;
this.name = null;
this.options = null;
+ this.otherDbs = [];
this._readyState = STATES.disconnected;
this._closeCalled = false;
this._hasOpened = false;
-};
+}
/*!
* Inherit from EventEmitter
@@ -84,21 +95,28 @@ Connection.prototype.__proto__ = EventEmitter.prototype;
*/
Object.defineProperty(Connection.prototype, 'readyState', {
- get: function(){ return this._readyState; }
- , set: function (val) {
- if (!(val in STATES)) {
- throw new Error('Invalid connection state: ' + val);
- }
-
- if (this._readyState !== val) {
- this._readyState = val;
+ get: function() {
+ return this._readyState;
+ },
+ set: function(val) {
+ if (!(val in STATES)) {
+ throw new Error('Invalid connection state: ' + val);
+ }
- if (STATES.connected === val)
- this._hasOpened = true;
+ if (this._readyState !== val) {
+ this._readyState = val;
+ // loop over the otherDbs on this connection and change their state
+ for (var i = 0; i < this.otherDbs.length; i++) {
+ this.otherDbs[i].readyState = val;
+ }
- this.emit(STATES[val]);
+ if (STATES.connected === val) {
+ this._hasOpened = true;
}
+
+ this.emit(STATES[val]);
}
+ }
});
/**
@@ -117,11 +135,20 @@ Connection.prototype.collections;
Connection.prototype.db;
+/**
+ * A hash of the global options that are associated with this connection
+ *
+ * @property config
+ */
+
+Connection.prototype.config;
+
/**
* Opens the connection to MongoDB.
*
* `options` is a hash with the following possible properties:
*
+ * config - passed to the connection config instance
* db - passed to the connection db instance
* server - passed to the connection server instance(s)
* replset - passed to the connection ReplSet instance
@@ -147,33 +174,37 @@ Connection.prototype.db;
* @api public
*/
-Connection.prototype.open = function (host, database, port, options, callback) {
- var self = this
- , parsed
- , uri;
+Connection.prototype.open = function(host, database, port, options, callback) {
+ var parsed;
- if ('string' === typeof database) {
+ if (typeof database === 'string') {
switch (arguments.length) {
case 2:
port = 27017;
+ break;
case 3:
switch (typeof port) {
case 'function':
- callback = port, port = 27017;
+ callback = port;
+ port = 27017;
break;
case 'object':
- options = port, port = 27017;
+ options = port;
+ port = 27017;
break;
}
break;
case 4:
- if ('function' === typeof options)
- callback = options, options = {};
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
}
} else {
switch (typeof database) {
case 'function':
- callback = database, database = undefined;
+ callback = database;
+ database = undefined;
break;
case 'object':
options = database;
@@ -219,32 +250,49 @@ Connection.prototype.open = function (host, database, port, options, callback) {
}
// authentication
- if (options && options.user && options.pass) {
+ if (this.optionsProvideAuthenticationData(options)) {
this.user = options.user;
this.pass = options.pass;
-
} else if (parsed && parsed.auth) {
this.user = parsed.auth.user;
this.pass = parsed.auth.pass;
- // Check hostname for user/pass
+ // Check hostname for user/pass
} else if (/@/.test(host) && /:/.test(host.split('@')[0])) {
host = host.split('@');
var auth = host.shift().split(':');
host = host.pop();
this.user = auth[0];
this.pass = auth[1];
-
} else {
this.user = this.pass = undefined;
}
+ // global configuration options
+ if (options && options.config) {
+ this.config.autoIndex = options.config.autoIndex !== false;
+ }
+
this.name = database;
this.host = host;
this.port = port;
- this._open(callback);
- return this;
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ var promise = new Promise.ES6(function(resolve, reject) {
+ _this._open(true, function(error) {
+ callback && callback(error);
+ if (error) {
+ reject(error);
+ if (!callback && !promise.$hasHandler) {
+ _this.emit('error', error);
+ }
+ return;
+ }
+ resolve();
+ });
+ });
+ return promise;
};
/**
@@ -268,6 +316,8 @@ Connection.prototype.open = function (host, database, port, options, callback) {
* auth - options for authentication (see http://mongodb.github.com/node-mongodb-native/api-generated/db.html#authenticate)
* mongos - Boolean - if true, enables High Availability support for mongos
*
+ * _Options passed take precedence over options included in connection strings._
+ *
* ####Notes:
*
* _If connecting to multiple mongos servers, set the `mongos` option to true._
@@ -289,13 +339,11 @@ Connection.prototype.open = function (host, database, port, options, callback) {
* @api public
*/
-Connection.prototype.openSet = function (uris, database, options, callback) {
+Connection.prototype.openSet = function(uris, database, options, callback) {
if (!rgxProtocol.test(uris)) {
uris = 'mongodb://' + uris;
}
- var self = this;
-
switch (arguments.length) {
case 3:
switch (typeof database) {
@@ -309,7 +357,7 @@ Connection.prototype.openSet = function (uris, database, options, callback) {
break;
}
- if ('function' === typeof options) {
+ if (typeof options === 'function') {
callback = options;
options = {};
}
@@ -320,10 +368,12 @@ Connection.prototype.openSet = function (uris, database, options, callback) {
this.name = database;
break;
case 'function':
- callback = database, database = null;
+ callback = database;
+ database = null;
break;
case 'object':
- options = database, database = null;
+ options = database;
+ database = null;
break;
}
}
@@ -350,20 +400,37 @@ Connection.prototype.openSet = function (uris, database, options, callback) {
}
// authentication
- if (options && options.user && options.pass) {
+ if (this.optionsProvideAuthenticationData(options)) {
this.user = options.user;
this.pass = options.pass;
-
} else if (parsed && parsed.auth) {
this.user = parsed.auth.user;
this.pass = parsed.auth.pass;
-
} else {
this.user = this.pass = undefined;
}
- this._open(callback);
- return this;
+ // global configuration options
+ if (options && options.config) {
+ this.config.autoIndex = options.config.autoIndex !== false;
+ }
+
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ var promise = new Promise.ES6(function(resolve, reject) {
+ _this._open(true, function(error) {
+ callback && callback(error);
+ if (error) {
+ reject(error);
+ if (!callback && !promise.$hasHandler) {
+ _this.emit('error', error);
+ }
+ return;
+ }
+ resolve();
+ });
+ });
+ return promise;
};
/**
@@ -377,10 +444,12 @@ Connection.prototype.openSet = function (uris, database, options, callback) {
* @api private
*/
-Connection.prototype.error = function (err, callback) {
- if (callback) return callback(err);
+Connection.prototype.error = function(err, callback) {
+ if (callback) {
+ return callback(err);
+ }
this.emit('error', err);
-}
+};
/**
* Handles opening the connection with the appropriate method based on connection type.
@@ -389,31 +458,33 @@ Connection.prototype.error = function (err, callback) {
* @api private
*/
-Connection.prototype._open = function (callback) {
+Connection.prototype._open = function(emit, callback) {
this.readyState = STATES.connecting;
this._closeCalled = false;
- var self = this;
+ var _this = this;
var method = this.replica
- ? 'doOpenSet'
- : 'doOpen';
+ ? 'doOpenSet'
+ : 'doOpen';
// open connection
- this[method](function (err) {
+ this[method](function(err) {
if (err) {
- self.readyState = STATES.disconnected;
- if (self._hasOpened) {
- if (callback) callback(err);
+ _this.readyState = STATES.disconnected;
+ if (_this._hasOpened) {
+ if (callback) {
+ callback(err);
+ }
} else {
- self.error(err, callback);
+ _this.error(err, emit && callback);
}
return;
}
- self.onOpen(callback);
+ _this.onOpen(callback);
});
-}
+};
/**
* Called when the connection is opened
@@ -421,37 +492,38 @@ Connection.prototype._open = function (callback) {
* @api private
*/
-Connection.prototype.onOpen = function (callback) {
- var self = this;
+Connection.prototype.onOpen = function(callback) {
+ var _this = this;
- function open (err) {
+ function open(err, isAuth) {
if (err) {
- self.readyState = STATES.disconnected;
- if (self._hasOpened) {
- if (callback) callback(err);
- } else {
- self.error(err, callback);
- }
+ _this.readyState = isAuth ? STATES.unauthorized : STATES.disconnected;
+ _this.error(err, callback);
return;
}
- self.readyState = STATES.connected;
+ _this.readyState = STATES.connected;
// avoid having the collection subscribe to our event emitter
// to prevent 0.3 warning
- for (var i in self.collections)
- self.collections[i].onOpen();
+ for (var i in _this.collections) {
+ if (utils.object.hasOwnProperty(_this.collections, i)) {
+ _this.collections[i].onOpen();
+ }
+ }
callback && callback();
- self.emit('open');
- };
-
- // re-authenticate
- if (self.user && self.pass) {
- self.db.authenticate(self.user, self.pass, self.options.auth, open);
+ _this.emit('open');
}
- else
+
+ // re-authenticate if we're not already connected #3871
+ if (this._readyState !== STATES.connected && this.shouldAuthenticate()) {
+ _this.db.authenticate(_this.user, _this.pass, _this.options.auth, function(err) {
+ open(err, true);
+ });
+ } else {
open();
+ }
};
/**
@@ -462,36 +534,60 @@ Connection.prototype.onOpen = function (callback) {
* @api public
*/
-Connection.prototype.close = function (callback) {
- var self = this;
+Connection.prototype.close = function(callback) {
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve, reject) {
+ _this._close(function(error) {
+ callback && callback(error);
+ if (error) {
+ reject(error);
+ return;
+ }
+ resolve();
+ });
+ });
+};
+
+/**
+ * Handles closing the connection
+ *
+ * @param {Function} callback
+ * @api private
+ */
+Connection.prototype._close = function(callback) {
+ var _this = this;
this._closeCalled = true;
- switch (this.readyState){
+ switch (this.readyState) {
case 0: // disconnected
callback && callback();
break;
case 1: // connected
+ case 4: // unauthorized
this.readyState = STATES.disconnecting;
- this.doClose(function(err){
- if (err){
- self.error(err, callback);
+ this.doClose(function(err) {
+ if (err) {
+ _this.error(err, callback);
} else {
- self.onClose();
+ _this.onClose();
callback && callback();
}
});
break;
case 2: // connecting
- this.once('open', function(){
- self.close(callback);
+ this.once('open', function() {
+ _this.close(callback);
});
break;
case 3: // disconnecting
- if (!callback) break;
- this.once('close', function () {
+ if (!callback) {
+ break;
+ }
+ this.once('close', function() {
callback();
});
break;
@@ -506,13 +602,16 @@ Connection.prototype.close = function (callback) {
* @api private
*/
-Connection.prototype.onClose = function () {
+Connection.prototype.onClose = function() {
this.readyState = STATES.disconnected;
// avoid having the collection subscribe to our event emitter
// to prevent 0.3 warning
- for (var i in this.collections)
- this.collections[i].onClose();
+ for (var i in this.collections) {
+ if (utils.object.hasOwnProperty(this.collections, i)) {
+ this.collections[i].onClose();
+ }
+ }
this.emit('close');
};
@@ -528,9 +627,10 @@ Connection.prototype.onClose = function () {
* @api public
*/
-Connection.prototype.collection = function (name, options) {
- if (!(name in this.collections))
+Connection.prototype.collection = function(name, options) {
+ if (!(name in this.collections)) {
this.collections[name] = new Collection(name, this, options);
+ }
return this.collections[name];
};
@@ -566,31 +666,31 @@ Connection.prototype.collection = function (name, options) {
* @api public
*/
-Connection.prototype.model = function (name, schema, collection) {
+Connection.prototype.model = function(name, schema, collection) {
// collection name discovery
- if ('string' == typeof schema) {
+ if (typeof schema === 'string') {
collection = schema;
schema = false;
}
- if (utils.isObject(schema) && !(schema instanceof Schema)) {
+ if (utils.isObject(schema) && !schema.instanceOfSchema) {
schema = new Schema(schema);
}
if (this.models[name] && !collection) {
// model exists but we are not subclassing with custom collection
- if (schema instanceof Schema && schema != this.models[name].schema) {
+ if (schema && schema.instanceOfSchema && schema !== this.models[name].schema) {
throw new MongooseError.OverwriteModelError(name);
}
return this.models[name];
}
- var opts = { cache: false, connection: this }
+ var opts = {cache: false, connection: this};
var model;
- if (schema instanceof Schema) {
+ if (schema && schema.instanceOfSchema) {
// compile a model
- model = this.base.model(name, schema, collection, opts)
+ model = this.base.model(name, schema, collection, opts);
// only the first model with this name is cached to allow
// for one-offs with custom collection names etc.
@@ -618,8 +718,8 @@ Connection.prototype.model = function (name, schema, collection) {
throw new MongooseError.MissingSchemaError(name);
}
- if (this == model.prototype.db
- && (!collection || collection == model.collection.name)) {
+ if (this === model.prototype.db
+ && (!collection || collection === model.collection.name)) {
// model already uses this connection.
// only the first model with this name is cached to allow
@@ -630,9 +730,9 @@ Connection.prototype.model = function (name, schema, collection) {
return model;
}
-
- return this.models[name] = model.__subclass(this, schema, collection);
-}
+ this.models[name] = model.__subclass(this, schema, collection);
+ return this.models[name];
+};
/**
* Returns an array of model names created on this connection.
@@ -640,68 +740,51 @@ Connection.prototype.model = function (name, schema, collection) {
* @return {Array}
*/
-Connection.prototype.modelNames = function () {
+Connection.prototype.modelNames = function() {
return Object.keys(this.models);
-}
+};
/**
- * Set profiling level.
- *
- * @param {Number|String} level either off (0), slow (1), or all (2)
- * @param {Number} [ms] the threshold in milliseconds above which queries will be logged when in `slow` mode. defaults to 100.
- * @param {Function} callback
- * @api public
+ * @brief Returns if the connection requires authentication after it is opened. Generally if a
+ * username and password are both provided than authentication is needed, but in some cases a
+ * password is not required.
+ * @api private
+ * @return {Boolean} true if the connection should be authenticated after it is opened, otherwise false.
*/
+Connection.prototype.shouldAuthenticate = function() {
+ return (this.user !== null && this.user !== void 0) &&
+ ((this.pass !== null || this.pass !== void 0) || this.authMechanismDoesNotRequirePassword());
+};
-Connection.prototype.setProfiling = function (level, ms, callback) {
- if (STATES.connected !== this.readyState) {
- return this.on('open', this.setProfiling.bind(this, level, ms, callback));
- }
-
- if (!callback) callback = ms, ms = 100;
-
- var cmd = {};
-
- switch (level) {
- case 0:
- case 'off':
- cmd.profile = 0;
- break;
- case 1:
- case 'slow':
- cmd.profile = 1;
- if ('number' !== typeof ms) {
- ms = parseInt(ms, 10);
- if (isNaN(ms)) ms = 100;
- }
- cmd.slowms = ms;
- break;
- case 2:
- case 'all':
- cmd.profile = 2;
- break;
- default:
- return callback(new Error('Invalid profiling level: '+ level));
+/**
+ * @brief Returns a boolean value that specifies if the current authentication mechanism needs a
+ * password to authenticate according to the auth objects passed into the open/openSet methods.
+ * @api private
+ * @return {Boolean} true if the authentication mechanism specified in the options object requires
+ * a password, otherwise false.
+ */
+Connection.prototype.authMechanismDoesNotRequirePassword = function() {
+ if (this.options && this.options.auth) {
+ return authMechanismsWhichDontRequirePassword.indexOf(this.options.auth.authMechanism) >= 0;
}
-
- this.db.executeDbCommand(cmd, function (err, resp) {
- if (err) return callback(err);
-
- var doc = resp.documents[0];
-
- err = 1 === doc.ok
- ? null
- : new Error('Could not set profiling level to: '+ level)
-
- callback(err, doc);
- });
+ return true;
};
-/*!
- * Noop.
+/**
+ * @brief Returns a boolean value that specifies if the provided objects object provides enough
+ * data to authenticate with. Generally this is true if the username and password are both specified
+ * but in some authentication methods, a password is not required for authentication so only a username
+ * is required.
+ * @param {Object} [options] the options object passed into the open/openSet methods.
+ * @api private
+ * @return {Boolean} true if the provided options object provides enough data to authenticate with,
+ * otherwise false.
*/
-
-function noop () {}
+Connection.prototype.optionsProvideAuthenticationData = function(options) {
+ return (options) &&
+ (options.user) &&
+ ((options.pass) || this.authMechanismDoesNotRequirePassword());
+};
/*!
* Module exports.
diff --git a/node_modules/mongoose/lib/connectionstate.js b/node_modules/mongoose/lib/connectionstate.js
index 2d05ab0..b9d1baf 100644
--- a/node_modules/mongoose/lib/connectionstate.js
+++ b/node_modules/mongoose/lib/connectionstate.js
@@ -9,16 +9,19 @@ var disconnected = 'disconnected';
var connected = 'connected';
var connecting = 'connecting';
var disconnecting = 'disconnecting';
+var unauthorized = 'unauthorized';
var uninitialized = 'uninitialized';
STATES[0] = disconnected;
STATES[1] = connected;
STATES[2] = connecting;
STATES[3] = disconnecting;
+STATES[4] = unauthorized;
STATES[99] = uninitialized;
STATES[disconnected] = 0;
STATES[connected] = 1;
STATES[connecting] = 2;
STATES[disconnecting] = 3;
+STATES[unauthorized] = 4;
STATES[uninitialized] = 99;
diff --git a/node_modules/mongoose/lib/document.js b/node_modules/mongoose/lib/document.js
index 8f65128..0242dad 100644
--- a/node_modules/mongoose/lib/document.js
+++ b/node_modules/mongoose/lib/document.js
@@ -2,31 +2,33 @@
* Module dependencies.
*/
-var EventEmitter = require('events').EventEmitter
- , setMaxListeners = EventEmitter.prototype.setMaxListeners
- , MongooseError = require('./error')
- , MixedSchema = require('./schema/mixed')
- , Schema = require('./schema')
- , ValidatorError = require('./schematype').ValidatorError
- , utils = require('./utils')
- , clone = utils.clone
- , isMongooseObject = utils.isMongooseObject
- , inspect = require('util').inspect
- , ElemMatchError = MongooseError.ElemMatchError
- , ValidationError = MongooseError.ValidationError
- , DocumentError = MongooseError.DocumentError
- , InternalCache = require('./internal')
- , deepEqual = utils.deepEqual
- , hooks = require('hooks')
- , DocumentArray
- , MongooseArray
- , Embedded
+var EventEmitter = require('events').EventEmitter;
+var MongooseError = require('./error');
+var MixedSchema = require('./schema/mixed');
+var Schema = require('./schema');
+var ObjectExpectedError = require('./error/objectExpected');
+var StrictModeError = require('./error/strict');
+var ValidatorError = require('./schematype').ValidatorError;
+var VersionError = require('./error').VersionError;
+var utils = require('./utils');
+var clone = utils.clone;
+var isMongooseObject = utils.isMongooseObject;
+var inspect = require('util').inspect;
+var ValidationError = MongooseError.ValidationError;
+var InternalCache = require('./internal');
+var deepEqual = utils.deepEqual;
+var hooks = require('hooks-fixed');
+var PromiseProvider = require('./promise_provider');
+var DocumentArray;
+var MongooseArray;
+var Embedded;
+var flatten = require('./services/common').flatten;
/**
* Document constructor.
*
* @param {Object} obj the values to set
- * @param {Object} [opts] optional object containing the fields which were selected in the query returning this document and any populated paths data
+ * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
* @param {Boolean} [skipId] bool, should we auto create an ObjectId _id
* @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
* @event `init`: Emitted on a document after it has was retreived from the db and fully hydrated by Mongoose.
@@ -34,14 +36,15 @@ var EventEmitter = require('events').EventEmitter
* @api private
*/
-function Document (obj, fields, skipId) {
+function Document(obj, fields, skipId) {
this.$__ = new InternalCache;
+ this.$__.emitter = new EventEmitter();
this.isNew = true;
this.errors = undefined;
var schema = this.schema;
- if ('boolean' === typeof fields) {
+ if (typeof fields === 'boolean') {
this.$__.strictMode = fields;
fields = undefined;
} else {
@@ -49,26 +52,49 @@ function Document (obj, fields, skipId) {
this.$__.selected = fields;
}
- var required = schema.requiredPaths();
+ var required = schema.requiredPaths(true);
for (var i = 0; i < required.length; ++i) {
this.$__.activePaths.require(required[i]);
}
- setMaxListeners.call(this, 0);
+ this.$__.emitter.setMaxListeners(0);
this._doc = this.$__buildDoc(obj, fields, skipId);
if (obj) {
+ if (obj instanceof Document) {
+ this.isNew = obj.isNew;
+ }
this.set(obj, undefined, true);
}
- this.$__registerHooks();
+ if (!schema.options.strict && obj) {
+ var _this = this,
+ keys = Object.keys(this._doc);
+
+ keys.forEach(function(key) {
+ if (!(key in schema.tree)) {
+ defineKey(key, null, _this);
+ }
+ });
+ }
+
+ this.$__registerHooksFromSchema();
}
/*!
- * Inherit from EventEmitter.
+ * Document exposes the NodeJS event emitter API, so you can use
+ * `on`, `once`, etc.
*/
+utils.each(
+ ['on', 'once', 'emit', 'listeners', 'removeListener', 'setMaxListeners',
+ 'removeAllListeners', 'addListener'],
+ function(emitterFn) {
+ Document.prototype[emitterFn] = function() {
+ return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments);
+ };
+ });
-Document.prototype.__proto__ = EventEmitter.prototype;
+Document.prototype.constructor = Document;
/**
* The documents schema.
@@ -125,91 +151,107 @@ Document.prototype.errors;
* @memberOf Document
*/
-Document.prototype.$__buildDoc = function (obj, fields, skipId) {
- var doc = {}
- , self = this
- , exclude
- , keys
- , key
- , ki
+Document.prototype.$__buildDoc = function(obj, fields, skipId) {
+ var doc = {};
+ var exclude = null;
+ var keys;
+ var ki;
+ var _this = this;
// determine if this doc is a result of a query with
// excluded fields
- if (fields && 'Object' === fields.constructor.name) {
+
+ if (fields && utils.getFunctionName(fields.constructor) === 'Object') {
keys = Object.keys(fields);
ki = keys.length;
- while (ki--) {
- if ('_id' !== keys[ki]) {
- exclude = 0 === fields[keys[ki]];
- break;
+ if (ki === 1 && keys[0] === '_id') {
+ exclude = !!fields[keys[ki]];
+ } else {
+ while (ki--) {
+ if (keys[ki] !== '_id' &&
+ (!fields[keys[ki]] || typeof fields[keys[ki]] !== 'object')) {
+ exclude = !fields[keys[ki]];
+ break;
+ }
}
}
}
- var paths = Object.keys(this.schema.paths)
- , plen = paths.length
- , ii = 0
+ var paths = Object.keys(this.schema.paths),
+ plen = paths.length,
+ ii = 0;
for (; ii < plen; ++ii) {
var p = paths[ii];
- if ('_id' == p) {
- if (skipId) continue;
- if (obj && '_id' in obj) continue;
+ if (p === '_id') {
+ if (skipId) {
+ continue;
+ }
+ if (obj && '_id' in obj) {
+ continue;
+ }
}
- var type = this.schema.paths[p]
- , path = p.split('.')
- , len = path.length
- , last = len-1
- , curPath = ''
- , doc_ = doc
- , i = 0
+ var type = this.schema.paths[p];
+ var path = p.split('.');
+ var len = path.length;
+ var last = len - 1;
+ var curPath = '';
+ var doc_ = doc;
+ var i = 0;
+ var included = false;
for (; i < len; ++i) {
- var piece = path[i]
- , def
+ var piece = path[i],
+ def;
+
+ curPath += piece;
// support excluding intermediary levels
- if (exclude) {
- curPath += piece;
- if (curPath in fields) break;
- curPath += '.';
+ if (exclude === true) {
+ if (curPath in fields) {
+ break;
+ }
+ } else if (fields && curPath in fields) {
+ included = true;
}
if (i === last) {
- if (fields) {
- if (exclude) {
+ if (fields && exclude !== null) {
+ if (exclude === true) {
// apply defaults to all non-excluded fields
- if (p in fields) continue;
+ if (p in fields) {
+ continue;
+ }
- def = type.getDefault(self, true);
- if ('undefined' !== typeof def) {
+ def = type.getDefault(_this, true);
+ if (typeof def !== 'undefined') {
doc_[piece] = def;
- self.$__.activePaths.default(p);
+ _this.$__.activePaths.default(p);
}
-
- } else if (p in fields) {
+ } else if (included) {
// selected field
- def = type.getDefault(self, true);
- if ('undefined' !== typeof def) {
+ def = type.getDefault(_this, true);
+ if (typeof def !== 'undefined') {
doc_[piece] = def;
- self.$__.activePaths.default(p);
+ _this.$__.activePaths.default(p);
}
}
} else {
- def = type.getDefault(self, true);
- if ('undefined' !== typeof def) {
+ def = type.getDefault(_this, true);
+ if (typeof def !== 'undefined') {
doc_[piece] = def;
- self.$__.activePaths.default(p);
+ _this.$__.activePaths.default(p);
}
}
} else {
doc_ = doc_[piece] || (doc_[piece] = {});
+ curPath += '.';
}
}
- };
+ }
return doc;
};
@@ -221,14 +263,14 @@ Document.prototype.$__buildDoc = function (obj, fields, skipId) {
*
* @param {Object} doc document returned by mongo
* @param {Function} fn callback
- * @api private
+ * @api public
*/
-Document.prototype.init = function (doc, opts, fn) {
+Document.prototype.init = function(doc, opts, fn) {
// do not prefix this method with $__ since its
// used by public hooks
- if ('function' == typeof opts) {
+ if (typeof opts === 'function') {
fn = opts;
opts = null;
}
@@ -236,7 +278,9 @@ Document.prototype.init = function (doc, opts, fn) {
this.isNew = false;
// handle docs with populated paths
- if (doc._id && opts && opts.populated && opts.populated.length) {
+ // If doc._id is not null or undefined
+ if (doc._id !== null && doc._id !== undefined &&
+ opts && opts.populated && opts.populated.length) {
var id = String(doc._id);
for (var i = 0; i < opts.populated.length; ++i) {
var item = opts.populated[i];
@@ -248,7 +292,9 @@ Document.prototype.init = function (doc, opts, fn) {
this.$__storeShard();
this.emit('init', this);
- if (fn) fn(null);
+ if (fn) {
+ fn(null);
+ }
return this;
};
@@ -261,14 +307,14 @@ Document.prototype.init = function (doc, opts, fn) {
* @api private
*/
-function init (self, obj, doc, prefix) {
+function init(self, obj, doc, prefix) {
prefix = prefix || '';
- var keys = Object.keys(obj)
- , len = keys.length
- , schema
- , path
- , i;
+ var keys = Object.keys(obj),
+ len = keys.length,
+ schema,
+ path,
+ i;
while (len--) {
i = keys[len];
@@ -276,27 +322,38 @@ function init (self, obj, doc, prefix) {
schema = self.schema.path(path);
if (!schema && utils.isObject(obj[i]) &&
- (!obj[i].constructor || 'Object' == obj[i].constructor.name)) {
+ (!obj[i].constructor || utils.getFunctionName(obj[i].constructor) === 'Object')) {
// assume nested object
- if (!doc[i]) doc[i] = {};
+ if (!doc[i]) {
+ doc[i] = {};
+ }
init(self, obj[i], doc[i], path + '.');
} else {
if (obj[i] === null) {
doc[i] = null;
} else if (obj[i] !== undefined) {
if (schema) {
- self.$__try(function(){
+ try {
doc[i] = schema.cast(obj[i], self, true);
- });
+ } catch (e) {
+ self.invalidate(e.path, new ValidatorError({
+ path: e.path,
+ message: e.message,
+ type: 'cast',
+ value: e.value
+ }));
+ }
} else {
doc[i] = obj[i];
}
}
// mark as hydrated
- self.$__.activePaths.init(path);
+ if (!self.isModified(path)) {
+ self.$__.activePaths.init(path);
+ }
}
}
-};
+}
/**
* Stores the current values of the shard keys.
@@ -310,34 +367,42 @@ function init (self, obj, doc, prefix) {
* @memberOf Document
*/
-Document.prototype.$__storeShard = function () {
+Document.prototype.$__storeShard = function() {
// backwards compat
var key = this.schema.options.shardKey || this.schema.options.shardkey;
- if (!(key && 'Object' == key.constructor.name)) return;
+ if (!(key && utils.getFunctionName(key.constructor) === 'Object')) {
+ return;
+ }
- var orig = this.$__.shardval = {}
- , paths = Object.keys(key)
- , len = paths.length
- , val
+ var orig = this.$__.shardval = {},
+ paths = Object.keys(key),
+ len = paths.length,
+ val;
for (var i = 0; i < len; ++i) {
val = this.getValue(paths[i]);
if (isMongooseObject(val)) {
- orig[paths[i]] = val.toObject({ depopulate: true })
- } else if (null != val && val.valueOf) {
+ orig[paths[i]] = val.toObject({depopulate: true});
+ } else if (val !== null && val !== undefined && val.valueOf &&
+ // Explicitly don't take value of dates
+ (!val.constructor || utils.getFunctionName(val.constructor) !== 'Date')) {
orig[paths[i]] = val.valueOf();
} else {
orig[paths[i]] = val;
}
}
-}
+};
/*!
* Set up middleware support
*/
for (var k in hooks) {
- Document.prototype[k] = Document[k] = hooks[k];
+ if (k === 'pre' || k === 'post') {
+ Document.prototype['$' + k] = Document['$' + k] = hooks[k];
+ } else {
+ Document.prototype[k] = Document[k] = hooks[k];
+ }
}
/**
@@ -359,11 +424,11 @@ for (var k in hooks) {
* @api public
*/
-Document.prototype.update = function update () {
+Document.prototype.update = function update() {
var args = utils.args(arguments);
args.unshift({_id: this._id});
return this.constructor.update.apply(this.constructor, args);
-}
+};
/**
* Sets the value of a path, or many paths.
@@ -381,10 +446,10 @@ Document.prototype.update = function update () {
* }
* })
*
- * // only-the-fly cast to number
+ * // on-the-fly cast to number
* doc.set(path, value, Number)
*
- * // only-the-fly cast to string
+ * // on-the-fly cast to string
* doc.set(path, value, String)
*
* // changing strict mode behavior
@@ -392,71 +457,106 @@ Document.prototype.update = function update () {
*
* @param {String|Object} path path or object of key/vals to set
* @param {Any} val the value to set
- * @param {Schema|String|Number|Buffer|etc..} [type] optionally specify a type for "on-the-fly" attributes
+ * @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for "on-the-fly" attributes
* @param {Object} [options] optionally specify options that modify the behavior of the set
* @api public
*/
-Document.prototype.set = function (path, val, type, options) {
- if (type && 'Object' == type.constructor.name) {
+Document.prototype.set = function(path, val, type, options) {
+ if (type && utils.getFunctionName(type.constructor) === 'Object') {
options = type;
type = undefined;
}
- var merge = options && options.merge
- , adhoc = type && true !== type
- , constructing = true === type
- , adhocs
+ var merge = options && options.merge,
+ adhoc = type && type !== true,
+ constructing = type === true,
+ adhocs;
var strict = options && 'strict' in options
- ? options.strict
- : this.$__.strictMode;
+ ? options.strict
+ : this.$__.strictMode;
if (adhoc) {
adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {});
- adhocs[path] = Schema.interpretAsType(path, type);
+ adhocs[path] = Schema.interpretAsType(path, type, this.schema.options);
}
- if ('string' !== typeof path) {
+ if (typeof path !== 'string') {
// new Document({ key: val })
- if (null === path || undefined === path) {
+ if (path === null || path === void 0) {
var _ = path;
path = val;
val = _;
-
} else {
var prefix = val
- ? val + '.'
- : '';
+ ? val + '.'
+ : '';
- if (path instanceof Document) path = path._doc;
+ if (path instanceof Document) {
+ if (path.$__isNested) {
+ path = path.toObject();
+ } else {
+ path = path._doc;
+ }
+ }
- var keys = Object.keys(path)
- , i = keys.length
- , pathtype
- , key
+ var keys = Object.keys(path);
+ var i = keys.length;
+ var pathtype;
+ var key;
+ if (i === 0 && !this.schema.options.minimize) {
+ if (val) {
+ this.set(val, {});
+ }
+ return this;
+ }
while (i--) {
key = keys[i];
- pathtype = this.schema.pathType(prefix + key);
- if (null != path[key]
- // need to know if plain object - no Buffer, ObjectId, ref, etc
+ var pathName = prefix + key;
+ pathtype = this.schema.pathType(pathName);
+
+ if (path[key] !== null
+ && path[key] !== void 0
+ // need to know if plain object - no Buffer, ObjectId, ref, etc
&& utils.isObject(path[key])
- && (!path[key].constructor || 'Object' == path[key].constructor.name)
- && 'virtual' != pathtype
- && !(this.$__path(prefix + key) instanceof MixedSchema)
- && !(this.schema.paths[key] && this.schema.paths[key].options.ref)
- ) {
+ && (!path[key].constructor || utils.getFunctionName(path[key].constructor) === 'Object')
+ && pathtype !== 'virtual'
+ && pathtype !== 'real'
+ && !(this.$__path(pathName) instanceof MixedSchema)
+ && !(this.schema.paths[pathName] &&
+ this.schema.paths[pathName].options &&
+ this.schema.paths[pathName].options.ref)) {
this.set(path[key], prefix + key, constructing);
} else if (strict) {
- if ('real' === pathtype || 'virtual' === pathtype) {
+ // Don't overwrite defaults with undefined keys (gh-3981)
+ if (constructing && path[key] === void 0 &&
+ this.get(key) !== void 0) {
+ continue;
+ }
+
+ if (pathtype === 'real' || pathtype === 'virtual') {
+ // Check for setting single embedded schema to document (gh-3535)
+ if (this.schema.paths[pathName] &&
+ this.schema.paths[pathName].$isSingleNested &&
+ path[key] instanceof Document) {
+ path[key] = path[key].toObject({virtuals: false});
+ }
this.set(prefix + key, path[key], constructing);
- } else if ('throw' == strict) {
- throw new Error("Field `" + key + "` is not in schema.");
+ } else if (pathtype === 'nested' && path[key] instanceof Document) {
+ this.set(prefix + key,
+ path[key].toObject({virtuals: false}), constructing);
+ } else if (strict === 'throw') {
+ if (pathtype === 'nested') {
+ throw new ObjectExpectedError(key, path[key]);
+ } else {
+ throw new StrictModeError(key);
+ }
}
- } else if (undefined !== path[key]) {
+ } else if (path[key] !== void 0) {
this.set(prefix + key, path[key], constructing);
}
}
@@ -469,23 +569,36 @@ Document.prototype.set = function (path, val, type, options) {
// docschema = new Schema({ path: { nest: 'string' }})
// doc.set('path', obj);
var pathType = this.schema.pathType(path);
- if ('nested' == pathType && val && utils.isObject(val) &&
- (!val.constructor || 'Object' == val.constructor.name)) {
- if (!merge) this.setValue(path, null);
- this.set(val, path, constructing);
+ if (pathType === 'nested' && val) {
+ if (utils.isObject(val) &&
+ (!val.constructor || utils.getFunctionName(val.constructor) === 'Object')) {
+ if (!merge) {
+ this.setValue(path, null);
+ cleanModifiedSubpaths(this, path);
+ }
+
+ if (Object.keys(val).length === 0) {
+ this.setValue(path, {});
+ this.markModified(path);
+ cleanModifiedSubpaths(this, path);
+ } else {
+ this.set(val, path, constructing);
+ }
+ return this;
+ }
+ this.invalidate(path, new MongooseError.CastError('Object', val, path));
return this;
}
var schema;
var parts = path.split('.');
- if ('adhocOrUndefined' == pathType && strict) {
-
+ if (pathType === 'adhocOrUndefined' && strict) {
// check for roots that are Mixed types
var mixed;
- for (var i = 0; i < parts.length; ++i) {
- var subpath = parts.slice(0, i+1).join('.');
+ for (i = 0; i < parts.length; ++i) {
+ var subpath = parts.slice(0, i + 1).join('.');
schema = this.schema.path(subpath);
if (schema instanceof MixedSchema) {
// allow changes to sub paths of mixed types
@@ -495,13 +608,12 @@ Document.prototype.set = function (path, val, type, options) {
}
if (!mixed) {
- if ('throw' == strict) {
- throw new Error("Field `" + path + "` is not in schema.");
+ if (strict === 'throw') {
+ throw new StrictModeError(path);
}
return this;
}
-
- } else if ('virtual' == pathType) {
+ } else if (pathType === 'virtual') {
schema = this.schema.virtualpath(path);
schema.applySetters(val, this);
return this;
@@ -517,39 +629,105 @@ Document.prototype.set = function (path, val, type, options) {
if (parts.length <= 1) {
pathToMark = path;
} else {
- for (var i = 0; i < parts.length; ++i) {
- var subpath = parts.slice(0, i+1).join('.');
+ for (i = 0; i < parts.length; ++i) {
+ subpath = parts.slice(0, i + 1).join('.');
if (this.isDirectModified(subpath) // earlier prefixes that are already
- // marked as dirty have precedence
+ // marked as dirty have precedence
|| this.get(subpath) === null) {
pathToMark = subpath;
break;
}
}
- if (!pathToMark) pathToMark = path;
+ if (!pathToMark) {
+ pathToMark = path;
+ }
}
// if this doc is being constructed we should not trigger getters
var priorVal = constructing
- ? undefined
- : this.get(path);
+ ? undefined
+ : this.getValue(path);
- if (!schema || undefined === val) {
+ if (!schema) {
this.$__set(pathToMark, path, constructing, parts, schema, val, priorVal);
return this;
}
- var self = this;
- var shouldSet = this.$__try(function(){
- val = schema.applySetters(val, self, false, priorVal);
- });
+ var shouldSet = true;
+ try {
+ // If the user is trying to set a ref path to a document with
+ // the correct model name, treat it as populated
+ var didPopulate = false;
+ if (schema.options &&
+ schema.options.ref &&
+ val instanceof Document &&
+ schema.options.ref === val.constructor.modelName) {
+ if (this.ownerDocument) {
+ this.ownerDocument().populated(this.$__fullPath(path),
+ val._id, {model: val.constructor});
+ } else {
+ this.populated(path, val._id, {model: val.constructor});
+ }
+ didPopulate = true;
+ }
+
+ var popOpts;
+ if (schema.options &&
+ Array.isArray(schema.options.type) &&
+ schema.options.type.length &&
+ schema.options.type[0].ref &&
+ Array.isArray(val) &&
+ val.length > 0 &&
+ val[0] instanceof Document &&
+ val[0].constructor.modelName &&
+ schema.options.type[0].ref === val[0].constructor.modelName) {
+ if (this.ownerDocument) {
+ popOpts = { model: val[0].constructor };
+ this.ownerDocument().populated(this.$__fullPath(path),
+ val.map(function(v) { return v._id; }), popOpts);
+ } else {
+ popOpts = { model: val[0].constructor };
+ this.populated(path, val.map(function(v) { return v._id; }), popOpts);
+ }
+ didPopulate = true;
+ }
+ val = schema.applySetters(val, this, false, priorVal);
+
+ if (!didPopulate && this.$__.populated) {
+ delete this.$__.populated[path];
+ }
+
+ this.$markValid(path);
+ } catch (e) {
+ this.invalidate(path,
+ new MongooseError.CastError(schema.instance, val, path, e));
+ shouldSet = false;
+ }
+
+ if (schema.$isSingleNested) {
+ cleanModifiedSubpaths(this, path);
+ }
if (shouldSet) {
this.$__set(pathToMark, path, constructing, parts, schema, val, priorVal);
}
return this;
+};
+
+/*!
+ * ignore
+ */
+
+function cleanModifiedSubpaths(doc, path) {
+ var _modifiedPaths = Object.keys(doc.$__.activePaths.states.modify);
+ var _numModifiedPaths = _modifiedPaths.length;
+ for (var j = 0; j < _numModifiedPaths; ++j) {
+ if (_modifiedPaths[j].indexOf(path + '.') === 0) {
+ delete doc.$__.activePaths.states.modify[_modifiedPaths[j]];
+ }
+ }
}
/**
@@ -561,11 +739,10 @@ Document.prototype.set = function (path, val, type, options) {
* @memberOf Document
*/
-Document.prototype.$__shouldModify = function (
- pathToMark, path, constructing, parts, schema, val, priorVal) {
-
- if (this.isNew) return true;
- if (this.isDirectModified(pathToMark)) return false;
+Document.prototype.$__shouldModify = function(pathToMark, path, constructing, parts, schema, val, priorVal) {
+ if (this.isNew) {
+ return true;
+ }
if (undefined === val && !this.isSelected(path)) {
// when a path is not selected in a query, its initial
@@ -578,21 +755,29 @@ Document.prototype.$__shouldModify = function (
return false;
}
+ // gh-3992: if setting a populated field to a doc, don't mark modified
+ // if they have the same _id
+ if (this.populated(path) &&
+ val instanceof Document &&
+ deepEqual(val._id, priorVal)) {
+ return false;
+ }
+
if (!deepEqual(val, priorVal || this.get(path))) {
return true;
}
if (!constructing &&
- null != val &&
+ val !== null &&
+ val !== undefined &&
path in this.$__.activePaths.states.default &&
deepEqual(val, schema.getDefault(this, constructing))) {
// a path with a default was $unset on the server
// and the user is setting it to the same value again
return true;
}
-
return false;
-}
+};
/**
* Handles the actual setting of the value and marking the path modified if appropriate.
@@ -602,33 +787,47 @@ Document.prototype.$__shouldModify = function (
* @memberOf Document
*/
-Document.prototype.$__set = function (
- pathToMark, path, constructing, parts, schema, val, priorVal) {
+Document.prototype.$__set = function(pathToMark, path, constructing, parts, schema, val, priorVal) {
+ Embedded = Embedded || require('./types/embedded');
- var shouldModify = this.$__shouldModify.apply(this, arguments);
+ var shouldModify = this.$__shouldModify(pathToMark, path, constructing, parts,
+ schema, val, priorVal);
+ var _this = this;
if (shouldModify) {
this.markModified(pathToMark, val);
// handle directly setting arrays (gh-1126)
MongooseArray || (MongooseArray = require('./types/array'));
- if (val instanceof MongooseArray) {
+ if (val && val.isMongooseArray) {
val._registerAtomic('$set', val);
+
+ // Small hack for gh-1638: if we're overwriting the entire array, ignore
+ // paths that were modified before the array overwrite
+ this.$__.activePaths.forEach(function(modifiedPath) {
+ if (modifiedPath.indexOf(path + '.') === 0) {
+ _this.$__.activePaths.ignore(modifiedPath);
+ }
+ });
}
}
- var obj = this._doc
- , i = 0
- , l = parts.length
+ var obj = this._doc,
+ i = 0,
+ l = parts.length;
for (; i < l; i++) {
- var next = i + 1
- , last = next === l;
+ var next = i + 1,
+ last = next === l;
if (last) {
obj[parts[i]] = val;
} else {
- if (obj[parts[i]] && 'Object' === obj[parts[i]].constructor.name) {
+ if (obj[parts[i]] && utils.getFunctionName(obj[parts[i]].constructor) === 'Object') {
+ obj = obj[parts[i]];
+ } else if (obj[parts[i]] && obj[parts[i]] instanceof Embedded) {
+ obj = obj[parts[i]];
+ } else if (obj[parts[i]] && obj[parts[i]].$isSingleNested) {
obj = obj[parts[i]];
} else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) {
obj = obj[parts[i]];
@@ -637,7 +836,7 @@ Document.prototype.$__set = function (
}
}
}
-}
+};
/**
* Gets a raw value from a path (no getters)
@@ -646,9 +845,9 @@ Document.prototype.$__set = function (
* @api private
*/
-Document.prototype.getValue = function (path) {
+Document.prototype.getValue = function(path) {
return utils.getValue(path, this._doc);
-}
+};
/**
* Sets a raw value for a path (no casting, setters, transformations)
@@ -658,10 +857,10 @@ Document.prototype.getValue = function (path) {
* @api private
*/
-Document.prototype.setValue = function (path, val) {
+Document.prototype.setValue = function(path, val) {
utils.setValue(path, val, this._doc);
return this;
-}
+};
/**
* Returns the value of a path.
@@ -675,28 +874,33 @@ Document.prototype.setValue = function (path, val) {
* doc.get('age', String) // "47"
*
* @param {String} path
- * @param {Schema|String|Number|Buffer|etc..} [type] optionally specify a type for on-the-fly attributes
+ * @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for on-the-fly attributes
* @api public
*/
-Document.prototype.get = function (path, type) {
- var adhocs;
+Document.prototype.get = function(path, type) {
+ var adhoc;
if (type) {
- adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {});
- adhocs[path] = Schema.interpretAsType(path, type);
+ adhoc = Schema.interpretAsType(path, type, this.schema.options);
}
- var schema = this.$__path(path) || this.schema.virtualpath(path)
- , pieces = path.split('.')
- , obj = this._doc;
+ var schema = this.$__path(path) || this.schema.virtualpath(path),
+ pieces = path.split('.'),
+ obj = this._doc;
for (var i = 0, l = pieces.length; i < l; i++) {
- obj = undefined === obj || null === obj
- ? undefined
- : obj[pieces[i]];
+ obj = obj === null || obj === void 0
+ ? undefined
+ : obj[pieces[i]];
}
- if (schema) {
+ if (adhoc) {
+ obj = adhoc.cast(obj);
+ }
+
+ // Check if this path is populated - don't apply getters if it is,
+ // because otherwise its a nested object. See gh-3357
+ if (schema && !this.populated(path)) {
obj = schema.applyGetters(obj, this);
}
@@ -712,15 +916,14 @@ Document.prototype.get = function (path, type) {
* @memberOf Document
*/
-Document.prototype.$__path = function (path) {
- var adhocs = this.$__.adhocPaths
- , adhocType = adhocs && adhocs[path];
+Document.prototype.$__path = function(path) {
+ var adhocs = this.$__.adhocPaths,
+ adhocType = adhocs && adhocs[path];
if (adhocType) {
return adhocType;
- } else {
- return this.schema.path(path);
}
+ return this.schema.path(path);
};
/**
@@ -738,30 +941,25 @@ Document.prototype.$__path = function (path) {
* @api public
*/
-Document.prototype.markModified = function (path) {
+Document.prototype.markModified = function(path) {
this.$__.activePaths.modify(path);
-}
+};
/**
- * Catches errors that occur during execution of `fn` and stores them to later be passed when `save()` is executed.
+ * Clears the modified state on the specified path.
*
- * @param {Function} fn function to execute
- * @param {Object} scope the scope with which to call fn
- * @api private
- * @method $__try
- * @memberOf Document
+ * ####Example:
+ *
+ * doc.foo = 'bar';
+ * doc.unmarkModified('foo');
+ * doc.save() // changes to foo will not be persisted
+ *
+ * @param {String} path the path to unmark modified
+ * @api public
*/
-Document.prototype.$__try = function (fn, scope) {
- var res;
- try {
- fn.call(scope);
- res = true;
- } catch (e) {
- this.$__error(e);
- res = false;
- }
- return res;
+Document.prototype.unmarkModified = function(path) {
+ this.$__.activePaths.init(path);
};
/**
@@ -771,14 +969,15 @@ Document.prototype.$__try = function (fn, scope) {
* @api public
*/
-Document.prototype.modifiedPaths = function () {
+Document.prototype.modifiedPaths = function() {
var directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
-
- return directModifiedPaths.reduce(function (list, path) {
+ return directModifiedPaths.reduce(function(list, path) {
var parts = path.split('.');
- return list.concat(parts.reduce(function (chains, part, i) {
+ return list.concat(parts.reduce(function(chains, part, i) {
return chains.concat(parts.slice(0, i).concat(part).join('.'));
- }, []));
+ }, []).filter(function(chain) {
+ return (list.indexOf(chain) === -1);
+ }));
}, []);
};
@@ -790,20 +989,47 @@ Document.prototype.modifiedPaths = function () {
* ####Example
*
* doc.set('documents.0.title', 'changed');
- * doc.isModified() // true
- * doc.isModified('documents') // true
- * doc.isModified('documents.0.title') // true
- * doc.isDirectModified('documents') // false
+ * doc.isModified() // true
+ * doc.isModified('documents') // true
+ * doc.isModified('documents.0.title') // true
+ * doc.isModified('documents otherProp') // true
+ * doc.isDirectModified('documents') // false
*
* @param {String} [path] optional
* @return {Boolean}
* @api public
*/
-Document.prototype.isModified = function (path) {
- return path
- ? !!~this.modifiedPaths().indexOf(path)
- : this.$__.activePaths.some('modify');
+Document.prototype.isModified = function(paths) {
+ if (paths) {
+ if (!Array.isArray(paths)) {
+ paths = paths.split(' ');
+ }
+ var modified = this.modifiedPaths();
+ return paths.some(function(path) {
+ return !!~modified.indexOf(path);
+ });
+ }
+ return this.$__.activePaths.some('modify');
+};
+
+/**
+ * Checks if a path is set to its default.
+ *
+ * ####Example
+ *
+ * MyModel = mongoose.model('test', { name: { type: String, default: 'Val '} });
+ * var m = new MyModel();
+ * m.$isDefault('name'); // true
+ *
+ * @param {String} [path]
+ * @return {Boolean}
+ * @method $isDefault
+ * @api public
+ */
+
+Document.prototype.$isDefault = function(path) {
+ return (path in this.$__.activePaths.states.default);
};
/**
@@ -820,7 +1046,7 @@ Document.prototype.isModified = function (path) {
* @api public
*/
-Document.prototype.isDirectModified = function (path) {
+Document.prototype.isDirectModified = function(path) {
return (path in this.$__.activePaths.states.modify);
};
@@ -832,7 +1058,7 @@ Document.prototype.isDirectModified = function (path) {
* @api public
*/
-Document.prototype.isInit = function (path) {
+Document.prototype.isInit = function(path) {
return (path in this.$__.activePaths.states.init);
};
@@ -851,27 +1077,28 @@ Document.prototype.isInit = function (path) {
* @api public
*/
-Document.prototype.isSelected = function isSelected (path) {
+Document.prototype.isSelected = function isSelected(path) {
if (this.$__.selected) {
-
- if ('_id' === path) {
- return 0 !== this.$__.selected._id;
+ if (path === '_id') {
+ return this.$__.selected._id !== 0;
}
- var paths = Object.keys(this.$__.selected)
- , i = paths.length
- , inclusive = false
- , cur
+ var paths = Object.keys(this.$__.selected),
+ i = paths.length,
+ inclusive = false,
+ cur;
- if (1 === i && '_id' === paths[0]) {
+ if (i === 1 && paths[0] === '_id') {
// only _id was selected.
- return 0 === this.$__.selected._id;
+ return this.$__.selected._id === 0;
}
while (i--) {
cur = paths[i];
- if ('_id' == cur) continue;
- inclusive = !! this.$__.selected[cur];
+ if (cur === '_id') {
+ continue;
+ }
+ inclusive = !!this.$__.selected[cur];
break;
}
@@ -884,22 +1111,24 @@ Document.prototype.isSelected = function isSelected (path) {
while (i--) {
cur = paths[i];
- if ('_id' == cur) continue;
+ if (cur === '_id') {
+ continue;
+ }
- if (0 === cur.indexOf(pathDot)) {
+ if (cur.indexOf(pathDot) === 0) {
return inclusive;
}
- if (0 === pathDot.indexOf(cur + '.')) {
+ if (pathDot.indexOf(cur + '.') === 0) {
return inclusive;
}
}
- return ! inclusive;
+ return !inclusive;
}
return true;
-}
+};
/**
* Executes registered validation rules for this document.
@@ -915,92 +1144,325 @@ Document.prototype.isSelected = function isSelected (path) {
* else // validation passed
* });
*
- * @param {Function} cb called after validation completes, passing an error if one occurred
+ * @param {Object} optional options internal options
+ * @param {Function} callback optional callback called after validation completes, passing an error if one occurred
+ * @return {Promise} Promise
* @api public
*/
-Document.prototype.validate = function (cb) {
- var self = this
+Document.prototype.validate = function(options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ this.$__validate(callback);
+};
+/*!
+ * ignore
+ */
+
+function _getPathsToValidate(doc) {
// only validate required fields when necessary
- var paths = Object.keys(this.$__.activePaths.states.require).filter(function (path) {
- if (!self.isSelected(path) && !self.isModified(path)) return false;
+ var paths = Object.keys(doc.$__.activePaths.states.require).filter(function(path) {
+ if (!doc.isSelected(path) && !doc.isModified(path)) {
+ return false;
+ }
+ var p = doc.schema.path(path);
+ if (typeof p.originalRequiredValue === 'function') {
+ return p.originalRequiredValue.call(doc);
+ }
return true;
});
- paths = paths.concat(Object.keys(this.$__.activePaths.states.init));
- paths = paths.concat(Object.keys(this.$__.activePaths.states.modify));
- paths = paths.concat(Object.keys(this.$__.activePaths.states.default));
+ paths = paths.concat(Object.keys(doc.$__.activePaths.states.init));
+ paths = paths.concat(Object.keys(doc.$__.activePaths.states.modify));
+ paths = paths.concat(Object.keys(doc.$__.activePaths.states.default));
+
+ // gh-661: if a whole array is modified, make sure to run validation on all
+ // the children as well
+ for (var i = 0; i < paths.length; ++i) {
+ var path = paths[i];
+ var val = doc.getValue(path);
+ if (val && val.isMongooseArray && !Buffer.isBuffer(val) && !val.isMongooseDocumentArray) {
+ var numElements = val.length;
+ for (var j = 0; j < numElements; ++j) {
+ paths.push(path + '.' + j);
+ }
+ }
+ }
+
+ var flattenOptions = { skipArrays: true };
+ for (i = 0; i < paths.length; ++i) {
+ var pathToCheck = paths[i];
+ if (doc.schema.nested[pathToCheck]) {
+ var _v = doc.getValue(pathToCheck);
+ if (isMongooseObject(_v)) {
+ _v = _v.toObject({ virtuals: false });
+ }
+ var flat = flatten(_v, '', flattenOptions);
+ var _subpaths = Object.keys(flat).map(function(p) {
+ return pathToCheck + '.' + p;
+ });
+ paths = paths.concat(_subpaths);
+ }
+ }
+
+ return paths;
+}
- if (0 === paths.length) {
- complete();
- return this;
+/*!
+ * ignore
+ */
+
+Document.prototype.$__validate = function(callback) {
+ var _this = this;
+ var _complete = function() {
+ var err = _this.$__.validationError;
+ _this.$__.validationError = undefined;
+ _this.emit('validate', _this);
+ if (err) {
+ for (var key in err.errors) {
+ // Make sure cast errors persist
+ if (!_this.__parent && err.errors[key] instanceof MongooseError.CastError) {
+ _this.invalidate(key, err.errors[key]);
+ }
+ }
+
+ return err;
+ }
+ };
+
+ // only validate required fields when necessary
+ var paths = _getPathsToValidate(this);
+
+ if (paths.length === 0) {
+ process.nextTick(function() {
+ var err = _complete();
+ if (err) {
+ callback(err);
+ return;
+ }
+ callback();
+ });
}
- var validating = {}
- , total = 0;
+ var validating = {},
+ total = 0;
- paths.forEach(validatePath);
- return this;
+ var complete = function() {
+ var err = _complete();
+ if (err) {
+ callback(err);
+ return;
+ }
+ callback();
+ };
- function validatePath (path) {
- if (validating[path]) return;
+ var validatePath = function(path) {
+ if (validating[path]) {
+ return;
+ }
validating[path] = true;
total++;
- process.nextTick(function(){
- var p = self.schema.path(path);
- if (!p) return --total || complete();
+ process.nextTick(function() {
+ var p = _this.schema.path(path);
+ if (!p) {
+ return --total || complete();
+ }
- var val = self.getValue(path);
- p.doValidate(val, function (err) {
+ // If user marked as invalid or there was a cast error, don't validate
+ if (!_this.$isValid(path)) {
+ --total || complete();
+ return;
+ }
+
+ var val = _this.getValue(path);
+ p.doValidate(val, function(err) {
if (err) {
- self.invalidate(
- path
- , err
- , undefined
- , true // embedded docs
- );
+ _this.invalidate(path, err, undefined, true);
}
--total || complete();
- }, self);
+ }, _this);
});
+ };
+
+ paths.forEach(validatePath);
+};
+
+/**
+ * Executes registered validation rules (skipping asynchronous validators) for this document.
+ *
+ * ####Note:
+ *
+ * This method is useful if you need synchronous validation.
+ *
+ * ####Example:
+ *
+ * var err = doc.validateSync();
+ * if ( err ){
+ * handleError( err );
+ * } else {
+ * // validation passed
+ * }
+ *
+ * @param {Array|string} pathsToValidate only validate the given paths
+ * @return {MongooseError|undefined} MongooseError if there are errors during validation, or undefined if there is no error.
+ * @api public
+ */
+
+Document.prototype.validateSync = function(pathsToValidate) {
+ var _this = this;
+
+ if (typeof pathsToValidate === 'string') {
+ pathsToValidate = pathsToValidate.split(' ');
}
- function complete () {
- var err = self.$__.validationError;
- self.$__.validationError = undefined;
- self.emit('validate', self);
- cb(err);
+ // only validate required fields when necessary
+ var paths = _getPathsToValidate(this);
+
+ if (pathsToValidate && pathsToValidate.length) {
+ var tmp = [];
+ for (var i = 0; i < paths.length; ++i) {
+ if (pathsToValidate.indexOf(paths[i]) !== -1) {
+ tmp.push(paths[i]);
+ }
+ }
+ paths = tmp;
}
+
+ var validating = {};
+
+ paths.forEach(function(path) {
+ if (validating[path]) {
+ return;
+ }
+
+ validating[path] = true;
+
+ var p = _this.schema.path(path);
+ if (!p) {
+ return;
+ }
+ if (!_this.$isValid(path)) {
+ return;
+ }
+
+ var val = _this.getValue(path);
+ var err = p.doValidateSync(val, _this);
+ if (err) {
+ _this.invalidate(path, err, undefined, true);
+ }
+ });
+
+ var err = _this.$__.validationError;
+ _this.$__.validationError = undefined;
+ _this.emit('validate', _this);
+
+ if (err) {
+ for (var key in err.errors) {
+ // Make sure cast errors persist
+ if (err.errors[key] instanceof MongooseError.CastError) {
+ _this.invalidate(key, err.errors[key]);
+ }
+ }
+ }
+
+ return err;
};
/**
* Marks a path as invalid, causing validation to fail.
*
+ * The `errorMsg` argument will become the message of the `ValidationError`.
+ *
+ * The `value` argument (if passed) will be available through the `ValidationError.value` property.
+ *
+ * doc.invalidate('size', 'must be less than 20', 14);
+
+ * doc.validate(function (err) {
+ * console.log(err)
+ * // prints
+ * { message: 'Validation failed',
+ * name: 'ValidationError',
+ * errors:
+ * { size:
+ * { message: 'must be less than 20',
+ * name: 'ValidatorError',
+ * path: 'size',
+ * type: 'user defined',
+ * value: 14 } } }
+ * })
+ *
* @param {String} path the field to invalidate
- * @param {String|Error} err the error which states the reason `path` was invalid
+ * @param {String|Error} errorMsg the error which states the reason `path` was invalid
* @param {Object|String|Number|any} value optional invalid value
+ * @param {String} [kind] optional `kind` property for the error
+ * @return {ValidationError} the current ValidationError, with all currently invalidated paths
* @api public
*/
-Document.prototype.invalidate = function (path, err, val) {
+Document.prototype.invalidate = function(path, err, val, kind) {
if (!this.$__.validationError) {
this.$__.validationError = new ValidationError(this);
}
- if (!err || 'string' === typeof err) {
- // sniffing arguments:
- // need to handle case where user does not pass value
- // so our error message is cleaner
- err = 2 < arguments.length
- ? new ValidatorError(path, err, val)
- : new ValidatorError(path, err)
+ if (this.$__.validationError.errors[path]) {
+ return;
+ }
+
+ if (!err || typeof err === 'string') {
+ err = new ValidatorError({
+ path: path,
+ message: err,
+ type: kind || 'user defined',
+ value: val
+ });
+ }
+
+ if (this.$__.validationError === err) {
+ return this.$__.validationError;
}
this.$__.validationError.errors[path] = err;
-}
+ return this.$__.validationError;
+};
+
+/**
+ * Marks a path as valid, removing existing validation errors.
+ *
+ * @param {String} path the field to mark as valid
+ * @api private
+ * @method $markValid
+ * @receiver Document
+ */
+
+Document.prototype.$markValid = function(path) {
+ if (!this.$__.validationError || !this.$__.validationError.errors[path]) {
+ return;
+ }
+
+ delete this.$__.validationError.errors[path];
+ if (Object.keys(this.$__.validationError.errors).length === 0) {
+ this.$__.validationError = null;
+ }
+};
+
+/**
+ * Checks if a path is invalid
+ *
+ * @param {String} path the field to check
+ * @method $isValid
+ * @api private
+ * @receiver Document
+ */
+
+Document.prototype.$isValid = function(path) {
+ return !this.$__.validationError || !this.$__.validationError.errors[path];
+};
/**
* Resets the internal modified state of this document.
@@ -1011,45 +1473,48 @@ Document.prototype.invalidate = function (path, err, val) {
* @memberOf Document
*/
-Document.prototype.$__reset = function reset () {
- var self = this;
+Document.prototype.$__reset = function reset() {
+ var _this = this;
DocumentArray || (DocumentArray = require('./types/documentarray'));
this.$__.activePaths
- .map('init', 'modify', function (i) {
- return self.getValue(i);
+ .map('init', 'modify', function(i) {
+ return _this.getValue(i);
})
- .filter(function (val) {
- return val && val instanceof DocumentArray && val.length;
+ .filter(function(val) {
+ return val && val instanceof Array && val.isMongooseDocumentArray && val.length;
})
- .forEach(function (array) {
+ .forEach(function(array) {
var i = array.length;
while (i--) {
var doc = array[i];
- if (!doc) continue;
+ if (!doc) {
+ continue;
+ }
doc.$__reset();
}
});
// clear atomics
- this.$__dirty().forEach(function (dirt) {
+ this.$__dirty().forEach(function(dirt) {
var type = dirt.value;
if (type && type._atomics) {
type._atomics = {};
}
});
- // Clear 'modify'('dirty') cache
+ // Clear 'dirty' cache
this.$__.activePaths.clear('modify');
+ this.$__.activePaths.clear('default');
this.$__.validationError = undefined;
this.errors = undefined;
- var self = this;
- this.schema.requiredPaths().forEach(function (path) {
- self.$__.activePaths.require(path);
+ _this = this;
+ this.schema.requiredPaths().forEach(function(path) {
+ _this.$__.activePaths.require(path);
});
return this;
-}
+};
/**
* Returns this documents dirty paths / vals.
@@ -1059,26 +1524,44 @@ Document.prototype.$__reset = function reset () {
* @memberOf Document
*/
-Document.prototype.$__dirty = function () {
- var self = this;
+Document.prototype.$__dirty = function() {
+ var _this = this;
- var all = this.$__.activePaths.map('modify', function (path) {
- return { path: path
- , value: self.getValue(path)
- , schema: self.$__path(path) };
+ var all = this.$__.activePaths.map('modify', function(path) {
+ return {
+ path: path,
+ value: _this.getValue(path),
+ schema: _this.$__path(path)
+ };
});
+ // gh-2558: if we had to set a default and the value is not undefined,
+ // we have to save as well
+ all = all.concat(this.$__.activePaths.map('default', function(path) {
+ if (path === '_id' || !_this.getValue(path)) {
+ return;
+ }
+ return {
+ path: path,
+ value: _this.getValue(path),
+ schema: _this.$__path(path)
+ };
+ }));
+
// Sort dirty paths in a flat hierarchy.
- all.sort(function (a, b) {
+ all.sort(function(a, b) {
return (a.path < b.path ? -1 : (a.path > b.path ? 1 : 0));
});
// Ignore "foo.a" if "foo" is dirty already.
- var minimal = []
- , lastPath
- , top;
+ var minimal = [],
+ lastPath,
+ top;
- all.forEach(function (item, i) {
+ all.forEach(function(item) {
+ if (!item) {
+ return;
+ }
if (item.path.indexOf(lastPath) !== 0) {
lastPath = item.path + '.';
minimal.push(item);
@@ -1097,96 +1580,140 @@ Document.prototype.$__dirty = function () {
top = lastPath = null;
return minimal;
-}
+};
/*!
* Compiles schemas.
*/
-function compile (tree, proto, prefix) {
- var keys = Object.keys(tree)
- , i = keys.length
- , limb
- , key;
+function compile(tree, proto, prefix, options) {
+ var keys = Object.keys(tree),
+ i = keys.length,
+ limb,
+ key;
while (i--) {
key = keys[i];
limb = tree[key];
- define(key
- , (('Object' === limb.constructor.name
- && Object.keys(limb).length)
- && (!limb.type || limb.type.type)
- ? limb
- : null)
+ defineKey(key,
+ ((utils.getFunctionName(limb.constructor) === 'Object'
+ && Object.keys(limb).length)
+ && (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type))
+ ? limb
+ : null)
, proto
, prefix
- , keys);
+ , keys
+ , options);
}
-};
+}
+
+// gets descriptors for all properties of `object`
+// makes all properties non-enumerable to match previous behavior to #2211
+function getOwnPropertyDescriptors(object) {
+ var result = {};
+
+ Object.getOwnPropertyNames(object).forEach(function(key) {
+ result[key] = Object.getOwnPropertyDescriptor(object, key);
+ result[key].enumerable = true;
+ });
+
+ return result;
+}
/*!
* Defines the accessor named prop on the incoming prototype.
*/
-function define (prop, subprops, prototype, prefix, keys) {
- var prefix = prefix || ''
- , path = (prefix ? prefix + '.' : '') + prop;
+function defineKey(prop, subprops, prototype, prefix, keys, options) {
+ var path = (prefix ? prefix + '.' : '') + prop;
+ prefix = prefix || '';
if (subprops) {
-
Object.defineProperty(prototype, prop, {
- enumerable: true
- , get: function () {
- if (!this.$__.getters)
- this.$__.getters = {};
-
- if (!this.$__.getters[path]) {
- var nested = Object.create(this);
-
- // save scope for nested getters/setters
- if (!prefix) nested.$__.scope = this;
-
- // shadow inherited getters from sub-objects so
- // thing.nested.nested.nested... doesn't occur (gh-366)
- var i = 0
- , len = keys.length;
-
- for (; i < len; ++i) {
- // over-write the parents getter without triggering it
- Object.defineProperty(nested, keys[i], {
- enumerable: false // It doesn't show up.
- , writable: true // We can set it later.
- , configurable: true // We can Object.defineProperty again.
- , value: undefined // It shadows its parent.
- });
- }
+ enumerable: true,
+ configurable: true,
+ get: function() {
+ var _this = this;
+ if (!this.$__.getters) {
+ this.$__.getters = {};
+ }
+
+ if (!this.$__.getters[path]) {
+ var nested = Object.create(Object.getPrototypeOf(this), getOwnPropertyDescriptors(this));
- nested.toObject = function () {
- return this.get(path);
- };
+ // save scope for nested getters/setters
+ if (!prefix) {
+ nested.$__.scope = this;
+ }
- compile(subprops, nested, path);
- this.$__.getters[path] = nested;
+ // shadow inherited getters from sub-objects so
+ // thing.nested.nested.nested... doesn't occur (gh-366)
+ var i = 0,
+ len = keys.length;
+
+ for (; i < len; ++i) {
+ // over-write the parents getter without triggering it
+ Object.defineProperty(nested, keys[i], {
+ enumerable: false, // It doesn't show up.
+ writable: true, // We can set it later.
+ configurable: true, // We can Object.defineProperty again.
+ value: undefined // It shadows its parent.
+ });
}
- return this.$__.getters[path];
+ Object.defineProperty(nested, 'toObject', {
+ enumerable: true,
+ configurable: true,
+ writable: false,
+ value: function() {
+ return _this.get(path);
+ }
+ });
+
+ Object.defineProperty(nested, 'toJSON', {
+ enumerable: true,
+ configurable: true,
+ writable: false,
+ value: function() {
+ return _this.get(path);
+ }
+ });
+
+ Object.defineProperty(nested, '$__isNested', {
+ enumerable: true,
+ configurable: true,
+ writable: false,
+ value: true
+ });
+
+ compile(subprops, nested, path, options);
+ this.$__.getters[path] = nested;
}
- , set: function (v) {
- if (v instanceof Document) v = v.toObject();
- return (this.$__.scope || this).set(path, v);
+
+ return this.$__.getters[path];
+ },
+ set: function(v) {
+ if (v instanceof Document) {
+ v = v.toObject();
}
+ return (this.$__.scope || this).set(path, v);
+ }
});
-
} else {
-
Object.defineProperty(prototype, prop, {
- enumerable: true
- , get: function ( ) { return this.get.call(this.$__.scope || this, path); }
- , set: function (v) { return this.set.call(this.$__.scope || this, path, v); }
+ enumerable: true,
+ configurable: true,
+ get: function() {
+ return this.get.call(this.$__.scope || this, path);
+ },
+ set: function(v) {
+ return this.set.call(this.$__.scope || this, path, v);
+ }
});
}
-};
+}
/**
* Assigns/compiles `schema` into this documents prototype.
@@ -1197,137 +1724,339 @@ function define (prop, subprops, prototype, prefix, keys) {
* @memberOf Document
*/
-Document.prototype.$__setSchema = function (schema) {
- compile(schema.tree, this);
+Document.prototype.$__setSchema = function(schema) {
+ compile(schema.tree, this, undefined, schema.options);
this.schema = schema;
-}
+};
+
/**
- * Register default hooks
+ * Get active path that were changed and are arrays
*
* @api private
- * @method $__registerHooks
+ * @method $__getArrayPathsToValidate
* @memberOf Document
*/
-Document.prototype.$__registerHooks = function () {
- if (!this.save) return;
-
+Document.prototype.$__getArrayPathsToValidate = function() {
DocumentArray || (DocumentArray = require('./types/documentarray'));
- this.pre('save', function (next) {
- // validate all document arrays.
- // we keep the error semaphore to make sure we don't
- // call `save` unnecessarily (we only need 1 error)
- var subdocs = 0
- , error = false
- , self = this;
-
- // check for DocumentArrays
- var arrays = this.$__.activePaths
- .map('init', 'modify', function (i) {
- return self.getValue(i);
- })
- .filter(function (val) {
- return val && val instanceof DocumentArray && val.length;
- });
-
- if (!arrays.length)
- return next();
+ // validate all document arrays.
+ return this.$__.activePaths
+ .map('init', 'modify', function(i) {
+ return this.getValue(i);
+ }.bind(this))
+ .filter(function(val) {
+ return val && val instanceof Array && val.isMongooseDocumentArray && val.length;
+ }).reduce(function(seed, array) {
+ return seed.concat(array);
+ }, [])
+ .filter(function(doc) {
+ return doc;
+ });
+};
- arrays.forEach(function (array) {
- if (error) return;
- // handle sparse arrays by using for loop vs array.forEach
- // which skips the sparse elements
+/**
+ * Get all subdocs (by bfs)
+ *
+ * @api private
+ * @method $__getAllSubdocs
+ * @memberOf Document
+ */
- var len = array.length
- subdocs += len;
+Document.prototype.$__getAllSubdocs = function() {
+ DocumentArray || (DocumentArray = require('./types/documentarray'));
+ Embedded = Embedded || require('./types/embedded');
- for (var i = 0; i < len; ++i) {
- if (error) break;
+ function docReducer(seed, path) {
+ var val = this[path];
- var doc = array[i];
- if (!doc) {
- --subdocs || next();
- continue;
+ if (val instanceof Embedded) {
+ seed.push(val);
+ }
+ if (val && val.$isSingleNested) {
+ seed = Object.keys(val._doc).reduce(docReducer.bind(val._doc), seed);
+ seed.push(val);
+ }
+ if (val && val.isMongooseDocumentArray) {
+ val.forEach(function _docReduce(doc) {
+ if (!doc || !doc._doc) {
+ return;
}
-
- doc.save(handleSave);
- }
- });
-
- function handleSave (err) {
- if (error) return;
-
- if (err) {
- self.$__.validationError = undefined;
- return next(error = err);
+ if (doc instanceof Embedded) {
+ seed.push(doc);
+ }
+ seed = Object.keys(doc._doc).reduce(docReducer.bind(doc._doc), seed);
+ });
+ } else if (val instanceof Document && val.$__isNested) {
+ val = val.toObject();
+ if (val) {
+ seed = Object.keys(val).reduce(docReducer.bind(val), seed);
}
-
- --subdocs || next();
}
+ return seed;
+ }
- }, function (err) {
- // emit on the Model if listening
- if (this.constructor.listeners('error').length) {
- this.constructor.emit('error', err);
- } else {
- // emit on the connection
- if (!this.db.listeners('error').length) {
- err.stack = 'No listeners detected, throwing. '
- + 'Consider adding an error listener to your connection.\n'
- + err.stack
- }
- this.db.emit('error', err);
- }
- }).pre('save', function checkForExistingErrors (next) {
- // if any doc.set() calls failed
- var err = this.$__.saveError;
- if (err) {
- this.$__.saveError = null;
- next(err);
- } else {
- next();
- }
- }).pre('save', function validation (next) {
- return this.validate(next);
- });
+ var subDocs = Object.keys(this._doc).reduce(docReducer.bind(this), []);
- // add user defined queues
- this.$__doQueue();
+ return subDocs;
};
/**
- * Registers an error
+ * Executes methods queued from the Schema definition
*
- * @param {Error} err
* @api private
- * @method $__error
+ * @method $__registerHooksFromSchema
* @memberOf Document
*/
-Document.prototype.$__error = function (err) {
- this.$__.saveError = err;
- return this;
+Document.prototype.$__registerHooksFromSchema = function() {
+ Embedded = Embedded || require('./types/embedded');
+ var Promise = PromiseProvider.get();
+
+ var _this = this;
+ var q = _this.schema && _this.schema.callQueue;
+ if (!q.length) {
+ return _this;
+ }
+
+ // we are only interested in 'pre' hooks, and group by point-cut
+ var toWrap = q.reduce(function(seed, pair) {
+ if (pair[0] !== 'pre' && pair[0] !== 'post' && pair[0] !== 'on') {
+ _this[pair[0]].apply(_this, pair[1]);
+ return seed;
+ }
+ var args = [].slice.call(pair[1]);
+ var pointCut = pair[0] === 'on' ? 'post' : args[0];
+ if (!(pointCut in seed)) {
+ seed[pointCut] = {post: [], pre: []};
+ }
+ if (pair[0] === 'post') {
+ seed[pointCut].post.push(args);
+ } else if (pair[0] === 'on') {
+ seed[pointCut].push(args);
+ } else {
+ seed[pointCut].pre.push(args);
+ }
+ return seed;
+ }, {post: []});
+
+ // 'post' hooks are simpler
+ toWrap.post.forEach(function(args) {
+ _this.on.apply(_this, args);
+ });
+ delete toWrap.post;
+
+ // 'init' should be synchronous on subdocuments
+ if (toWrap.init && _this instanceof Embedded) {
+ if (toWrap.init.pre) {
+ toWrap.init.pre.forEach(function(args) {
+ _this.$pre.apply(_this, args);
+ });
+ }
+ if (toWrap.init.post) {
+ toWrap.init.post.forEach(function(args) {
+ _this.$post.apply(_this, args);
+ });
+ }
+ delete toWrap.init;
+ } else if (toWrap.set) {
+ // Set hooks also need to be sync re: gh-3479
+ if (toWrap.set.pre) {
+ toWrap.set.pre.forEach(function(args) {
+ _this.$pre.apply(_this, args);
+ });
+ }
+ if (toWrap.set.post) {
+ toWrap.set.post.forEach(function(args) {
+ _this.$post.apply(_this, args);
+ });
+ }
+ delete toWrap.set;
+ }
+
+ Object.keys(toWrap).forEach(function(pointCut) {
+ // this is so we can wrap everything into a promise;
+ var newName = ('$__original_' + pointCut);
+ if (!_this[pointCut]) {
+ return;
+ }
+ _this[newName] = _this[pointCut];
+ _this[pointCut] = function wrappedPointCut() {
+ var args = [].slice.call(arguments);
+ var lastArg = args.pop();
+ var fn;
+ var originalStack = new Error().stack;
+ var $results;
+ if (lastArg && typeof lastArg !== 'function') {
+ args.push(lastArg);
+ } else {
+ fn = lastArg;
+ }
+
+ var promise = new Promise.ES6(function(resolve, reject) {
+ args.push(function(error) {
+ if (error) {
+ // gh-2633: since VersionError is very generic, take the
+ // stack trace of the original save() function call rather
+ // than the async trace
+ if (error instanceof VersionError) {
+ error.stack = originalStack;
+ }
+ _this.$__handleReject(error);
+ reject(error);
+ return;
+ }
+
+ // There may be multiple results and promise libs other than
+ // mpromise don't support passing multiple values to `resolve()`
+ $results = Array.prototype.slice.call(arguments, 1);
+ resolve.apply(promise, $results);
+ });
+
+ _this[newName].apply(_this, args);
+ });
+ if (fn) {
+ if (_this.constructor.$wrapCallback) {
+ fn = _this.constructor.$wrapCallback(fn);
+ }
+ return promise.then(
+ function() {
+ process.nextTick(function() {
+ fn.apply(null, [null].concat($results));
+ });
+ },
+ function(error) {
+ process.nextTick(function() {
+ fn(error);
+ });
+ });
+ }
+ return promise;
+ };
+
+ toWrap[pointCut].pre.forEach(function(args) {
+ args[0] = newName;
+ _this.$pre.apply(_this, args);
+ });
+ toWrap[pointCut].post.forEach(function(args) {
+ args[0] = newName;
+ _this.$post.apply(_this, args);
+ });
+ });
+ return _this;
+};
+
+Document.prototype.$__handleReject = function handleReject(err) {
+ // emit on the Model if listening
+ if (this.listeners('error').length) {
+ this.emit('error', err);
+ } else if (this.constructor.listeners && this.constructor.listeners('error').length) {
+ this.constructor.emit('error', err);
+ } else if (this.listeners && this.listeners('error').length) {
+ this.emit('error', err);
+ }
};
/**
- * Executes methods queued from the Schema definition
+ * Internal helper for toObject() and toJSON() that doesn't manipulate options
*
* @api private
- * @method $__doQueue
+ * @method $toObject
* @memberOf Document
*/
-Document.prototype.$__doQueue = function () {
- var q = this.schema && this.schema.callQueue;
- if (q) {
- for (var i = 0, l = q.length; i < l; i++) {
- this[q[i][0]].apply(this, q[i][1]);
+Document.prototype.$toObject = function(options, json) {
+ var defaultOptions = {transform: true, json: json};
+
+ if (options && options.depopulate && !options._skipDepopulateTopLevel && this.$__.wasPopulated) {
+ // populated paths that we set to a document
+ return clone(this._id, options);
+ }
+
+ // If we're calling toObject on a populated doc, we may want to skip
+ // depopulated on the top level
+ if (options && options._skipDepopulateTopLevel) {
+ options._skipDepopulateTopLevel = false;
+ }
+
+ // When internally saving this document we always pass options,
+ // bypassing the custom schema options.
+ if (!(options && utils.getFunctionName(options.constructor) === 'Object') ||
+ (options && options._useSchemaOptions)) {
+ if (json) {
+ options = this.schema.options.toJSON ?
+ clone(this.schema.options.toJSON) :
+ {};
+ options.json = true;
+ options._useSchemaOptions = true;
+ } else {
+ options = this.schema.options.toObject ?
+ clone(this.schema.options.toObject) :
+ {};
+ options.json = false;
+ options._useSchemaOptions = true;
}
}
- return this;
+
+ for (var key in defaultOptions) {
+ if (options[key] === undefined) {
+ options[key] = defaultOptions[key];
+ }
+ }
+
+ ('minimize' in options) || (options.minimize = this.schema.options.minimize);
+
+ // remember the root transform function
+ // to save it from being overwritten by sub-transform functions
+ var originalTransform = options.transform;
+
+ var ret = clone(this._doc, options) || {};
+
+ if (options.getters) {
+ applyGetters(this, ret, 'paths', options);
+ // applyGetters for paths will add nested empty objects;
+ // if minimize is set, we need to remove them.
+ if (options.minimize) {
+ ret = minimize(ret) || {};
+ }
+ }
+
+ if (options.virtuals || options.getters && options.virtuals !== false) {
+ applyGetters(this, ret, 'virtuals', options);
+ }
+
+ if (options.versionKey === false && this.schema.options.versionKey) {
+ delete ret[this.schema.options.versionKey];
+ }
+
+ var transform = options.transform;
+
+ // In the case where a subdocument has its own transform function, we need to
+ // check and see if the parent has a transform (options.transform) and if the
+ // child schema has a transform (this.schema.options.toObject) In this case,
+ // we need to adjust options.transform to be the child schema's transform and
+ // not the parent schema's
+ if (transform === true ||
+ (this.schema.options.toObject && transform)) {
+ var opts = options.json ? this.schema.options.toJSON : this.schema.options.toObject;
+
+ if (opts) {
+ transform = (typeof options.transform === 'function' ? options.transform : opts.transform);
+ }
+ } else {
+ options.transform = originalTransform;
+ }
+
+ if (typeof transform === 'function') {
+ var xformed = transform(this, ret, options);
+ if (typeof xformed !== 'undefined') {
+ ret = xformed;
+ }
+ }
+
+ return ret;
};
/**
@@ -1341,6 +2070,9 @@ Document.prototype.$__doQueue = function () {
* - `virtuals` apply virtual getters (can override `getters` option)
* - `minimize` remove empty objects (defaults to true)
* - `transform` a transform function to apply to the resulting document before returning
+ * - `depopulate` depopulate any populated paths, replacing them with their original refs (defaults to false)
+ * - `versionKey` whether to include the version key (defaults to true)
+ * - `retainKeyOrder` keep the order of object keys. If this is set to true, `Object.keys(new Doc({ a: 1, b: 2}).toObject())` will always produce `['a', 'b']` (defaults to false)
*
* ####Getters/Virtuals
*
@@ -1379,6 +2111,7 @@ Document.prototype.$__doQueue = function () {
* schema.options.toObject.transform = function (doc, ret, options) {
* // remove the _id of every document before returning the result
* delete ret._id;
+ * return ret;
* }
*
* // without the transformation in the schema
@@ -1421,6 +2154,7 @@ Document.prototype.$__doQueue = function () {
* delete ret[prop];
* });
* }
+ * return ret;
* }
*
* var doc = new Doc({ _id: 'anId', secret: 47, name: 'Wreck-it Ralph' });
@@ -1428,11 +2162,7 @@ Document.prototype.$__doQueue = function () {
* doc.toObject({ hide: 'secret _id' }); // { _id: 'anId', secret: 47, name: 'Wreck-it Ralph' }
* doc.toObject({ hide: 'secret _id', transform: true }); // { name: 'Wreck-it Ralph' }
*
- * Transforms are applied to the document _and each of its sub-documents_. To determine whether or not you are currently operating on a sub-document you might use the following guard:
- *
- * if ('function' == typeof doc.ownerDocument) {
- * // working with a sub doc
- * }
+ * Transforms are applied _only to the document and are not applied to sub-documents_.
*
* Transforms, like all of these options, are also available for `toJSON`.
*
@@ -1446,48 +2176,44 @@ Document.prototype.$__doQueue = function () {
* @api public
*/
-Document.prototype.toObject = function (options) {
- if (options && options.depopulate && this.$__.wasPopulated) {
- // populated paths that we set to a document
- return clone(this._id, options);
- }
-
- // When internally saving this document we always pass options,
- // bypassing the custom schema options.
- if (!(options && 'Object' == options.constructor.name)) {
- options = this.schema.options.toObject
- ? clone(this.schema.options.toObject)
- : {};
- }
+Document.prototype.toObject = function(options) {
+ return this.$toObject(options);
+};
- ;('minimize' in options) || (options.minimize = this.schema.options.minimize);
+/*!
+ * Minimizes an object, removing undefined values and empty objects
+ *
+ * @param {Object} object to minimize
+ * @return {Object}
+ */
- var ret = clone(this._doc, options);
+function minimize(obj) {
+ var keys = Object.keys(obj),
+ i = keys.length,
+ hasKeys,
+ key,
+ val;
- if (options.virtuals || options.getters && false !== options.virtuals) {
- applyGetters(this, ret, 'virtuals', options);
- }
+ while (i--) {
+ key = keys[i];
+ val = obj[key];
- if (options.getters) {
- applyGetters(this, ret, 'paths', options);
- }
+ if (utils.isObject(val)) {
+ obj[key] = minimize(val);
+ }
- if (true === options.transform) {
- var opts = options.json
- ? this.schema.options.toJSON
- : this.schema.options.toObject;
- if (opts) {
- options.transform = opts.transform;
+ if (undefined === obj[key]) {
+ delete obj[key];
+ continue;
}
- }
- if ('function' == typeof options.transform) {
- var xformed = options.transform(this, ret, options);
- if ('undefined' != typeof xformed) ret = xformed;
+ hasKeys = true;
}
- return ret;
-};
+ return hasKeys
+ ? obj
+ : undefined;
+}
/*!
* Applies virtuals properties to `json`.
@@ -1498,20 +2224,20 @@ Document.prototype.toObject = function (options) {
* @return {Object} `json`
*/
-function applyGetters (self, json, type, options) {
- var schema = self.schema
- , paths = Object.keys(schema[type])
- , i = paths.length
- , path
+function applyGetters(self, json, type, options) {
+ var schema = self.schema,
+ paths = Object.keys(schema[type]),
+ i = paths.length,
+ path;
while (i--) {
path = paths[i];
- var parts = path.split('.')
- , plen = parts.length
- , last = plen - 1
- , branch = json
- , part
+ var parts = path.split('.'),
+ plen = parts.length,
+ last = plen - 1,
+ branch = json,
+ part;
for (var ii = 0; ii < plen; ++ii) {
part = parts[ii];
@@ -1535,24 +2261,14 @@ function applyGetters (self, json, type, options) {
*
* See [schema options](/docs/guide.html#toJSON) for details.
*
- * @param {Object} options same options as [Document#toObject](#document_Document-toObject)
+ * @param {Object} options
* @return {Object}
* @see Document#toObject #document_Document-toObject
-
* @api public
*/
-Document.prototype.toJSON = function (options) {
- // check for object type since an array of documents
- // being stringified passes array indexes instead
- // of options objects. JSON.stringify([doc, doc])
- if (!(options && 'Object' == options.constructor.name)) {
- options = this.schema.options.toJSON
- ? clone(this.schema.options.toJSON)
- : {};
- }
- options.json = true;
- return this.toObject(options);
+Document.prototype.toJSON = function(options) {
+ return this.$toObject(options, true);
};
/**
@@ -1561,12 +2277,16 @@ Document.prototype.toJSON = function (options) {
* @api public
*/
-Document.prototype.inspect = function (options) {
- var opts = options && 'Object' == options.constructor.name ? options :
- this.schema.options.toObject ? clone(this.schema.options.toObject) :
- {};
- opts.minimize = false;
- return inspect(this.toObject(opts));
+Document.prototype.inspect = function(options) {
+ var isPOJO = options &&
+ utils.getFunctionName(options.constructor) === 'Object';
+ var opts;
+ if (isPOJO) {
+ opts = options;
+ opts.minimize = false;
+ opts.retainKeyOrder = true;
+ }
+ return this.toObject(opts);
};
/**
@@ -1576,28 +2296,41 @@ Document.prototype.inspect = function (options) {
* @method toString
*/
-Document.prototype.toString = Document.prototype.inspect;
+Document.prototype.toString = function() {
+ return inspect(this.inspect());
+};
/**
* Returns true if the Document stores the same data as doc.
*
- * Documents are considered equal when they have matching `_id`s.
+ * Documents are considered equal when they have matching `_id`s, unless neither
+ * document has an `_id`, in which case this function falls back to using
+ * `deepEqual()`.
*
* @param {Document} doc a document to compare
* @return {Boolean}
* @api public
*/
-Document.prototype.equals = function (doc) {
+Document.prototype.equals = function(doc) {
+ if (!doc) {
+ return false;
+ }
+
var tid = this.get('_id');
- var docid = doc.get('_id');
+ var docid = doc.get ? doc.get('_id') : doc;
+ if (!tid && !docid) {
+ return deepEqual(this, doc);
+ }
return tid && tid.equals
- ? tid.equals(docid)
- : tid === docid;
-}
+ ? tid.equals(docid)
+ : tid === docid;
+};
/**
* Populates document references, executing the `callback` when complete.
+ * If you want to use promises instead, use this function with
+ * [`execPopulate()`](#document_Document-execPopulate)
*
* ####Example:
*
@@ -1610,38 +2343,43 @@ Document.prototype.equals = function (doc) {
* model: 'modelName'
* options: opts
* }, function (err, user) {
- * assert(doc._id == user._id) // the document itself is passed
+ * assert(doc._id === user._id) // the document itself is passed
* })
*
* // summary
- * doc.populate(path) // not executed
- * doc.populate(options); // not executed
- * doc.populate(path, callback) // executed
- * doc.populate(options, callback); // executed
- * doc.populate(callback); // executed
+ * doc.populate(path) // not executed
+ * doc.populate(options); // not executed
+ * doc.populate(path, callback) // executed
+ * doc.populate(options, callback); // executed
+ * doc.populate(callback); // executed
+ * doc.populate(options).execPopulate() // executed, returns promise
*
*
* ####NOTE:
*
- * Population does not occur unless a `callback` is passed.
+ * Population does not occur unless a `callback` is passed *or* you explicitly
+ * call `execPopulate()`.
* Passing the same path a second time will overwrite the previous path options.
* See [Model.populate()](#model_Model.populate) for explaination of options.
*
* @see Model.populate #model_Model.populate
+ * @see Document.execPopulate #document_Document-execPopulate
* @param {String|Object} [path] The path to populate or an options object
* @param {Function} [callback] When passed, population is invoked
* @api public
* @return {Document} this
*/
-Document.prototype.populate = function populate () {
- if (0 === arguments.length) return this;
+Document.prototype.populate = function populate() {
+ if (arguments.length === 0) {
+ return this;
+ }
var pop = this.$__.populate || (this.$__.populate = {});
var args = utils.args(arguments);
var fn;
- if ('function' == typeof args[args.length-1]) {
+ if (typeof args[args.length - 1] === 'function') {
fn = args.pop();
}
@@ -1657,11 +2395,52 @@ Document.prototype.populate = function populate () {
if (fn) {
var paths = utils.object.vals(pop);
this.$__.populate = undefined;
+ paths.__noPromise = true;
this.constructor.populate(this, paths, fn);
}
return this;
-}
+};
+
+/**
+ * Explicitly executes population and returns a promise. Useful for ES2015
+ * integration.
+ *
+ * ####Example:
+ *
+ * var promise = doc.
+ * populate('company').
+ * populate({
+ * path: 'notes',
+ * match: /airline/,
+ * select: 'text',
+ * model: 'modelName'
+ * options: opts
+ * }).
+ * execPopulate();
+ *
+ * // summary
+ * doc.execPopulate().then(resolve, reject);
+ *
+ *
+ * @see Document.populate #document_Document-populate
+ * @api public
+ * @return {Promise} promise that resolves to the document when population is done
+ */
+
+Document.prototype.execPopulate = function() {
+ var Promise = PromiseProvider.get();
+ var _this = this;
+ return new Promise.ES6(function(resolve, reject) {
+ _this.populate(function(error, res) {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(res);
+ }
+ });
+ });
+};
/**
* Gets _id(s) used during population of the given `path`.
@@ -1680,27 +2459,61 @@ Document.prototype.populate = function populate () {
* @api public
*/
-Document.prototype.populated = function (path, val, options) {
+Document.prototype.populated = function(path, val, options) {
// val and options are internal
- if (null == val) {
- if (!this.$__.populated) return undefined;
+ if (val === null || val === void 0) {
+ if (!this.$__.populated) {
+ return undefined;
+ }
var v = this.$__.populated[path];
- if (v) return v.value;
+ if (v) {
+ return v.value;
+ }
return undefined;
}
// internal
- if (true === val) {
- if (!this.$__.populated) return undefined;
+ if (val === true) {
+ if (!this.$__.populated) {
+ return undefined;
+ }
return this.$__.populated[path];
}
this.$__.populated || (this.$__.populated = {});
- this.$__.populated[path] = { value: val, options: options };
+ this.$__.populated[path] = {value: val, options: options};
return val;
-}
+};
+
+/**
+ * Takes a populated field and returns it to its unpopulated state.
+ *
+ * ####Example:
+ *
+ * Model.findOne().populate('author').exec(function (err, doc) {
+ * console.log(doc.author.name); // Dr.Seuss
+ * console.log(doc.depopulate('author'));
+ * console.log(doc.author); // '5144cf8050f071d979c118a7'
+ * })
+ *
+ * If the path was not populated, this is a no-op.
+ *
+ * @param {String} path
+ * @see Document.populate #document_Document-populate
+ * @api public
+ */
+
+Document.prototype.depopulate = function(path) {
+ var populatedIds = this.populated(path);
+ if (!populatedIds) {
+ return;
+ }
+ delete this.$__.populated[path];
+ this.set(path, populatedIds);
+};
+
/**
* Returns the full path to this document.
@@ -1712,10 +2525,10 @@ Document.prototype.populated = function (path, val, options) {
* @memberOf Document
*/
-Document.prototype.$__fullPath = function (path) {
+Document.prototype.$__fullPath = function(path) {
// overridden in SubDocuments
return path || '';
-}
+};
/*!
* Module exports.
@@ -1723,4 +2536,3 @@ Document.prototype.$__fullPath = function (path) {
Document.ValidationError = ValidationError;
module.exports = exports = Document;
-exports.Error = DocumentError;
diff --git a/node_modules/mongoose/lib/document_provider.js b/node_modules/mongoose/lib/document_provider.js
new file mode 100644
index 0000000..10087cc
--- /dev/null
+++ b/node_modules/mongoose/lib/document_provider.js
@@ -0,0 +1,21 @@
+'use strict';
+
+/* eslint-env browser */
+
+/*!
+ * Module dependencies.
+ */
+var Document = require('./document.js');
+var BrowserDocument = require('./browserDocument.js');
+
+/**
+ * Returns the Document constructor for the current context
+ *
+ * @api private
+ */
+module.exports = function() {
+ if (typeof window !== 'undefined' && typeof document !== 'undefined' && document === window.document) {
+ return BrowserDocument;
+ }
+ return Document;
+};
diff --git a/node_modules/mongoose/lib/drivers/browser/ReadPreference.js b/node_modules/mongoose/lib/drivers/browser/ReadPreference.js
new file mode 100644
index 0000000..f20cd6c
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/ReadPreference.js
@@ -0,0 +1,5 @@
+/*!
+ * ignore
+ */
+
+module.exports = function() {};
diff --git a/node_modules/mongoose/lib/drivers/browser/binary.js b/node_modules/mongoose/lib/drivers/browser/binary.js
new file mode 100644
index 0000000..4d7a395
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/binary.js
@@ -0,0 +1,12 @@
+
+/*!
+ * Module dependencies.
+ */
+
+var Binary = require('bson').Binary;
+
+/*!
+ * Module exports.
+ */
+
+module.exports = exports = Binary;
diff --git a/node_modules/mongoose/lib/drivers/browser/index.js b/node_modules/mongoose/lib/drivers/browser/index.js
new file mode 100644
index 0000000..fa5dbb4
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/index.js
@@ -0,0 +1,7 @@
+/*!
+ * Module exports.
+ */
+
+exports.Binary = require('./binary');
+exports.ObjectId = require('./objectid');
+exports.ReadPreference = require('./ReadPreference');
diff --git a/node_modules/mongoose/lib/drivers/browser/objectid.js b/node_modules/mongoose/lib/drivers/browser/objectid.js
new file mode 100644
index 0000000..e63c04a
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/browser/objectid.js
@@ -0,0 +1,14 @@
+
+/*!
+ * [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) ObjectId
+ * @constructor NodeMongoDbObjectId
+ * @see ObjectId
+ */
+
+var ObjectId = require('bson').ObjectID;
+
+/*!
+ * ignore
+ */
+
+module.exports = exports = ObjectId;
diff --git a/node_modules/mongoose/lib/drivers/index.js b/node_modules/mongoose/lib/drivers/index.js
new file mode 100644
index 0000000..5a842ba
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/index.js
@@ -0,0 +1,17 @@
+/*!
+ * ignore
+ */
+
+var driver;
+
+if (typeof window === 'undefined') {
+ driver = require(global.MONGOOSE_DRIVER_PATH || './node-mongodb-native');
+} else {
+ driver = require('./browser');
+}
+
+/*!
+ * ignore
+ */
+
+module.exports = driver;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/ReadPreference.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/ReadPreference.js
new file mode 100644
index 0000000..e921d60
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/ReadPreference.js
@@ -0,0 +1,45 @@
+/*!
+ * Module dependencies.
+ */
+
+var mongodb = require('mongodb');
+var ReadPref = mongodb.ReadPreference;
+
+/*!
+ * Converts arguments to ReadPrefs the driver
+ * can understand.
+ *
+ * @param {String|Array} pref
+ * @param {Array} [tags]
+ */
+
+module.exports = function readPref(pref, tags) {
+ if (Array.isArray(pref)) {
+ tags = pref[1];
+ pref = pref[0];
+ }
+
+ if (pref instanceof ReadPref) {
+ return pref;
+ }
+
+ switch (pref) {
+ case 'p':
+ pref = 'primary';
+ break;
+ case 'pp':
+ pref = 'primaryPreferred';
+ break;
+ case 's':
+ pref = 'secondary';
+ break;
+ case 'sp':
+ pref = 'secondaryPreferred';
+ break;
+ case 'n':
+ pref = 'nearest';
+ break;
+ }
+
+ return new ReadPref(pref, tags);
+};
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js
index 0480d31..657efde 100644
--- a/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js
@@ -3,6 +3,6 @@
* Module dependencies.
*/
-var Binary = require('mongodb').BSONPure.Binary;
+var Binary = require('mongodb').Binary;
module.exports = exports = Binary;
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
index ff5dd5c..0976ffc 100644
--- a/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
@@ -1,12 +1,10 @@
-
/*!
* Module dependencies.
*/
-var MongooseCollection = require('../../collection')
- , Collection = require('mongodb').Collection
- , STATES = require('../../connectionstate')
- , utils = require('../../utils')
+var MongooseCollection = require('../../collection'),
+ Collection = require('mongodb').Collection,
+ utils = require('../../utils');
/**
* A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
@@ -17,7 +15,7 @@ var MongooseCollection = require('../../collection')
* @api private
*/
-function NativeCollection () {
+function NativeCollection() {
this.collection = null;
MongooseCollection.apply(this, arguments);
}
@@ -34,54 +32,58 @@ NativeCollection.prototype.__proto__ = MongooseCollection.prototype;
* @api private
*/
-NativeCollection.prototype.onOpen = function () {
- var self = this;
+NativeCollection.prototype.onOpen = function() {
+ var _this = this;
// always get a new collection in case the user changed host:port
// of parent db instance when re-opening the connection.
- if (!self.opts.capped.size) {
+ if (!_this.opts.capped.size) {
// non-capped
- return self.conn.db.collection(self.name, callback);
+ return _this.conn.db.collection(_this.name, callback);
}
// capped
- return self.conn.db.collection(self.name, function (err, c) {
+ return _this.conn.db.collection(_this.name, function(err, c) {
if (err) return callback(err);
// discover if this collection exists and if it is capped
- c.options(function (err, exists) {
- if (err) return callback(err);
+ _this.conn.db.listCollections({name: _this.name}).toArray(function(err, docs) {
+ if (err) {
+ return callback(err);
+ }
+ var doc = docs[0];
+ var exists = !!doc;
if (exists) {
- if (exists.capped) {
+ if (doc.options && doc.options.capped) {
callback(null, c);
} else {
- var msg = 'A non-capped collection exists with the name: '+ self.name +'\n\n'
- + ' To use this collection as a capped collection, please '
- + 'first convert it.\n'
- + ' http://www.mongodb.org/display/DOCS/Capped+Collections#CappedCollections-Convertingacollectiontocapped'
+ var msg = 'A non-capped collection exists with the name: ' + _this.name + '\n\n'
+ + ' To use this collection as a capped collection, please '
+ + 'first convert it.\n'
+ + ' http://www.mongodb.org/display/DOCS/Capped+Collections#CappedCollections-Convertingacollectiontocapped';
err = new Error(msg);
callback(err);
}
} else {
// create
- var opts = utils.clone(self.opts.capped);
+ var opts = utils.clone(_this.opts.capped);
opts.capped = true;
- self.conn.db.createCollection(self.name, opts, callback);
+ _this.conn.db.createCollection(_this.name, opts, callback);
}
});
});
- function callback (err, collection) {
+ function callback(err, collection) {
if (err) {
// likely a strict mode error
- self.conn.emit('error', err);
+ _this.conn.emit('error', err);
} else {
- self.collection = collection;
- MongooseCollection.prototype.onOpen.call(self);
+ _this.collection = collection;
+ MongooseCollection.prototype.onOpen.call(_this);
}
- };
+ }
};
/**
@@ -90,7 +92,7 @@ NativeCollection.prototype.onOpen = function () {
* @api private
*/
-NativeCollection.prototype.onClose = function () {
+NativeCollection.prototype.onClose = function() {
MongooseCollection.prototype.onClose.call(this);
};
@@ -98,89 +100,135 @@ NativeCollection.prototype.onClose = function () {
* Copy the collection methods and make them subject to queues
*/
-for (var i in Collection.prototype) {
- (function(i){
- NativeCollection.prototype[i] = function () {
- if (this.buffer) {
- this.addQueue(i, arguments);
- return;
- }
+function iter(i) {
+ NativeCollection.prototype[i] = function() {
+ if (this.buffer) {
+ this.addQueue(i, arguments);
+ return;
+ }
- var collection = this.collection
- , args = arguments
- , self = this
- , debug = self.conn.base.options.debug;
+ var collection = this.collection,
+ args = arguments,
+ _this = this,
+ debug = _this.conn.base.options.debug;
- if (debug) {
- if ('function' === typeof debug) {
- debug.apply(debug
- , [self.name, i].concat(utils.args(args, 0, args.length-1)));
- } else {
- console.error('\x1B[0;36mMongoose:\x1B[0m %s.%s(%s) %s %s %s'
- , self.name
- , i
- , print(args[0])
- , print(args[1])
- , print(args[2])
- , print(args[3]))
- }
+ if (debug) {
+ if (typeof debug === 'function') {
+ debug.apply(debug,
+ [_this.name, i].concat(utils.args(args, 0, args.length - 1)));
+ } else {
+ this.$print(_this.name, i, args);
}
+ }
- collection[i].apply(collection, args);
- };
- })(i);
+ try {
+ return collection[i].apply(collection, args);
+ } catch (error) {
+ // Collection operation may throw because of max bson size, catch it here
+ // See gh-3906
+ if (args.length > 0 &&
+ typeof args[args.length - 1] === 'function') {
+ args[args.length - 1](error);
+ } else {
+ throw error;
+ }
+ }
+ };
}
-/*!
+for (var i in Collection.prototype) {
+ // Janky hack to work around gh-3005 until we can get rid of the mongoose
+ // collection abstraction
+ try {
+ if (typeof Collection.prototype[i] !== 'function') {
+ continue;
+ }
+ } catch (e) {
+ continue;
+ }
+
+ iter(i);
+}
+
+/**
* Debug print helper
+ *
+ * @api public
+ * @method $print
+ */
+
+NativeCollection.prototype.$print = function(name, i, args) {
+ var moduleName = '\x1B[0;36mMongoose:\x1B[0m ';
+ var functionCall = [name, i].join('.');
+ var _args = [];
+ for (var j = args.length - 1; j >= 0; --j) {
+ if (this.$format(args[j]) || _args.length) {
+ _args.unshift(this.$format(args[j]));
+ }
+ }
+ var params = '(' + _args.join(', ') + ')';
+
+ console.error(moduleName + functionCall + params);
+};
+
+/**
+ * Formatter for debug print args
+ *
+ * @api public
+ * @method $format
*/
-function print (arg) {
+NativeCollection.prototype.$format = function(arg) {
var type = typeof arg;
- if ('function' === type || 'undefined' === type) return '';
+ if (type === 'function' || type === 'undefined') return '';
return format(arg);
-}
+};
/*!
* Debug print helper
*/
-function format (obj, sub) {
- var x = utils.clone(obj);
- if (x) {
- if ('Binary' === x.constructor.name) {
+function map(o) {
+ return format(o, true);
+}
+function formatObjectId(x, key) {
+ var representation = 'ObjectId("' + x[key].toHexString() + '")';
+ x[key] = {inspect: function() { return representation; }};
+}
+function formatDate(x, key) {
+ var representation = 'new Date("' + x[key].toUTCString() + '")';
+ x[key] = {inspect: function() { return representation; }};
+}
+function format(obj, sub) {
+ var x = utils.clone(obj, {retainKeyOrder: 1});
+ var representation;
+
+ if (x != null) {
+ if (x.constructor.name === 'Binary') {
x = '[object Buffer]';
- } else if ('ObjectID' === x.constructor.name) {
- var representation = 'ObjectId("' + x.toHexString() + '")';
- x = { inspect: function() { return representation; } };
- } else if ('Date' === x.constructor.name) {
- var representation = 'new Date("' + x.toUTCString() + '")';
- x = { inspect: function() { return representation; } };
- } else if ('Object' === x.constructor.name) {
- var keys = Object.keys(x)
- , i = keys.length
- , key
- while (i--) {
+ } else if (x.constructor.name === 'ObjectID') {
+ representation = 'ObjectId("' + x.toHexString() + '")';
+ x = {inspect: function() { return representation; }};
+ } else if (x.constructor.name === 'Date') {
+ representation = 'new Date("' + x.toUTCString() + '")';
+ x = {inspect: function() { return representation; }};
+ } else if (x.constructor.name === 'Object') {
+ var keys = Object.keys(x);
+ var numKeys = keys.length;
+ var key;
+ for (var i = 0; i < numKeys; ++i) {
key = keys[i];
if (x[key]) {
- if ('Binary' === x[key].constructor.name) {
+ if (x[key].constructor.name === 'Binary') {
x[key] = '[object Buffer]';
- } else if ('Object' === x[key].constructor.name) {
+ } else if (x[key].constructor.name === 'Object') {
x[key] = format(x[key], true);
- } else if ('ObjectID' === x[key].constructor.name) {
- ;(function(x){
- var representation = 'ObjectId("' + x[key].toHexString() + '")';
- x[key] = { inspect: function() { return representation; } };
- })(x)
- } else if ('Date' === x[key].constructor.name) {
- ;(function(x){
- var representation = 'new Date("' + x[key].toUTCString() + '")';
- x[key] = { inspect: function() { return representation; } };
- })(x)
+ } else if (x[key].constructor.name === 'ObjectID') {
+ formatObjectId(x, key);
+ } else if (x[key].constructor.name === 'Date') {
+ formatDate(x, key);
} else if (Array.isArray(x[key])) {
- x[key] = x[key].map(function (o) {
- return format(o, true)
- });
+ x[key] = x[key].map(map);
}
}
}
@@ -189,9 +237,9 @@ function format (obj, sub) {
}
return require('util')
- .inspect(x, false, 10, true)
- .replace(/\n/g, '')
- .replace(/\s{2,}/g, ' ')
+ .inspect(x, false, 10, true)
+ .replace(/\n/g, '')
+ .replace(/\s{2,}/g, ' ');
}
/**
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js
index c185738..36d0207 100644
--- a/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js
@@ -2,13 +2,14 @@
* Module dependencies.
*/
-var MongooseConnection = require('../../connection')
- , mongo = require('mongodb')
- , Db = mongo.Db
- , Server = mongo.Server
- , Mongos = mongo.Mongos
- , STATES = require('../../connectionstate')
- , ReplSetServers = mongo.ReplSetServers;
+var MongooseConnection = require('../../connection');
+var mongo = require('mongodb');
+var Db = mongo.Db;
+var Server = mongo.Server;
+var Mongos = mongo.Mongos;
+var STATES = require('../../connectionstate');
+var ReplSetServers = mongo.ReplSet;
+var DisconnectedError = require('../../error/disconnected');
/**
* A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation.
@@ -20,7 +21,7 @@ var MongooseConnection = require('../../connection')
function NativeConnection() {
MongooseConnection.apply(this, arguments);
this._listening = false;
-};
+}
/**
* Expose the possible connection states.
@@ -43,33 +44,107 @@ NativeConnection.prototype.__proto__ = MongooseConnection.prototype;
* @api private
*/
-NativeConnection.prototype.doOpen = function (fn) {
- if (this.db) {
- mute(this);
+NativeConnection.prototype.doOpen = function(fn) {
+ var _this = this;
+ var server = new Server(this.host, this.port, this.options.server);
+
+ if (this.options && this.options.mongos) {
+ var mongos = new Mongos([server], this.options.mongos);
+ this.db = new Db(this.name, mongos, this.options.db);
+ } else {
+ this.db = new Db(this.name, server, this.options.db);
}
- var server = new Server(this.host, this.port, this.options.server);
- this.db = new Db(this.name, server, this.options.db);
+ this.db.open(function(err) {
+ listen(_this);
+
+ if (!mongos) {
+ server.s.server.on('error', function(error) {
+ if (/after \d+ retries/.test(error.message)) {
+ _this.emit('error', new DisconnectedError(server.s.server.name));
+ }
+ });
+ }
- var self = this;
- this.db.open(function (err) {
if (err) return fn(err);
- listen(self);
+
fn();
});
return this;
};
+/**
+ * Switches to a different database using the same connection pool.
+ *
+ * Returns a new connection object, with the new db.
+ *
+ * @param {String} name The database name
+ * @return {Connection} New Connection Object
+ * @api public
+ */
+
+NativeConnection.prototype.useDb = function(name) {
+ // we have to manually copy all of the attributes...
+ var newConn = new this.constructor();
+ newConn.name = name;
+ newConn.base = this.base;
+ newConn.collections = {};
+ newConn.models = {};
+ newConn.replica = this.replica;
+ newConn.hosts = this.hosts;
+ newConn.host = this.host;
+ newConn.port = this.port;
+ newConn.user = this.user;
+ newConn.pass = this.pass;
+ newConn.options = this.options;
+ newConn._readyState = this._readyState;
+ newConn._closeCalled = this._closeCalled;
+ newConn._hasOpened = this._hasOpened;
+ newConn._listening = false;
+
+ // First, when we create another db object, we are not guaranteed to have a
+ // db object to work with. So, in the case where we have a db object and it
+ // is connected, we can just proceed with setting everything up. However, if
+ // we do not have a db or the state is not connected, then we need to wait on
+ // the 'open' event of the connection before doing the rest of the setup
+ // the 'connected' event is the first time we'll have access to the db object
+
+ var _this = this;
+
+ if (this.db && this._readyState === STATES.connected) {
+ wireup();
+ } else {
+ this.once('connected', wireup);
+ }
+
+ function wireup() {
+ newConn.db = _this.db.db(name);
+ newConn.onOpen();
+ // setup the events appropriately
+ listen(newConn);
+ }
+
+ newConn.name = name;
+
+ // push onto the otherDbs stack, this is used when state changes
+ this.otherDbs.push(newConn);
+ newConn.otherDbs.push(this);
+
+ return newConn;
+};
+
/*!
* Register listeners for important events and bubble appropriately.
*/
-function listen (conn) {
- if (conn._listening) return;
- conn._listening = true;
+function listen(conn) {
+ if (conn.db._listening) {
+ return;
+ }
+ conn.db._listening = true;
- conn.db.on('close', function(){
+ conn.db.on('close', function() {
if (conn._closeCalled) return;
// the driver never emits an `open` event. auto_reconnect still
@@ -82,33 +157,26 @@ function listen (conn) {
}
conn.onClose();
});
- conn.db.on('error', function(err){
+ conn.db.on('error', function(err) {
conn.emit('error', err);
});
- conn.db.on('timeout', function(err){
+ conn.db.on('reconnect', function() {
+ conn.readyState = STATES.connected;
+ conn.emit('reconnected');
+ });
+ conn.db.on('timeout', function(err) {
var error = new Error(err && err.err || 'connection timeout');
conn.emit('error', error);
});
- conn.db.on('open', function (err, db) {
+ conn.db.on('open', function(err, db) {
if (STATES.disconnected === conn.readyState && db && db.databaseName) {
conn.readyState = STATES.connected;
- conn.emit('reconnected')
+ conn.emit('reconnected');
}
- })
-}
-
-/*!
- * Remove listeners registered in `listen`
- */
-
-function mute (conn) {
- if (!conn.db) throw new Error('missing db');
- conn.db.removeAllListeners("close");
- conn.db.removeAllListeners("error");
- conn.db.removeAllListeners("timeout");
- conn.db.removeAllListeners("open");
- conn.db.removeAllListeners("fullsetup");
- conn._listening = false;
+ });
+ conn.db.on('parseError', function(err) {
+ conn.emit('parseError', err);
+ });
}
/**
@@ -121,33 +189,33 @@ function mute (conn) {
* @return {Connection} this
*/
-NativeConnection.prototype.doOpenSet = function (fn) {
- if (this.db) {
- mute(this);
- }
-
- var servers = []
- , self = this;
+NativeConnection.prototype.doOpenSet = function(fn) {
+ var servers = [],
+ _this = this;
- this.hosts.forEach(function (server) {
+ this.hosts.forEach(function(server) {
var host = server.host || server.ipc;
var port = server.port || 27017;
- servers.push(new Server(host, port, self.options.server));
- })
+ servers.push(new Server(host, port, _this.options.server));
+ });
var server = this.options.mongos
? new Mongos(servers, this.options.mongos)
- : new ReplSetServers(servers, this.options.replset);
+ : new ReplSetServers(servers, this.options.replset || this.options.replSet);
this.db = new Db(this.name, server, this.options.db);
- this.db.on('fullsetup', function () {
- self.emit('fullsetup')
+ this.db.on('fullsetup', function() {
+ _this.emit('fullsetup');
+ });
+
+ this.db.on('all', function() {
+ _this.emit('all');
});
- this.db.open(function (err) {
+ this.db.open(function(err) {
if (err) return fn(err);
fn();
- listen(self);
+ listen(_this);
});
return this;
@@ -161,11 +229,10 @@ NativeConnection.prototype.doOpenSet = function (fn) {
* @api private
*/
-NativeConnection.prototype.doClose = function (fn) {
- this.db.close();
- if (fn) fn();
+NativeConnection.prototype.doClose = function(fn) {
+ this.db.close(fn);
return this;
-}
+};
/**
* Prepares default connection options for the node-mongodb-native driver.
@@ -177,69 +244,75 @@ NativeConnection.prototype.doClose = function (fn) {
* @api private
*/
-NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
+NativeConnection.prototype.parseOptions = function(passed, connStrOpts) {
var o = passed || {};
o.db || (o.db = {});
o.auth || (o.auth = {});
o.server || (o.server = {});
- o.replset || (o.replset = {});
+ o.replset || (o.replset = o.replSet) || (o.replset = {});
o.server.socketOptions || (o.server.socketOptions = {});
o.replset.socketOptions || (o.replset.socketOptions = {});
+ o.mongos || (o.mongos = (connStrOpts && connStrOpts.mongos));
+ (o.mongos === true) && (o.mongos = {});
var opts = connStrOpts || {};
- Object.keys(opts).forEach(function (name) {
+ Object.keys(opts).forEach(function(name) {
switch (name) {
+ case 'ssl':
+ o.server.ssl = opts.ssl;
+ o.replset.ssl = opts.ssl;
+ o.mongos && (o.mongos.ssl = opts.ssl);
+ break;
case 'poolSize':
- if ('undefined' == typeof o.server.poolSize) {
- o.server.poolSize = o.replset.poolSize = opts[name];
+ if (typeof o.server[name] === 'undefined') {
+ o.server[name] = o.replset[name] = opts[name];
}
break;
case 'slaveOk':
- if ('undefined' == typeof o.server.slave_ok) {
+ if (typeof o.server.slave_ok === 'undefined') {
o.server.slave_ok = opts[name];
}
break;
case 'autoReconnect':
- if ('undefined' == typeof o.server.auto_reconnect) {
+ if (typeof o.server.auto_reconnect === 'undefined') {
o.server.auto_reconnect = opts[name];
}
break;
- case 'ssl':
case 'socketTimeoutMS':
case 'connectTimeoutMS':
- if ('undefined' == typeof o.server.socketOptions[name]) {
+ if (typeof o.server.socketOptions[name] === 'undefined') {
o.server.socketOptions[name] = o.replset.socketOptions[name] = opts[name];
}
break;
case 'authdb':
- if ('undefined' == typeof o.auth.authdb) {
+ if (typeof o.auth.authdb === 'undefined') {
o.auth.authdb = opts[name];
}
break;
case 'authSource':
- if ('undefined' == typeof o.auth.authSource) {
+ if (typeof o.auth.authSource === 'undefined') {
o.auth.authSource = opts[name];
}
break;
case 'retries':
case 'reconnectWait':
case 'rs_name':
- if ('undefined' == typeof o.replset[name]) {
+ if (typeof o.replset[name] === 'undefined') {
o.replset[name] = opts[name];
}
break;
case 'replicaSet':
- if ('undefined' == typeof o.replset.rs_name) {
+ if (typeof o.replset.rs_name === 'undefined') {
o.replset.rs_name = opts[name];
}
break;
case 'readSecondary':
- if ('undefined' == typeof o.replset.read_secondary) {
+ if (typeof o.replset.read_secondary === 'undefined') {
o.replset.read_secondary = opts[name];
}
break;
case 'nativeParser':
- if ('undefined' == typeof o.db.native_parser) {
+ if (typeof o.db.native_parser === 'undefined') {
o.db.native_parser = opts[name];
}
break;
@@ -248,32 +321,31 @@ NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
case 'fsync':
case 'journal':
case 'wtimeoutMS':
- if ('undefined' == typeof o.db[name]) {
+ if (typeof o.db[name] === 'undefined') {
o.db[name] = opts[name];
}
break;
case 'readPreference':
- if ('undefined' == typeof o.db.read_preference) {
- o.db.read_preference = opts[name];
+ if (typeof o.db.readPreference === 'undefined') {
+ o.db.readPreference = opts[name];
}
break;
case 'readPreferenceTags':
- if ('undefined' == typeof o.db.read_preference_tags) {
+ if (typeof o.db.read_preference_tags === 'undefined') {
o.db.read_preference_tags = opts[name];
}
break;
+ case 'sslValidate':
+ o.server.sslValidate = opts.sslValidate;
+ o.replset.sslValidate = opts.sslValidate;
+ o.mongos && (o.mongos.sslValidate = opts.sslValidate);
}
- })
+ });
if (!('auto_reconnect' in o.server)) {
o.server.auto_reconnect = true;
}
- if (!o.db.read_preference) {
- // read from primaries by default
- o.db.read_preference = 'primary';
- }
-
// mongoose creates its own ObjectIds
o.db.forceServerObjectId = false;
@@ -283,9 +355,13 @@ NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
o.db.w = 1;
}
+ if (o.promiseLibrary) {
+ o.db.promiseLibrary = o.promiseLibrary;
+ }
+
validate(o);
return o;
-}
+};
/*!
* Validates the driver db options.
@@ -293,8 +369,8 @@ NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
* @param {Object} o
*/
-function validate (o) {
- if (-1 === o.db.w || 0 === o.db.w) {
+function validate(o) {
+ if (o.db.w === -1 || o.db.w === 0) {
if (o.db.journal || o.db.fsync || o.db.safe) {
throw new Error(
'Invalid writeConcern: '
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js
new file mode 100644
index 0000000..fa5dbb4
--- /dev/null
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js
@@ -0,0 +1,7 @@
+/*!
+ * Module exports.
+ */
+
+exports.Binary = require('./binary');
+exports.ObjectId = require('./objectid');
+exports.ReadPreference = require('./ReadPreference');
diff --git a/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js b/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js
index 3c46c93..69fc08f 100644
--- a/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js
@@ -5,25 +5,10 @@
* @see ObjectId
*/
-var ObjectId = require('mongodb').BSONPure.ObjectID;
+var ObjectId = require('mongodb').ObjectId;
/*!
* ignore
*/
-var ObjectIdToString = ObjectId.toString.bind(ObjectId);
module.exports = exports = ObjectId;
-
-ObjectId.fromString = function(str){
- // patch native driver bug in V0.9.6.4
- if (!('string' === typeof str && 24 === str.length)) {
- throw new Error("Invalid ObjectId");
- }
-
- return ObjectId.createFromHexString(str);
-};
-
-ObjectId.toString = function(oid){
- if (!arguments.length) return ObjectIdToString();
- return oid.toHexString();
-};
diff --git a/node_modules/mongoose/lib/error.js b/node_modules/mongoose/lib/error.js
index 6d00f80..b084e76 100644
--- a/node_modules/mongoose/lib/error.js
+++ b/node_modules/mongoose/lib/error.js
@@ -6,18 +6,23 @@
* @inherits Error https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error
*/
-function MongooseError (msg) {
+function MongooseError(msg) {
Error.call(this);
- Error.captureStackTrace(this, arguments.callee);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
this.message = msg;
this.name = 'MongooseError';
-};
+}
/*!
* Inherits from Error.
*/
-MongooseError.prototype.__proto__ = Error.prototype;
+MongooseError.prototype = Object.create(Error.prototype);
+MongooseError.prototype.constructor = Error;
/*!
* Module exports.
@@ -25,15 +30,26 @@ MongooseError.prototype.__proto__ = Error.prototype;
module.exports = exports = MongooseError;
+/**
+ * The default built-in validator error messages.
+ *
+ * @see Error.messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+MongooseError.messages = require('./error/messages');
+
+// backward compat
+MongooseError.Messages = MongooseError.messages;
+
/*!
* Expose subclasses
*/
-MongooseError.CastError = require('./errors/cast');
-MongooseError.DocumentError = require('./errors/document');
-MongooseError.ValidationError = require('./errors/validation')
-MongooseError.ValidatorError = require('./errors/validator')
-MongooseError.VersionError =require('./errors/version')
-MongooseError.OverwriteModelError = require('./errors/overwriteModel')
-MongooseError.MissingSchemaError = require('./errors/missingSchema')
-MongooseError.DivergentArrayError = require('./errors/divergentArray')
+MongooseError.CastError = require('./error/cast');
+MongooseError.ValidationError = require('./error/validation');
+MongooseError.ValidatorError = require('./error/validator');
+MongooseError.VersionError = require('./error/version');
+MongooseError.OverwriteModelError = require('./error/overwriteModel');
+MongooseError.MissingSchemaError = require('./error/missingSchema');
+MongooseError.DivergentArrayError = require('./error/divergentArray');
diff --git a/node_modules/mongoose/lib/error/browserMissingSchema.js b/node_modules/mongoose/lib/error/browserMissingSchema.js
new file mode 100644
index 0000000..1c7892e
--- /dev/null
+++ b/node_modules/mongoose/lib/error/browserMissingSchema.js
@@ -0,0 +1,32 @@
+/*!
+ * Module dependencies.
+ */
+
+var MongooseError = require('../error.js');
+
+/*!
+ * MissingSchema Error constructor.
+ *
+ * @inherits MongooseError
+ */
+
+function MissingSchemaError() {
+ var msg = 'Schema hasn\'t been registered for document.\n'
+ + 'Use mongoose.Document(name, schema)';
+ MongooseError.call(this, msg);
+ Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
+ this.name = 'MissingSchemaError';
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+MissingSchemaError.prototype = Object.create(MongooseError.prototype);
+MissingSchemaError.prototype.constructor = MongooseError;
+
+/*!
+ * exports
+ */
+
+module.exports = MissingSchemaError;
diff --git a/node_modules/mongoose/lib/errors/cast.js b/node_modules/mongoose/lib/error/cast.js
similarity index 54%
rename from node_modules/mongoose/lib/errors/cast.js
rename to node_modules/mongoose/lib/error/cast.js
index 055063a..fcace73 100644
--- a/node_modules/mongoose/lib/errors/cast.js
+++ b/node_modules/mongoose/lib/error/cast.js
@@ -2,7 +2,7 @@
* Module dependencies.
*/
-var MongooseError = require('../error');
+var MongooseError = require('../error.js');
/**
* Casting Error constructor.
@@ -13,20 +13,27 @@ var MongooseError = require('../error');
* @api private
*/
-function CastError (type, value, path) {
+function CastError(type, value, path, reason) {
MongooseError.call(this, 'Cast to ' + type + ' failed for value "' + value + '" at path "' + path + '"');
- Error.captureStackTrace(this, arguments.callee);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
this.name = 'CastError';
- this.type = type;
+ this.kind = type;
this.value = value;
this.path = path;
-};
+ this.reason = reason;
+}
/*!
* Inherits from MongooseError.
*/
-CastError.prototype.__proto__ = MongooseError.prototype;
+CastError.prototype = Object.create(MongooseError.prototype);
+CastError.prototype.constructor = MongooseError;
+
/*!
* exports
diff --git a/node_modules/mongoose/lib/error/disconnected.js b/node_modules/mongoose/lib/error/disconnected.js
new file mode 100644
index 0000000..b4eece1
--- /dev/null
+++ b/node_modules/mongoose/lib/error/disconnected.js
@@ -0,0 +1,40 @@
+/*!
+ * Module dependencies.
+ */
+
+var MongooseError = require('../error.js');
+
+/**
+ * Casting Error constructor.
+ *
+ * @param {String} type
+ * @param {String} value
+ * @inherits MongooseError
+ * @api private
+ */
+
+function DisconnectedError(connectionString) {
+ MongooseError.call(this, 'Ran out of retries trying to reconnect to "' +
+ connectionString + '". Try setting `server.reconnectTries` and ' +
+ '`server.reconnectInterval` to something higher.');
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.name = 'DisconnectedError';
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+DisconnectedError.prototype = Object.create(MongooseError.prototype);
+DisconnectedError.prototype.constructor = MongooseError;
+
+
+/*!
+ * exports
+ */
+
+module.exports = DisconnectedError;
diff --git a/node_modules/mongoose/lib/errors/divergentArray.js b/node_modules/mongoose/lib/error/divergentArray.js
similarity index 74%
rename from node_modules/mongoose/lib/errors/divergentArray.js
rename to node_modules/mongoose/lib/error/divergentArray.js
index 45809bc..1cbaa25 100644
--- a/node_modules/mongoose/lib/errors/divergentArray.js
+++ b/node_modules/mongoose/lib/error/divergentArray.js
@@ -3,7 +3,7 @@
* Module dependencies.
*/
-var MongooseError = require('../error');
+var MongooseError = require('../error.js');
/*!
* DivergentArrayError constructor.
@@ -11,7 +11,7 @@ var MongooseError = require('../error');
* @inherits MongooseError
*/
-function DivergentArrayError (paths) {
+function DivergentArrayError(paths) {
var msg = 'For your own good, using `document.save()` to update an array '
+ 'which was selected using an $elemMatch projection OR '
+ 'populated using skip, limit, query conditions, or exclusion of '
@@ -19,19 +19,21 @@ function DivergentArrayError (paths) {
+ 'the entire array is not supported. The following '
+ 'path(s) would have been modified unsafely:\n'
+ ' ' + paths.join('\n ') + '\n'
- + 'Use Model.update() to update these arrays instead.'
+ + 'Use Model.update() to update these arrays instead.';
// TODO write up a docs page (FAQ) and link to it
MongooseError.call(this, msg);
- Error.captureStackTrace(this, arguments.callee);
+ Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
this.name = 'DivergentArrayError';
-};
+}
/*!
* Inherits from MongooseError.
*/
-DivergentArrayError.prototype.__proto__ = MongooseError.prototype;
+DivergentArrayError.prototype = Object.create(MongooseError.prototype);
+DivergentArrayError.prototype.constructor = MongooseError;
+
/*!
* exports
diff --git a/node_modules/mongoose/lib/error/messages.js b/node_modules/mongoose/lib/error/messages.js
new file mode 100644
index 0000000..88ddf6e
--- /dev/null
+++ b/node_modules/mongoose/lib/error/messages.js
@@ -0,0 +1,42 @@
+
+/**
+ * The default built-in validator error messages. These may be customized.
+ *
+ * // customize within each schema or globally like so
+ * var mongoose = require('mongoose');
+ * mongoose.Error.messages.String.enum = "Your custom message for {PATH}.";
+ *
+ * As you might have noticed, error messages support basic templating
+ *
+ * - `{PATH}` is replaced with the invalid document path
+ * - `{VALUE}` is replaced with the invalid value
+ * - `{TYPE}` is replaced with the validator type such as "regexp", "min", or "user defined"
+ * - `{MIN}` is replaced with the declared min value for the Number.min validator
+ * - `{MAX}` is replaced with the declared max value for the Number.max validator
+ *
+ * Click the "show code" link below to see all defaults.
+ *
+ * @static messages
+ * @receiver MongooseError
+ * @api public
+ */
+
+var msg = module.exports = exports = {};
+
+msg.general = {};
+msg.general.default = 'Validator failed for path `{PATH}` with value `{VALUE}`';
+msg.general.required = 'Path `{PATH}` is required.';
+
+msg.Number = {};
+msg.Number.min = 'Path `{PATH}` ({VALUE}) is less than minimum allowed value ({MIN}).';
+msg.Number.max = 'Path `{PATH}` ({VALUE}) is more than maximum allowed value ({MAX}).';
+
+msg.Date = {};
+msg.Date.min = 'Path `{PATH}` ({VALUE}) is before minimum allowed value ({MIN}).';
+msg.Date.max = 'Path `{PATH}` ({VALUE}) is after maximum allowed value ({MAX}).';
+
+msg.String = {};
+msg.String.enum = '`{VALUE}` is not a valid enum value for path `{PATH}`.';
+msg.String.match = 'Path `{PATH}` is invalid ({VALUE}).';
+msg.String.minlength = 'Path `{PATH}` (`{VALUE}`) is shorter than the minimum allowed length ({MINLENGTH}).';
+msg.String.maxlength = 'Path `{PATH}` (`{VALUE}`) is longer than the maximum allowed length ({MAXLENGTH}).';
diff --git a/node_modules/mongoose/lib/errors/missingSchema.js b/node_modules/mongoose/lib/error/missingSchema.js
similarity index 58%
rename from node_modules/mongoose/lib/errors/missingSchema.js
rename to node_modules/mongoose/lib/error/missingSchema.js
index 02a02ee..25eabfa 100644
--- a/node_modules/mongoose/lib/errors/missingSchema.js
+++ b/node_modules/mongoose/lib/error/missingSchema.js
@@ -3,7 +3,7 @@
* Module dependencies.
*/
-var MongooseError = require('../error');
+var MongooseError = require('../error.js');
/*!
* MissingSchema Error constructor.
@@ -11,19 +11,20 @@ var MongooseError = require('../error');
* @inherits MongooseError
*/
-function MissingSchemaError (name) {
+function MissingSchemaError(name) {
var msg = 'Schema hasn\'t been registered for model "' + name + '".\n'
+ 'Use mongoose.model(name, schema)';
MongooseError.call(this, msg);
- Error.captureStackTrace(this, arguments.callee);
+ Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
this.name = 'MissingSchemaError';
-};
+}
/*!
* Inherits from MongooseError.
*/
-MissingSchemaError.prototype.__proto__ = MongooseError.prototype;
+MissingSchemaError.prototype = Object.create(MongooseError.prototype);
+MissingSchemaError.prototype.constructor = MongooseError;
/*!
* exports
diff --git a/node_modules/mongoose/lib/error/objectExpected.js b/node_modules/mongoose/lib/error/objectExpected.js
new file mode 100644
index 0000000..fa863bc
--- /dev/null
+++ b/node_modules/mongoose/lib/error/objectExpected.js
@@ -0,0 +1,35 @@
+/*!
+ * Module dependencies.
+ */
+
+var MongooseError = require('../error.js');
+
+/**
+ * Strict mode error constructor
+ *
+ * @param {String} type
+ * @param {String} value
+ * @inherits MongooseError
+ * @api private
+ */
+
+function ObjectExpectedError(path, val) {
+ MongooseError.call(this, 'Tried to set nested object field `' + path +
+ '` to primitive value `' + val + '` and strict mode is set to throw.');
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.name = 'ObjectExpectedError';
+ this.path = path;
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+ObjectExpectedError.prototype = Object.create(MongooseError.prototype);
+ObjectExpectedError.prototype.constructor = MongooseError;
+
+module.exports = ObjectExpectedError;
diff --git a/node_modules/mongoose/lib/errors/overwriteModel.js b/node_modules/mongoose/lib/error/overwriteModel.js
similarity index 53%
rename from node_modules/mongoose/lib/errors/overwriteModel.js
rename to node_modules/mongoose/lib/error/overwriteModel.js
index 2591581..c14ae7f 100644
--- a/node_modules/mongoose/lib/errors/overwriteModel.js
+++ b/node_modules/mongoose/lib/error/overwriteModel.js
@@ -3,7 +3,7 @@
* Module dependencies.
*/
-var MongooseError = require('../error');
+var MongooseError = require('../error.js');
/*!
* OverwriteModel Error constructor.
@@ -11,17 +11,18 @@ var MongooseError = require('../error');
* @inherits MongooseError
*/
-function OverwriteModelError (name) {
+function OverwriteModelError(name) {
MongooseError.call(this, 'Cannot overwrite `' + name + '` model once compiled.');
- Error.captureStackTrace(this, arguments.callee);
+ Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
this.name = 'OverwriteModelError';
-};
+}
/*!
* Inherits from MongooseError.
*/
-OverwriteModelError.prototype.__proto__ = MongooseError.prototype;
+OverwriteModelError.prototype = Object.create(MongooseError.prototype);
+OverwriteModelError.prototype.constructor = MongooseError;
/*!
* exports
diff --git a/node_modules/mongoose/lib/error/strict.js b/node_modules/mongoose/lib/error/strict.js
new file mode 100644
index 0000000..6e34431
--- /dev/null
+++ b/node_modules/mongoose/lib/error/strict.js
@@ -0,0 +1,35 @@
+/*!
+ * Module dependencies.
+ */
+
+var MongooseError = require('../error.js');
+
+/**
+ * Strict mode error constructor
+ *
+ * @param {String} type
+ * @param {String} value
+ * @inherits MongooseError
+ * @api private
+ */
+
+function StrictModeError(path) {
+ MongooseError.call(this, 'Field `' + path + '` is not in schema and strict ' +
+ 'mode is set to throw.');
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.name = 'StrictModeError';
+ this.path = path;
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+StrictModeError.prototype = Object.create(MongooseError.prototype);
+StrictModeError.prototype.constructor = MongooseError;
+
+module.exports = StrictModeError;
diff --git a/node_modules/mongoose/lib/error/validation.js b/node_modules/mongoose/lib/error/validation.js
new file mode 100644
index 0000000..bcbad61
--- /dev/null
+++ b/node_modules/mongoose/lib/error/validation.js
@@ -0,0 +1,63 @@
+/*!
+ * Module requirements
+ */
+
+var MongooseError = require('../error.js');
+
+/**
+ * Document Validation Error
+ *
+ * @api private
+ * @param {Document} instance
+ * @inherits MongooseError
+ */
+
+function ValidationError(instance) {
+ if (instance && instance.constructor.name === 'model') {
+ MongooseError.call(this, instance.constructor.modelName + ' validation failed');
+ } else {
+ MongooseError.call(this, 'Validation failed');
+ }
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.name = 'ValidationError';
+ this.errors = {};
+ if (instance) {
+ instance.errors = this.errors;
+ }
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+ValidationError.prototype = Object.create(MongooseError.prototype);
+ValidationError.prototype.constructor = MongooseError;
+
+
+/**
+ * Console.log helper
+ */
+
+ValidationError.prototype.toString = function() {
+ var ret = this.name + ': ';
+ var msgs = [];
+
+ Object.keys(this.errors).forEach(function(key) {
+ if (this === this.errors[key]) {
+ return;
+ }
+ msgs.push(String(this.errors[key]));
+ }, this);
+
+ return ret + msgs.join(', ');
+};
+
+/*!
+ * Module exports
+ */
+
+module.exports = exports = ValidationError;
diff --git a/node_modules/mongoose/lib/error/validator.js b/node_modules/mongoose/lib/error/validator.js
new file mode 100644
index 0000000..96a3ba1
--- /dev/null
+++ b/node_modules/mongoose/lib/error/validator.js
@@ -0,0 +1,81 @@
+/*!
+ * Module dependencies.
+ */
+
+var MongooseError = require('../error.js');
+
+/**
+ * Schema validator error
+ *
+ * @param {Object} properties
+ * @inherits MongooseError
+ * @api private
+ */
+
+function ValidatorError(properties) {
+ var msg = properties.message;
+ if (!msg) {
+ msg = MongooseError.messages.general.default;
+ }
+
+ var message = this.formatMessage(msg, properties);
+ MongooseError.call(this, message);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack;
+ }
+ this.properties = properties;
+ this.name = 'ValidatorError';
+ this.kind = properties.type;
+ this.path = properties.path;
+ this.value = properties.value;
+}
+
+/*!
+ * Inherits from MongooseError
+ */
+
+ValidatorError.prototype = Object.create(MongooseError.prototype);
+ValidatorError.prototype.constructor = MongooseError;
+
+/*!
+ * The object used to define this validator. Not enumerable to hide
+ * it from `require('util').inspect()` output re: gh-3925
+ */
+
+Object.defineProperty(ValidatorError.prototype, 'properties', {
+ enumerable: false,
+ writable: true,
+ value: null
+});
+
+/*!
+ * Formats error messages
+ */
+
+ValidatorError.prototype.formatMessage = function(msg, properties) {
+ var propertyNames = Object.keys(properties);
+ for (var i = 0; i < propertyNames.length; ++i) {
+ var propertyName = propertyNames[i];
+ if (propertyName === 'message') {
+ continue;
+ }
+ msg = msg.replace('{' + propertyName.toUpperCase() + '}', properties[propertyName]);
+ }
+ return msg;
+};
+
+/*!
+ * toString helper
+ */
+
+ValidatorError.prototype.toString = function() {
+ return this.message;
+};
+
+/*!
+ * exports
+ */
+
+module.exports = ValidatorError;
diff --git a/node_modules/mongoose/lib/error/version.js b/node_modules/mongoose/lib/error/version.js
new file mode 100644
index 0000000..e509b9b
--- /dev/null
+++ b/node_modules/mongoose/lib/error/version.js
@@ -0,0 +1,33 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+var MongooseError = require('../error.js');
+
+/**
+ * Version Error constructor.
+ *
+ * @inherits MongooseError
+ * @api private
+ */
+
+function VersionError(doc) {
+ MongooseError.call(this, 'No matching document found for id "' + doc._id +
+ '"');
+ this.name = 'VersionError';
+}
+
+/*!
+ * Inherits from MongooseError.
+ */
+
+VersionError.prototype = Object.create(MongooseError.prototype);
+VersionError.prototype.constructor = MongooseError;
+
+/*!
+ * exports
+ */
+
+module.exports = VersionError;
diff --git a/node_modules/mongoose/lib/errors/document.js b/node_modules/mongoose/lib/errors/document.js
deleted file mode 100644
index 6955256..0000000
--- a/node_modules/mongoose/lib/errors/document.js
+++ /dev/null
@@ -1,32 +0,0 @@
-
-/*!
- * Module requirements
- */
-
-var MongooseError = require('../error')
-
-/**
- * Document Error
- *
- * @param {String} msg
- * @inherits MongooseError
- * @api private
- */
-
-function DocumentError (msg) {
- MongooseError.call(this, msg);
- Error.captureStackTrace(this, arguments.callee);
- this.name = 'DocumentError';
-};
-
-/*!
- * Inherits from MongooseError.
- */
-
-DocumentError.prototype.__proto__ = MongooseError.prototype;
-
-/*!
- * Module exports.
- */
-
-module.exports = exports = DocumentError;
diff --git a/node_modules/mongoose/lib/errors/validation.js b/node_modules/mongoose/lib/errors/validation.js
deleted file mode 100644
index 1679849..0000000
--- a/node_modules/mongoose/lib/errors/validation.js
+++ /dev/null
@@ -1,49 +0,0 @@
-
-/*!
- * Module requirements
- */
-
-var MongooseError = require('../error')
-
-/**
- * Document Validation Error
- *
- * @api private
- * @param {Document} instance
- * @inherits MongooseError
- */
-
-function ValidationError (instance) {
- MongooseError.call(this, "Validation failed");
- Error.captureStackTrace(this, arguments.callee);
- this.name = 'ValidationError';
- this.errors = instance.errors = {};
-};
-
-/**
- * Console.log helper
- */
-
-ValidationError.prototype.toString = function () {
- var ret = this.name + ': ';
- var msgs = [];
-
- Object.keys(this.errors).forEach(function (key) {
- if (this == this.errors[key]) return;
- msgs.push(String(this.errors[key]));
- }, this)
-
- return ret + msgs.join(', ');
-};
-
-/*!
- * Inherits from MongooseError.
- */
-
-ValidationError.prototype.__proto__ = MongooseError.prototype;
-
-/*!
- * Module exports
- */
-
-module.exports = exports = ValidationError;
diff --git a/node_modules/mongoose/lib/errors/validator.js b/node_modules/mongoose/lib/errors/validator.js
deleted file mode 100644
index 2498b05..0000000
--- a/node_modules/mongoose/lib/errors/validator.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*!
- * Module dependencies.
- */
-
-var MongooseError = require('../error');
-
-/**
- * Schema validator error
- *
- * @param {String} path
- * @param {String} msg
- * @param {String|Number|any} val
- * @inherits MongooseError
- * @api private
- */
-
-function ValidatorError (path, type, val) {
- var msg = type
- ? '"' + type + '" '
- : '';
-
- var message = 'Validator ' + msg + 'failed for path ' + path
- if (2 < arguments.length) message += ' with value `' + String(val) + '`';
-
- MongooseError.call(this, message);
- Error.captureStackTrace(this, arguments.callee);
- this.name = 'ValidatorError';
- this.path = path;
- this.type = type;
- this.value = val;
-};
-
-/*!
- * toString helper
- */
-
-ValidatorError.prototype.toString = function () {
- return this.message;
-}
-
-/*!
- * Inherits from MongooseError
- */
-
-ValidatorError.prototype.__proto__ = MongooseError.prototype;
-
-/*!
- * exports
- */
-
-module.exports = ValidatorError;
diff --git a/node_modules/mongoose/lib/errors/version.js b/node_modules/mongoose/lib/errors/version.js
deleted file mode 100644
index b2388aa..0000000
--- a/node_modules/mongoose/lib/errors/version.js
+++ /dev/null
@@ -1,31 +0,0 @@
-
-/*!
- * Module dependencies.
- */
-
-var MongooseError = require('../error');
-
-/**
- * Version Error constructor.
- *
- * @inherits MongooseError
- * @api private
- */
-
-function VersionError () {
- MongooseError.call(this, 'No matching document found.');
- Error.captureStackTrace(this, arguments.callee);
- this.name = 'VersionError';
-};
-
-/*!
- * Inherits from MongooseError.
- */
-
-VersionError.prototype.__proto__ = MongooseError.prototype;
-
-/*!
- * exports
- */
-
-module.exports = VersionError;
diff --git a/node_modules/mongoose/lib/index.js b/node_modules/mongoose/lib/index.js
index 7610a4b..7b72ed9 100644
--- a/node_modules/mongoose/lib/index.js
+++ b/node_modules/mongoose/lib/index.js
@@ -4,43 +4,22 @@
* Module dependencies.
*/
-var Schema = require('./schema')
- , SchemaType = require('./schematype')
- , VirtualType = require('./virtualtype')
- , SchemaTypes = Schema.Types
- , SchemaDefaults = require('./schemadefault')
- , Types = require('./types')
- , Query = require('./query')
- , Promise = require('./promise')
- , Model = require('./model')
- , Document = require('./document')
- , utils = require('./utils')
- , format = utils.toCollectionName
- , mongodb = require('mongodb')
- , pkg = require('../package.json')
-
-/*!
- * Warn users if they are running an unstable release.
- *
- * Disable the warning by setting the MONGOOSE_DISABLE_STABILITY_WARNING
- * environment variable.
- */
-
-if (pkg.publishConfig && 'unstable' == pkg.publishConfig.tag) {
- if (!process.env.MONGOOSE_DISABLE_STABILITY_WARNING) {
- console.log('\u001b[33m');
- console.log('##############################################################');
- console.log('#');
- console.log('# !!! MONGOOSE WARNING !!!');
- console.log('#');
- console.log('# This is an UNSTABLE release of Mongoose.');
- console.log('# Unstable releases are available for preview/testing only.');
- console.log('# DO NOT run this in production.');
- console.log('#');
- console.log('##############################################################');
- console.log('\u001b[0m');
- }
-}
+var Schema = require('./schema'),
+ SchemaType = require('./schematype'),
+ VirtualType = require('./virtualtype'),
+ STATES = require('./connectionstate'),
+ Types = require('./types'),
+ Query = require('./query'),
+ Model = require('./model'),
+ Document = require('./document'),
+ utils = require('./utils'),
+ format = utils.toCollectionName,
+ pkg = require('../package.json');
+
+var querystring = require('querystring');
+
+var Aggregate = require('./aggregate');
+var PromiseProvider = require('./promise_provider');
/**
* Mongoose constructor.
@@ -51,14 +30,24 @@ if (pkg.publishConfig && 'unstable' == pkg.publishConfig.tag) {
* @api public
*/
-function Mongoose () {
+function Mongoose() {
this.connections = [];
this.plugins = [];
this.models = {};
this.modelSchemas = {};
- this.options = {};
- this.createConnection(); // default connection
-};
+ // default global options
+ this.options = {
+ pluralization: true
+ };
+ var conn = this.createConnection(); // default connection
+ conn.models = this.models;
+}
+
+/**
+ * Expose connection states for user-land
+ *
+ */
+Mongoose.prototype.STATES = STATES;
/**
* Sets mongoose options
@@ -69,17 +58,22 @@ function Mongoose () {
*
* mongoose.set('debug', true) // enable logging collection methods + arguments to the console
*
+ * mongoose.set('debug', function(collectionName, methodName, arg1, arg2...) {}); // use custom function to log collection methods + arguments
+ *
* @param {String} key
- * @param {String} value
+ * @param {String|Function} value
* @api public
*/
-Mongoose.prototype.set = function (key, value) {
- if (arguments.length == 1)
+Mongoose.prototype.set = function(key, value) {
+ if (arguments.length === 1) {
return this.options[key];
+ }
+
this.options[key] = value;
return this;
};
+Mongoose.prototype.set.$hasSideEffects = true;
/**
* Gets mongoose options
@@ -101,12 +95,45 @@ Mongoose.prototype.get = Mongoose.prototype.set;
var rgxReplSet = /^.+,.+$/;
+/**
+ * Checks if ?replicaSet query parameter is specified in URI
+ *
+ * ####Example:
+ *
+ * checkReplicaSetInUri('localhost:27000?replicaSet=rs0'); // true
+ *
+ * @param {String} uri
+ * @return {boolean}
+ * @api private
+ */
+
+var checkReplicaSetInUri = function(uri) {
+ if (!uri) {
+ return false;
+ }
+
+ var queryStringStart = uri.indexOf('?');
+ var isReplicaSet = false;
+ if (queryStringStart !== -1) {
+ try {
+ var obj = querystring.parse(uri.substr(queryStringStart + 1));
+ if (obj && obj.replicaSet) {
+ isReplicaSet = true;
+ }
+ } catch (e) {
+ return false;
+ }
+ }
+
+ return isReplicaSet;
+};
+
/**
* Creates a Connection instance.
*
* Each `connection` instance maps to a single database. This method is helpful when mangaging multiple db connections.
*
- * If arguments are passed, they are proxied to either [Connection#open](#connection_Connection-open) or [Connection#openSet](#connection_Connection-openSet) appropriately. This means we can pass `db`, `server`, and `replset` options to the driver.
+ * If arguments are passed, they are proxied to either [Connection#open](#connection_Connection-open) or [Connection#openSet](#connection_Connection-openSet) appropriately. This means we can pass `db`, `server`, and `replset` options to the driver. _Note that the `safe` option specified in your schema will overwrite the `safe` db option specified here unless you set your schemas `safe` option to `undefined`. See [this](/docs/guide.html#safe) for more information._
*
* _Options passed take precedence over options included in connection strings._
*
@@ -120,11 +147,11 @@ var rgxReplSet = /^.+,.+$/;
* db = mongoose.createConnection('mongodb://user:pass@localhost:port/database', opts);
*
* // replica sets
- * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database,mongodb://anotherhost:port,mongodb://yetanother:port');
+ * db = mongoose.createConnection('mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database');
*
* // and options
* var opts = { replset: { strategy: 'ping', rs_name: 'testSet' }}
- * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database,mongodb://anotherhost:port,mongodb://yetanother:port', opts);
+ * db = mongoose.createConnection('mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database', opts);
*
* // with [host, database_name[, port] signature
* db = mongoose.createConnection('localhost', 'database', port)
@@ -139,18 +166,23 @@ var rgxReplSet = /^.+,.+$/;
*
* @param {String} [uri] a mongodb:// URI
* @param {Object} [options] options to pass to the driver
+ * @param {Object} [options.config] mongoose-specific options
+ * @param {Boolean} [options.config.autoIndex] set to false to disable automatic index creation for all models associated with this connection.
* @see Connection#open #connection_Connection-open
* @see Connection#openSet #connection_Connection-openSet
* @return {Connection} the created Connection object
* @api public
*/
-Mongoose.prototype.createConnection = function () {
+Mongoose.prototype.createConnection = function(uri, options) {
var conn = new Connection(this);
this.connections.push(conn);
if (arguments.length) {
- if (rgxReplSet.test(arguments[0])) {
+ if (rgxReplSet.test(arguments[0]) || checkReplicaSetInUri(arguments[0])) {
+ conn.openSet.apply(conn, arguments);
+ } else if (options && options.replset &&
+ (options.replset.replicaSet || options.replset.rs_name)) {
conn.openSet.apply(conn, arguments);
} else {
conn.open.apply(conn, arguments);
@@ -159,11 +191,14 @@ Mongoose.prototype.createConnection = function () {
return conn;
};
+Mongoose.prototype.createConnection.$hasSideEffects = true;
/**
* Opens the default mongoose connection.
*
- * If arguments are passed, they are proxied to either [Connection#open](#connection_Connection-open) or [Connection#openSet](#connection_Connection-openSet) appropriately.
+ * If arguments are passed, they are proxied to either
+ * [Connection#open](#connection_Connection-open) or
+ * [Connection#openSet](#connection_Connection-openSet) appropriately.
*
* _Options passed take precedence over options included in connection strings._
*
@@ -172,7 +207,7 @@ Mongoose.prototype.createConnection = function () {
* mongoose.connect('mongodb://user:pass@localhost:port/database');
*
* // replica sets
- * var uri = 'mongodb://user:pass@localhost:port/database,mongodb://anotherhost:port,mongodb://yetanother:port';
+ * var uri = 'mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/mydatabase';
* mongoose.connect(uri);
*
* // with options
@@ -183,54 +218,62 @@ Mongoose.prototype.createConnection = function () {
* var opts = { mongos: true };
* mongoose.connect(uri, opts);
*
+ * // optional callback that gets fired when initial connection completed
+ * var uri = 'mongodb://nonexistent.domain:27000';
+ * mongoose.connect(uri, function(error) {
+ * // if error is truthy, the initial connection failed.
+ * })
+ *
* @param {String} uri(s)
* @param {Object} [options]
* @param {Function} [callback]
* @see Mongoose#createConnection #index_Mongoose-createConnection
* @api public
- * @return {Mongoose} this
+ * @return {MongooseThenable} pseudo-promise wrapper around this
*/
-Mongoose.prototype.connect = function () {
+Mongoose.prototype.connect = function() {
var conn = this.connection;
-
- if (rgxReplSet.test(arguments[0])) {
- conn.openSet.apply(conn, arguments);
- } else {
- conn.open.apply(conn, arguments);
+ if (rgxReplSet.test(arguments[0]) || checkReplicaSetInUri(arguments[0])) {
+ return new MongooseThenable(this, conn.openSet.apply(conn, arguments));
}
- return this;
+ return new MongooseThenable(this, conn.open.apply(conn, arguments));
};
+Mongoose.prototype.connect.$hasSideEffects = true;
/**
* Disconnects all connections.
*
* @param {Function} [fn] called after all connection close.
- * @return {Mongoose} this
+ * @return {MongooseThenable} pseudo-promise wrapper around this
* @api public
*/
-Mongoose.prototype.disconnect = function (fn) {
- var count = this.connections.length
- , error
-
- this.connections.forEach(function(conn){
- conn.close(function(err){
- if (error) return;
-
+Mongoose.prototype.disconnect = function(fn) {
+ var error;
+ this.connections.forEach(function(conn) {
+ conn.close(function(err) {
+ if (error) {
+ return;
+ }
if (err) {
error = err;
- if (fn) return fn(err);
- throw err;
}
-
- if (fn)
- --count || fn();
});
});
- return this;
+
+ var Promise = PromiseProvider.get();
+ return new MongooseThenable(this, new Promise.ES6(function(resolve, reject) {
+ fn && fn(error);
+ if (error) {
+ reject(error);
+ return;
+ }
+ resolve();
+ }));
};
+Mongoose.prototype.disconnect.$hasSideEffects = true;
/**
* Defines a model or retrieves it.
@@ -272,17 +315,17 @@ Mongoose.prototype.disconnect = function (fn) {
* @api public
*/
-Mongoose.prototype.model = function (name, schema, collection, skipInit) {
- if ('string' == typeof schema) {
+Mongoose.prototype.model = function(name, schema, collection, skipInit) {
+ if (typeof schema === 'string') {
collection = schema;
schema = false;
}
- if (utils.isObject(schema) && !(schema instanceof Schema)) {
+ if (utils.isObject(schema) && !(schema.instanceOfSchema)) {
schema = new Schema(schema);
}
- if ('boolean' === typeof collection) {
+ if (typeof collection === 'boolean') {
skipInit = collection;
collection = null;
}
@@ -296,13 +339,8 @@ Mongoose.prototype.model = function (name, schema, collection, skipInit) {
options = {};
}
- // look up schema for the collection. this might be a
- // default schema like system.indexes stored in SchemaDefaults.
+ // look up schema for the collection.
if (!this.modelSchemas[name]) {
- if (!schema && name in SchemaDefaults) {
- schema = SchemaDefaults[name];
- }
-
if (schema) {
// cache it so we only apply plugins once
this.modelSchemas[name] = schema;
@@ -317,8 +355,8 @@ Mongoose.prototype.model = function (name, schema, collection, skipInit) {
// connection.model() may be passing a different schema for
// an existing model name. in this case don't read from cache.
- if (this.models[name] && false !== options.cache) {
- if (schema instanceof Schema && schema != this.models[name].schema) {
+ if (this.models[name] && options.cache !== false) {
+ if (schema && schema.instanceOfSchema && schema !== this.models[name].schema) {
throw new mongoose.Error.OverwriteModelError(name);
}
@@ -342,8 +380,12 @@ Mongoose.prototype.model = function (name, schema, collection, skipInit) {
}
}
+ // Apply relevant "global" options to the schema
+ if (!('pluralization' in schema.options)) schema.options.pluralization = this.options.pluralization;
+
+
if (!collection) {
- collection = schema.get('collection') || format(name);
+ collection = schema.get('collection') || format(name, schema.options);
}
var connection = options.connection || this.connection;
@@ -353,12 +395,14 @@ Mongoose.prototype.model = function (name, schema, collection, skipInit) {
model.init();
}
- if (false === options.cache) {
+ if (options.cache === false) {
return model;
}
- return this.models[name] = model;
-}
+ this.models[name] = model;
+ return this.models[name];
+};
+Mongoose.prototype.model.$hasSideEffects = true;
/**
* Returns an array of model names created on this instance of Mongoose.
@@ -371,10 +415,11 @@ Mongoose.prototype.model = function (name, schema, collection, skipInit) {
* @return {Array}
*/
-Mongoose.prototype.modelNames = function () {
+Mongoose.prototype.modelNames = function() {
var names = Object.keys(this.models);
return names;
-}
+};
+Mongoose.prototype.modelNames.$hasSideEffects = true;
/**
* Applies global plugins to `schema`.
@@ -383,11 +428,21 @@ Mongoose.prototype.modelNames = function () {
* @api private
*/
-Mongoose.prototype._applyPlugins = function (schema) {
- for (var i = 0, l = this.plugins.length; i < l; i++) {
+Mongoose.prototype._applyPlugins = function(schema) {
+ if (schema.$globalPluginsApplied) {
+ return;
+ }
+ var i;
+ var len;
+ for (i = 0, len = this.plugins.length; i < len; ++i) {
schema.plugin(this.plugins[i][0], this.plugins[i][1]);
}
-}
+ schema.$globalPluginsApplied = true;
+ for (i = 0, len = schema.childSchemas.length; i < len; ++i) {
+ this._applyPlugins(schema.childSchemas[i]);
+ }
+};
+Mongoose.prototype._applyPlugins.$hasSideEffects = true;
/**
* Declares a global plugin executed on all Schemas.
@@ -401,10 +456,11 @@ Mongoose.prototype._applyPlugins = function (schema) {
* @api public
*/
-Mongoose.prototype.plugin = function (fn, opts) {
+Mongoose.prototype.plugin = function(fn, opts) {
this.plugins.push([fn, opts]);
return this;
};
+Mongoose.prototype.plugin.$hasSideEffects = true;
/**
* The default connection of the mongoose module.
@@ -422,10 +478,14 @@ Mongoose.prototype.plugin = function (fn, opts) {
* @api public
*/
-Mongoose.prototype.__defineGetter__('connection', function(){
+Mongoose.prototype.__defineGetter__('connection', function() {
return this.connections[0];
});
+Mongoose.prototype.__defineSetter__('connection', function(v) {
+ this.connections[0] = v;
+});
+
/*!
* Driver depentend APIs
*/
@@ -444,6 +504,15 @@ var Connection = require(driver + '/connection');
var Collection = require(driver + '/collection');
+/**
+ * The Mongoose Aggregate constructor
+ *
+ * @method Aggregate
+ * @api public
+ */
+
+Mongoose.prototype.Aggregate = Aggregate;
+
/**
* The Mongoose Collection constructor
*
@@ -577,7 +646,23 @@ Mongoose.prototype.Query = Query;
* @api public
*/
-Mongoose.prototype.Promise = Promise;
+Object.defineProperty(Mongoose.prototype, 'Promise', {
+ get: function() {
+ return PromiseProvider.get();
+ },
+ set: function(lib) {
+ PromiseProvider.set(lib);
+ }
+});
+
+/**
+ * Storage layer for mongoose promises
+ *
+ * @method PromiseProvider
+ * @api public
+ */
+
+Mongoose.prototype.PromiseProvider = PromiseProvider;
/**
* The Mongoose [Model](#model_Model) constructor.
@@ -597,6 +682,15 @@ Mongoose.prototype.Model = Model;
Mongoose.prototype.Document = Document;
+/**
+ * The Mongoose DocumentProvider constructor.
+ *
+ * @method DocumentProvider
+ * @api public
+ */
+
+Mongoose.prototype.DocumentProvider = require('./document_provider');
+
/**
* The [MongooseError](#error_MongooseError) constructor.
*
@@ -606,6 +700,19 @@ Mongoose.prototype.Document = Document;
Mongoose.prototype.Error = require('./error');
+/**
+ * The Mongoose CastError constructor
+ *
+ * @method CastError
+ * @param {String} type The name of the type
+ * @param {Any} value The value that failed to cast
+ * @param {String} path The path `a.b.c` in the doc where this cast error occurred
+ * @param {Error} [reason] The original error that was thrown
+ * @api public
+ */
+
+Mongoose.prototype.CastError = require('./error/cast');
+
/**
* The [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver Mongoose uses.
*
@@ -615,6 +722,77 @@ Mongoose.prototype.Error = require('./error');
Mongoose.prototype.mongo = require('mongodb');
+/**
+ * The [mquery](https://github.com/aheckmann/mquery) query builder Mongoose uses.
+ *
+ * @property mquery
+ * @api public
+ */
+
+Mongoose.prototype.mquery = require('mquery');
+
+/**
+ * Wraps the given Mongoose instance into a thenable (pseudo-promise). This
+ * is so `connect()` and `disconnect()` can return a thenable while maintaining
+ * backwards compatibility.
+ *
+ * @api private
+ */
+
+function MongooseThenable(mongoose, promise) {
+ var _this = this;
+ for (var key in mongoose) {
+ if (typeof mongoose[key] === 'function' && mongoose[key].$hasSideEffects) {
+ (function(key) {
+ _this[key] = function() {
+ return mongoose[key].apply(mongoose, arguments);
+ };
+ })(key);
+ } else if (['connection', 'connections'].indexOf(key) !== -1) {
+ _this[key] = mongoose[key];
+ }
+ }
+ this.$opPromise = promise;
+}
+
+MongooseThenable.prototype = new Mongoose;
+
+/**
+ * Ability to use mongoose object as a pseudo-promise so `.connect().then()`
+ * and `.disconnect().then()` are viable.
+ *
+ * @param {Function} onFulfilled
+ * @param {Function} onRejected
+ * @return {Promise}
+ * @api private
+ */
+
+MongooseThenable.prototype.then = function(onFulfilled, onRejected) {
+ var Promise = PromiseProvider.get();
+ if (!this.$opPromise) {
+ return new Promise.ES6(function(resolve, reject) {
+ reject(new Error('Can only call `.then()` if connect() or disconnect() ' +
+ 'has been called'));
+ }).then(onFulfilled, onRejected);
+ }
+ this.$opPromise.$hasHandler = true;
+ return this.$opPromise.then(onFulfilled, onRejected);
+};
+
+/**
+ * Ability to use mongoose object as a pseudo-promise so `.connect().then()`
+ * and `.disconnect().then()` are viable.
+ *
+ * @param {Function} onFulfilled
+ * @param {Function} onRejected
+ * @return {Promise}
+ * @api private
+ */
+
+MongooseThenable.prototype.catch = function(onRejected) {
+ return this.then(null, onRejected);
+};
+
/*!
* The exports object is an instance of Mongoose.
*
diff --git a/node_modules/mongoose/lib/internal.js b/node_modules/mongoose/lib/internal.js
index d5a3f12..edf3338 100644
--- a/node_modules/mongoose/lib/internal.js
+++ b/node_modules/mongoose/lib/internal.js
@@ -2,12 +2,12 @@
* Dependencies
*/
-var StateMachine = require('./statemachine')
-var ActiveRoster = StateMachine.ctor('require', 'modify', 'init', 'default')
+var StateMachine = require('./statemachine');
+var ActiveRoster = StateMachine.ctor('require', 'modify', 'init', 'default', 'ignore');
module.exports = exports = InternalCache;
-function InternalCache () {
+function InternalCache() {
this.strictMode = undefined;
this.selected = undefined;
this.shardval = undefined;
diff --git a/node_modules/mongoose/lib/model.js b/node_modules/mongoose/lib/model.js
index 1f11884..08a6d16 100644
--- a/node_modules/mongoose/lib/model.js
+++ b/node_modules/mongoose/lib/model.js
@@ -2,27 +2,26 @@
* Module dependencies.
*/
-var Document = require('./document')
- , MongooseArray = require('./types/array')
- , MongooseBuffer = require('./types/buffer')
- , MongooseError = require('./error')
- , VersionError = MongooseError.VersionError
- , DivergentArrayError = MongooseError.DivergentArrayError
- , Query = require('./query')
- , Schema = require('./schema')
- , Types = require('./schema/index')
- , utils = require('./utils')
- , hasOwnProperty = utils.object.hasOwnProperty
- , isMongooseObject = utils.isMongooseObject
- , EventEmitter = require('events').EventEmitter
- , merge = utils.merge
- , Promise = require('./promise')
- , assert = require('assert')
- , tick = utils.tick
-
-var VERSION_WHERE = 1
- , VERSION_INC = 2
- , VERSION_ALL = VERSION_WHERE | VERSION_INC;
+var Document = require('./document');
+var MongooseError = require('./error');
+var VersionError = MongooseError.VersionError;
+var DivergentArrayError = MongooseError.DivergentArrayError;
+var Query = require('./query');
+var Aggregate = require('./aggregate');
+var Schema = require('./schema');
+var utils = require('./utils');
+var hasOwnProperty = utils.object.hasOwnProperty;
+var isMongooseObject = utils.isMongooseObject;
+var EventEmitter = require('events').EventEmitter;
+var util = require('util');
+var tick = utils.tick;
+
+var async = require('async');
+var PromiseProvider = require('./promise_provider');
+
+var VERSION_WHERE = 1,
+ VERSION_INC = 2,
+ VERSION_ALL = VERSION_WHERE | VERSION_INC;
/**
* Model constructor
@@ -30,15 +29,17 @@ var VERSION_WHERE = 1
* Provides the interface to MongoDB collections as well as creates document instances.
*
* @param {Object} doc values with which to create the document
- * @inherits Document
+ * @inherits Document http://mongoosejs.com/docs/api.html#document-js
* @event `error`: If listening to this event, it is emitted when a document was saved without passing a callback and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model.
* @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event.
+ * @event `index-single-start`: Emitted when an individual index starts within `Model#ensureIndexes`. The fields and options being used to build the index are also passed with the event.
+ * @event `index-single-done`: Emitted when an individual index finishes within `Model#ensureIndexes`. If an error occurred it is passed with the event. The fields, options, and index name are also passed.
* @api public
*/
-function Model (doc, fields, skipId) {
+function Model(doc, fields, skipId) {
Document.call(this, doc, fields, skipId);
-};
+}
/*!
* Inherits from Document.
@@ -76,61 +77,157 @@ Model.prototype.collection;
Model.prototype.modelName;
-/*!
- * Handles doc.save() callbacks
+/**
+ * If this is a discriminator model, `baseModelName` is the name of
+ * the base model.
+ *
+ * @api public
+ * @property baseModelName
*/
-function handleSave (promise, self) {
- return tick(function handleSave (err, result) {
- if (err) {
- // If the initial insert fails provide a second chance.
- // (If we did this all the time we would break updates)
- if (self.$__.inserting) {
- self.isNew = true;
- self.emit('isNew', true);
+Model.prototype.baseModelName;
+
+Model.prototype.$__handleSave = function(options, callback) {
+ var _this = this;
+ if (!options.safe && this.schema.options.safe) {
+ options.safe = this.schema.options.safe;
+ }
+ if (typeof options.safe === 'boolean') {
+ options.safe = null;
+ }
+
+ if (this.isNew) {
+ // send entire doc
+ var toObjectOptions = {};
+ if (this.schema.options.toObject &&
+ this.schema.options.toObject.retainKeyOrder) {
+ toObjectOptions.retainKeyOrder = true;
+ }
+
+ toObjectOptions.depopulate = 1;
+ toObjectOptions._skipDepopulateTopLevel = true;
+ toObjectOptions.transform = false;
+
+ var obj = this.toObject(toObjectOptions);
+
+ if (!utils.object.hasOwnProperty(obj || {}, '_id')) {
+ // documents must have an _id else mongoose won't know
+ // what to update later if more changes are made. the user
+ // wouldn't know what _id was generated by mongodb either
+ // nor would the ObjectId generated my mongodb necessarily
+ // match the schema definition.
+ setTimeout(function() {
+ callback(new Error('document must have an _id before saving'));
+ }, 0);
+ return;
+ }
+
+ this.$__version(true, obj);
+ this.collection.insert(obj, options.safe, function(err, ret) {
+ if (err) {
+ _this.isNew = true;
+ _this.emit('isNew', true);
+
+ callback(err);
+ return;
+ }
+
+ callback(null, ret);
+ });
+ this.$__reset();
+ this.isNew = false;
+ this.emit('isNew', false);
+ // Make it possible to retry the insert
+ this.$__.inserting = true;
+ } else {
+ // Make sure we don't treat it as a new object on error,
+ // since it already exists
+ this.$__.inserting = false;
+
+ var delta = this.$__delta();
+
+ if (delta) {
+ if (delta instanceof Error) {
+ callback(delta);
+ return;
+ }
+ var where = this.$__where(delta[0]);
+
+ if (where instanceof Error) {
+ callback(where);
+ return;
}
- promise.error(err);
- promise = self = null;
+
+ this.collection.update(where, delta[1], options.safe, function(err, ret) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ callback(null, ret);
+ });
+ } else {
+ this.$__reset();
+ callback();
return;
}
- self.$__storeShard();
+ this.emit('isNew', false);
+ }
+};
+
+/*!
+ * ignore
+ */
+
+Model.prototype.$__save = function(options, callback) {
+ var _this = this;
+
+ _this.$__handleSave(options, function(error, result) {
+ if (error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
+ callback(error);
+ });
+ }
+
+ _this.$__reset();
+ _this.$__storeShard();
- var numAffected;
+ var numAffected = 0;
if (result) {
- // when inserting, the array of created docs is returned
- numAffected = result.length
- ? result.length
- : result;
- } else {
- numAffected = 0;
+ if (Array.isArray(result)) {
+ numAffected = result.length;
+ } else if (result.result && result.result.n !== undefined) {
+ numAffected = result.result.n;
+ } else if (result.result && result.result.nModified !== undefined) {
+ numAffected = result.result.nModified;
+ } else {
+ numAffected = result;
+ }
}
// was this an update that required a version bump?
- if (self.$__.version && !self.$__.inserting) {
- var doIncrement = VERSION_INC === (VERSION_INC & self.$__.version);
- self.$__.version = undefined;
+ if (_this.$__.version && !_this.$__.inserting) {
+ var doIncrement = VERSION_INC === (VERSION_INC & _this.$__.version);
+ _this.$__.version = undefined;
- // increment version if was successful
- if (numAffected > 0) {
- if (doIncrement) {
- var key = self.schema.options.versionKey;
- var version = self.getValue(key) | 0;
- self.setValue(key, version + 1);
- }
- } else {
+ if (numAffected <= 0) {
// the update failed. pass an error back
- promise.error(new VersionError);
- promise = self = null;
- return;
+ var err = new VersionError(_this);
+ return callback(err);
+ }
+
+ // increment version if was successful
+ if (doIncrement) {
+ var key = _this.schema.options.versionKey;
+ var version = _this.getValue(key) | 0;
+ _this.setValue(key, version + 1);
}
}
- self.emit('save', self, numAffected);
- promise.complete(self, numAffected);
- promise = self = null;
+ _this.emit('save', _this, numAffected);
+ callback(null, _this, numAffected);
});
-}
+};
/**
* Saves this document.
@@ -138,68 +235,70 @@ function handleSave (promise, self) {
* ####Example:
*
* product.sold = Date.now();
- * product.save(function (err, product, numberAffected) {
+ * product.save(function (err, product, numAffected) {
* if (err) ..
* })
*
- * The callback will receive three parameters, `err` if an error occurred, `product` which is the saved `product`, and `numberAffected` which will be 1 when the document was found and updated in the database, otherwise 0.
- *
- * The `fn` callback is optional. If no `fn` is passed and validation fails, the validation error will be emitted on the connection used to create this model.
+ * The callback will receive three parameters
*
- * var db = mongoose.createConnection(..);
- * var schema = new Schema(..);
- * var Product = db.model('Product', schema);
+ * 1. `err` if an error occurred
+ * 2. `product` which is the saved `product`
+ * 3. `numAffected` will be 1 when the document was successfully persisted to MongoDB, otherwise 0. Unless you tweak mongoose's internals, you don't need to worry about checking this parameter for errors - checking `err` is sufficient to make sure your document was properly saved.
*
- * db.on('error', handleError);
- *
- * However, if you desire more local error handling you can add an `error` listener to the model and handle errors there instead.
+ * As an extra measure of flow control, save will return a Promise.
+ * ####Example:
+ * product.save().then(function(product) {
+ * ...
+ * });
*
- * Product.on('error', handleError);
+ * For legacy reasons, mongoose stores object keys in reverse order on initial
+ * save. That is, `{ a: 1, b: 2 }` will be saved as `{ b: 2, a: 1 }` in
+ * MongoDB. To override this behavior, set
+ * [the `toObject.retainKeyOrder` option](http://mongoosejs.com/docs/api.html#document_Document-toObject)
+ * to true on your schema.
*
+ * @param {Object} [options] options optional options
+ * @param {Object} [options.safe] overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe)
+ * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
* @param {Function} [fn] optional callback
+ * @return {Promise} Promise
* @api public
* @see middleware http://mongoosejs.com/docs/middleware.html
*/
-Model.prototype.save = function save (fn) {
- var promise = new Promise(fn)
- , complete = handleSave(promise, this)
- , options = {}
+Model.prototype.save = function(options, fn) {
+ if (typeof options === 'function') {
+ fn = options;
+ options = undefined;
+ }
- if (this.schema.options.safe) {
- options.safe = this.schema.options.safe;
+ if (!options) {
+ options = {};
}
- if (this.isNew) {
- // send entire doc
- var obj = this.toObject({ depopulate: 1 });
- this.$__version(true, obj);
- this.collection.insert(obj, options, complete);
- this.$__reset();
- this.isNew = false;
- this.emit('isNew', false);
- // Make it possible to retry the insert
- this.$__.inserting = true;
+ if (fn) {
+ fn = this.constructor.$wrapCallback(fn);
+ }
- } else {
- // Make sure we don't treat it as a new object on error,
- // since it already exists
- this.$__.inserting = false;
+ return this.$__save(options, fn);
+};
- var delta = this.$__delta();
- if (delta) {
- if (delta instanceof Error) return complete(delta);
- var where = this.$__where(delta[0]);
- this.$__reset();
- this.collection.update(where, delta[1], options, complete);
- } else {
- this.$__reset();
- complete(null);
- }
+/*!
+ * Determines whether versioning should be skipped for the given path
+ *
+ * @param {Document} self
+ * @param {String} path
+ * @return {Boolean} true if versioning should be skipped for the given path
+ */
+function shouldSkipVersioning(self, path) {
+ var skipVersioning = self.schema.options.skipVersioning;
+ if (!skipVersioning) return false;
- this.emit('isNew', false);
- }
-};
+ // Remove any array indexes from the path
+ path = path.replace(/\.\d+\./, '.');
+
+ return skipVersioning[path];
+}
/*!
* Apply the operation to the delta (update) clause as
@@ -213,14 +312,17 @@ Model.prototype.save = function save (fn) {
* @param {String} [operation]
*/
-function operand (self, where, delta, data, val, op) {
+function operand(self, where, delta, data, val, op) {
// delta
op || (op = '$set');
if (!delta[op]) delta[op] = {};
delta[op][data.path] = val;
// disabled versioning?
- if (false === self.schema.options.versionKey) return;
+ if (self.schema.options.versionKey === false) return;
+
+ // path excluded from versioning?
+ if (shouldSkipVersioning(self, data.path)) return;
// already marked for versioning?
if (VERSION_ALL === (VERSION_ALL & self.$__.version)) return;
@@ -245,19 +347,16 @@ function operand (self, where, delta, data, val, op) {
// only increment the version if an array position changes.
// modifying elements of an array is ok if position does not change.
- if ('$push' == op || '$pushAll' == op || '$addToSet' == op) {
+ if (op === '$push' || op === '$pushAll' || op === '$addToSet') {
self.$__.version = VERSION_INC;
- }
- else if (/^\$p/.test(op)) {
+ } else if (/^\$p/.test(op)) {
// potentially changing array positions
self.increment();
- }
- else if (Array.isArray(val)) {
+ } else if (Array.isArray(val)) {
// $set an array
self.increment();
- }
- // now handling $set, $unset
- else if (/\.\d+\.|\.\d+$/.test(data.path)) {
+ } else if (/\.\d+\.|\.\d+$/.test(data.path)) {
+ // now handling $set, $unset
// subpath of array
self.$__.version = VERSION_WHERE;
}
@@ -273,34 +372,34 @@ function operand (self, where, delta, data, val, op) {
* @param {Array} value
*/
-function handleAtomics (self, where, delta, data, value) {
+function handleAtomics(self, where, delta, data, value) {
if (delta.$set && delta.$set[data.path]) {
// $set has precedence over other atomics
return;
}
- if ('function' == typeof value.$__getAtomics) {
- value.$__getAtomics().forEach(function (atomic) {
+ if (typeof value.$__getAtomics === 'function') {
+ value.$__getAtomics().forEach(function(atomic) {
var op = atomic[0];
var val = atomic[1];
operand(self, where, delta, data, val, op);
- })
+ });
return;
}
// legacy support for plugins
- var atomics = value._atomics
- , ops = Object.keys(atomics)
- , i = ops.length
- , val
- , op;
+ var atomics = value._atomics,
+ ops = Object.keys(atomics),
+ i = ops.length,
+ val,
+ op;
- if (0 === i) {
+ if (i === 0) {
// $set
if (isMongooseObject(value)) {
- value = value.toObject({ depopulate: 1 });
+ value = value.toObject({depopulate: 1});
} else if (value.valueOf) {
value = value.valueOf();
}
@@ -308,24 +407,27 @@ function handleAtomics (self, where, delta, data, value) {
return operand(self, where, delta, data, value);
}
+ function iter(mem) {
+ return isMongooseObject(mem)
+ ? mem.toObject({depopulate: 1})
+ : mem;
+ }
+
while (i--) {
op = ops[i];
val = atomics[op];
if (isMongooseObject(val)) {
- val = val.toObject({ depopulate: 1 })
+ val = val.toObject({depopulate: 1});
} else if (Array.isArray(val)) {
- val = val.map(function (mem) {
- return isMongooseObject(mem)
- ? mem.toObject({ depopulate: 1 })
- : mem;
- })
+ val = val.map(iter);
} else if (val.valueOf) {
- val = val.valueOf()
+ val = val.valueOf();
}
- if ('$addToSet' === op)
- val = { $each: val };
+ if (op === '$addToSet') {
+ val = {$each: val};
+ }
operand(self, where, delta, data, val, op);
}
@@ -339,22 +441,21 @@ function handleAtomics (self, where, delta, data, value) {
* @memberOf Model
*/
-Model.prototype.$__delta = function () {
+Model.prototype.$__delta = function() {
var dirty = this.$__dirty();
- if (!dirty.length && VERSION_ALL != this.$__.version) return;
+ if (!dirty.length && VERSION_ALL !== this.$__.version) return;
+
+ var where = {},
+ delta = {},
+ len = dirty.length,
+ divergent = [],
+ d = 0;
- var where = {}
- , delta = {}
- , len = dirty.length
- , divergent = []
- , d = 0
- , val
- , obj
+ where._id = this._doc._id;
for (; d < len; ++d) {
- var data = dirty[d]
- var value = data.value
- var schema = data.schema
+ var data = dirty[d];
+ var value = data.value;
var match = checkDivergentArray(this, data.path, value);
if (match) {
@@ -362,25 +463,42 @@ Model.prototype.$__delta = function () {
continue;
}
+ var pop = this.populated(data.path, true);
+ if (!pop && this.$__.selected) {
+ // If any array was selected using an $elemMatch projection, we alter the path and where clause
+ // NOTE: MongoDB only supports projected $elemMatch on top level array.
+ var pathSplit = data.path.split('.');
+ var top = pathSplit[0];
+ if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) {
+ // If the selected array entry was modified
+ if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') {
+ where[top] = this.$__.selected[top];
+ pathSplit[1] = '$';
+ data.path = pathSplit.join('.');
+ }
+ // if the selected array was modified in any other way throw an error
+ else {
+ divergent.push(data.path);
+ continue;
+ }
+ }
+ }
+
if (divergent.length) continue;
if (undefined === value) {
operand(this, where, delta, data, 1, '$unset');
-
- } else if (null === value) {
+ } else if (value === null) {
operand(this, where, delta, data, null);
-
} else if (value._path && value._atomics) {
// arrays and other custom types (support plugins etc)
handleAtomics(this, where, delta, data, value);
-
} else if (value._path && Buffer.isBuffer(value)) {
// MongooseBuffer
value = value.toObject();
operand(this, where, delta, data, value);
-
} else {
- value = utils.clone(value, { depopulate: 1 });
+ value = utils.clone(value, {depopulate: 1});
operand(this, where, delta, data, value);
}
}
@@ -394,19 +512,19 @@ Model.prototype.$__delta = function () {
}
return [where, delta];
-}
+};
/*!
* Determine if array was populated with some form of filter and is now
* being updated in a manner which could overwrite data unintentionally.
*
- * @see https://github.com/LearnBoost/mongoose/issues/1334
+ * @see https://github.com/Automattic/mongoose/issues/1334
* @param {Document} doc
* @param {String} path
* @return {String|undefined}
*/
-function checkDivergentArray (doc, path, array) {
+function checkDivergentArray(doc, path, array) {
// see if we populated this path
var pop = doc.populated(path, true);
@@ -414,12 +532,12 @@ function checkDivergentArray (doc, path, array) {
// If any array was selected using an $elemMatch projection, we deny the update.
// NOTE: MongoDB only supports projected $elemMatch on top level array.
var top = path.split('.')[0];
- if (doc.$__.selected[top] && doc.$__.selected[top].$elemMatch) {
+ if (doc.$__.selected[top + '.$']) {
return top;
}
}
- if (!(pop && array instanceof MongooseArray)) return;
+ if (!(pop && array && array.isMongooseArray)) return;
// If the array was populated using options that prevented all
// documents from being returned (match, skip, limit) or they
@@ -431,15 +549,15 @@ function checkDivergentArray (doc, path, array) {
// would be similarily destructive as we never received all
// elements of the array and potentially would overwrite data.
var check = pop.options.match ||
- pop.options.options && hasOwnProperty(pop.options.options, 'limit') || // 0 is not permitted
- pop.options.options && pop.options.options.skip || // 0 is permitted
- pop.options.select && // deselected _id?
- (0 === pop.options.select._id ||
- /\s?-_id\s?/.test(pop.options.select))
+ pop.options.options && hasOwnProperty(pop.options.options, 'limit') || // 0 is not permitted
+ pop.options.options && pop.options.options.skip || // 0 is permitted
+ pop.options.select && // deselected _id?
+ (pop.options.select._id === 0 ||
+ /\s?-_id\s?/.test(pop.options.select));
if (check) {
var atomics = array._atomics;
- if (0 === Object.keys(atomics).length || atomics.$set || atomics.$pop) {
+ if (Object.keys(atomics).length === 0 || atomics.$set || atomics.$pop) {
return path;
}
}
@@ -453,10 +571,10 @@ function checkDivergentArray (doc, path, array) {
* @memberOf Model
*/
-Model.prototype.$__version = function (where, delta) {
+Model.prototype.$__version = function(where, delta) {
var key = this.schema.options.versionKey;
- if (true === where) {
+ if (where === true) {
// this is an insert
if (key) this.setValue(key, delta[key] = 0);
return;
@@ -478,10 +596,12 @@ Model.prototype.$__version = function (where, delta) {
}
if (VERSION_INC === (VERSION_INC & this.$__.version)) {
- delta.$inc || (delta.$inc = {});
- delta.$inc[key] = 1;
+ if (!delta.$set || typeof delta.$set[key] === 'undefined') {
+ delta.$inc || (delta.$inc = {});
+ delta.$inc[key] = 1;
+ }
}
-}
+};
/**
* Signal that we desire an increment of this documents version.
@@ -497,10 +617,10 @@ Model.prototype.$__version = function (where, delta) {
* @api public
*/
-Model.prototype.increment = function increment () {
+Model.prototype.increment = function increment() {
this.$__.version = VERSION_ALL;
return this;
-}
+};
/**
* Returns a query object which applies shardkeys if they exist.
@@ -510,68 +630,108 @@ Model.prototype.increment = function increment () {
* @memberOf Model
*/
-Model.prototype.$__where = function _where (where) {
+Model.prototype.$__where = function _where(where) {
where || (where = {});
- var paths
- , len
+ var paths,
+ len;
+
+ if (!where._id) {
+ where._id = this._doc._id;
+ }
if (this.$__.shardval) {
- paths = Object.keys(this.$__.shardval)
- len = paths.length
+ paths = Object.keys(this.$__.shardval);
+ len = paths.length;
for (var i = 0; i < len; ++i) {
where[paths[i]] = this.$__.shardval[paths[i]];
}
}
- where._id = this._doc._id;
+ if (this._doc._id == null) {
+ return new Error('No _id found on document!');
+ }
+
return where;
-}
+};
/**
* Removes this document from the db.
*
* ####Example:
- *
- * product.remove(function (err) {
+ * product.remove(function (err, product) {
* if (err) return handleError(err);
* Product.findById(product._id, function (err, product) {
* console.log(product) // null
* })
* })
*
- * @param {Function} [fn] optional callback
+ *
+ * As an extra measure of flow control, remove will return a Promise (bound to `fn` if passed) so it could be chained, or hooked to recive errors
+ *
+ * ####Example:
+ * product.remove().then(function (product) {
+ * ...
+ * }).onRejected(function (err) {
+ * assert.ok(err)
+ * })
+ *
+ * @param {function(err,product)} [fn] optional callback
+ * @return {Promise} Promise
* @api public
*/
-Model.prototype.remove = function remove (fn) {
+Model.prototype.remove = function remove(options, fn) {
+ if (typeof options === 'function') {
+ fn = options;
+ options = undefined;
+ }
+
+ if (!options) {
+ options = {};
+ }
+
if (this.$__.removing) {
- this.$__.removing.addBack(fn);
+ if (fn) {
+ this.$__.removing.then(
+ function(res) { fn(null, res); },
+ function(err) { fn(err); });
+ }
return this;
}
- var promise = this.$__.removing = new Promise(fn)
- , where = this.$__where()
- , self = this
- , options = {}
+ var _this = this;
+ var Promise = PromiseProvider.get();
- if (this.schema.options.safe) {
- options.safe = this.schema.options.safe;
+ if (fn) {
+ fn = this.constructor.$wrapCallback(fn);
}
- this.collection.remove(where, options, tick(function (err) {
- if (err) {
- promise.error(err);
- promise = self = self.$__.removing = where = options = null;
+ this.$__.removing = new Promise.ES6(function(resolve, reject) {
+ var where = _this.$__where();
+ if (where instanceof Error) {
+ reject(where);
+ fn && fn(where);
return;
}
- self.emit('remove', self);
- promise.complete();
- promise = self = where = options = null;
- }));
- return this;
+ if (!options.safe && _this.schema.options.safe) {
+ options.safe = _this.schema.options.safe;
+ }
+
+ _this.collection.remove(where, options, function(err) {
+ if (!err) {
+ _this.emit('remove', _this);
+ resolve(_this);
+ fn && fn(null, _this);
+ return;
+ }
+ reject(err);
+ fn && fn(err);
+ });
+ });
+ return this.$__.removing;
};
/**
@@ -586,18 +746,150 @@ Model.prototype.remove = function remove (fn) {
* @api public
*/
-Model.prototype.model = function model (name) {
+Model.prototype.model = function model(name) {
return this.db.model(name);
};
+/**
+ * Adds a discriminator type.
+ *
+ * ####Example:
+ *
+ * function BaseSchema() {
+ * Schema.apply(this, arguments);
+ *
+ * this.add({
+ * name: String,
+ * createdAt: Date
+ * });
+ * }
+ * util.inherits(BaseSchema, Schema);
+ *
+ * var PersonSchema = new BaseSchema();
+ * var BossSchema = new BaseSchema({ department: String });
+ *
+ * var Person = mongoose.model('Person', PersonSchema);
+ * var Boss = Person.discriminator('Boss', BossSchema);
+ *
+ * @param {String} name discriminator model name
+ * @param {Schema} schema discriminator model schema
+ * @api public
+ */
+
+Model.discriminator = function discriminator(name, schema) {
+ var CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
+ toJSON: true,
+ toObject: true,
+ _id: true,
+ id: true
+ };
+
+ if (!(schema && schema.instanceOfSchema)) {
+ throw new Error('You must pass a valid discriminator Schema');
+ }
+
+ if (this.schema.discriminatorMapping && !this.schema.discriminatorMapping.isRoot) {
+ throw new Error('Discriminator "' + name +
+ '" can only be a discriminator of the root model');
+ }
+
+ var key = this.schema.options.discriminatorKey;
+ if (schema.path(key)) {
+ throw new Error('Discriminator "' + name +
+ '" cannot have field with name "' + key + '"');
+ }
+
+ function merge(schema, baseSchema) {
+ utils.merge(schema, baseSchema);
+
+ var obj = {};
+ obj[key] = {
+ default: name,
+ set: function() {
+ throw new Error('Can\'t set discriminator key "' + key + '"');
+ }
+ };
+ obj[key][schema.options.typeKey] = String;
+ schema.add(obj);
+ schema.discriminatorMapping = {key: key, value: name, isRoot: false};
+
+ if (baseSchema.options.collection) {
+ schema.options.collection = baseSchema.options.collection;
+ }
+
+ var toJSON = schema.options.toJSON;
+ var toObject = schema.options.toObject;
+ var _id = schema.options._id;
+ var id = schema.options.id;
+
+ var keys = Object.keys(schema.options);
+
+ for (var i = 0; i < keys.length; ++i) {
+ var _key = keys[i];
+ if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) {
+ if (!utils.deepEqual(schema.options[_key], baseSchema.options[_key])) {
+ throw new Error('Can\'t customize discriminator option ' + _key +
+ ' (can only modify ' +
+ Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
+ ')');
+ }
+ }
+ }
+
+ schema.options = utils.clone(baseSchema.options);
+ if (toJSON) schema.options.toJSON = toJSON;
+ if (toObject) schema.options.toObject = toObject;
+ if (typeof _id !== 'undefined') {
+ schema.options._id = _id;
+ }
+ schema.options.id = id;
+
+ schema.callQueue = baseSchema.callQueue.concat(schema.callQueue.slice(schema._defaultMiddleware.length));
+ schema._requiredpaths = undefined; // reset just in case Schema#requiredPaths() was called on either schema
+ }
+
+ // merges base schema into new discriminator schema and sets new type field.
+ merge(schema, this.schema);
+
+ if (!this.discriminators) {
+ this.discriminators = {};
+ }
+
+ if (!this.schema.discriminatorMapping) {
+ this.schema.discriminatorMapping = {key: key, value: null, isRoot: true};
+ }
+
+ if (this.discriminators[name]) {
+ throw new Error('Discriminator with name "' + name + '" already exists');
+ }
+ if (this.db.models[name]) {
+ throw new MongooseError.OverwriteModelError(name);
+ }
+
+ this.discriminators[name] = this.db.model(name, schema, this.collection.name);
+ this.discriminators[name].prototype.__proto__ = this.prototype;
+ Object.defineProperty(this.discriminators[name], 'baseModelName', {
+ value: this.modelName,
+ configurable: true,
+ writable: false
+ });
+
+ // apply methods and statics
+ applyMethods(this.discriminators[name], schema);
+ applyStatics(this.discriminators[name], schema);
+
+ return this.discriminators[name];
+};
+
// Model (class) features
/*!
* Give the constructor the ability to emit events.
*/
-for (var i in EventEmitter.prototype)
+for (var i in EventEmitter.prototype) {
Model[i] = EventEmitter.prototype[i];
+}
/**
* Called when the model compiles.
@@ -605,9 +897,10 @@ for (var i in EventEmitter.prototype)
* @api private
*/
-Model.init = function init () {
- if (this.schema.options.autoIndex) {
- this.ensureIndexes();
+Model.init = function init() {
+ if ((this.schema.options.autoIndex) ||
+ (this.schema.options.autoIndex === null && this.db.config.autoIndex)) {
+ this.ensureIndexes({ __noPromise: true });
}
this.schema.emit('init', this);
@@ -635,42 +928,106 @@ Model.init = function init () {
*
* _NOTE: It is not recommended that you run this in production. Index creation may impact database performance depending on your load. Use with caution._
*
- * The `ensureIndex` commands are not sent in parallel. This is to avoid the `MongoError: cannot add index with a background operation in progress` error. See [this ticket](https://github.com/LearnBoost/mongoose/issues/1365) for more information.
+ * The `ensureIndex` commands are not sent in parallel. This is to avoid the `MongoError: cannot add index with a background operation in progress` error. See [this ticket](https://github.com/Automattic/mongoose/issues/1365) for more information.
*
+ * @param {Object} [options] internal options
* @param {Function} [cb] optional callback
+ * @return {Promise}
* @api public
*/
-Model.ensureIndexes = function ensureIndexes (cb) {
- var indexes = this.schema.indexes();
- if (!indexes.length) {
- return cb && process.nextTick(cb);
+Model.ensureIndexes = function ensureIndexes(options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ if (options && options.__noPromise) {
+ _ensureIndexes(this, callback);
+ return;
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
}
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve, reject) {
+ _ensureIndexes(_this, function(error) {
+ if (error) {
+ callback && callback(error);
+ reject(error);
+ }
+ callback && callback();
+ resolve();
+ });
+ });
+};
+
+function _ensureIndexes(model, callback) {
+ var indexes = model.schema.indexes();
+ if (!indexes.length) {
+ setImmediate(function() {
+ callback && callback();
+ });
+ return;
+ }
// Indexes are created one-by-one to support how MongoDB < 2.4 deals
// with background indexes.
- var self = this
- , safe = self.schema.options.safe
+ var done = function(err) {
+ if (err && model.schema.options.emitIndexErrors) {
+ model.emit('error', err);
+ }
+ model.emit('index', err);
+ callback && callback(err);
+ };
- function done (err) {
- self.emit('index', err);
- cb && cb(err);
- }
+ var indexSingleDone = function(err, fields, options, name) {
+ model.emit('index-single-done', err, fields, options, name);
+ };
+ var indexSingleStart = function(fields, options) {
+ model.emit('index-single-start', fields, options);
+ };
- function create () {
+ var create = function() {
var index = indexes.shift();
if (!index) return done();
+ var indexFields = index[0];
var options = index[1];
- options.safe = safe;
- self.collection.ensureIndex(index[0], options, tick(function (err) {
- if (err) return done(err);
+ _handleSafe(options);
+
+ indexSingleStart(indexFields, options);
+
+ model.collection.ensureIndex(indexFields, options, tick(function(err, name) {
+ indexSingleDone(err, indexFields, options, name);
+ if (err) {
+ return done(err);
+ }
create();
}));
- }
+ };
- create();
+ setImmediate(function() {
+ create();
+ });
+}
+
+function _handleSafe(options) {
+ if (options.safe) {
+ if (typeof options.safe === 'boolean') {
+ options.w = options.safe;
+ delete options.safe;
+ }
+ if (typeof options.safe === 'object') {
+ options.w = options.safe.w;
+ options.j = options.safe.j;
+ options.wtimeout = options.safe.wtimeout;
+ delete options.safe;
+ }
+ }
}
/**
@@ -713,6 +1070,16 @@ Model.collection;
Model.base;
+/**
+ * Registered discriminators for this model.
+ *
+ * @property discriminators
+ * @receiver Model
+ * @api public
+ */
+
+Model.discriminators;
+
/**
* Removes documents from the collection.
*
@@ -739,19 +1106,20 @@ Model.base;
* @api public
*/
-Model.remove = function remove (conditions, callback) {
- if ('function' === typeof conditions) {
+Model.remove = function remove(conditions, callback) {
+ if (typeof conditions === 'function') {
callback = conditions;
conditions = {};
}
- var query = new Query(conditions).bind(this, 'remove');
+ // get the mongodb collection object
+ var mq = new this.Query(conditions, {}, this, this.collection);
- if ('undefined' === typeof callback)
- return query;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
- this._applyNamedScope(query);
- return query.remove(callback);
+ return mq.remove(callback);
};
/**
@@ -786,7 +1154,7 @@ Model.remove = function remove (conditions, callback) {
* promise.addBack(function (err, docs) {});
*
* @param {Object} conditions
- * @param {Object} [fields] optional fields to select
+ * @param {Object} [projection] optional fields to return (http://bit.ly/1HotzBo)
* @param {Object} [options] optional
* @param {Function} [callback]
* @return {Query}
@@ -795,60 +1163,47 @@ Model.remove = function remove (conditions, callback) {
* @api public
*/
-Model.find = function find (conditions, fields, options, callback) {
- if ('function' == typeof conditions) {
+Model.find = function find(conditions, projection, options, callback) {
+ if (typeof conditions === 'function') {
callback = conditions;
conditions = {};
- fields = null;
+ projection = null;
options = null;
- } else if ('function' == typeof fields) {
- callback = fields;
- fields = null;
+ } else if (typeof projection === 'function') {
+ callback = projection;
+ projection = null;
options = null;
- } else if ('function' == typeof options) {
+ } else if (typeof options === 'function') {
callback = options;
options = null;
}
- var query = new Query(conditions, options);
- query.bind(this, 'find');
- query.select(fields);
+ var mq = new this.Query({}, {}, this, this.collection);
+ mq.select(projection);
+ mq.setOptions(options);
+ if (this.schema.discriminatorMapping && mq.selectedInclusively()) {
+ mq.select(this.schema.options.discriminatorKey);
+ }
- if ('undefined' === typeof callback)
- return query;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
- this._applyNamedScope(query);
- return query.find(callback);
+ return mq.find(conditions, callback);
};
/**
- * Merges the current named scope query into `query`.
- *
- * @param {Query} query
- * @return {Query}
- * @api private
- */
-
-Model._applyNamedScope = function _applyNamedScope (query) {
- var cQuery = this._cumulativeQuery;
-
- if (cQuery) {
- merge(query._conditions, cQuery._conditions);
- if (query._fields && cQuery._fields)
- merge(query._fields, cQuery._fields);
- if (query.options && cQuery.options)
- merge(query.options, cQuery.options);
- delete this._cumulativeQuery;
- }
-
- return query;
-}
-
-/**
- * Finds a single document by id.
+ * Finds a single document by its _id field. `findById(id)` is almost*
+ * equivalent to `findOne({ _id: id })`.
*
* The `id` is cast based on the Schema before sending the command.
*
+ * Note: `findById()` triggers `findOne` hooks.
+ *
+ * * Except for how it treats `undefined`. Because the MongoDB driver
+ * deletes keys that have value `undefined`, `findById(undefined)` gets
+ * translated to `findById({ _id: null })`.
+ *
* ####Example:
*
* // find adventure by id and execute immediately
@@ -872,8 +1227,8 @@ Model._applyNamedScope = function _applyNamedScope (query) {
* // same as above
* Adventure.findById(id, 'name').lean().exec(function (err, doc) {});
*
- * @param {ObjectId|HexId} id objectid, or a value that can be casted to one
- * @param {Object} [fields] optional fields to select
+ * @param {Object|String|Number} id value of `_id` to query by
+ * @param {Object} [projection] optional fields to return (http://bit.ly/1HotzBo)
* @param {Object} [options] optional
* @param {Function} [callback]
* @return {Query}
@@ -882,8 +1237,16 @@ Model._applyNamedScope = function _applyNamedScope (query) {
* @api public
*/
-Model.findById = function findById (id, fields, options, callback) {
- return this.findOne({ _id: id }, fields, options, callback);
+Model.findById = function findById(id, projection, options, callback) {
+ if (typeof id === 'undefined') {
+ id = null;
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ return this.findOne({_id: id}, projection, options, callback);
};
/**
@@ -914,8 +1277,8 @@ Model.findById = function findById (id, fields, options, callback) {
* // chaining findOne queries (same as above)
* Adventure.findOne({ type: 'iphone' }).select('name').lean().exec(callback);
*
- * @param {Object} conditions
- * @param {Object} [fields] optional fields to select
+ * @param {Object} [conditions]
+ * @param {Object} [projection] optional fields to return (http://bit.ly/1HotzBo)
* @param {Object} [options] optional
* @param {Function} [callback]
* @return {Query}
@@ -924,28 +1287,34 @@ Model.findById = function findById (id, fields, options, callback) {
* @api public
*/
-Model.findOne = function findOne (conditions, fields, options, callback) {
- if ('function' == typeof options) {
+Model.findOne = function findOne(conditions, projection, options, callback) {
+ if (typeof options === 'function') {
callback = options;
options = null;
- } else if ('function' == typeof fields) {
- callback = fields;
- fields = null;
+ } else if (typeof projection === 'function') {
+ callback = projection;
+ projection = null;
options = null;
- } else if ('function' == typeof conditions) {
+ } else if (typeof conditions === 'function') {
callback = conditions;
conditions = {};
- fields = null;
+ projection = null;
options = null;
}
- var query = new Query(conditions, options).select(fields).bind(this, 'findOne');
+ // get the mongodb collection object
+ var mq = new this.Query({}, {}, this, this.collection);
+ mq.select(projection);
+ mq.setOptions(options);
+ if (this.schema.discriminatorMapping && mq.selectedInclusively()) {
+ mq.select(this.schema.options.discriminatorKey);
+ }
- if ('undefined' == typeof callback)
- return query;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
- this._applyNamedScope(query);
- return query.findOne(callback);
+ return mq.findOne(conditions, callback);
};
/**
@@ -964,20 +1333,26 @@ Model.findOne = function findOne (conditions, fields, options, callback) {
* @api public
*/
-Model.count = function count (conditions, callback) {
- if ('function' === typeof conditions)
- callback = conditions, conditions = {};
+Model.count = function count(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ }
+
+ // get the mongodb collection object
+ var mq = new this.Query({}, {}, this, this.collection);
- var query = new Query(conditions).bind(this, 'count');
- if ('undefined' == typeof callback)
- return query;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
- this._applyNamedScope(query);
- return query.count(callback);
+ return mq.count(conditions, callback);
};
/**
- * Executes a DISTINCT command
+ * Creates a Query for a `distinct` operation.
+ *
+ * Passing a `callback` immediately executes the query.
*
* ####Example
*
@@ -988,6 +1363,9 @@ Model.count = function count (conditions, callback) {
* console.log('unique urls with more than 100 clicks', result);
* })
*
+ * var query = Link.distinct('url');
+ * query.exec(callback);
+ *
* @param {String} field
* @param {Object} [conditions] optional
* @param {Function} [callback]
@@ -995,20 +1373,19 @@ Model.count = function count (conditions, callback) {
* @api public
*/
-Model.distinct = function distinct (field, conditions, callback) {
- if ('function' == typeof conditions) {
+Model.distinct = function distinct(field, conditions, callback) {
+ // get the mongodb collection object
+ var mq = new this.Query({}, {}, this, this.collection);
+
+ if (typeof conditions === 'function') {
callback = conditions;
conditions = {};
}
-
- var query = new Query(conditions).bind(this, 'distinct');
- if ('undefined' == typeof callback) {
- query._distinctArg = field;
- return query;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
}
- this._applyNamedScope(query);
- return query.distinct(field, callback);
+ return mq.distinct(field, conditions, callback);
};
/**
@@ -1035,9 +1412,11 @@ Model.distinct = function distinct (field, conditions, callback) {
* @api public
*/
-Model.where = function where (path, val) {
- var q = new Query().bind(this, 'find');
- return q.where.apply(q, arguments);
+Model.where = function where(path, val) {
+ void val; // eslint
+ // get the mongodb collection object
+ var mq = new this.Query({}, {}, this, this.collection).find({});
+ return mq.where.apply(mq, arguments);
};
/**
@@ -1045,7 +1424,7 @@ Model.where = function where (path, val) {
*
* Sometimes you need to query for things in mongodb using a JavaScript expression. You can do so via `find({ $where: javascript })`, or you can use the mongoose shortcut method $where via a Query chain or from your mongoose Model.
*
- * Blog.$where('this.comments.length > 5').exec(function (err, docs) {});
+ * Blog.$where('this.username.indexOf("val") !== -1').exec(function (err, docs) {});
*
* @param {String|Function} argument is a javascript string or anonymous function
* @method $where
@@ -1055,9 +1434,9 @@ Model.where = function where (path, val) {
* @api public
*/
-Model.$where = function $where () {
- var q = new Query().bind(this, 'find');
- return q.$where.apply(q, arguments);
+Model.$where = function $where() {
+ var mq = new this.Query({}, {}, this, this.collection).find({});
+ return mq.$where.apply(mq, arguments);
};
/**
@@ -1067,10 +1446,15 @@ Model.$where = function $where () {
*
* ####Options:
*
- * - `new`: bool - true to return the modified document rather than the original. defaults to true
+ * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
* - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
+ * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
- * - `select`: sets the document fields to return
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
+ * - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ *
*
* ####Examples:
*
@@ -1096,20 +1480,24 @@ Model.$where = function $where () {
*
* ####Note:
*
- * Although values are cast to their appropriate types when using the findAndModify helpers, the following are *not* applied:
+ * Values are cast to their appropriate types when using the findAndModify helpers.
+ * However, the below are never executed.
*
* - defaults
* - setters
- * - validators
- * - middleware
*
- * If you need those features, use the traditional approach of first retrieving the document.
+ * `findAndModify` helpers support limited defaults and validation. You can
+ * enable these by setting the `setDefaultsOnInsert` and `runValidators` options,
+ * respectively.
*
- * Model.findOne({ name: 'borne' }, function (err, doc) {
+ * If you need full-fledged validation, use the traditional approach of first
+ * retrieving the document.
+ *
+ * Model.findById(id, function (err, doc) {
* if (err) ..
* doc.name = 'jason borne';
* doc.save(callback);
- * })
+ * });
*
* @param {Object} [conditions]
* @param {Object} [update]
@@ -1120,52 +1508,63 @@ Model.$where = function $where () {
* @api public
*/
-Model.findOneAndUpdate = function (conditions, update, options, callback) {
- if ('function' == typeof options) {
+Model.findOneAndUpdate = function(conditions, update, options, callback) {
+ if (typeof options === 'function') {
callback = options;
options = null;
- }
- else if (1 === arguments.length) {
- if ('function' == typeof conditions) {
+ } else if (arguments.length === 1) {
+ if (typeof conditions === 'function') {
var msg = 'Model.findOneAndUpdate(): First argument must not be a function.\n\n'
- + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)\n'
- + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options)\n'
- + ' ' + this.modelName + '.findOneAndUpdate(conditions, update)\n'
- + ' ' + this.modelName + '.findOneAndUpdate(update)\n'
- + ' ' + this.modelName + '.findOneAndUpdate()\n';
- throw new TypeError(msg)
+ + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)\n'
+ + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options)\n'
+ + ' ' + this.modelName + '.findOneAndUpdate(conditions, update)\n'
+ + ' ' + this.modelName + '.findOneAndUpdate(update)\n'
+ + ' ' + this.modelName + '.findOneAndUpdate()\n';
+ throw new TypeError(msg);
}
update = conditions;
conditions = undefined;
}
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
var fields;
if (options && options.fields) {
fields = options.fields;
- options.fields = undefined;
}
- var query = new Query(conditions);
- query.setOptions(options);
- query.select(fields);
- query.bind(this, 'findOneAndUpdate', update);
+ update = utils.clone(update, {depopulate: 1});
+ if (this.schema.options.versionKey && options && options.upsert) {
+ if (!update.$setOnInsert) {
+ update.$setOnInsert = {};
+ }
+ update.$setOnInsert[this.schema.options.versionKey] = 0;
+ }
- if ('undefined' == typeof callback)
- return query;
+ var mq = new this.Query({}, {}, this, this.collection);
+ mq.select(fields);
- this._applyNamedScope(query);
- return query.findOneAndUpdate(callback);
-}
+ return mq.findOneAndUpdate(conditions, update, options, callback);
+};
/**
- * Issues a mongodb findAndModify update command by a documents id.
+ * Issues a mongodb findAndModify update command by a document's _id field.
+ * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`.
*
- * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
+ * Finds a matching document, updates it according to the `update` arg,
+ * passing any `options`, and returns the found document (if any) to the
+ * callback. The query executes immediately if `callback` is passed else a
+ * Query object is returned.
+ *
+ * This function triggers `findOneAndUpdate` middleware.
*
* ####Options:
*
- * - `new`: bool - true to return the modified document rather than the original. defaults to true
+ * - `new`: bool - true to return the modified document rather than the original. defaults to false
* - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
* - `select`: sets the document fields to return
*
@@ -1177,14 +1576,6 @@ Model.findOneAndUpdate = function (conditions, update, options, callback) {
* A.findByIdAndUpdate(id, update) // returns Query
* A.findByIdAndUpdate() // returns Query
*
- * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
- *
- * ####Options:
- *
- * - `new`: bool - true to return the modified document rather than the original. defaults to true
- * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
- * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
- *
* ####Note:
*
* All top level update keys which are not `atomic` operation names are treated as set operations:
@@ -1200,22 +1591,26 @@ Model.findOneAndUpdate = function (conditions, update, options, callback) {
*
* ####Note:
*
- * Although values are cast to their appropriate types when using the findAndModify helpers, the following are *not* applied:
+ * Values are cast to their appropriate types when using the findAndModify helpers.
+ * However, the below are never executed.
*
* - defaults
* - setters
- * - validators
- * - middleware
*
- * If you need those features, use the traditional approach of first retrieving the document.
+ * `findAndModify` helpers support limited defaults and validation. You can
+ * enable these by setting the `setDefaultsOnInsert` and `runValidators` options,
+ * respectively.
+ *
+ * If you need full-fledged validation, use the traditional approach of first
+ * retrieving the document.
*
* Model.findById(id, function (err, doc) {
* if (err) ..
* doc.name = 'jason borne';
* doc.save(callback);
- * })
+ * });
*
- * @param {ObjectId|HexId} id an ObjectId or string that can be cast to one.
+ * @param {Object|Number|String} id value of `_id` to query by
* @param {Object} [update]
* @param {Object} [options]
* @param {Function} [callback]
@@ -1225,24 +1620,28 @@ Model.findOneAndUpdate = function (conditions, update, options, callback) {
* @api public
*/
-Model.findByIdAndUpdate = function (id, update, options, callback) {
- var args;
-
- if (1 === arguments.length) {
- if ('function' == typeof id) {
+Model.findByIdAndUpdate = function(id, update, options, callback) {
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+ if (arguments.length === 1) {
+ if (typeof id === 'function') {
var msg = 'Model.findByIdAndUpdate(): First argument must not be a function.\n\n'
- + ' ' + this.modelName + '.findByIdAndUpdate(id, callback)\n'
- + ' ' + this.modelName + '.findByIdAndUpdate(id)\n'
- + ' ' + this.modelName + '.findByIdAndUpdate()\n';
- throw new TypeError(msg)
+ + ' ' + this.modelName + '.findByIdAndUpdate(id, callback)\n'
+ + ' ' + this.modelName + '.findByIdAndUpdate(id)\n'
+ + ' ' + this.modelName + '.findByIdAndUpdate()\n';
+ throw new TypeError(msg);
}
- return this.findOneAndUpdate({_id: id }, undefined);
+ return this.findOneAndUpdate({_id: id}, undefined);
}
- args = utils.args(arguments, 1);
- args.unshift({ _id: id });
- return this.findOneAndUpdate.apply(this, args);
-}
+ // if a model is passed in instead of an id
+ if (id instanceof Document) {
+ id = id._id;
+ }
+
+ return this.findOneAndUpdate.call(this, {_id: id}, update, options, callback);
+};
/**
* Issue a mongodb findAndModify remove command.
@@ -1254,6 +1653,7 @@ Model.findByIdAndUpdate = function (id, update, options, callback) {
* ####Options:
*
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
* - `select`: sets the document fields to return
*
* ####Examples:
@@ -1264,19 +1664,24 @@ Model.findByIdAndUpdate = function (id, update, options, callback) {
* A.findOneAndRemove(conditions) // returns Query
* A.findOneAndRemove() // returns Query
*
- * Although values are cast to their appropriate types when using the findAndModify helpers, the following are *not* applied:
+ * Values are cast to their appropriate types when using the findAndModify helpers.
+ * However, the below are never executed.
*
* - defaults
* - setters
- * - validators
- * - middleware
*
- * If you need those features, use the traditional approach of first retrieving the document.
+ * `findAndModify` helpers support limited defaults and validation. You can
+ * enable these by setting the `setDefaultsOnInsert` and `runValidators` options,
+ * respectively.
+ *
+ * If you need full-fledged validation, use the traditional approach of first
+ * retrieving the document.
*
* Model.findById(id, function (err, doc) {
* if (err) ..
- * doc.remove(callback);
- * })
+ * doc.name = 'jason borne';
+ * doc.save(callback);
+ * });
*
* @param {Object} conditions
* @param {Object} [options]
@@ -1286,19 +1691,22 @@ Model.findByIdAndUpdate = function (id, update, options, callback) {
* @api public
*/
-Model.findOneAndRemove = function (conditions, options, callback) {
- if (1 === arguments.length && 'function' == typeof conditions) {
+Model.findOneAndRemove = function(conditions, options, callback) {
+ if (arguments.length === 1 && typeof conditions === 'function') {
var msg = 'Model.findOneAndRemove(): First argument must not be a function.\n\n'
- + ' ' + this.modelName + '.findOneAndRemove(conditions, callback)\n'
- + ' ' + this.modelName + '.findOneAndRemove(conditions)\n'
- + ' ' + this.modelName + '.findOneAndRemove()\n';
- throw new TypeError(msg)
+ + ' ' + this.modelName + '.findOneAndRemove(conditions, callback)\n'
+ + ' ' + this.modelName + '.findOneAndRemove(conditions)\n'
+ + ' ' + this.modelName + '.findOneAndRemove()\n';
+ throw new TypeError(msg);
}
- if ('function' == typeof options) {
+ if (typeof options === 'function') {
callback = options;
options = undefined;
}
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
var fields;
if (options) {
@@ -1306,20 +1714,14 @@ Model.findOneAndRemove = function (conditions, options, callback) {
options.select = undefined;
}
- var query = new Query(conditions);
- query.setOptions(options);
- query.select(fields);
- query.bind(this, 'findOneAndRemove');
+ var mq = new this.Query({}, {}, this, this.collection);
+ mq.select(fields);
- if ('undefined' == typeof callback)
- return query;
-
- this._applyNamedScope(query);
- return query.findOneAndRemove(callback);
-}
+ return mq.findOneAndRemove(conditions, options, callback);
+};
/**
- * Issue a mongodb findAndModify remove command by a documents id.
+ * Issue a mongodb findAndModify remove command by a document's _id field. `findByIdAndRemove(id, ...)` is equivalent to `findOneAndRemove({ _id: id }, ...)`.
*
* Finds a matching document, removes it, passing the found document (if any) to the callback.
*
@@ -1338,7 +1740,7 @@ Model.findOneAndRemove = function (conditions, options, callback) {
* A.findByIdAndRemove(id) // returns Query
* A.findByIdAndRemove() // returns Query
*
- * @param {ObjectId|HexString} id ObjectId or string that can be cast to one
+ * @param {Object|Number|String} id value of `_id` to query by
* @param {Object} [options]
* @param {Function} [callback]
* @return {Query}
@@ -1346,79 +1748,225 @@ Model.findOneAndRemove = function (conditions, options, callback) {
* @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
*/
-Model.findByIdAndRemove = function (id, options, callback) {
- if (1 === arguments.length && 'function' == typeof id) {
+Model.findByIdAndRemove = function(id, options, callback) {
+ if (arguments.length === 1 && typeof id === 'function') {
var msg = 'Model.findByIdAndRemove(): First argument must not be a function.\n\n'
- + ' ' + this.modelName + '.findByIdAndRemove(id, callback)\n'
- + ' ' + this.modelName + '.findByIdAndRemove(id)\n'
- + ' ' + this.modelName + '.findByIdAndRemove()\n';
- throw new TypeError(msg)
+ + ' ' + this.modelName + '.findByIdAndRemove(id, callback)\n'
+ + ' ' + this.modelName + '.findByIdAndRemove(id)\n'
+ + ' ' + this.modelName + '.findByIdAndRemove()\n';
+ throw new TypeError(msg);
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
}
- return this.findOneAndRemove({ _id: id }, options, callback);
-}
+ return this.findOneAndRemove({_id: id}, options, callback);
+};
/**
- * Shortcut for creating a new Document that is automatically saved to the db if valid.
+ * Shortcut for saving one or more documents to the database.
+ * `MyModel.create(docs)` does `new MyModel(doc).save()` for every doc in
+ * docs.
+ *
+ * Hooks Triggered:
+ * - `save()`
*
* ####Example:
*
+ * // pass individual docs
* Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
* if (err) // ...
* });
*
+ * // pass an array
* var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
- * Candy.create(array, function (err, jellybean, snickers) {
+ * Candy.create(array, function (err, candies) {
* if (err) // ...
+ *
+ * var jellybean = candies[0];
+ * var snickers = candies[1];
+ * // ...
* });
*
- * @param {Array|Object...} doc
- * @param {Function} fn callback
+ * // callback is optional; use the returned promise if you like:
+ * var promise = Candy.create({ type: 'jawbreaker' });
+ * promise.then(function (jawbreaker) {
+ * // ...
+ * })
+ *
+ * @param {Array|Object|*} doc(s)
+ * @param {Function} [callback] callback
+ * @return {Promise}
* @api public
*/
-Model.create = function create (doc, fn) {
- if (1 === arguments.length) {
- return 'function' === typeof doc && doc(null);
- }
-
- var self = this
- , docs = [null]
- , promise
- , count
- , args
+Model.create = function create(doc, callback) {
+ var args;
+ var cb;
if (Array.isArray(doc)) {
args = doc;
+ cb = callback;
} else {
- args = utils.args(arguments, 0, arguments.length - 1);
- fn = arguments[arguments.length - 1];
+ var last = arguments[arguments.length - 1];
+ if (typeof last === 'function') {
+ cb = last;
+ args = utils.args(arguments, 0, arguments.length - 1);
+ } else {
+ args = utils.args(arguments);
+ }
+ }
+
+ var Promise = PromiseProvider.get();
+ var _this = this;
+ if (cb) {
+ cb = this.$wrapCallback(cb);
}
- if (0 === args.length) return fn(null);
+ var promise = new Promise.ES6(function(resolve, reject) {
+ if (args.length === 0) {
+ setImmediate(function() {
+ cb && cb(null);
+ resolve(null);
+ });
+ return;
+ }
+
+ var toExecute = [];
+ args.forEach(function(doc) {
+ toExecute.push(function(callback) {
+ var toSave = new _this(doc);
+ var callbackWrapper = function(error, doc) {
+ if (error) {
+ return callback(error);
+ }
+ callback(null, doc);
+ };
+
+ // Hack to avoid getting a promise because of
+ // $__registerHooksFromSchema
+ if (toSave.$__original_save) {
+ toSave.$__original_save({ __noPromise: true }, callbackWrapper);
+ } else {
+ toSave.save({ __noPromise: true }, callbackWrapper);
+ }
+ });
+ });
+
+ async.parallel(toExecute, function(error, savedDocs) {
+ if (error) {
+ cb && cb(error);
+ reject(error);
+ return;
+ }
+
+ if (doc instanceof Array) {
+ resolve(savedDocs);
+ cb && cb.call(_this, null, savedDocs);
+ } else {
+ resolve.apply(promise, savedDocs);
+ if (cb) {
+ savedDocs.unshift(null);
+ cb.apply(_this, savedDocs);
+ }
+ }
+ });
+ });
+
+ return promise;
+};
+
+/**
+ * Shortcut for validating an array of documents and inserting them into
+ * MongoDB if they're all valid. This function is faster than `.create()`
+ * because it only sends one operation to the server, rather than one for each
+ * document.
+ *
+ * This function does **not** trigger save middleware.
+ *
+ * ####Example:
+ *
+ * var arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
+ * Movies.insertMany(arr, function(error, docs) {});
+ *
+ * @param {Array|Object|*} doc(s)
+ * @param {Function} [callback] callback
+ * @return {Promise}
+ * @api public
+ */
+
+Model.insertMany = function(arr, callback) {
+ var _this = this;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
- promise = new Promise(fn);
- count = args.length;
+ var toExecute = [];
+ arr.forEach(function(doc) {
+ toExecute.push(function(callback) {
+ doc = new _this(doc);
+ doc.validate({ __noPromise: true }, function(error) {
+ if (error) {
+ return callback(error);
+ }
+ callback(null, doc);
+ });
+ });
+ });
- args.forEach(function (arg, i) {
- var doc = new self(arg);
- docs[i+1] = doc;
- doc.save(function (err) {
- if (err) return promise.error(err);
- --count || fn.apply(null, docs);
+ async.parallel(toExecute, function(error, docs) {
+ if (error) {
+ callback && callback(error);
+ return;
+ }
+ var docObjects = docs.map(function(doc) {
+ if (doc.initializeTimestamps) {
+ return doc.initializeTimestamps().toObject({ virtuals: false });
+ }
+ return doc.toObject({ virtuals: false });
+ });
+ _this.collection.insertMany(docObjects, function(error) {
+ if (error) {
+ callback && callback(error);
+ return;
+ }
+ for (var i = 0; i < docs.length; ++i) {
+ docs[i].isNew = false;
+ docs[i].emit('isNew', false);
+ }
+ callback && callback(null, docs);
});
});
};
+/**
+ * Shortcut for creating a new Document from existing raw data, pre-saved in the DB.
+ * The document returned has no paths marked as modified initially.
+ *
+ * ####Example:
+ *
+ * // hydrate previous data into a Mongoose document
+ * var mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c', type: 'jelly bean' });
+ *
+ * @param {Object} obj
+ * @return {Document}
+ * @api public
+ */
+
+Model.hydrate = function(obj) {
+ var model = require('./queryhelpers').createModel(this, obj);
+ model.init(obj);
+ return model;
+};
+
/**
* Updates documents in the database without returning them.
*
* ####Examples:
*
* MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
- * MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, function (err, numberAffected, raw) {
+ * MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, function (err, raw) {
* if (err) return handleError(err);
- * console.log('The number of updated documents was %d', numberAffected);
* console.log('The raw response from Mongo was ', raw);
* });
*
@@ -1427,14 +1975,16 @@ Model.create = function create (doc, fn) {
* - `safe` (boolean) safe mode (defaults to value set in schema (true))
* - `upsert` (boolean) whether to create the doc if it doesn't match (false)
* - `multi` (boolean) whether multiple documents should be updated (false)
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
* - `strict` (boolean) overrides the `strict` option for this update
+ * - `overwrite` (boolean) disables update-only mode, allowing you to overwrite the doc (false)
*
* All `update` values are cast to their appropriate SchemaTypes before being sent.
*
- * The `callback` function receives `(err, numberAffected, rawResponse)`.
+ * The `callback` function receives `(err, rawResponse)`.
*
* - `err` is the error if any occurred
- * - `numberAffected` is the count of updated documents Mongo reported
* - `rawResponse` is the full response from Mongo
*
* ####Note:
@@ -1448,6 +1998,7 @@ Model.create = function create (doc, fn) {
*
* // is sent as
* Model.update(query, { $set: { name: 'jason borne' }}, options, callback)
+ * // if overwrite option is false. If overwrite is true, sent without the $set wrapper.
*
* This helps prevent accidentally overwriting all documents in your collection with `{ name: 'jason borne' }`.
*
@@ -1478,43 +2029,37 @@ Model.create = function create (doc, fn) {
* doc.save(callback);
* })
*
- * @see strict schemas http://mongoosejs.com/docs/guide.html#strict
+ * @see strict http://mongoosejs.com/docs/guide.html#strict
+ * @see response http://docs.mongodb.org/v2.6/reference/command/update/#output
* @param {Object} conditions
- * @param {Object} update
+ * @param {Object} doc
* @param {Object} [options]
* @param {Function} [callback]
* @return {Query}
* @api public
*/
-Model.update = function update (conditions, doc, options, callback) {
- if (arguments.length < 4) {
- if ('function' === typeof options) {
- // Scenario: update(conditions, doc, callback)
- callback = options;
- options = null;
- } else if ('function' === typeof doc) {
- // Scenario: update(doc, callback);
- callback = doc;
- doc = conditions;
- conditions = {};
- options = null;
- }
+Model.update = function update(conditions, doc, options, callback) {
+ var mq = new this.Query({}, {}, this, this.collection);
+ if (callback) {
+ callback = this.$wrapCallback(callback);
}
+ // gh-2406
+ // make local deep copy of conditions
+ if (conditions instanceof Document) {
+ conditions = conditions.toObject();
+ } else {
+ conditions = utils.clone(conditions, {retainKeyOrder: true});
+ }
+ options = typeof options === 'function' ? options : utils.clone(options);
- var query = new Query(conditions, options).bind(this, 'update', doc);
-
- if ('undefined' == typeof callback)
- return query;
-
- this._applyNamedScope(query);
- return query.update(doc, callback);
+ return mq.update(conditions, doc, options, callback);
};
/**
* Executes a mapReduce command.
*
- * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation.
+ * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation. See [node-mongodb-native mapReduce() documentation](http://mongodb.github.io/node-mongodb-native/api-generated/collection.html#mapreduce) for more detail about options.
*
* ####Example:
*
@@ -1528,12 +2073,14 @@ Model.update = function update (conditions, doc, options, callback) {
* ####Other options:
*
* - `query` {Object} query filter object.
+ * - `sort` {Object} sort input objects using this key
* - `limit` {Number} max number of documents
* - `keeptemp` {Boolean, default:false} keep temporary data
* - `finalize` {Function} finalize function
* - `scope` {Object} scope variables exposed to map/reduce/finalize during execution
* - `jsMode` {Boolean, default:false} it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X
* - `verbose` {Boolean, default:false} provide statistics on job execution time.
+ * - `readPreference` {String}
* - `out*` {Object, default: {inline:1}} sets the output target for the map reduce job.
*
* ####* out options:
@@ -1552,6 +2099,7 @@ Model.update = function update (conditions, doc, options, callback) {
* o.reduce = function (k, vals) { return vals.length }
* o.out = { replace: 'createdCollectionNameForResults' }
* o.verbose = true;
+ *
* User.mapReduce(o, function (err, model, stats) {
* console.log('map reduce took %d ms', stats.processtime)
* model.find().where('value').gt(10).exec(function (err, docs) {
@@ -1559,86 +2107,355 @@ Model.update = function update (conditions, doc, options, callback) {
* });
* })
*
+ * // a promise is returned so you may instead write
+ * var promise = User.mapReduce(o);
+ * promise.then(function (model, stats) {
+ * console.log('map reduce took %d ms', stats.processtime)
+ * return model.find().where('value').gt(10).exec();
+ * }).then(function (docs) {
+ * console.log(docs);
+ * }).then(null, handleError).end()
+ *
* @param {Object} o an object specifying map-reduce options
- * @param {Function} callback
+ * @param {Function} [callback] optional callback
* @see http://www.mongodb.org/display/DOCS/MapReduce
+ * @return {Promise}
* @api public
*/
-Model.mapReduce = function mapReduce (o, callback) {
- if ('function' != typeof callback) throw new Error('missing callback');
+Model.mapReduce = function mapReduce(o, callback) {
+ var _this = this;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve, reject) {
+ if (!Model.mapReduce.schema) {
+ var opts = {noId: true, noVirtualId: true, strict: false};
+ Model.mapReduce.schema = new Schema({}, opts);
+ }
- var self = this;
+ if (!o.out) o.out = {inline: 1};
+ if (o.verbose !== false) o.verbose = true;
- if (!Model.mapReduce.schema) {
- var opts = { noId: true, noVirtualId: true, strict: false }
- Model.mapReduce.schema = new Schema({}, opts);
- }
+ o.map = String(o.map);
+ o.reduce = String(o.reduce);
+
+ if (o.query) {
+ var q = new _this.Query(o.query);
+ q.cast(_this);
+ o.query = q._conditions;
+ q = undefined;
+ }
+
+ _this.collection.mapReduce(null, null, o, function(err, ret, stats) {
+ if (err) {
+ callback && callback(err);
+ reject(err);
+ return;
+ }
+
+ if (ret.findOne && ret.mapReduce) {
+ // returned a collection, convert to Model
+ var model = Model.compile(
+ '_mapreduce_' + ret.collectionName
+ , Model.mapReduce.schema
+ , ret.collectionName
+ , _this.db
+ , _this.base);
+
+ model._mapreduce = true;
+
+ callback && callback(null, model, stats);
+ return resolve(model, stats);
+ }
- if (!o.out) o.out = { inline: 1 };
+ callback && callback(null, ret, stats);
+ resolve(ret, stats);
+ });
+ });
+};
+
+/**
+ * geoNear support for Mongoose
+ *
+ * ####Options:
+ * - `lean` {Boolean} return the raw object
+ * - All options supported by the driver are also supported
+ *
+ * ####Example:
+ *
+ * // Legacy point
+ * Model.geoNear([1,3], { maxDistance : 5, spherical : true }, function(err, results, stats) {
+ * console.log(results);
+ * });
+ *
+ * // geoJson
+ * var point = { type : "Point", coordinates : [9,9] };
+ * Model.geoNear(point, { maxDistance : 5, spherical : true }, function(err, results, stats) {
+ * console.log(results);
+ * });
+ *
+ * @param {Object|Array} GeoJSON point or legacy coordinate pair [x,y] to search near
+ * @param {Object} options for the qurery
+ * @param {Function} [callback] optional callback for the query
+ * @return {Promise}
+ * @see http://docs.mongodb.org/manual/core/2dsphere/
+ * @see http://mongodb.github.io/node-mongodb-native/api-generated/collection.html?highlight=geonear#geoNear
+ * @api public
+ */
- o.map = String(o.map);
- o.reduce = String(o.reduce);
+Model.geoNear = function(near, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
- if (o.query) {
- var q = new Query(o.query);
- q.cast(this);
- o.query = q._conditions;
- q = undefined;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
}
- this.collection.mapReduce(null, null, o, function (err, ret, stats) {
- if (err) return callback(err);
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ if (!near) {
+ return new Promise.ES6(function(resolve, reject) {
+ var error = new Error('Must pass a near option to geoNear');
+ reject(error);
+ callback && callback(error);
+ });
+ }
+
+ var x, y;
+
+ return new Promise.ES6(function(resolve, reject) {
+ var handler = function(err, res) {
+ if (err) {
+ reject(err);
+ callback && callback(err);
+ return;
+ }
+ if (options.lean) {
+ resolve(res.results, res.stats);
+ callback && callback(null, res.results, res.stats);
+ return;
+ }
- if (ret.findOne && ret.mapReduce) {
- // returned a collection, convert to Model
- var model = Model.compile(
- '_mapreduce_' + ret.collectionName
- , Model.mapReduce.schema
- , ret.collectionName
- , self.db
- , self.base);
+ var count = res.results.length;
+ // if there are no results, fulfill the promise now
+ if (count === 0) {
+ resolve(res.results, res.stats);
+ callback && callback(null, res.results, res.stats);
+ return;
+ }
- model._mapreduce = true;
+ var errSeen = false;
- return callback(err, model, stats);
- }
+ function init(err) {
+ if (err && !errSeen) {
+ errSeen = true;
+ reject(err);
+ callback && callback(err);
+ return;
+ }
+ if (--count <= 0) {
+ resolve(res.results, res.stats);
+ callback && callback(null, res.results, res.stats);
+ }
+ }
+
+ for (var i = 0; i < res.results.length; i++) {
+ var temp = res.results[i].obj;
+ res.results[i].obj = new _this();
+ res.results[i].obj.init(temp, init);
+ }
+ };
+
+ if (Array.isArray(near)) {
+ if (near.length !== 2) {
+ var error = new Error('If using legacy coordinates, must be an array ' +
+ 'of size 2 for geoNear');
+ reject(error);
+ callback && callback(error);
+ return;
+ }
+ x = near[0];
+ y = near[1];
+ _this.collection.geoNear(x, y, options, handler);
+ } else {
+ if (near.type !== 'Point' || !Array.isArray(near.coordinates)) {
+ error = new Error('Must pass either a legacy coordinate array or ' +
+ 'GeoJSON Point to geoNear');
+ reject(error);
+ callback && callback(error);
+ return;
+ }
- callback(err, ret, stats);
+ _this.collection.geoNear(near, options, handler);
+ }
});
-}
+};
/**
- * Executes an aggregate command on this models collection.
+ * Performs [aggregations](http://docs.mongodb.org/manual/applications/aggregation/) on the models collection.
+ *
+ * If a `callback` is passed, the `aggregate` is executed and a `Promise` is returned. If a callback is not passed, the `aggregate` itself is returned.
*
* ####Example:
*
- * // find the max age of all users
+ * // Find the max balance of all accounts
* Users.aggregate(
- * { $group: { _id: null, maxAge: { $max: '$age' }}}
- * , { $project: { _id: 0, maxAge: 1 }}
+ * { $group: { _id: null, maxBalance: { $max: '$balance' }}}
+ * , { $project: { _id: 0, maxBalance: 1 }}
* , function (err, res) {
* if (err) return handleError(err);
- * console.log(res); // [ { maxAge: 98 } ]
+ * console.log(res); // [ { maxBalance: 98000 } ]
+ * });
+ *
+ * // Or use the aggregation pipeline builder.
+ * Users.aggregate()
+ * .group({ _id: null, maxBalance: { $max: '$balance' } })
+ * .select('-id maxBalance')
+ * .exec(function (err, res) {
+ * if (err) return handleError(err);
+ * console.log(res); // [ { maxBalance: 98 } ]
* });
*
* ####NOTE:
*
- * - At this time, arguments are not cast to the schema because $project operators allow redefining the "shape" of the documents at any stage of the pipeline.
- * - The documents returned are plain javascript objects, not mongoose documents cast to this models schema definition (since any shape of document can be returned).
+ * - Arguments are not cast to the model's schema because `$project` operators allow redefining the "shape" of the documents at any stage of the pipeline, which may leave documents in an incompatible format.
+ * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
* - Requires MongoDB >= 2.1
*
- * @param {Array} array an array of pipeline commands
- * @param {Object} [options]
- * @param {Function} callback
- * @see aggregation http://docs.mongodb.org/manual/applications/aggregation/
- * @see driver http://mongodb.github.com/node-mongodb-native/api-generated/collection.html#aggregate
+ * @see Aggregate #aggregate_Aggregate
+ * @see MongoDB http://docs.mongodb.org/manual/applications/aggregation/
+ * @param {Object|Array} [...] aggregation pipeline operator(s) or operator array
+ * @param {Function} [callback]
+ * @return {Aggregate|Promise}
* @api public
*/
-Model.aggregate = function aggregate () {
- return this.collection.aggregate.apply(this.collection, arguments);
-}
+Model.aggregate = function aggregate() {
+ var args = [].slice.call(arguments),
+ aggregate,
+ callback;
+
+ if (typeof args[args.length - 1] === 'function') {
+ callback = args.pop();
+ }
+
+ if (args.length === 1 && util.isArray(args[0])) {
+ aggregate = new Aggregate(args[0]);
+ } else {
+ aggregate = new Aggregate(args);
+ }
+
+ aggregate.model(this);
+
+ if (typeof callback === 'undefined') {
+ return aggregate;
+ }
+
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ aggregate.exec(callback);
+};
+
+/**
+ * Implements `$geoSearch` functionality for Mongoose
+ *
+ * ####Example:
+ *
+ * var options = { near: [10, 10], maxDistance: 5 };
+ * Locations.geoSearch({ type : "house" }, options, function(err, res) {
+ * console.log(res);
+ * });
+ *
+ * ####Options:
+ * - `near` {Array} x,y point to search for
+ * - `maxDistance` {Number} the maximum distance from the point near that a result can be
+ * - `limit` {Number} The maximum number of results to return
+ * - `lean` {Boolean} return the raw object instead of the Mongoose Model
+ *
+ * @param {Object} conditions an object that specifies the match condition (required)
+ * @param {Object} options for the geoSearch, some (near, maxDistance) are required
+ * @param {Function} [callback] optional callback
+ * @return {Promise}
+ * @see http://docs.mongodb.org/manual/reference/command/geoSearch/
+ * @see http://docs.mongodb.org/manual/core/geohaystack/
+ * @api public
+ */
+
+Model.geoSearch = function(conditions, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve, reject) {
+ var error;
+ if (conditions === undefined || !utils.isObject(conditions)) {
+ error = new Error('Must pass conditions to geoSearch');
+ } else if (!options.near) {
+ error = new Error('Must specify the near option in geoSearch');
+ } else if (!Array.isArray(options.near)) {
+ error = new Error('near option must be an array [x, y]');
+ }
+
+ if (error) {
+ callback && callback(error);
+ reject(error);
+ return;
+ }
+
+ // send the conditions in the options object
+ options.search = conditions;
+
+ _this.collection.geoHaystackSearch(options.near[0], options.near[1], options, function(err, res) {
+ // have to deal with driver problem. Should be fixed in a soon-ish release
+ // (7/8/2013)
+ if (err) {
+ callback && callback(err);
+ reject(err);
+ return;
+ }
+
+ var count = res.results.length;
+ if (options.lean || count === 0) {
+ callback && callback(null, res.results, res.stats);
+ resolve(res.results, res.stats);
+ return;
+ }
+
+ var errSeen = false;
+
+ function init(err) {
+ if (err && !errSeen) {
+ callback && callback(err);
+ reject(err);
+ return;
+ }
+
+ if (!--count && !errSeen) {
+ callback && callback(null, res.results, res.stats);
+ resolve(res.results, res.stats);
+ }
+ }
+
+ for (var i = 0; i < res.results.length; i++) {
+ var temp = res.results[i];
+ res.results[i] = new _this();
+ res.results[i].init(temp, {}, init);
+ }
+ });
+ });
+};
/**
* Populates document references.
@@ -1669,9 +2486,8 @@ Model.aggregate = function aggregate () {
* User.find(match, function (err, users) {
* var opts = [{ path: 'company', match: { x: 1 }, select: 'name' }]
*
- * User.populate(users, opts, function (err, user) {
- * console.log(user);
- * })
+ * var promise = User.populate(users, opts);
+ * promise.then(console.log).end();
* })
*
* // imagine a Weapon model exists with two saved documents:
@@ -1698,26 +2514,57 @@ Model.aggregate = function aggregate () {
*
* @param {Document|Array} docs Either a single document or array of documents to populate.
* @param {Object} options A hash of key/val (path, options) used for population.
- * @param {Function} cb(err,doc) A callback, executed upon completion. Receives `err` and the `doc(s)`.
+ * @param {Function} [callback(err,doc)] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
+ * @return {Promise}
* @api public
*/
-Model.populate = function (docs, paths, cb) {
- assert.equal('function', typeof cb);
+Model.populate = function(docs, paths, callback) {
+ var _this = this;
+ if (callback) {
+ callback = this.$wrapCallback(callback);
+ }
+
+ // normalized paths
+ var noPromise = paths && !!paths.__noPromise;
+ paths = utils.populate(paths);
+
+ // data that should persist across subPopulate calls
+ var cache = {};
- // always callback on nextTick for consistent async behavior
- function callback () {
- var args = utils.args(arguments);
- process.nextTick(function () {
- cb.apply(null, args);
+ if (noPromise) {
+ _populate(this, docs, paths, cache, callback);
+ } else {
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve, reject) {
+ _populate(_this, docs, paths, cache, function(error, docs) {
+ if (error) {
+ callback && callback(error);
+ reject(error);
+ } else {
+ callback && callback(null, docs);
+ resolve(docs);
+ }
+ });
});
}
+};
- // normalized paths
- var paths = utils.populate(paths);
+/*!
+ * Populate helper
+ *
+ * @param {Model} model the model to use
+ * @param {Document|Array} docs Either a single document or array of documents to populate.
+ * @param {Object} paths
+ * @param {Function} [cb(err,doc)] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
+ * @return {Function}
+ * @api private
+ */
+
+function _populate(model, docs, paths, cache, callback) {
var pending = paths.length;
- if (0 === pending) {
+ if (pending === 0) {
return callback(null, docs);
}
@@ -1726,207 +2573,185 @@ Model.populate = function (docs, paths, cb) {
var path;
while (i--) {
path = paths[i];
- populate(this, docs, path, next);
+ populate(model, docs, path, next);
}
- function next (err) {
- if (next.err) return;
- if (err) return callback(next.err = err);
- if (--pending) return;
+ function next(err) {
+ if (err) {
+ return callback(err);
+ }
+ if (--pending) {
+ return;
+ }
callback(null, docs);
}
}
/*!
- * Prepare population options
+ * Populates `docs`
*/
+var excludeIdReg = /\s?-_id\s?/,
+ excludeIdRegGlobal = /\s?-_id\s?/g;
-function populate (model, docs, options, cb) {
- var path = options.path
- var schema = model._getSchema(path);
-
- // handle document arrays
- if (schema && schema.caster) {
- schema = schema.caster;
- }
-
- // model name for the populate query
- var modelName = options.model && options.model.modelName
- || options.model // query options
- || schema && schema.options.ref // declared in schema
- || model.modelName // an ad-hoc structure
-
- var Model = model.db.model(modelName);
-
- // expose the model used
- options.model = Model;
+function populate(model, docs, options, callback) {
+ var modelsMap;
// normalize single / multiple docs passed
if (!Array.isArray(docs)) {
docs = [docs];
}
- if (0 === docs.length || docs.every(utils.isNullOrUndefined)) {
- return cb();
+ if (docs.length === 0 || docs.every(utils.isNullOrUndefined)) {
+ return callback();
}
- populateDocs(docs, options, cb);
-}
-
-/*!
- * Populates `docs`
- */
+ modelsMap = getModelsMapForPopulate(model, docs, options);
-function populateDocs (docs, options, cb) {
- var select = options.select;
- var match = options.match;
- var path = options.path;
- var Model = options.model;
+ var i, len = modelsMap.length,
+ mod, match, select, vals = [];
- var rawIds = [];
- var i, doc, id;
- var len = docs.length;
- var found = 0;
- var isDocument;
- var subpath;
- var ret;
+ function flatten(item) {
+ // no need to include undefined values in our query
+ return undefined !== item;
+ }
+ var _remaining = len;
+ var hasOne = false;
for (i = 0; i < len; i++) {
- ret = undefined;
- doc = docs[i];
- id = String(utils.getValue("_id", doc));
- isDocument = !! doc.$__;
+ mod = modelsMap[i];
+ select = mod.options.select;
- if (isDocument && !doc.isModified(path)) {
- // it is possible a previously populated path is being
- // populated again. Because users can specify matcher
- // clauses in their populate arguments we use the cached
- // _ids from the original populate call to ensure all _ids
- // are looked up, but only if the path wasn't modified which
- // signifies the users intent of the state of the path.
- ret = doc.populated(path);
+ if (mod.options.match) {
+ match = utils.object.shallowCopy(mod.options.match);
+ } else {
+ match = {};
}
- if (!ret || Array.isArray(ret) && 0 === ret.length) {
- ret = utils.getValue(path, doc);
- }
+ var ids = utils.array.flatten(mod.ids, flatten);
+ ids = utils.array.unique(ids);
- if (ret) {
- ret = convertTo_id(ret);
+ if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
+ --_remaining;
+ continue;
+ }
- // previously we always assigned this even if the document had no _id
- options._docs[id] = Array.isArray(ret)
- ? ret.slice()
- : ret;
+ hasOne = true;
+ if (mod.foreignField !== '_id' || !match['_id']) {
+ match[mod.foreignField] = { $in: ids };
}
- // always retain original values, even empty values. these are
- // used to map the query results back to the correct position.
- rawIds.push(ret);
+ var assignmentOpts = {};
+ assignmentOpts.sort = mod.options.options && mod.options.options.sort || undefined;
+ assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0);
- if (isDocument) {
- // cache original populated _ids and model used
- doc.populated(path, options._docs[id], options);
+ if (assignmentOpts.excludeId) {
+ // override the exclusion from the query so we can use the _id
+ // for document matching during assignment. we'll delete the
+ // _id back off before returning the result.
+ if (typeof select === 'string') {
+ select = select.replace(excludeIdRegGlobal, ' ');
+ } else {
+ // preserve original select conditions by copying
+ select = utils.object.shallowCopy(select);
+ delete select._id;
+ }
}
- }
- var ids = utils.array.flatten(rawIds, function (item) {
- // no need to include undefined values in our query
- return undefined !== item;
- });
+ if (mod.options.options && mod.options.options.limit) {
+ assignmentOpts.originalLimit = mod.options.options.limit;
+ mod.options.options.limit = mod.options.options.limit * ids.length;
+ }
- if (0 === ids.length || ids.every(utils.isNullOrUndefined)) {
- return cb();
+ var subPopulate = mod.options.populate;
+ var query = mod.Model.find(match, select, mod.options.options);
+ if (subPopulate) {
+ query.populate(subPopulate);
+ }
+ query.exec(next.bind(this, mod, assignmentOpts));
}
- // preserve original match conditions by copying
- if (match) {
- match = utils.object.shallowCopy(match);
- } else {
- match = {};
+ if (!hasOne) {
+ return callback();
}
- match._id || (match._id = { $in: ids });
-
- var assignmentOpts = {};
- assignmentOpts.sort = options.options && options.options.sort || undefined;
- assignmentOpts.excludeId = /\s?-_id\s?/.test(select) || (select && 0 === select._id);
-
- if (assignmentOpts.excludeId) {
- // override the exclusion from the query so we can use the _id
- // for document matching during assignment. we'll delete the
- // _id back off before returning the result.
- if ('string' == typeof select) {
- select = select.replace(/\s?-_id\s?/g, ' ');
- } else {
- // preserve original select conditions by copying
- select = utils.object.shallowCopy(select);
- delete select._id;
+ function next(options, assignmentOpts, err, valsFromDb) {
+ if (err) return callback(err);
+ vals = vals.concat(valsFromDb);
+ _assign(null, vals, options, assignmentOpts);
+ if (--_remaining === 0) {
+ callback();
}
}
- // if a limit option is passed, we should have the limit apply to *each*
- // document, not apply in the aggregate
- if (options.options && options.options.limit) {
- options.options.limit = options.options.limit * len;
- }
-
- Model.find(match, select, options.options, function (err, vals) {
- if (err) return cb(err);
+ function _assign(err, vals, mod, assignmentOpts) {
+ if (err) return callback(err);
- var lean = options.options && options.options.lean;
- var len = vals.length;
- var rawOrder = {};
- var rawDocs = {}
- var key;
- var val;
+ var options = mod.options;
+ var _val;
+ var lean = options.options && options.options.lean,
+ len = vals.length,
+ rawOrder = {}, rawDocs = {}, key, val;
// optimization:
// record the document positions as returned by
// the query result.
for (var i = 0; i < len; i++) {
val = vals[i];
- key = String(utils.getValue('_id', val));
- rawDocs[key] = val;
- rawOrder[key] = i;
-
- // flag each as result of population
- if (!lean) val.$__.wasPopulated = true;
+ if (val) {
+ _val = utils.getValue(mod.foreignField, val);
+ if (Array.isArray(_val)) {
+ var _valLength = _val.length;
+ for (var j = 0; j < _valLength; ++j) {
+ key = String(_val[j]);
+ if (rawDocs[key]) {
+ if (Array.isArray(rawDocs[key])) {
+ rawDocs[key].push(val);
+ rawOrder[key].push(i);
+ } else {
+ rawDocs[key] = [rawDocs[key], val];
+ rawOrder[key] = [rawOrder[key], i];
+ }
+ } else {
+ rawDocs[key] = val;
+ rawOrder[key] = i;
+ }
+ }
+ } else {
+ key = String(utils.getValue(mod.foreignField, val));
+ if (rawDocs[key]) {
+ if (Array.isArray(rawDocs[key])) {
+ rawDocs[key].push(val);
+ rawOrder[key].push(i);
+ } else {
+ rawDocs[key] = [rawDocs[key], val];
+ rawOrder[key] = [rawOrder[key], i];
+ }
+ } else {
+ rawDocs[key] = val;
+ rawOrder[key] = i;
+ }
+ }
+ // flag each as result of population
+ if (!lean) {
+ val.$__.wasPopulated = true;
+ }
+ }
}
assignVals({
- rawIds: rawIds,
+ originalModel: model,
+ rawIds: mod.ids,
+ localField: mod.localField,
+ foreignField: mod.foreignField,
rawDocs: rawDocs,
rawOrder: rawOrder,
- docs: docs,
- path: path,
- options: assignmentOpts
+ docs: mod.docs,
+ path: options.path,
+ options: assignmentOpts,
+ justOne: mod.justOne,
+ isVirtual: mod.isVirtual
});
-
- cb();
- });
-}
-
-/*!
- * Retrieve the _id of `val` if a Document or Array of Documents.
- *
- * @param {Array|Document|Any} val
- * @return {Array|Document|Any}
- */
-
-function convertTo_id (val) {
- if (val instanceof Model) return val._id;
-
- if (Array.isArray(val)) {
- for (var i = 0; i < val.length; ++i) {
- if (val[i] instanceof Model) {
- val[i] = val[i]._id;
- }
- }
- return val;
}
-
- return val;
}
/*!
@@ -1934,107 +2759,36 @@ function convertTo_id (val) {
* to the original document path.
*/
-function assignVals (o) {
+function assignVals(o) {
// replace the original ids in our intermediate _ids structure
// with the documents found by query
-
- assignRawDocsToIdStructure(o.rawIds, o.rawDocs, o.rawOrder, o.options);
+ assignRawDocsToIdStructure(o.rawIds, o.rawDocs, o.rawOrder, o.options,
+ o.localField, o.foreignField);
// now update the original documents being populated using the
// result structure that contains real documents.
var docs = o.docs;
- var path = o.path;
var rawIds = o.rawIds;
var options = o.options;
- for (var i = 0; i < docs.length; ++i) {
- utils.setValue(path, rawIds[i], docs[i], function (val) {
- return valueFilter(val, options);
- });
+ function setValue(val) {
+ return valueFilter(val, options);
}
-}
-/*!
- * 1) Apply backwards compatible find/findOne behavior to sub documents
- *
- * find logic:
- * a) filter out non-documents
- * b) remove _id from sub docs when user specified
- *
- * findOne
- * a) if no doc found, set to null
- * b) remove _id from sub docs when user specified
- *
- * 2) Remove _ids when specified by users query.
- *
- * background:
- * _ids are left in the query even when user excludes them so
- * that population mapping can occur.
- */
-
-function valueFilter (val, assignmentOpts) {
- if (Array.isArray(val)) {
- // find logic
- var ret = [];
- for (var i = 0; i < val.length; ++i) {
- var subdoc = val[i];
- if (!isDoc(subdoc)) continue;
- maybeRemoveId(subdoc, assignmentOpts);
- ret.push(subdoc);
+ for (var i = 0; i < docs.length; ++i) {
+ if (utils.getValue(o.path, docs[i]) == null &&
+ !o.originalModel.schema.virtuals[o.path]) {
+ continue;
}
- return ret;
- }
-
- // findOne
- if (isDoc(val)) {
- maybeRemoveId(val, assignmentOpts);
- return val;
- }
-
- return null;
-}
-
-/*!
- * Remove _id from `subdoc` if user specified "lean" query option
- */
-function maybeRemoveId (subdoc, assignmentOpts) {
- if (assignmentOpts.excludeId) {
- if ('function' == typeof subdoc.setValue) {
- subdoc.setValue('_id', undefined);
- } else {
- delete subdoc._id;
+ if (o.isVirtual && !o.justOne && !Array.isArray(rawIds[i])) {
+ rawIds[i] = [rawIds[i]];
}
+ utils.setValue(o.path, rawIds[i], docs[i], setValue);
}
}
-/*!
- * Determine if `doc` is a document returned
- * by a populate query.
- */
-
-function isDoc (doc) {
- if (null == doc)
- return false;
-
- var type = typeof doc;
- if ('string' == type)
- return false;
-
- if ('number' == type)
- return false;
-
- if (Buffer.isBuffer(doc))
- return false;
-
- if ('ObjectID' == doc.constructor.name)
- return false;
-
- // only docs
- return true;
-}
-
/*!
* Assign `vals` returned by mongo query to the `rawIds`
* structure returned from utils.getVals() honoring
@@ -2055,11 +2809,10 @@ function isDoc (doc) {
* @api private
*/
-function assignRawDocsToIdStructure (rawIds, resultDocs, resultOrder, options, recursed) {
+function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, localFields, foreignFields, recursed) {
// honor user specified sort order
var newOrder = [];
var sorting = options.sort && rawIds.length > 1;
- var found;
var doc;
var sid;
var id;
@@ -2069,12 +2822,12 @@ function assignRawDocsToIdStructure (rawIds, resultDocs, resultOrder, options, r
if (Array.isArray(id)) {
// handle [ [id0, id2], [id3] ]
- assignRawDocsToIdStructure(id, resultDocs, resultOrder, options, true);
+ assignRawDocsToIdStructure(id, resultDocs, resultOrder, options, localFields, foreignFields, true);
newOrder.push(id);
continue;
}
- if (null === id && !sorting) {
+ if (id === null && !sorting) {
// keep nulls for findOne unless sorting, which always
// removes them (backward compat)
newOrder.push(id);
@@ -2082,7 +2835,6 @@ function assignRawDocsToIdStructure (rawIds, resultDocs, resultOrder, options, r
}
sid = String(id);
- found = false;
if (recursed) {
// apply find behavior
@@ -2111,68 +2863,278 @@ function assignRawDocsToIdStructure (rawIds, resultDocs, resultOrder, options, r
// forEach skips over sparse entries in arrays so we
// can safely use this to our advantage dealing with sorted
// result sets too.
- newOrder.forEach(function (doc, i) {
+ newOrder.forEach(function(doc, i) {
+ if (!doc) {
+ return;
+ }
rawIds[i] = doc;
});
}
}
-/**
- * Finds the schema for `path`. This is different than
- * calling `schema.path` as it also resolves paths with
- * positional selectors (something.$.another.$.path).
- *
- * @param {String} path
- * @return {Schema}
- * @api private
- */
+function getModelsMapForPopulate(model, docs, options) {
+ var i, doc, len = docs.length,
+ available = {},
+ map = [],
+ modelNameFromQuery = options.model && options.model.modelName || options.model,
+ schema, refPath, Model, currentOptions, modelNames, modelName, discriminatorKey, modelForFindSchema;
-Model._getSchema = function _getSchema (path) {
- var schema = this.schema
- , pathschema = schema.path(path);
+ var originalOptions = utils.clone(options);
+ var isVirtual = false;
- if (pathschema)
- return pathschema;
+ schema = model._getSchema(options.path);
- // look for arrays
- return (function search (parts, schema) {
- var p = parts.length + 1
- , foundschema
- , trypath
+ if (schema && schema.caster) {
+ schema = schema.caster;
+ }
- while (p--) {
- trypath = parts.slice(0, p).join('.');
- foundschema = schema.path(trypath);
- if (foundschema) {
- if (foundschema.caster) {
+ if (!schema && model.discriminators) {
+ discriminatorKey = model.schema.discriminatorMapping.key;
+ }
- // array of Mixed?
- if (foundschema.caster instanceof Types.Mixed) {
- return foundschema.caster;
- }
+ refPath = schema && schema.options && schema.options.refPath;
- // Now that we found the array, we need to check if there
- // are remaining document paths to look up for casting.
- // Also we need to handle array.$.path since schema.path
- // doesn't work for that.
- // If there is no foundschema.schema we are dealing with
- // a path like array.$
- if (p !== parts.length && foundschema.schema) {
- if ('$' === parts[p]) {
- // comments.$.comments.$.title
- return search(parts.slice(p+1), foundschema.schema);
- } else {
- // this is the last path of the selector
- return search(parts.slice(p), foundschema.schema);
+ for (i = 0; i < len; i++) {
+ doc = docs[i];
+
+ if (refPath) {
+ modelNames = utils.getValue(refPath, doc);
+ } else {
+ if (!modelNameFromQuery) {
+ var modelForCurrentDoc = model;
+ var schemaForCurrentDoc;
+
+ if (!schema && discriminatorKey) {
+ modelForFindSchema = utils.getValue(discriminatorKey, doc);
+
+ if (modelForFindSchema) {
+ modelForCurrentDoc = model.db.model(modelForFindSchema);
+ schemaForCurrentDoc = modelForCurrentDoc._getSchema(options.path);
+
+ if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
+ schemaForCurrentDoc = schemaForCurrentDoc.caster;
}
}
+ } else {
+ schemaForCurrentDoc = schema;
+ }
+ var virtual = modelForCurrentDoc.schema.virtuals[options.path];
+
+ if (schemaForCurrentDoc && schemaForCurrentDoc.options && schemaForCurrentDoc.options.ref) {
+ modelNames = [schemaForCurrentDoc.options.ref];
+ } else if (virtual && virtual.options && virtual.options.ref) {
+ modelNames = [virtual && virtual.options && virtual.options.ref];
+ isVirtual = true;
+ } else {
+ modelNames = [model.modelName];
}
- return foundschema;
+ } else {
+ modelNames = [modelNameFromQuery]; // query options
+ }
+ }
+
+ if (!modelNames) {
+ continue;
+ }
+
+ if (!Array.isArray(modelNames)) {
+ modelNames = [modelNames];
+ }
+
+ virtual = model.schema.virtuals[options.path];
+ var localField = virtual && virtual.options ?
+ virtual.options.localField :
+ options.path;
+ var foreignField = virtual && virtual.options ?
+ virtual.options.foreignField :
+ '_id';
+ var justOne = virtual && virtual.options && virtual.options.justOne;
+ if (virtual && virtual.options && virtual.options.ref) {
+ isVirtual = true;
+ }
+
+ var ret = convertTo_id(utils.getValue(localField, doc));
+ var id = String(utils.getValue(foreignField, doc));
+ options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
+ if (doc.$__) {
+ doc.populated(options.path, options._docs[id], options);
+ }
+
+ var k = modelNames.length;
+ while (k--) {
+ modelName = modelNames[k];
+ Model = originalOptions.model && originalOptions.model.modelName ?
+ originalOptions.model :
+ model.db.model(modelName);
+
+ if (!available[modelName]) {
+ currentOptions = {
+ model: Model
+ };
+
+ utils.merge(currentOptions, options);
+ if (schema && !discriminatorKey) {
+ currentOptions.model = Model;
+ }
+ options.model = Model;
+
+ available[modelName] = {
+ Model: Model,
+ options: currentOptions,
+ docs: [doc],
+ ids: [ret],
+ // Assume only 1 localField + foreignField
+ localField: localField,
+ foreignField: foreignField,
+ justOne: justOne,
+ isVirtual: isVirtual
+ };
+ map.push(available[modelName]);
+ } else {
+ available[modelName].docs.push(doc);
+ available[modelName].ids.push(ret);
+ }
+ }
+ }
+
+ return map;
+}
+
+/*!
+ * Retrieve the _id of `val` if a Document or Array of Documents.
+ *
+ * @param {Array|Document|Any} val
+ * @return {Array|Document|Any}
+ */
+
+function convertTo_id(val) {
+ if (val instanceof Model) return val._id;
+
+ if (Array.isArray(val)) {
+ for (var i = 0; i < val.length; ++i) {
+ if (val[i] instanceof Model) {
+ val[i] = val[i]._id;
}
}
- })(path.split('.'), schema)
+ return val;
+ }
+
+ return val;
}
+/*!
+ * 1) Apply backwards compatible find/findOne behavior to sub documents
+ *
+ * find logic:
+ * a) filter out non-documents
+ * b) remove _id from sub docs when user specified
+ *
+ * findOne
+ * a) if no doc found, set to null
+ * b) remove _id from sub docs when user specified
+ *
+ * 2) Remove _ids when specified by users query.
+ *
+ * background:
+ * _ids are left in the query even when user excludes them so
+ * that population mapping can occur.
+ */
+
+function valueFilter(val, assignmentOpts) {
+ if (Array.isArray(val)) {
+ // find logic
+ var ret = [];
+ var numValues = val.length;
+ for (var i = 0; i < numValues; ++i) {
+ var subdoc = val[i];
+ if (!isDoc(subdoc)) continue;
+ maybeRemoveId(subdoc, assignmentOpts);
+ ret.push(subdoc);
+ if (assignmentOpts.originalLimit &&
+ ret.length >= assignmentOpts.originalLimit) {
+ break;
+ }
+ }
+
+ // Since we don't want to have to create a new mongoosearray, make sure to
+ // modify the array in place
+ while (val.length > ret.length) {
+ Array.prototype.pop.apply(val, []);
+ }
+ for (i = 0; i < ret.length; ++i) {
+ val[i] = ret[i];
+ }
+ return val;
+ }
+
+ // findOne
+ if (isDoc(val)) {
+ maybeRemoveId(val, assignmentOpts);
+ return val;
+ }
+
+ return null;
+}
+
+/*!
+ * Remove _id from `subdoc` if user specified "lean" query option
+ */
+
+function maybeRemoveId(subdoc, assignmentOpts) {
+ if (assignmentOpts.excludeId) {
+ if (typeof subdoc.setValue === 'function') {
+ delete subdoc._doc._id;
+ } else {
+ delete subdoc._id;
+ }
+ }
+}
+
+/*!
+ * Determine if `doc` is a document returned
+ * by a populate query.
+ */
+
+function isDoc(doc) {
+ if (doc == null) {
+ return false;
+ }
+
+ var type = typeof doc;
+ if (type === 'string') {
+ return false;
+ }
+
+ if (type === 'number') {
+ return false;
+ }
+
+ if (Buffer.isBuffer(doc)) {
+ return false;
+ }
+
+ if (doc.constructor.name === 'ObjectID') {
+ return false;
+ }
+
+ // only docs
+ return true;
+}
+
+/**
+ * Finds the schema for `path`. This is different than
+ * calling `schema.path` as it also resolves paths with
+ * positional selectors (something.$.another.$.path).
+ *
+ * @param {String} path
+ * @return {Schema}
+ * @api private
+ */
+
+Model._getSchema = function _getSchema(path) {
+ return this.schema._getSchema(path);
+};
+
/*!
* Compiler utility.
*
@@ -2183,8 +3145,8 @@ Model._getSchema = function _getSchema (path) {
* @param {Mongoose} base mongoose instance
*/
-Model.compile = function compile (name, schema, collectionName, connection, base) {
- var versioningEnabled = false !== schema.options.versionKey;
+Model.compile = function compile(name, schema, collectionName, connection, base) {
+ var versioningEnabled = schema.options.versionKey !== false;
if (versioningEnabled && !schema.paths[schema.options.versionKey]) {
// add versioning to top level documents only
@@ -2194,49 +3156,121 @@ Model.compile = function compile (name, schema, collectionName, connection, base
}
// generate new class
- function model (doc, fields, skipId) {
- if (!(this instanceof model))
+ function model(doc, fields, skipId) {
+ if (!(this instanceof model)) {
return new model(doc, fields, skipId);
+ }
Model.call(this, doc, fields, skipId);
- };
+ }
+ model.hooks = schema.s.hooks.clone();
model.base = base;
model.modelName = name;
model.__proto__ = Model;
model.prototype.__proto__ = Model.prototype;
model.model = Model.prototype.model;
model.db = model.prototype.db = connection;
+ model.discriminators = model.prototype.discriminators = undefined;
model.prototype.$__setSchema(schema);
var collectionOptions = {
- bufferCommands: schema.options.bufferCommands
- , capped: schema.options.capped
+ bufferCommands: schema.options.bufferCommands,
+ capped: schema.options.capped
};
model.prototype.collection = connection.collection(
collectionName
- , collectionOptions
+ , collectionOptions
);
- // apply methods
- for (var i in schema.methods)
- model.prototype[i] = schema.methods[i];
-
- // apply statics
- for (var i in schema.statics)
- model[i] = schema.statics[i];
-
- // apply named scopes
- if (schema.namedScopes) schema.namedScopes.compile(model);
+ // apply methods and statics
+ applyMethods(model, schema);
+ applyStatics(model, schema);
model.schema = model.prototype.schema;
- model.options = model.prototype.options;
model.collection = model.prototype.collection;
+ // Create custom query constructor
+ model.Query = function() {
+ Query.apply(this, arguments);
+ };
+ model.Query.prototype = Object.create(Query.prototype);
+ model.Query.base = Query.base;
+ applyQueryMethods(model, schema.query);
+
+ var kareemOptions = { useErrorHandlers: true };
+ model.$__insertMany = model.hooks.createWrapper('insertMany',
+ model.insertMany, model, kareemOptions);
+ model.insertMany = function(arr, callback) {
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve, reject) {
+ model.$__insertMany(arr, function(error, result) {
+ if (error) {
+ callback && callback(error);
+ return reject(error);
+ }
+ callback && callback(null, result);
+ resolve(result);
+ });
+ });
+ };
+
return model;
};
+/*!
+ * Register methods for this model
+ *
+ * @param {Model} model
+ * @param {Schema} schema
+ */
+var applyMethods = function(model, schema) {
+ function apply(method, schema) {
+ Object.defineProperty(model.prototype, method, {
+ get: function() {
+ var h = {};
+ for (var k in schema.methods[method]) {
+ h[k] = schema.methods[method][k].bind(this);
+ }
+ return h;
+ },
+ configurable: true
+ });
+ }
+ for (var method in schema.methods) {
+ if (typeof schema.methods[method] === 'function') {
+ model.prototype[method] = schema.methods[method];
+ } else {
+ apply(method, schema);
+ }
+ }
+};
+
+/*!
+ * Register statics for this model
+ * @param {Model} model
+ * @param {Schema} schema
+ */
+var applyStatics = function(model, schema) {
+ for (var i in schema.statics) {
+ model[i] = schema.statics[i];
+ }
+};
+
+/*!
+ * Register custom query methods for this model
+ *
+ * @param {Model} model
+ * @param {Schema} schema
+ */
+
+function applyQueryMethods(model, methods) {
+ for (var i in methods) {
+ model.Query.prototype[i] = methods[i];
+ }
+}
+
/*!
* Subclass this model with `conn`, `schema`, and `collection` settings.
*
@@ -2246,40 +3280,53 @@ Model.compile = function compile (name, schema, collectionName, connection, base
* @return {Model}
*/
-Model.__subclass = function subclass (conn, schema, collection) {
+Model.__subclass = function subclass(conn, schema, collection) {
// subclass model using this connection and collection name
- var model = this;
+ var _this = this;
- var Model = function Model (doc, fields, skipId) {
+ var Model = function Model(doc, fields, skipId) {
if (!(this instanceof Model)) {
return new Model(doc, fields, skipId);
}
- model.call(this, doc, fields, skipId);
- }
+ _this.call(this, doc, fields, skipId);
+ };
- Model.__proto__ = model;
- Model.prototype.__proto__ = model.prototype;
+ Model.__proto__ = _this;
+ Model.prototype.__proto__ = _this.prototype;
Model.db = Model.prototype.db = conn;
- var s = schema && 'string' != typeof schema
- ? schema
- : model.prototype.schema;
+ var s = schema && typeof schema !== 'string'
+ ? schema
+ : _this.prototype.schema;
+
+ var options = s.options || {};
if (!collection) {
- collection = model.prototype.schema.get('collection')
- || utils.toCollectionName(model.modelName);
+ collection = _this.prototype.schema.get('collection')
+ || utils.toCollectionName(_this.modelName, options);
}
var collectionOptions = {
- bufferCommands: s ? s.options.bufferCommands : true
- , capped: s && s.options.capped
+ bufferCommands: s ? options.bufferCommands : true,
+ capped: s && options.capped
};
Model.prototype.collection = conn.collection(collection, collectionOptions);
Model.collection = Model.prototype.collection;
Model.init();
return Model;
-}
+};
+
+Model.$wrapCallback = function(callback) {
+ var _this = this;
+ return function() {
+ try {
+ callback.apply(null, arguments);
+ } catch (error) {
+ _this.emit('error', error);
+ }
+ };
+};
/*!
* Module exports.
diff --git a/node_modules/mongoose/lib/namedscope.js b/node_modules/mongoose/lib/namedscope.js
deleted file mode 100644
index 1b3f5d4..0000000
--- a/node_modules/mongoose/lib/namedscope.js
+++ /dev/null
@@ -1,70 +0,0 @@
-var Query = require('./query');
-function NamedScope () {}
-
-NamedScope.prototype.query;
-
-NamedScope.prototype.where = function () {
- var q = this.query || (this.query = new Query());
- q.where.apply(q, arguments);
- return q;
-};
-
-/**
- * Decorate
- *
- * @param {NamedScope} target
- * @param {Object} getters
- * @api private
- */
-
-NamedScope.prototype.decorate = function (target, getters) {
- var name = this.name
- , block = this.block
- , query = this.query;
- if (block) {
- if (block.length === 0) {
- Object.defineProperty(target, name, {
- get: getters.block0(block)
- });
- } else {
- target[name] = getters.blockN(block);
- }
- } else {
- Object.defineProperty(target, name, {
- get: getters.basic(query)
- });
- }
-};
-
-NamedScope.prototype.compile = function (model) {
- var allScopes = this.scopesByName
- , scope;
- for (var k in allScopes) {
- scope = allScopes[k];
- scope.decorate(model, {
- block0: function (block) {
- return function () {
- var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
- block.call(cquery);
- return this;
- };
- },
- blockN: function (block) {
- return function () {
- var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
- block.apply(cquery, arguments);
- return this;
- };
- },
- basic: function (query) {
- return function () {
- var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this));
- cquery.find(query);
- return this;
- };
- }
- });
- }
-};
-
-module.exports = NamedScope;
diff --git a/node_modules/mongoose/lib/promise.js b/node_modules/mongoose/lib/promise.js
index 20ab42a..72163ea 100644
--- a/node_modules/mongoose/lib/promise.js
+++ b/node_modules/mongoose/lib/promise.js
@@ -1,9 +1,9 @@
-
/*!
* Module dependencies
*/
var MPromise = require('mpromise');
+var util = require('util');
/**
* Promise constructor.
@@ -13,31 +13,82 @@ var MPromise = require('mpromise');
* var query = Candy.find({ bar: true });
* var promise = query.exec();
*
+ * DEPRECATED. Mongoose 5.0 will use native promises by default (or bluebird,
+ * if native promises are not present) but still
+ * support plugging in your own ES6-compatible promises library. Mongoose 5.0
+ * will **not** support mpromise.
+ *
* @param {Function} fn a function which will be called when the promise is resolved that accepts `fn(err, ...){}` as signature
* @inherits mpromise https://github.com/aheckmann/mpromise
* @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
* @event `err`: Emits when the promise is rejected
* @event `complete`: Emits when the promise is fulfilled
* @api public
+ * @deprecated
*/
-function Promise (fn) {
+function Promise(fn) {
MPromise.call(this, fn);
}
+/**
+ * ES6-style promise constructor wrapper around mpromise.
+ *
+ * @param {Function} resolver
+ * @return {Promise} new promise
+ * @api public
+ */
+Promise.ES6 = function(resolver) {
+ var promise = new Promise();
+
+ // No try/catch for backwards compatibility
+ resolver(
+ function() {
+ promise.complete.apply(promise, arguments);
+ },
+ function(e) {
+ promise.error(e);
+ });
+
+ return promise;
+};
+
/*!
* Inherit from mpromise
*/
Promise.prototype = Object.create(MPromise.prototype, {
- constructor: {
- value: Promise
- , enumerable: false
- , writable: true
- , configurable: true
- }
+ constructor: {
+ value: Promise,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
});
+/*!
+ * ignore
+ */
+
+Promise.prototype.then = util.deprecate(Promise.prototype.then,
+ 'Mongoose: mpromise (mongoose\'s default promise library) is deprecated, ' +
+ 'plug in your own promise library instead: ' +
+ 'http://mongoosejs.com/docs/promises.html');
+
+/**
+ * ES6-style `.catch()` shorthand
+ *
+ * @method catch
+ * @memberOf Promise
+ * @param {Function} onReject
+ * @return {Promise}
+ * @api public
+ */
+
+Promise.prototype.catch = function(onReject) {
+ return this.then(null, onReject);
+};
+
/*!
* Override event names for backward compatibility.
*/
@@ -84,10 +135,15 @@ Promise.FAILURE = 'err';
* @return {Promise} this
*/
-Promise.prototype.error = function (err) {
- if (!(err instanceof Error)) err = new Error(err);
+Promise.prototype.error = function(err) {
+ if (!(err instanceof Error)) {
+ if (err instanceof Object) {
+ err = util.inspect(err);
+ }
+ err = new Error(err);
+ }
return this.reject(err);
-}
+};
/**
* Resolves this promise to a rejected state if `err` is passed or a fulfilled state if no `err` is passed.
@@ -101,17 +157,18 @@ Promise.prototype.error = function (err) {
* @param {Error} [err] error or null
* @param {Object} [val] value to fulfill the promise with
* @api public
+ * @deprecated
*/
-Promise.prototype.resolve = function (err, val) {
+Promise.prototype.resolve = function(err) {
if (err) return this.error(err);
- return this.fulfill(val);
-}
+ return this.fulfill.apply(this, Array.prototype.slice.call(arguments, 1));
+};
/**
* Adds a single function as a listener to both err and complete.
*
- * It will be executed with traditional node.js argument position when the promise is resolved.
+ * It will be executed with traditional node.js argument position when the promise is resolved.
*
* promise.addBack(function (err, args...) {
* if (err) return handleError(err);
@@ -120,21 +177,39 @@ Promise.prototype.resolve = function (err, val) {
*
* Alias of [mpromise#onResolve](https://github.com/aheckmann/mpromise#onresolve).
*
+ * _Deprecated. Use `onResolve` instead._
+ *
* @method addBack
* @param {Function} listener
* @return {Promise} this
+ * @deprecated
*/
Promise.prototype.addBack = Promise.prototype.onResolve;
+/**
+ * Fulfills this promise with passed arguments.
+ *
+ * @method fulfill
+ * @receiver Promise
+ * @see https://github.com/aheckmann/mpromise#fulfill
+ * @param {any} args
+ * @api public
+ * @deprecated
+ */
+
/**
* Fulfills this promise with passed arguments.
*
* Alias of [mpromise#fulfill](https://github.com/aheckmann/mpromise#fulfill).
*
+ * _Deprecated. Use `fulfill` instead._
+ *
* @method complete
+ * @receiver Promise
* @param {any} args
* @api public
+ * @deprecated
*/
Promise.prototype.complete = MPromise.prototype.fulfill;
@@ -144,10 +219,13 @@ Promise.prototype.complete = MPromise.prototype.fulfill;
*
* Alias of [mpromise#onFulfill](https://github.com/aheckmann/mpromise#onfulfill).
*
+ * _Deprecated. Use `onFulfill` instead._
+ *
* @method addCallback
* @param {Function} listener
* @return {Promise} this
* @api public
+ * @deprecated
*/
Promise.prototype.addCallback = Promise.prototype.onFulfill;
@@ -157,10 +235,13 @@ Promise.prototype.addCallback = Promise.prototype.onFulfill;
*
* Alias of [mpromise#onReject](https://github.com/aheckmann/mpromise#onreject).
*
+ * _Deprecated. Use `onReject` instead._
+ *
* @method addErrback
* @param {Function} listener
* @return {Promise} this
* @api public
+ * @deprecated
*/
Promise.prototype.addErrback = Promise.prototype.onReject;
@@ -195,6 +276,7 @@ Promise.prototype.addErrback = Promise.prototype.onReject;
* @param {Function} onFulFill
* @param {Function} onReject
* @return {Promise} newPromise
+ * @deprecated
*/
/**
@@ -222,6 +304,7 @@ Promise.prototype.addErrback = Promise.prototype.onReject;
* @see mpromise#end https://github.com/aheckmann/mpromise#end
* @method end
* @memberOf Promise
+ * @deprecated
*/
/*!
diff --git a/node_modules/mongoose/lib/promise_provider.js b/node_modules/mongoose/lib/promise_provider.js
new file mode 100644
index 0000000..368cf85
--- /dev/null
+++ b/node_modules/mongoose/lib/promise_provider.js
@@ -0,0 +1,51 @@
+/*!
+ * Module dependencies.
+ */
+
+var MPromise = require('./promise');
+
+/**
+ * Helper for multiplexing promise implementations
+ *
+ * @api private
+ */
+
+var Promise = {
+ _promise: MPromise
+};
+
+/**
+ * Get the current promise constructor
+ *
+ * @api private
+ */
+Promise.get = function() {
+ return Promise._promise;
+};
+
+/**
+ * Set the current promise constructor
+ *
+ * @api private
+ */
+
+Promise.set = function(lib) {
+ if (lib === MPromise) {
+ return Promise.reset();
+ }
+ Promise._promise = require('./ES6Promise');
+ Promise._promise.use(lib);
+ require('mquery').Promise = Promise._promise.ES6;
+};
+
+/**
+ * Resets to using mpromise
+ *
+ * @api private
+ */
+
+Promise.reset = function() {
+ Promise._promise = MPromise;
+};
+
+module.exports = Promise;
diff --git a/node_modules/mongoose/lib/query.js b/node_modules/mongoose/lib/query.js
index 740e238..4d8ef67 100644
--- a/node_modules/mongoose/lib/query.js
+++ b/node_modules/mongoose/lib/query.js
@@ -2,545 +2,200 @@
* Module dependencies.
*/
-var utils = require('./utils')
- , merge = utils.merge
- , Promise = require('./promise')
- , Document = require('./document')
- , Types = require('./schema/index')
- , inGroupsOf = utils.inGroupsOf
- , tick = utils.tick
- , QueryStream = require('./querystream')
- , helpers = require('./queryhelpers')
- , ReadPref = require('mongodb').ReadPreference
+var PromiseProvider = require('./promise_provider');
+var QueryCursor = require('./querycursor');
+var QueryStream = require('./querystream');
+var StrictModeError = require('./error/strict');
+var cast = require('./cast');
+var helpers = require('./queryhelpers');
+var mquery = require('mquery');
+var readPref = require('./drivers').ReadPreference;
+var setDefaultsOnInsert = require('./services/setDefaultsOnInsert');
+var updateValidators = require('./services/updateValidators');
+var util = require('util');
+var utils = require('./utils');
/**
* Query constructor used for building queries.
*
* ####Example:
*
- * var query = Model.find();
+ * var query = new Query();
+ * query.setOptions({ lean : true });
+ * query.collection(model.collection);
* query.where('age').gte(21).exec(callback);
*
- * @param {Object} criteria
- * @param {Object} options
- * @api public
- */
-
-function Query (criteria, options) {
- this.setOptions(options, true);
- this._conditions = {};
- this._updateArg = {};
- this._fields = undefined;
- this._geoComparison = undefined;
- if (criteria) this.find(criteria);
-}
-
-/**
- * Sets query options.
- *
- * ####Options:
- *
- * - [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors) *
- * - [sort](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsort(\)%7D%7D) *
- * - [limit](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D) *
- * - [skip](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D) *
- * - [maxscan](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24maxScan) *
- * - [batchSize](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D) *
- * - [comment](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment) *
- * - [snapshot](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D) *
- * - [hint](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint) *
- * - [slaveOk](http://docs.mongodb.org/manual/applications/replication/#read-preference) *
- * - [lean](./api.html#query_Query-lean) *
- *
- * All other [options](http://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#constructor) specified will be passed unaltered to the driver.
- *
- * _* denotes a query helper method is also available_
- *
- * @param {Object} options
- * @api public
+ * @param {Object} [options]
+ * @param {Object} [model]
+ * @param {Object} [conditions]
+ * @param {Object} [collection] Mongoose collection
+ * @api private
*/
-Query.prototype.setOptions = function (options, overwrite) {
- // overwrite is internal use only
- if (overwrite) {
- options = this.options = options || {};
- this.safe = options.safe;
- if ('populate' in options) {
- this.populate(this.options.populate);
- }
- return this;
+function Query(conditions, options, model, collection) {
+ // this stuff is for dealing with custom queries created by #toConstructor
+ if (!this._mongooseOptions) {
+ this._mongooseOptions = {};
}
- if (!(options && 'Object' == options.constructor.name))
- return this;
-
- if ('safe' in options)
- this.safe = options.safe;
-
- // set arbitrary options
- var methods = Object.keys(options)
- , i = methods.length
- , method
-
- while (i--) {
- method = methods[i];
-
- // use methods if exist (safer option manipulation)
- if ('function' == typeof this[method]) {
- var args = Array.isArray(options[method])
- ? options[method]
- : [options[method]];
- this[method].apply(this, args)
- } else {
- this.options[method] = options[method];
+ // this is the case where we have a CustomQuery, we need to check if we got
+ // options passed in, and if we did, merge them in
+ if (options) {
+ var keys = Object.keys(options);
+ for (var i = 0; i < keys.length; ++i) {
+ var k = keys[i];
+ this._mongooseOptions[k] = options[k];
}
}
- return this;
-}
-
-/**
- * Binds this query to a model.
- *
- * @param {Model} model the model to which the query is bound
- * @param {String} op the operation to execute
- * @param {Object} updateArg used in update methods
- * @return {Query}
- * @api private
- */
-
-Query.prototype.bind = function bind (model, op, updateArg) {
- this.model = model;
- this.op = op;
-
- if (model._mapreduce) this.options.lean = true;
-
- if (op == 'update' || op == 'findOneAndUpdate') {
- merge(this._updateArg, updateArg || {});
+ if (collection) {
+ this.mongooseCollection = collection;
}
- return this;
-};
-
-/**
- * Executes the query
- *
- * ####Examples
- *
- * query.exec();
- * query.exec(callback);
- * query.exec('update');
- * query.exec('find', callback);
- *
- * @param {String|Function} [operation]
- * @param {Function} [callback]
- * @return {Promise}
- * @api public
- */
-
-Query.prototype.exec = function exec (op, callback) {
- var promise = new Promise();
-
- switch (typeof op) {
- case 'function':
- callback = op;
- op = null;
- break;
- case 'string':
- this.op = op;
- break;
+ if (model) {
+ this.model = model;
+ this.schema = model.schema;
}
- if (callback) promise.addBack(callback);
-
- if (!this.op) {
- promise.complete();
- return promise;
+ // this is needed because map reduce returns a model that can be queried, but
+ // all of the queries on said model should be lean
+ if (this.model && this.model._mapreduce) {
+ this.lean();
}
- if ('update' == this.op) {
- this[this.op](this._updateArg, promise.resolve.bind(promise));
- return promise;
- }
+ // inherit mquery
+ mquery.call(this, this.mongooseCollection, options);
- if ('distinct' == this.op) {
- this.distinct(this._distinctArg, promise.resolve.bind(promise));
- return promise;
+ if (conditions) {
+ this.find(conditions);
+ }
+
+ if (this.schema) {
+ var kareemOptions = { useErrorHandlers: true };
+ this._count = this.model.hooks.createWrapper('count',
+ Query.prototype._count, this, kareemOptions);
+ this._execUpdate = this.model.hooks.createWrapper('update',
+ Query.prototype._execUpdate, this, kareemOptions);
+ this._find = this.model.hooks.createWrapper('find',
+ Query.prototype._find, this, kareemOptions);
+ this._findOne = this.model.hooks.createWrapper('findOne',
+ Query.prototype._findOne, this, kareemOptions);
+ this._findOneAndRemove = this.model.hooks.createWrapper('findOneAndRemove',
+ Query.prototype._findOneAndRemove, this, kareemOptions);
+ this._findOneAndUpdate = this.model.hooks.createWrapper('findOneAndUpdate',
+ Query.prototype._findOneAndUpdate, this, kareemOptions);
}
+}
- this[this.op](promise.resolve.bind(promise));
- return promise;
-};
+/*!
+ * inherit mquery
+ */
+
+Query.prototype = new mquery;
+Query.prototype.constructor = Query;
+Query.base = mquery.prototype;
/**
- * Finds documents.
+ * Flag to opt out of using `$geoWithin`.
*
- * When no `callback` is passed, the query is not executed.
- *
- * ####Example
+ * mongoose.Query.use$geoWithin = false;
*
- * query.find({ name: 'Los Pollos Hermanos' }).find(callback)
+ * MongoDB 2.4 deprecated the use of `$within`, replacing it with `$geoWithin`. Mongoose uses `$geoWithin` by default (which is 100% backward compatible with $within). If you are running an older version of MongoDB, set this flag to `false` so your `within()` queries continue to work.
*
- * @param {Object} [criteria] mongodb selector
- * @param {Function} [callback]
- * @return {Query} this
+ * @see http://docs.mongodb.org/manual/reference/operator/geoWithin/
+ * @default true
+ * @property use$geoWithin
+ * @memberOf Query
+ * @receiver Query
* @api public
*/
-Query.prototype.find = function (criteria, callback) {
- this.op = 'find';
- if ('function' === typeof criteria) {
- callback = criteria;
- criteria = {};
- } else if (criteria instanceof Query) {
- // TODO Merge options, too
- merge(this._conditions, criteria._conditions);
- } else if (criteria instanceof Document) {
- merge(this._conditions, criteria.toObject());
- } else if (criteria && 'Object' === criteria.constructor.name) {
- merge(this._conditions, criteria);
- }
- if (!callback) return this;
- return this.execFind(callback);
-};
+Query.use$geoWithin = mquery.use$geoWithin;
/**
- * Casts this query to the schema of `model`
+ * Converts this query to a customized, reusable query constructor with all arguments and options retained.
*
- * ####Note
+ * ####Example
*
- * If `obj` is present, it is cast instead of this query.
+ * // Create a query for adventure movies and read from the primary
+ * // node in the replica-set unless it is down, in which case we'll
+ * // read from a secondary node.
+ * var query = Movie.find({ tags: 'adventure' }).read('primaryPreferred');
+ *
+ * // create a custom Query constructor based off these settings
+ * var Adventure = query.toConstructor();
+ *
+ * // Adventure is now a subclass of mongoose.Query and works the same way but with the
+ * // default query parameters and options set.
+ * Adventure().exec(callback)
+ *
+ * // further narrow down our query results while still using the previous settings
+ * Adventure().where({ name: /^Life/ }).exec(callback);
+ *
+ * // since Adventure is a stand-alone constructor we can also add our own
+ * // helper methods and getters without impacting global queries
+ * Adventure.prototype.startsWith = function (prefix) {
+ * this.where({ name: new RegExp('^' + prefix) })
+ * return this;
+ * }
+ * Object.defineProperty(Adventure.prototype, 'highlyRated', {
+ * get: function () {
+ * this.where({ rating: { $gt: 4.5 }});
+ * return this;
+ * }
+ * })
+ * Adventure().highlyRated.startsWith('Life').exec(callback)
*
- * @param {Model} model
- * @param {Object} [obj]
- * @return {Object}
+ * New in 3.7.3
+ *
+ * @return {Query} subclass-of-Query
* @api public
*/
-Query.prototype.cast = function (model, obj) {
- obj || (obj= this._conditions);
-
- var schema = model.schema
- , paths = Object.keys(obj)
- , i = paths.length
- , any$conditionals
- , schematype
- , nested
- , path
- , type
- , val;
-
- while (i--) {
- path = paths[i];
- val = obj[path];
-
- if ('$or' === path || '$nor' === path || '$and' === path) {
- var k = val.length
- , orComponentQuery;
-
- while (k--) {
- orComponentQuery = new Query(val[k]);
- orComponentQuery.cast(model);
- val[k] = orComponentQuery._conditions;
- }
-
- } else if (path === '$where') {
- type = typeof val;
-
- if ('string' !== type && 'function' !== type) {
- throw new Error("Must have a string or function for $where");
- }
-
- if ('function' === type) {
- obj[path] = val.toString();
- }
-
- continue;
-
- } else {
-
- if (!schema) {
- // no casting for Mixed types
- continue;
- }
-
- schematype = schema.path(path);
-
- if (!schematype) {
- // Handle potential embedded array queries
- var split = path.split('.')
- , j = split.length
- , pathFirstHalf
- , pathLastHalf
- , remainingConds
- , castingQuery;
-
- // Find the part of the var path that is a path of the Schema
- while (j--) {
- pathFirstHalf = split.slice(0, j).join('.');
- schematype = schema.path(pathFirstHalf);
- if (schematype) break;
- }
-
- // If a substring of the input path resolves to an actual real path...
- if (schematype) {
- // Apply the casting; similar code for $elemMatch in schema/array.js
- if (schematype.caster && schematype.caster.schema) {
- remainingConds = {};
- pathLastHalf = split.slice(j).join('.');
- remainingConds[pathLastHalf] = val;
- castingQuery = new Query(remainingConds);
- castingQuery.cast(schematype.caster);
- obj[path] = castingQuery._conditions[pathLastHalf];
- } else {
- obj[path] = val;
- }
- continue;
- }
-
- if (utils.isObject(val)) {
- // handle geo schemas that use object notation
- // { loc: { long: Number, lat: Number }
-
- var geo = val.$near ? '$near' :
- val.$nearSphere ? '$nearSphere' :
- val.$within ? '$within' :
- val.$geoIntersects ? '$geoIntersects' : '';
-
- if (!geo) {
- continue;
- }
-
- var numbertype = new Types.Number('__QueryCasting__')
- var value = val[geo];
-
- if (val.$maxDistance) {
- val.$maxDistance = numbertype.castForQuery(val.$maxDistance);
- }
-
- if ('$within' == geo) {
- var withinType = value.$center
- || value.$centerSphere
- || value.$box
- || value.$polygon;
-
- if (!withinType) {
- throw new Error('Bad $within paramater: ' + JSON.stringify(val));
- }
-
- value = withinType;
-
- } else if ('$near' == geo &&
- 'string' == typeof value.type && Array.isArray(value.coordinates)) {
- // geojson; cast the coordinates
- value = value.coordinates;
-
- } else if (('$near' == geo || '$geoIntersects' == geo) &&
- value.$geometry && 'string' == typeof value.$geometry.type &&
- Array.isArray(value.$geometry.coordinates)) {
- // geojson; cast the coordinates
- value = value.$geometry.coordinates;
- }
-
- ;(function _cast (val) {
- if (Array.isArray(val)) {
- val.forEach(function (item, i) {
- if (Array.isArray(item) || utils.isObject(item)) {
- return _cast(item);
- }
- val[i] = numbertype.castForQuery(item);
- });
- } else {
- var nearKeys= Object.keys(val);
- var nearLen = nearKeys.length;
- while (nearLen--) {
- var nkey = nearKeys[nearLen];
- var item = val[nkey];
- if (Array.isArray(item) || utils.isObject(item)) {
- _cast(item);
- val[nkey] = item;
- } else {
- val[nkey] = numbertype.castForQuery(item);
- }
- }
- }
- })(value);
- }
-
- } else if (val === null || val === undefined) {
- continue;
- } else if ('Object' === val.constructor.name) {
-
- any$conditionals = Object.keys(val).some(function (k) {
- return k.charAt(0) === '$' && k !== '$id' && k !== '$ref';
- });
-
- if (!any$conditionals) {
- obj[path] = schematype.castForQuery(val);
- } else {
+Query.prototype.toConstructor = function toConstructor() {
+ var model = this.model;
+ var coll = this.mongooseCollection;
- var ks = Object.keys(val)
- , k = ks.length
- , $cond;
-
- while (k--) {
- $cond = ks[k];
- nested = val[$cond];
-
- if ('$exists' === $cond) {
- if ('boolean' !== typeof nested) {
- throw new Error("$exists parameter must be Boolean");
- }
- continue;
- }
-
- if ('$type' === $cond) {
- if ('number' !== typeof nested) {
- throw new Error("$type parameter must be Number");
- }
- continue;
- }
-
- if ('$not' === $cond) {
- this.cast(model, nested);
- } else {
- val[$cond] = schematype.castForQuery($cond, nested);
- }
- }
- }
- } else {
- obj[path] = schematype.castForQuery(val);
- }
+ var CustomQuery = function(criteria, options) {
+ if (!(this instanceof CustomQuery)) {
+ return new CustomQuery(criteria, options);
}
- }
+ this._mongooseOptions = utils.clone(p._mongooseOptions);
+ Query.call(this, criteria, options || null, model, coll);
+ };
- return obj;
-};
+ util.inherits(CustomQuery, Query);
-/**
- * Returns default options.
- * @param {Model} model
- * @api private
- */
+ // set inherited defaults
+ var p = CustomQuery.prototype;
-Query.prototype._optionsForExec = function (model) {
- var options = utils.clone(this.options, { retainKeyOrder: true });
- delete options.populate;
+ p.options = {};
- if (!('safe' in options))
- options.safe = model.schema.options.safe;
+ p.setOptions(this.options);
- if (!('readPreference' in options) && model.schema.options.read)
- options.readPreference = model.schema.options.read;
+ p.op = this.op;
+ p._conditions = utils.clone(this._conditions);
+ p._fields = utils.clone(this._fields);
+ p._update = utils.clone(this._update);
+ p._path = this._path;
+ p._distinct = this._distinct;
+ p._collection = this._collection;
+ p._mongooseOptions = this._mongooseOptions;
- return options;
+ return CustomQuery;
};
-/**
- * Applies schematype selected options to this query.
- * @api private
- */
-
-Query.prototype._applyPaths = function applyPaths () {
- // determine if query is selecting or excluding fields
-
- var fields = this._fields
- , exclude
- , keys
- , ki
-
- if (fields) {
- keys = Object.keys(fields);
- ki = keys.length;
-
- while (ki--) {
- if ('+' == keys[ki][0]) continue;
- exclude = 0 === fields[keys[ki]];
- break;
- }
- }
-
- // if selecting, apply default schematype select:true fields
- // if excluding, apply schematype select:false fields
-
- var selected = []
- , excluded = []
- , seen = [];
-
- analyzeSchema(this.model.schema);
-
- switch (exclude) {
- case true:
- excluded.length && this.select('-' + excluded.join(' -'));
- break;
- case false:
- selected.length && this.select(selected.join(' '));
- break;
- case undefined:
- // user didn't specify fields, implies returning all fields.
- // only need to apply excluded fields
- excluded.length && this.select('-' + excluded.join(' -'));
- break;
- }
-
- return seen = excluded = selected = keys = fields = null;
-
- function analyzeSchema (schema, prefix) {
- prefix || (prefix = '');
-
- // avoid recursion
- if (~seen.indexOf(schema)) return;
- seen.push(schema);
-
- schema.eachPath(function (path, type) {
- if (prefix) path = prefix + '.' + path;
-
- analyzePath(path, type);
-
- // array of subdocs?
- if (type.schema) {
- analyzeSchema(type.schema, path);
- }
-
- });
- }
-
- function analyzePath (path, type) {
- if ('boolean' != typeof type.selected) return;
-
- var plusPath = '+' + path;
- if (fields && plusPath in fields) {
- // forced inclusion
- delete fields[plusPath];
-
- // if there are other fields being included, add this one
- // if no other included fields, leave this out (implied inclusion)
- if (false === exclude && keys.length > 1 && !~keys.indexOf(path)) {
- fields[path] = 1;
- }
-
- return
- };
-
- // check for parent exclusions
- var root = path.split('.')[0];
- if (~excluded.indexOf(root)) return;
-
- ;(type.selected ? selected : excluded).push(path);
- }
-}
-
/**
* Specifies a javascript function or expression to pass to MongoDBs query system.
*
* ####Example
*
- * query.$where('this.comments.length > 10 || this.name.length > 5')
+ * query.$where('this.comments.length === 10 || this.name.length === 5')
*
* // or
*
* query.$where(function () {
- * return this.comments.length > 10 || this.name.length > 5;
+ * return this.comments.length === 10 || this.name.length === 5;
* })
*
* ####NOTE:
@@ -549,6 +204,7 @@ Query.prototype._applyPaths = function applyPaths () {
* **Be sure to read about all of [its caveats](http://docs.mongodb.org/manual/reference/operator/where/) before using.**
*
* @see $where http://docs.mongodb.org/manual/reference/operator/where/
+ * @method $where
* @param {String|Function} js javascript string or function
* @return {Query} this
* @memberOf Query
@@ -556,11 +212,6 @@ Query.prototype._applyPaths = function applyPaths () {
* @api public
*/
-Query.prototype.$where = function (js) {
- this._conditions['$where'] = js;
- return this;
-};
-
/**
* Specifies a `path` for use with chaining.
*
@@ -572,36 +223,24 @@ Query.prototype.$where = function (js) {
* // we can instead write:
* User.where('age').gte(21).lte(65);
*
- * // Moreover, you can also chain a bunch of these together:
+ * // passing query conditions is permitted
+ * User.find().where({ name: 'vonderful' })
*
+ * // chaining
* User
* .where('age').gte(21).lte(65)
- * .where('name', /^b/i)
+ * .where('name', /^vonderful/i)
* .where('friends').slice(10)
* .exec(callback)
*
- * @param {String} [path]
- * @param {Object} [val]
+ * @method where
+ * @memberOf Query
+ * @param {String|Object} [path]
+ * @param {any} [val]
* @return {Query} this
* @api public
*/
-Query.prototype.where = function (path, val) {
- if (!arguments.length) return this;
-
- if ('string' != typeof path) {
- throw new TypeError('path must be a string');
- }
-
- this._currPath = path;
-
- if (2 === arguments.length) {
- this._conditions[path] = val;
- }
-
- return this;
-};
-
/**
* Specifies the complementary comparison value for paths specified with `where()`
*
@@ -613,18 +252,13 @@ Query.prototype.where = function (path, val) {
*
* User.where('age', 49);
*
+ * @method equals
+ * @memberOf Query
* @param {Object} val
* @return {Query} this
* @api public
*/
-Query.prototype.equals = function equals (val) {
- var path = this._currPath;
- if (!path) throw new Error('equals() must be used after where()');
- this._conditions[path] = val;
- return this;
-}
-
/**
* Specifies arguments for an `$or` condition.
*
@@ -633,18 +267,13 @@ Query.prototype.equals = function equals (val) {
* query.or([{ color: 'red' }, { status: 'emergency' }])
*
* @see $or http://docs.mongodb.org/manual/reference/operator/or/
+ * @method or
+ * @memberOf Query
* @param {Array} array array of conditions
* @return {Query} this
* @api public
*/
-Query.prototype.or = function or (array) {
- var or = this._conditions.$or || (this._conditions.$or = []);
- if (!Array.isArray(array)) array = [array];
- or.push.apply(or, array);
- return this;
-}
-
/**
* Specifies arguments for a `$nor` condition.
*
@@ -653,18 +282,13 @@ Query.prototype.or = function or (array) {
* query.nor([{ color: 'green' }, { status: 'ok' }])
*
* @see $nor http://docs.mongodb.org/manual/reference/operator/nor/
+ * @method nor
+ * @memberOf Query
* @param {Array} array array of conditions
* @return {Query} this
* @api public
*/
-Query.prototype.nor = function nor (array) {
- var nor = this._conditions.$nor || (this._conditions.$nor = []);
- if (!Array.isArray(array)) array = [array];
- nor.push.apply(nor, array);
- return this;
-}
-
/**
* Specifies arguments for a `$and` condition.
*
@@ -672,19 +296,14 @@ Query.prototype.nor = function nor (array) {
*
* query.and([{ color: 'green' }, { status: 'ok' }])
*
+ * @method and
+ * @memberOf Query
* @see $and http://docs.mongodb.org/manual/reference/operator/and/
* @param {Array} array array of conditions
* @return {Query} this
* @api public
*/
-Query.prototype.and = function and (array) {
- var and = this._conditions.$and || (this._conditions.$and = []);
- if (!Array.isArray(array)) array = [array];
- and.push.apply(and, array);
- return this;
-}
-
/**
* Specifies a $gt query condition.
*
@@ -697,11 +316,11 @@ Query.prototype.and = function and (array) {
* // or
* Thing.find().gt('age', 21)
*
- * @see $gt http://docs.mongodb.org/manual/reference/operator/gt/
* @method gt
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
+ * @see $gt http://docs.mongodb.org/manual/reference/operator/gt/
* @api public
*/
@@ -710,11 +329,11 @@ Query.prototype.and = function and (array) {
*
* When called with one argument, the most recent path passed to `where()` is used.
*
- * @see $gte http://docs.mongodb.org/manual/reference/operator/gte/
* @method gte
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
+ * @see $gte http://docs.mongodb.org/manual/reference/operator/gte/
* @api public
*/
@@ -723,11 +342,11 @@ Query.prototype.and = function and (array) {
*
* When called with one argument, the most recent path passed to `where()` is used.
*
- * @see $lt http://docs.mongodb.org/manual/reference/operator/lt/
* @method lt
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
+ * @see $lt http://docs.mongodb.org/manual/reference/operator/lt/
* @api public
*/
@@ -736,10 +355,10 @@ Query.prototype.and = function and (array) {
*
* When called with one argument, the most recent path passed to `where()` is used.
*
- * @see $lte http://docs.mongodb.org/manual/reference/operator/lte/
* @method lte
+ * @see $lte http://docs.mongodb.org/manual/reference/operator/lte/
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
* @api public
*/
@@ -752,7 +371,7 @@ Query.prototype.and = function and (array) {
* @see $ne http://docs.mongodb.org/manual/reference/operator/ne/
* @method ne
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
* @api public
*/
@@ -765,7 +384,7 @@ Query.prototype.and = function and (array) {
* @see $in http://docs.mongodb.org/manual/reference/operator/in/
* @method in
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
* @api public
*/
@@ -778,7 +397,7 @@ Query.prototype.and = function and (array) {
* @see $nin http://docs.mongodb.org/manual/reference/operator/nin/
* @method nin
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
* @api public
*/
@@ -791,13 +410,15 @@ Query.prototype.and = function and (array) {
* @see $all http://docs.mongodb.org/manual/reference/operator/all/
* @method all
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
* @api public
*/
/**
- * Specifies an $size query condition.
+ * Specifies a $size query condition.
+ *
+ * When called with one argument, the most recent path passed to `where()` is used.
*
* ####Example
*
@@ -808,12 +429,10 @@ Query.prototype.and = function and (array) {
* console.log('documents with 0 tags', docs);
* })
*
- * When called with one argument, the most recent path passed to `where()` is used.
- *
* @see $size http://docs.mongodb.org/manual/reference/operator/size/
* @method size
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
* @api public
*/
@@ -826,7 +445,7 @@ Query.prototype.and = function and (array) {
* @see $regex http://docs.mongodb.org/manual/reference/operator/regex/
* @method regex
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
* @api public
*/
@@ -839,143 +458,46 @@ Query.prototype.and = function and (array) {
* @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
* @method maxDistance
* @memberOf Query
- * @param {String} path
+ * @param {String} [path]
* @param {Number} val
* @api public
*/
-/*!
- * gt, gte, lt, lte, ne, in, nin, all, regex, size, maxDistance
- *
- * Thing.where('type').nin(array)
- */
-
-'gt gte lt lte ne in nin all regex size maxDistance'.split(' ').forEach(function ($conditional) {
- Query.prototype[$conditional] = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds['$' + $conditional] = val;
- return this;
- };
-});
-
-/**
- * Specifies a `$near` condition
- *
- * query.near('loc', [10, 20])
- * query.near([10, 20])
- *
- * _NOTE: does not currently support GeoJSON._
- *
- * @param {String} path
- * @param {Array} val
- * @return {Query} this
- * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
- * @see $near http://docs.mongodb.org/manual/reference/operator/near/
- * @api public
- */
-
-Query.prototype.near = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath
- } else if (arguments.length === 2 && !Array.isArray(val)) {
- val = utils.args(arguments);
- path = this._currPath;
- } else if (arguments.length === 3) {
- val = utils.args(arguments, 1);
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds.$near = val;
- return this;
-}
-
-/**
- * Specifies a `$nearSphere` condition.
- *
- * query.nearSphere('loc', [10, 20])
- * query.nearSphere([10, 20])
- *
- * _NOTE: does not currently support GeoJSON._
- *
- * @param {String} path
- * @param {Array} val
- * @return {Query} this
- * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
- * @see $nearSphere http://docs.mongodb.org/manual/reference/operator/nearSphere/
- * @api public
- */
-
-Query.prototype.nearSphere = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath
- } else if (arguments.length === 2 && !Array.isArray(val)) {
- val = utils.args(arguments);
- path = this._currPath;
- } else if (arguments.length === 3) {
- val = utils.args(arguments, 1);
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds.$nearSphere = val;
- return this;
-}
-
/**
* Specifies a `$mod` condition
*
- * @param {String} path
+ * @method mod
+ * @memberOf Query
+ * @param {String} [path]
* @param {Number} val
* @return {Query} this
* @see $mod http://docs.mongodb.org/manual/reference/operator/mod/
* @api public
*/
-Query.prototype.mod = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath
- } else if (arguments.length === 2 && !Array.isArray(val)) {
- val = utils.args(arguments);
- path = this._currPath;
- } else if (arguments.length === 3) {
- val = utils.args(arguments, 1);
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds.$mod = val;
- return this;
-}
-
/**
* Specifies an `$exists` condition
*
- * @param {String} path
+ * ####Example
+ *
+ * // { name: { $exists: true }}
+ * Thing.where('name').exists()
+ * Thing.where('name').exists(true)
+ * Thing.find().exists('name')
+ *
+ * // { name: { $exists: false }}
+ * Thing.where('name').exists(false);
+ * Thing.find().exists('name', false);
+ *
+ * @method exists
+ * @memberOf Query
+ * @param {String} [path]
* @param {Number} val
* @return {Query} this
* @see $exists http://docs.mongodb.org/manual/reference/operator/exists/
* @api public
*/
-Query.prototype.exists = function (path, val) {
- if (arguments.length === 0) {
- path = this._currPath
- val = true;
- } else if (arguments.length === 1) {
- if ('boolean' === typeof path) {
- val = path;
- path = this._currPath;
- } else {
- val = true;
- }
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds['$exists'] = val;
- return this;
-};
-
/**
* Specifies an `$elemMatch` condition
*
@@ -991,10 +513,12 @@ Query.prototype.exists = function (path, val) {
* })
*
* query.where('comment').elemMatch(function (elem) {
- * elem.where('author').equals('autobot');
+ * elem.where({ author: 'autobot' });
* elem.where('votes').gte(5);
* })
*
+ * @method elemMatch
+ * @memberOf Query
* @param {String|Object|Function} path
* @param {Object|Function} criteria
* @return {Query} this
@@ -1002,560 +526,260 @@ Query.prototype.exists = function (path, val) {
* @api public
*/
-Query.prototype.elemMatch = function (path, criteria) {
- var block;
- if ('Object' === path.constructor.name) {
- criteria = path;
- path = this._currPath;
- } else if ('function' === typeof path) {
- block = path;
- path = this._currPath;
- } else if ('Object' === criteria.constructor.name) {
- } else if ('function' === typeof criteria) {
- block = criteria;
- } else {
- throw new Error("Argument error");
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- if (block) {
- criteria = new Query();
- block(criteria);
- conds['$elemMatch'] = criteria._conditions;
- } else {
- conds['$elemMatch'] = criteria;
- }
- return this;
-};
-
-// Spatial queries
-
/**
- * Defines a $within query for `box()`, `center()`, etc
+ * Defines a `$within` or `$geoWithin` argument for geo-spatial queries.
*
* ####Example
*
- * query.within.box()
- * query.within.center()
- * query.within.geometry()
+ * query.where(path).within().box()
+ * query.where(path).within().circle()
+ * query.where(path).within().geometry()
+ *
+ * query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
+ * query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
+ * query.where('loc').within({ polygon: [[],[],[],[]] });
+ *
+ * query.where('loc').within([], [], []) // polygon
+ * query.where('loc').within([], []) // box
+ * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
+ *
+ * **MUST** be used after `where()`.
+ *
+ * ####NOTE:
+ *
+ * As of Mongoose 3.7, `$geoWithin` is always used for queries. To change this behavior, see [Query.use$geoWithin](#query_Query-use%2524geoWithin).
+ *
+ * ####NOTE:
*
- * @property within
+ * In Mongoose 3.7, `within` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within).
+ *
+ * @method within
+ * @see $polygon http://docs.mongodb.org/manual/reference/operator/polygon/
+ * @see $box http://docs.mongodb.org/manual/reference/operator/box/
+ * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
+ * @see $center http://docs.mongodb.org/manual/reference/operator/center/
+ * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
* @memberOf Query
- * @see Query#box #query_Query-box
- * @see Query#center #query_Query-center
- * @see Query#centerSphere #query_Query-centerSphere
- * @see Query#polygon #query_Query-polygon
- * @see Query#geometry #query_Query-geometry
- * @see $geoWithin http://docs.mongodb.org/manual/reference/operator/within/
* @return {Query} this
* @api public
*/
-Object.defineProperty(Query.prototype, 'within', {
- get: function () {
- this._geoComparison = '$within';
- return this
- }
-});
-
/**
- * Declares an intersects query for `geometry()`.
+ * Specifies a $slice projection for an array.
*
* ####Example
*
- * query.intersects.geometry({
- * type: 'LineString'
- * , coordinates: [[180.0, 11.0], [180, 9.0]]
- * })
+ * query.slice('comments', 5)
+ * query.slice('comments', -5)
+ * query.slice('comments', [10, 5])
+ * query.where('comments').slice(5)
+ * query.where('comments').slice([-10, 5])
*
- * @property intersects
- * @see Query#geometry #query_Query-geometry
- * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
- * @see geoIntersects http://docs.mongodb.org/manual/reference/operator/geoIntersects/
+ * @method slice
* @memberOf Query
+ * @param {String} [path]
+ * @param {Number} val number/range of elements to slice
* @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements
+ * @see $slice http://docs.mongodb.org/manual/reference/projection/slice/#prj._S_slice
* @api public
*/
-Object.defineProperty(Query.prototype, 'intersects', {
- get: function () {
- this._geoComparison = '$geoIntersects';
- return this
- }
-});
-
/**
- * Specifies a $box condition
+ * Specifies the maximum number of documents the query will return.
*
* ####Example
*
- * var lowerLeft = [40.73083, -73.99756]
- * var upperRight= [40.741404, -73.988135]
- * query.where('loc').within.box({ ll: lowerLeft , ur: upperRight })
+ * query.limit(20)
*
- * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
- * @see Query#within #query_Query-within
- * @see $box http://docs.mongodb.org/manual/reference/operator/box/
- * @param {String} path
- * @param {Object} val
- * @return {Query} this
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method limit
+ * @memberOf Query
+ * @param {Number} val
* @api public
*/
-Query.prototype.box = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath;
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds['$within'] = { '$box': [val.ll, val.ur] };
- return this;
-};
-
/**
- * Specifies a $center condition
+ * Specifies the number of documents to skip.
*
* ####Example
*
- * var area = { center: [50, 50], radius: 10 }
- * query.where('loc').within.center(area)
+ * query.skip(100).limit(20)
*
- * @param {String} path
- * @param {Object} val
- * @param {Object} [opts] options e.g. { $uniqueDocs: true }
- * @return {Query} this
- * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
- * @see $center http://docs.mongodb.org/manual/reference/operator/center/
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method skip
+ * @memberOf Query
+ * @param {Number} val
+ * @see cursor.skip http://docs.mongodb.org/manual/reference/method/cursor.skip/
* @api public
*/
-Query.prototype.center = function (path, val, opts) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath;
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds['$within'] = { '$center': [val.center, val.radius] };
-
- // copy any options
- if (opts && 'Object' == opts.constructor.name) {
- utils.options(opts, conds.$within);
- }
-
- return this;
-};
-
/**
- * Specifies a $centerSphere condition
+ * Specifies the maxScan option.
*
* ####Example
*
- * var area = { center: [50, 50], radius: 10 }
- * query.where('loc').within.centerSphere(area)
+ * query.maxScan(100)
*
- * @param {String} [path]
- * @param {Object} val
- * @return {Query} this
- * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
- * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method maxScan
+ * @memberOf Query
+ * @param {Number} val
+ * @see maxScan http://docs.mongodb.org/manual/reference/operator/maxScan/
* @api public
*/
-Query.prototype.centerSphere = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath;
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds['$within'] = { '$centerSphere': [val.center, val.radius] };
- return this;
-};
-
/**
- * Specifies a $polygon condition
+ * Specifies the batchSize option.
*
* ####Example
*
- * var polyA = [ [ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ] ]
- * query.where('loc').within.polygon(polyA)
+ * query.batchSize(100)
*
- * // or
- * var polyB = { a : { x : 10, y : 20 }, b : { x : 15, y : 25 }, c : { x : 20, y : 20 } }
- * query.where('loc').within.polygon(polyB)
+ * ####Note
*
- * @param {String} [path]
- * @param {Array|Object} val
- * @return {Query} this
- * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
- * @see $polygon http://docs.mongodb.org/manual/reference/operator/polygon/
+ * Cannot be used with `distinct()`
+ *
+ * @method batchSize
+ * @memberOf Query
+ * @param {Number} val
+ * @see batchSize http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
* @api public
*/
-Query.prototype.polygon = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath;
- }
- var conds = this._conditions[path] || (this._conditions[path] = {});
- conds['$within'] = { '$polygon': val };
- return this;
-};
-
/**
- * Specifies a $geometry condition
+ * Specifies the `comment` option.
*
* ####Example
*
- * var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
- * query.where('loc').within.geometry({ type: 'Polygon', coordinates: polyA })
+ * query.comment('login query')
*
- * // or
- * var polyB = [[ 0, 0 ], [ 1, 1 ]]
- * query.where('loc').within.geometry({ type: 'LineString', coordinates: polyB })
+ * ####Note
*
- * // or
- * var polyC = [ 0, 0 ]
- * query.where('loc').within.geometry({ type: 'Point', coordinates: polyC })
+ * Cannot be used with `distinct()`
*
- * // or
- * var polyC = [ 0, 0 ]
- * query.where('loc').intersects.geometry({ type: 'Point', coordinates: polyC })
+ * @method comment
+ * @memberOf Query
+ * @param {Number} val
+ * @see comment http://docs.mongodb.org/manual/reference/operator/comment/
+ * @api public
+ */
+
+/**
+ * Specifies this query as a `snapshot` query.
*
- * ####NOTE:
+ * ####Example
*
- * `geometry()` **must** come after either `intersects` or `within`.
+ * query.snapshot() // true
+ * query.snapshot(true)
+ * query.snapshot(false)
*
- * The `object` argument must contain `type` and `coordinates` properties.
- * - type {String}
- * - coordinates {Array}
+ * ####Note
*
- * When called with one argument, the most recent path passed to `where()` is used.
+ * Cannot be used with `distinct()`
*
- * @param {String} [path] Optional name of a path to match against
- * @param {Object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the example.
+ * @method snapshot
+ * @memberOf Query
+ * @see snapshot http://docs.mongodb.org/manual/reference/operator/snapshot/
* @return {Query} this
- * @see http://docs.mongodb.org/manual/release-notes/2.4/#new-geospatial-indexes-with-geojson-and-improved-spherical-geometry
- * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
- * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
* @api public
*/
-Query.prototype.geometry = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath;
- }
-
- var conds = this._conditions[path] || (this._conditions[path] = {});
-
- if (!this._geoComparison) {
- throw new Error('query.geometry() must come after either `within` or `intersects`');
- }
-
- conds[this._geoComparison] = { $geometry: val };
- return this;
-};
-
-/**
- * Specifies which document fields to include or exclude
- *
- * When using string syntax, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. Lastly, if a path is prefixed with `+`, it forces inclusion of the path, which is useful for paths excluded at the [schema level](/docs/api.html#schematype_SchemaType-select).
- *
- * ####Example
- *
- * // include a and b, exclude c
- * query.select('a b -c');
- *
- * // or you may use object notation, useful when
- * // you have keys already prefixed with a "-"
- * query.select({a: 1, b: 1, c: 0});
- *
- * // force inclusion of field excluded at schema level
- * query.select('+path')
- *
- * ####NOTE:
- *
- * _v2 had slightly different syntax such as allowing arrays of field names. This support was removed in v3._
- *
- * @param {Object|String} arg
- * @return {Query} this
- * @see SchemaType
- * @api public
- */
-
-Query.prototype.select = function select (arg) {
- if (!arg) return this;
-
- var fields = this._fields || (this._fields = {});
-
- if ('Object' === arg.constructor.name) {
- Object.keys(arg).forEach(function (field) {
- fields[field] = arg[field];
- });
- } else if (1 === arguments.length && 'string' == typeof arg) {
- arg.split(/\s+/).forEach(function (field) {
- if (!field) return;
- var include = '-' == field[0] ? 0 : 1;
- if (include === 0) field = field.substring(1);
- fields[field] = include;
- });
- } else {
- throw new TypeError('Invalid select() argument. Must be a string or object.');
- }
-
- return this;
-};
-
/**
- * Specifies a $slice projection for an array.
+ * Sets query hints.
*
* ####Example
*
- * query.slice('comments', 5)
- * query.slice('comments', -5)
- * query.slice('comments', [10, 5])
- * query.where('comments').slice(5)
- * query.where('comments').slice([-10, 5])
- *
- * @param {String} path
- * @param {Number} val number of elements to slice
- * @return {Query} this
- * @see mongodb http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements
- * @see $slice http://docs.mongodb.org/manual/reference/projection/slice/#prj._S_slice
- * @api public
- */
-
-Query.prototype.slice = function (path, val) {
- if (arguments.length === 1) {
- val = path;
- path = this._currPath
- } else if (arguments.length === 2) {
- if ('number' === typeof path) {
- val = [path, val];
- path = this._currPath;
- }
- } else if (arguments.length === 3) {
- val = utils.args(arguments, 1);
- }
- var myFields = this._fields || (this._fields = {});
- myFields[path] = { '$slice': val };
- return this;
-};
-
-/**
- * Sets the sort order
- *
- * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
- *
- * If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending.
+ * query.hint({ indexA: 1, indexB: -1})
*
- * ####Example
- *
- * // sort by "field" ascending and "test" descending
- * query.sort({ field: 'asc', test: -1 });
+ * ####Note
*
- * // equivalent
- * query.sort('field -test');
+ * Cannot be used with `distinct()`
*
- * @param {Object|String} arg
+ * @method hint
+ * @memberOf Query
+ * @param {Object} val a hint object
* @return {Query} this
- * @see cursor.sort http://docs.mongodb.org/manual/reference/method/cursor.sort/
+ * @see $hint http://docs.mongodb.org/manual/reference/operator/hint/
* @api public
*/
-Query.prototype.sort = function (arg) {
- if (!arg) return this;
-
- var sort = this.options.sort || (this.options.sort = []);
-
- if ('Object' === arg.constructor.name) {
- Object.keys(arg).forEach(function (field) {
- push(sort, field, arg[field]);
- });
- } else if (1 === arguments.length && 'string' == typeof arg) {
- arg.split(/\s+/).forEach(function (field) {
- if (!field) return;
- var ascend = '-' == field[0] ? -1 : 1;
- if (ascend === -1) field = field.substring(1);
- push(sort, field, ascend);
- });
- } else {
- throw new TypeError('Invalid sort() argument. Must be a string or object.');
- }
-
- return this;
-};
-
-/*!
- * @ignore
- */
-
-function push (arr, field, value) {
- var val = String(value || 1).toLowerCase();
- if (!/^(?:ascending|asc|descending|desc|1|-1)$/.test(val)) {
- if (Array.isArray(value)) value = '['+value+']';
- throw new TypeError('Invalid sort value: {' + field + ': ' + value + ' }');
- }
- arr.push([field, value]);
-}
-
/**
- * Specifies the maximum number of documents the query will return.
- *
- * ####Example
- *
- * Kitten.find().limit(20).exec(callback)
- *
- * @method limit
- * @memberOf Query
- * @param {Number} val
- * @api public
- */
-/**
- * Specifies the number of documents to skip.
+ * Specifies which document fields to include or exclude (also known as the query "projection")
*
- * ####Example
- *
- * Kitten.find().skip(100).limit(20)
- *
- * @method skip
- * @memberOf Query
- * @param {Number} val
- * @see cursor.skip http://docs.mongodb.org/manual/reference/method/cursor.skip/
- * @api public
- */
-/**
- * Specifies the maxscan option.
+ * When using string syntax, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. Lastly, if a path is prefixed with `+`, it forces inclusion of the path, which is useful for paths excluded at the [schema level](/docs/api.html#schematype_SchemaType-select).
*
* ####Example
*
- * Kitten.find().maxscan(100)
+ * // include a and b, exclude other fields
+ * query.select('a b');
*
- * @method maxscan
- * @memberOf Query
- * @param {Number} val
- * @see maxScan http://docs.mongodb.org/manual/reference/operator/maxScan/
- * @api public
- */
-/**
- * Specifies the batchSize option.
+ * // exclude c and d, include other fields
+ * query.select('-c -d');
*
- * ####Example
+ * // or you may use object notation, useful when
+ * // you have keys already prefixed with a "-"
+ * query.select({ a: 1, b: 1 });
+ * query.select({ c: 0, d: 0 });
*
- * Kitten.find().batchSize(100)
+ * // force inclusion of field excluded at schema level
+ * query.select('+path')
*
- * @method batchSize
- * @memberOf Query
- * @param {Number} val
- * @see batchSize http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
- * @api public
- */
-/**
- * Specifies the `comment` option.
+ * ####NOTE:
*
- * ####Example
+ * Cannot be used with `distinct()`.
*
- * Kitten.findOne(condition).comment('login query')
+ * _v2 had slightly different syntax such as allowing arrays of field names. This support was removed in v3._
*
- * @method comment
+ * @method select
* @memberOf Query
- * @param {Number} val
- * @see comment http://docs.mongodb.org/manual/reference/operator/comment/
- * @api public
- */
-
-/*!
- * limit, skip, maxscan, batchSize, comment
- *
- * Sets these associated options.
- *
- * query.comment('feed query');
- */
-
-;['limit', 'skip', 'maxscan', 'batchSize', 'comment'].forEach(function (method) {
- Query.prototype[method] = function (v) {
- this.options[method] = v;
- return this;
- };
-});
-
-/**
- * Specifies this query as a `snapshot` query.
- *
- * ####Example
- *
- * Kitten.find().snapshot()
- *
- * @see snapshot http://docs.mongodb.org/manual/reference/operator/snapshot/
+ * @param {Object|String} arg
* @return {Query} this
+ * @see SchemaType
* @api public
*/
-Query.prototype.snapshot = function () {
- this.options.snapshot = true;
- return this;
-};
-
/**
- * Sets query hints.
- *
- * ####Example
+ * _DEPRECATED_ Sets the slaveOk option.
*
- * Model.find().hint({ indexA: 1, indexB: -1})
- *
- * @param {Object} val a hint object
- * @return {Query} this
- * @see $hint http://docs.mongodb.org/manual/reference/operator/hint/
- * @api public
- */
-
-Query.prototype.hint = function (val) {
- if (!val) return this;
-
- var hint = this.options.hint || (this.options.hint = {});
-
- if ('Object' === val.constructor.name) {
- // must keep object keys in order so don't use Object.keys()
- for (var k in val) {
- hint[k] = val[k];
- }
- } else {
- throw new TypeError('Invalid hint. ' + val);
- }
-
- return this;
-};
-
-/**
- * Sets the slaveOk option. _Deprecated_ in MongoDB 2.2 in favor of [read preferences](#query_Query-read).
+ * **Deprecated** in MongoDB 2.2 in favor of [read preferences](#query_Query-read).
*
* ####Example:
*
- * new Query().slaveOk() // true
- * new Query().slaveOk(true)
- * new Query().slaveOk(false)
+ * query.slaveOk() // true
+ * query.slaveOk(true)
+ * query.slaveOk(false)
*
+ * @method slaveOk
+ * @memberOf Query
+ * @deprecated use read() preferences instead if on mongodb >= 2.2
* @param {Boolean} v defaults to true
* @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
* @see slaveOk http://docs.mongodb.org/manual/reference/method/rs.slaveOk/
+ * @see read() #query_Query-read
* @return {Query} this
* @api public
*/
-Query.prototype.slaveOk = function (v) {
- this.options.slaveOk = arguments.length ? !!v : true;
- return this;
-}
-
/**
* Determines the MongoDB nodes from which to read.
*
* ####Preferences:
*
* primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags.
- * secondary Read from secondary if available, otherwise error.
- * primaryPreferred Read from primary if available, otherwise a secondary.
- * secondaryPreferred Read from a secondary if available, otherwise read from the primary.
- * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the selection.
+ * secondary Read from secondary if available, otherwise error.
+ * primaryPreferred Read from primary if available, otherwise a secondary.
+ * secondaryPreferred Read from a secondary if available, otherwise read from the primary.
+ * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection.
*
* Aliases
*
@@ -1583,10 +807,12 @@ Query.prototype.slaveOk = function (v) {
* new Query().read('n') // same as nearest
*
* // read from secondaries with matching tags
- * new Query().read('secondary', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }])
+ * new Query().read('s', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }])
*
* Read more about how to use read preferrences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences).
*
+ * @method read
+ * @memberOf Query
* @param {String} pref one of the listed preference options or aliases
* @param {Array} [tags] optional tags for this query
* @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
@@ -1595,119 +821,327 @@ Query.prototype.slaveOk = function (v) {
* @api public
*/
-Query.prototype.read = function (pref, tags) {
- this.options.readPreference = utils.readPref(pref, tags);
- return this;
-}
+Query.prototype.read = function read(pref, tags) {
+ // first cast into a ReadPreference object to support tags
+ var read = readPref.call(readPref, pref, tags);
+ return Query.base.read.call(this, read);
+};
/**
- * Sets the lean option.
+ * Merges another Query or conditions object into this one.
*
- * Documents returned from queries with the `lean` option enabled are plain javascript objects, not [MongooseDocuments](#document-js). They have no `save` method, getters/setters or other Mongoose magic applied.
+ * When a Query is passed, conditions, field selection and options are merged.
*
- * ####Example:
+ * New in 3.7.0
*
- * new Query().lean() // true
- * new Query().lean(true)
- * new Query().lean(false)
+ * @method merge
+ * @memberOf Query
+ * @param {Query|Object} source
+ * @return {Query} this
+ */
+
+/**
+ * Sets query options.
*
- * Model.find().lean().exec(function (err, docs) {
- * docs[0] instanceof mongoose.Document // false
- * });
+ * ####Options:
*
- * This is a [great](https://groups.google.com/forum/#!topic/mongoose-orm/u2_DzDydcnA/discussion) option in high-performance read-only scenarios, especially when combined with [stream](#query_Query-stream).
+ * - [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors) *
+ * - [sort](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsort(\)%7D%7D) *
+ * - [limit](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D) *
+ * - [skip](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D) *
+ * - [maxscan](https://docs.mongodb.org/v3.2/reference/operator/meta/maxScan/#metaOp._S_maxScan) *
+ * - [batchSize](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D) *
+ * - [comment](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment) *
+ * - [snapshot](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D) *
+ * - [hint](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint) *
+ * - [readPreference](http://docs.mongodb.org/manual/applications/replication/#read-preference) **
+ * - [lean](./api.html#query_Query-lean) *
+ * - [safe](http://www.mongodb.org/display/DOCS/getLastError+Command)
*
- * @param {Boolean} bool defaults to true
- * @return {Query} this
+ * _* denotes a query helper method is also available_
+ * _** query helper method to set `readPreference` is `read()`_
+ *
+ * @param {Object} options
* @api public
*/
-Query.prototype.lean = function (v) {
- this.options.lean = arguments.length ? !!v : true;
- return this;
-}
+Query.prototype.setOptions = function(options, overwrite) {
+ // overwrite is only for internal use
+ if (overwrite) {
+ // ensure that _mongooseOptions & options are two different objects
+ this._mongooseOptions = (options && utils.clone(options)) || {};
+ this.options = options || {};
+
+ if ('populate' in options) {
+ this.populate(this._mongooseOptions);
+ }
+ return this;
+ }
+
+ if (!(options && options.constructor.name === 'Object')) {
+ return this;
+ }
+
+ return Query.base.setOptions.call(this, options);
+};
/**
- * Sets the tailable option (for use with capped collections).
+ * Returns the current query conditions as a JSON object.
*
- * ####Example
+ * ####Example:
*
- * Kitten.find().tailable() // true
- * Kitten.find().tailable(true)
- * Kitten.find().tailable(false)
+ * var query = new Query();
+ * query.find({ a: 1 }).where('b').gt(2);
+ * query.getQuery(); // { a: 1, b: { $gt: 2 } }
*
- * @param {Boolean} bool defaults to true
- * @see tailable http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/
+ * @return {Object} current query conditions
* @api public
*/
-Query.prototype.tailable = function (v) {
- this.options.tailable = arguments.length ? !!v : true;
- return this;
+Query.prototype.getQuery = function() {
+ return this._conditions;
};
/**
- * Executes the query as a find() operation.
+ * Returns the current update operations as a JSON object.
*
- * @param {Function} callback
- * @return {Query} this
- * @api private
+ * ####Example:
+ *
+ * var query = new Query();
+ * query.update({}, { $set: { a: 5 } });
+ * query.getUpdate(); // { $set: { a: 5 } }
+ *
+ * @return {Object} current update operations
+ * @api public
*/
-Query.prototype.execFind = function (callback) {
- var model = this.model
- , promise = new Promise(callback);
+Query.prototype.getUpdate = function() {
+ return this._update;
+};
- try {
- this.cast(model);
- } catch (err) {
- promise.error(err);
- return this;
+/**
+ * Returns fields selection for this query.
+ *
+ * @method _fieldsForExec
+ * @return {Object}
+ * @api private
+ * @receiver Query
+ */
+
+/**
+ * Return an update document with corrected $set operations.
+ *
+ * @method _updateForExec
+ * @api private
+ * @receiver Query
+ */
+
+Query.prototype._updateForExec = function() {
+ var update = utils.clone(this._update, { retainKeyOrder: true });
+ var ops = Object.keys(update);
+ var i = ops.length;
+ var ret = {};
+
+ while (i--) {
+ var op = ops[i];
+
+ if (this.options.overwrite) {
+ ret[op] = update[op];
+ continue;
+ }
+
+ if ('$' !== op[0]) {
+ // fix up $set sugar
+ if (!ret.$set) {
+ if (update.$set) {
+ ret.$set = update.$set;
+ } else {
+ ret.$set = {};
+ }
+ }
+ ret.$set[op] = update[op];
+ ops.splice(i, 1);
+ if (!~ops.indexOf('$set')) ops.push('$set');
+ } else if ('$set' === op) {
+ if (!ret.$set) {
+ ret[op] = update[op];
+ }
+ } else {
+ ret[op] = update[op];
+ }
}
- // apply default schematype path selections
- this._applyPaths();
+ this._compiledUpdate = ret;
+ return ret;
+};
+
+/**
+ * Makes sure _path is set.
+ *
+ * @method _ensurePath
+ * @param {String} method
+ * @api private
+ * @receiver Query
+ */
+
+/**
+ * Determines if `conds` can be merged using `mquery().merge()`
+ *
+ * @method canMerge
+ * @memberOf Query
+ * @param {Object} conds
+ * @return {Boolean}
+ * @api private
+ */
+
+/**
+ * Returns default options for this query.
+ *
+ * @param {Model} model
+ * @api private
+ */
+
+Query.prototype._optionsForExec = function(model) {
+ var options = Query.base._optionsForExec.call(this);
+
+ delete options.populate;
+ model = model || this.model;
+
+ if (!model) {
+ return options;
+ }
+
+ if (!('safe' in options) && model.schema.options.safe) {
+ options.safe = model.schema.options.safe;
+ }
+
+ if (!('readPreference' in options) && model.schema.options.read) {
+ options.readPreference = model.schema.options.read;
+ }
- var self = this
- , castQuery = this._conditions
- , options = this._optionsForExec(model)
- , fields = utils.clone(this._fields)
+ return options;
+};
+
+/**
+ * Sets the lean option.
+ *
+ * Documents returned from queries with the `lean` option enabled are plain javascript objects, not [MongooseDocuments](#document-js). They have no `save` method, getters/setters or other Mongoose magic applied.
+ *
+ * ####Example:
+ *
+ * new Query().lean() // true
+ * new Query().lean(true)
+ * new Query().lean(false)
+ *
+ * Model.find().lean().exec(function (err, docs) {
+ * docs[0] instanceof mongoose.Document // false
+ * });
+ *
+ * This is a [great](https://groups.google.com/forum/#!topic/mongoose-orm/u2_DzDydcnA/discussion) option in high-performance read-only scenarios, especially when combined with [stream](#query_Query-stream).
+ *
+ * @param {Boolean} bool defaults to true
+ * @return {Query} this
+ * @api public
+ */
- options.fields = this._castFields(fields);
- if (options.fields instanceof Error) {
- promise.error(options.fields);
+Query.prototype.lean = function(v) {
+ this._mongooseOptions.lean = arguments.length ? !!v : true;
+ return this;
+};
+
+/**
+ * Thunk around find()
+ *
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api private
+ */
+Query.prototype._find = function(callback) {
+ if (this._castError) {
+ callback(this._castError);
return this;
}
- model.collection.find(castQuery, options, function (err, cursor) {
- if (err) return promise.error(err);
- cursor.toArray(tick(cb));
- });
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
+
+ var fields = this._fieldsForExec();
+ var options = this._mongooseOptions;
+ var _this = this;
- function cb (err, docs) {
- if (err) return promise.error(err);
+ var cb = function(err, docs) {
+ if (err) {
+ return callback(err);
+ }
- if (0 === docs.length) {
- return promise.complete(docs);
+ if (docs.length === 0) {
+ return callback(null, docs);
}
- if (!self.options.populate) {
- return true === options.lean
- ? promise.complete(docs)
- : completeMany(model, docs, fields, self, null, promise);
+ if (!options.populate) {
+ return options.lean === true
+ ? callback(null, docs)
+ : completeMany(_this.model, docs, fields, _this, null, callback);
}
- var pop = helpers.preparePopulationOptions(self, options);
- model.populate(docs, pop, function (err, docs) {
- if (err) return promise.error(err);
- return true === options.lean
- ? promise.complete(docs)
- : completeMany(model, docs, fields, self, pop, promise);
+ var pop = helpers.preparePopulationOptionsMQ(_this, options);
+ pop.__noPromise = true;
+ _this.model.populate(docs, pop, function(err, docs) {
+ if (err) return callback(err);
+ return options.lean === true
+ ? callback(null, docs)
+ : completeMany(_this.model, docs, fields, _this, pop, callback);
});
+ };
+
+ return Query.base.find.call(this, {}, cb);
+};
+
+/**
+ * Finds documents.
+ *
+ * When no `callback` is passed, the query is not executed. When the query is executed, the result will be an array of documents.
+ *
+ * ####Example
+ *
+ * query.find({ name: 'Los Pollos Hermanos' }).find(callback)
+ *
+ * @param {Object} [criteria] mongodb selector
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.find = function(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = {};
+ }
+
+ conditions = utils.toObject(conditions);
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ prepareDiscriminatorCriteria(this);
+
+ try {
+ this.cast(this.model);
+ this._castError = null;
+ } catch (err) {
+ this._castError = err;
}
+ // if we don't have a callback, then just return the query object
+ if (!callback) {
+ return Query.base.find.call(this);
+ }
+
+ this._find(callback);
+
return this;
-}
+};
/*!
* hydrates many documents
@@ -1717,574 +1151,440 @@ Query.prototype.execFind = function (callback) {
* @param {Object} fields
* @param {Query} self
* @param {Array} [pop] array of paths used in population
- * @param {Promise} promise
+ * @param {Function} callback
*/
-function completeMany (model, docs, fields, self, pop, promise) {
+function completeMany(model, docs, fields, self, pop, callback) {
var arr = [];
var count = docs.length;
var len = count;
- var i = 0;
var opts = pop ?
- { populated: pop }
- : undefined;
-
- for (; i < len; ++i) {
- arr[i] = new model(undefined, fields, true);
- arr[i].init(docs[i], opts, function (err) {
- if (err) return promise.error(err);
- --count || promise.complete(arr);
- });
+ {populated: pop}
+ : undefined;
+ function init(err) {
+ if (err) return callback(err);
+ --count || callback(null, arr);
+ }
+ for (var i = 0; i < len; ++i) {
+ arr[i] = helpers.createModel(model, docs[i], fields);
+ arr[i].init(docs[i], opts, init);
}
}
/**
- * Executes the query as a findOne() operation, passing the first found document to the callback.
+ * Thunk around findOne()
*
- * ####Example
+ * @param {Function} [callback]
+ * @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
+ * @api private
+ */
+
+Query.prototype._findOne = function(callback) {
+ if (this._castError) {
+ return callback(this._castError);
+ }
+
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
+
+ var options = this._mongooseOptions;
+ var projection = this._fieldsForExec();
+ var _this = this;
+
+ // don't pass in the conditions because we already merged them in
+ Query.base.findOne.call(_this, {}, function(err, doc) {
+ if (err) {
+ return callback(err);
+ }
+ if (!doc) {
+ return callback(null, null);
+ }
+
+ if (!options.populate) {
+ return options.lean === true
+ ? callback(null, doc)
+ : completeOne(_this.model, doc, null, projection, _this, null, callback);
+ }
+
+ var pop = helpers.preparePopulationOptionsMQ(_this, options);
+ pop.__noPromise = true;
+ _this.model.populate(doc, pop, function(err, doc) {
+ if (err) {
+ return callback(err);
+ }
+ return options.lean === true
+ ? callback(null, doc)
+ : completeOne(_this.model, doc, null, projection, _this, pop, callback);
+ });
+ });
+};
+
+/**
+ * Declares the query a findOne operation. When executed, the first found document is passed to the callback.
*
- * var query = Kitten.find({ color: 'white'});
+ * Passing a `callback` executes the query. The result of the query is a single document.
+ *
+ * ####Example
*
+ * var query = Kitten.where({ color: 'white' });
* query.findOne(function (err, kitten) {
* if (err) return handleError(err);
- *
- * // kitten may be null if no document matched
* if (kitten) {
- * ...
+ * // doc may be null if no document matched
* }
- * })
+ * });
*
- * @param {Function} callback
+ * @param {Object|Query} [criteria] mongodb selector
+ * @param {Object} [projection] optional fields to return
+ * @param {Function} [callback]
* @return {Query} this
* @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
+ * @see Query.select #query_Query-select
* @api public
*/
-Query.prototype.findOne = function (callback) {
- this.op = 'findOne';
+Query.prototype.findOne = function(conditions, projection, options, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = null;
+ projection = null;
+ options = null;
+ } else if (typeof projection === 'function') {
+ callback = projection;
+ options = null;
+ projection = null;
+ } else if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
- if (!callback) return this;
+ // make sure we don't send in the whole Document to merge()
+ conditions = utils.toObject(conditions);
- var model = this.model;
- var promise = new Promise(callback);
+ this.op = 'findOne';
- try {
- this.cast(model);
- } catch (err) {
- promise.error(err);
- return this;
+ if (options) {
+ this.setOptions(options);
}
- // apply default schematype path selections
- this._applyPaths();
-
- var self = this
- , castQuery = this._conditions
- , options = this._optionsForExec(model)
- , fields = utils.clone(this._fields)
+ if (projection) {
+ this.select(projection);
+ }
- options.fields = this._castFields(fields);
- if (options.fields instanceof Error) {
- promise.error(options.fields);
- return this;
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ } else if (conditions != null) {
+ throw new Error('Invalid argument to findOne(): ' +
+ util.inspect(conditions));
}
- model.collection.findOne(castQuery, options, tick(function (err, doc) {
- if (err) return promise.error(err);
- if (!doc) return promise.complete(null);
+ prepareDiscriminatorCriteria(this);
- if (!self.options.populate) {
- return true === options.lean
- ? promise.complete(doc)
- : completeOne(model, doc, fields, self, null, promise);
- }
+ try {
+ this.cast(this.model);
+ this._castError = null;
+ } catch (err) {
+ this._castError = err;
+ }
+
+ if (!callback) {
+ // already merged in the conditions, don't need to send them in.
+ return Query.base.findOne.call(this);
+ }
- var pop = helpers.preparePopulationOptions(self, options);
- model.populate(doc, pop, function (err, doc) {
- if (err) return promise.error(err);
- return true === options.lean
- ? promise.complete(doc)
- : completeOne(model, doc, fields, self, pop, promise);
- })
- }));
+ this._findOne(callback);
return this;
-}
+};
-/*!
- * hydrates a document
+/**
+ * Thunk around count()
*
- * @param {Model} model
- * @param {Document} doc
- * @param {Object} fields
- * @param {Query} self
- * @param {Array} [pop] array of paths used in population
- * @param {Promise} promise
+ * @param {Function} [callback]
+ * @see count http://docs.mongodb.org/manual/reference/method/db.collection.count/
+ * @api private
*/
-function completeOne (model, doc, fields, self, pop, promise) {
- var opts = pop ?
- { populated: pop }
- : undefined;
+Query.prototype._count = function(callback) {
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ process.nextTick(function() {
+ callback(err);
+ });
+ return this;
+ }
- var casted = new model(undefined, fields, true);
- casted.init(doc, opts, function (err) {
- if (err) return promise.error(err);
- promise.complete(casted);
- });
-}
+ var conds = this._conditions;
+ var options = this._optionsForExec();
+
+ this._collection.count(conds, options, utils.tick(callback));
+};
/**
- * Exectues the query as a count() operation.
+ * Specifying this query as a `count` query.
*
- * ####Example
+ * Passing a `callback` executes the query.
*
- * Kitten.where('color', 'black').count(function (err, count) {
+ * ####Example:
+ *
+ * var countQuery = model.where({ 'color': 'black' }).count();
+ *
+ * query.count({ color: 'black' }).count(callback)
+ *
+ * query.count({ color: 'black' }, callback)
+ *
+ * query.where('color', 'black').count(function (err, count) {
* if (err) return handleError(err);
- * console.log('there are %d black kittens', count);
+ * console.log('there are %d kittens', count);
* })
*
- * @param {Function} callback
+ * @param {Object} [criteria] mongodb selector
+ * @param {Function} [callback]
* @return {Query} this
* @see count http://docs.mongodb.org/manual/reference/method/db.collection.count/
* @api public
*/
-Query.prototype.count = function (callback) {
- this.op = 'count';
- if (!callback) return this;
+Query.prototype.count = function(conditions, callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = undefined;
+ }
- var model = this.model;
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
- try {
- this.cast(model);
- } catch (err) {
- return callback(err);
+ this.op = 'count';
+ if (!callback) {
+ return this;
}
- var castQuery = this._conditions;
- model.collection.count(castQuery, tick(callback));
+ this._count(callback);
return this;
};
/**
- * Executes this query as a distict() operation.
+ * Declares or executes a distict() operation.
*
- * ####Example
+ * Passing a `callback` executes the query.
*
- * Link.find({ clicks: { $gt: 100 }}).distinct('url', function (err, result) {
- * if (err) return handleError(err);
+ * ####Example
*
- * assert(Array.isArray(result));
- * console.log('unique urls with more than 100 clicks', result);
- * })
+ * distinct(field, conditions, callback)
+ * distinct(field, conditions)
+ * distinct(field, callback)
+ * distinct(field)
+ * distinct(callback)
+ * distinct()
*
- * @param {String} field
- * @param {Function} callback
+ * @param {String} [field]
+ * @param {Object|Query} [criteria]
+ * @param {Function} [callback]
* @return {Query} this
* @see distinct http://docs.mongodb.org/manual/reference/method/db.collection.distinct/
* @api public
*/
-Query.prototype.distinct = function (field, callback) {
- this.op = 'distinct';
- var model = this.model;
-
- try {
- this.cast(model);
- } catch (err) {
- return callback(err);
+Query.prototype.distinct = function(field, conditions, callback) {
+ if (!callback) {
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = undefined;
+ } else if (typeof field === 'function') {
+ callback = field;
+ field = undefined;
+ conditions = undefined;
+ }
}
- var castQuery = this._conditions;
- model.collection.distinct(field, castQuery, tick(callback));
+ conditions = utils.toObject(conditions);
- return this;
-};
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
-/*!
- * These operators require casting docs
- * to real Documents for Update operations.
- */
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ if (!callback) {
+ throw err;
+ }
+ callback(err);
+ return this;
+ }
-var castOps = {
- $push: 1
- , $pushAll: 1
- , $addToSet: 1
- , $set: 1
+ return Query.base.distinct.call(this, {}, field, callback);
};
-/*!
- * These operators should be cast to numbers instead
- * of their path schema type.
- */
-
-var numberOps = {
- $pop: 1
- , $unset: 1
- , $inc: 1
-}
-
/**
- * Executes this query as an update() operation.
+ * Sets the sort order
+ *
+ * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
*
- * _All paths passed that are not $atomic operations will become $set ops so we retain backwards compatibility._
+ * If a string is passed, it must be a space delimited list of path names. The
+ * sort order of each path is ascending unless the path name is prefixed with `-`
+ * which will be treated as descending.
*
* ####Example
*
- * Model.update({..}, { title: 'remove words' }, ...)
- *
- * // becomes
+ * // sort by "field" ascending and "test" descending
+ * query.sort({ field: 'asc', test: -1 });
*
- * Model.update({..}, { $set: { title: 'remove words' }}, ...)
+ * // equivalent
+ * query.sort('field -test');
*
* ####Note
*
- * Passing an empty object `{}` as the doc will result in a no-op. The update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting the collection.
+ * Cannot be used with `distinct()`
*
- * @param {Object} doc the update conditions
- * @param {Function} callback
+ * @param {Object|String} arg
* @return {Query} this
+ * @see cursor.sort http://docs.mongodb.org/manual/reference/method/cursor.sort/
* @api public
- * @see Model.update #model_Model.update
- * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
*/
-Query.prototype.update = function update (doc, callback) {
- this.op = 'update';
- this._updateArg = doc;
+Query.prototype.sort = function(arg) {
+ var nArg = {};
- var model = this.model
- , options = this._optionsForExec(model)
- , fn = 'function' == typeof callback
- , castedQuery
- , castedDoc
-
- castedQuery = castQuery(this);
- if (castedQuery instanceof Error) {
- if (fn) {
- process.nextTick(callback.bind(null, castedQuery));
- return this;
- }
- throw castedQuery;
- }
-
- castedDoc = castDoc(this);
- if (!castedDoc) {
- fn && process.nextTick(callback.bind(null, null, 0));
- return this;
+ if (arguments.length > 1) {
+ throw new Error('sort() only takes 1 Argument');
}
- if (castedDoc instanceof Error) {
- if (fn) {
- process.nextTick(callback.bind(null, castedDoc));
- return this;
+ if (Array.isArray(arg)) {
+ // time to deal with the terrible syntax
+ for (var i = 0; i < arg.length; i++) {
+ if (!Array.isArray(arg[i])) throw new Error('Invalid sort() argument.');
+ nArg[arg[i][0]] = arg[i][1];
}
- throw castedDoc;
- }
-
- if (!fn) {
- options.safe = { w: 0 };
+ } else {
+ nArg = arg;
}
- model.collection.update(castedQuery, castedDoc, options, tick(callback));
- return this;
+ return Query.base.sort.call(this, nArg);
};
/**
- * Casts obj for an update command.
+ * Declare and/or execute this query as a remove() operation.
*
- * @param {Object} obj
- * @return {Object} obj after casting its values
- * @api private
- */
-
-Query.prototype._castUpdate = function _castUpdate (obj) {
- var ops = Object.keys(obj)
- , i = ops.length
- , ret = {}
- , hasKeys
- , val
-
- while (i--) {
- var op = ops[i];
- if ('$' !== op[0]) {
- // fix up $set sugar
- if (!ret.$set) {
- if (obj.$set) {
- ret.$set = obj.$set;
- } else {
- ret.$set = {};
- }
- }
- ret.$set[op] = obj[op];
- ops.splice(i, 1);
- if (!~ops.indexOf('$set')) ops.push('$set');
- } else if ('$set' === op) {
- if (!ret.$set) {
- ret[op] = obj[op];
- }
- } else {
- ret[op] = obj[op];
- }
- }
-
- // cast each value
- i = ops.length;
-
- while (i--) {
- op = ops[i];
- val = ret[op];
- if ('Object' === val.constructor.name) {
- hasKeys |= this._walkUpdatePath(val, op);
- } else {
- var msg = 'Invalid atomic update value for ' + op + '. '
- + 'Expected an object, received ' + typeof val;
- throw new Error(msg);
- }
- }
-
- return hasKeys && ret;
-}
-
-/**
- * Walk each path of obj and cast its values
- * according to its schema.
+ * ####Example
*
- * @param {Object} obj - part of a query
- * @param {String} op - the atomic operator ($pull, $set, etc)
- * @param {String} pref - path prefix (internal only)
- * @return {Bool} true if this path has keys to update
- * @api private
- */
-
-Query.prototype._walkUpdatePath = function _walkUpdatePath (obj, op, pref) {
- var prefix = pref ? pref + '.' : ''
- , keys = Object.keys(obj)
- , i = keys.length
- , hasKeys = false
- , schema
- , key
- , val
-
- var strict = 'strict' in this.options
- ? this.options.strict
- : this.model.schema.options.strict;
-
- while (i--) {
- key = keys[i];
- val = obj[key];
-
- if (val && 'Object' === val.constructor.name) {
- // watch for embedded doc schemas
- schema = this._getSchema(prefix + key);
- if (schema && schema.caster && op in castOps) {
- // embedded doc schema
-
- if (strict && !schema) {
- // path is not in our strict schema
- if ('throw' == strict) {
- throw new Error('Field `' + key + '` is not in schema.');
- } else {
- // ignore paths not specified in schema
- delete obj[key];
- }
- } else {
- hasKeys = true;
-
- if ('$each' in val) {
- obj[key] = {
- $each: this._castUpdateVal(schema, val.$each, op)
- }
-
- if (val.$slice) {
- obj[key].$slice = val.$slice | 0;
- }
-
- if (val.$sort) {
- obj[key].$sort = val.$sort;
- }
-
- } else {
- obj[key] = this._castUpdateVal(schema, val, op);
- }
- }
- } else {
- hasKeys |= this._walkUpdatePath(val, op, prefix + key);
- }
- } else {
- schema = '$each' === key
- ? this._getSchema(pref)
- : this._getSchema(prefix + key);
-
- var skip = strict &&
- !schema &&
- !/real|nested/.test(this.model.schema.pathType(prefix + key));
-
- if (skip) {
- if ('throw' == strict) {
- throw new Error('Field `' + prefix + key + '` is not in schema.');
- } else {
- delete obj[key];
- }
- } else {
- hasKeys = true;
- obj[key] = this._castUpdateVal(schema, val, op, key);
- }
- }
- }
- return hasKeys;
-}
-
-/**
- * Casts `val` according to `schema` and atomic `op`.
+ * Model.remove({ artist: 'Anne Murray' }, callback)
*
- * @param {Schema} schema
- * @param {Object} val
- * @param {String} op - the atomic operator ($pull, $set, etc)
- * @param {String} [$conditional]
- * @api private
+ * ####Note
+ *
+ * The operation is only executed when a callback is passed. To force execution without a callback, you must first call `remove()` and then execute it by using the `exec()` method.
+ *
+ * // not executed
+ * var query = Model.find().remove({ name: 'Anne Murray' })
+ *
+ * // executed
+ * query.remove({ name: 'Anne Murray' }, callback)
+ * query.remove({ name: 'Anne Murray' }).remove(callback)
+ *
+ * // executed without a callback
+ * query.exec()
+ *
+ * // summary
+ * query.remove(conds, fn); // executes
+ * query.remove(conds)
+ * query.remove(fn) // executes
+ * query.remove()
+ *
+ * @param {Object|Query} [criteria] mongodb selector
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @see remove http://docs.mongodb.org/manual/reference/method/db.collection.remove/
+ * @api public
*/
-Query.prototype._castUpdateVal = function _castUpdateVal (schema, val, op, $conditional) {
- if (!schema) {
- // non-existing schema path
- return op in numberOps
- ? Number(val)
- : val
+Query.prototype.remove = function(cond, callback) {
+ if (typeof cond === 'function') {
+ callback = cond;
+ cond = null;
}
- if (schema.caster && op in castOps &&
- (utils.isObject(val) || Array.isArray(val))) {
- // Cast values for ops that add data to MongoDB.
- // Ensures embedded documents get ObjectIds etc.
- var tmp = schema.cast(val);
+ var cb = typeof callback === 'function';
- if (Array.isArray(val)) {
- val = tmp;
- } else {
- val = tmp[0];
- }
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ if (cb) return process.nextTick(callback.bind(null, err));
+ return this;
}
- if (op in numberOps) return Number(val);
- if (/^\$/.test($conditional)) return schema.castForQuery($conditional, val);
- return schema.castForQuery(val)
-}
-
-/**
- * Finds the schema for `path`. This is different than
- * calling `schema.path` as it also resolves paths with
- * positional selectors (something.$.another.$.path).
- *
- * @param {String} path
- * @api private
- */
-
-Query.prototype._getSchema = function _getSchema (path) {
- return this.model._getSchema(path);
-}
+ return Query.base.remove.call(this, cond, callback);
+};
-/**
- * Casts selected field arguments for field selection with mongo 2.2
- *
- * query.select({ ids: { $elemMatch: { $in: [hexString] }})
+/*!
+ * hydrates a document
*
+ * @param {Model} model
+ * @param {Document} doc
+ * @param {Object} res 3rd parameter to callback
* @param {Object} fields
- * @see https://github.com/LearnBoost/mongoose/issues/1091
- * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
- * @api private
+ * @param {Query} self
+ * @param {Array} [pop] array of paths used in population
+ * @param {Function} callback
*/
-Query.prototype._castFields = function _castFields (fields) {
- var selected
- , elemMatchKeys
- , keys
- , key
- , out
- , i
-
- if (fields) {
- keys = Object.keys(fields);
- elemMatchKeys = [];
- i = keys.length;
-
- // collect $elemMatch args
- while (i--) {
- key = keys[i];
- if (fields[key].$elemMatch) {
- selected || (selected = {});
- selected[key] = fields[key];
- elemMatchKeys.push(key);
- }
- }
- }
+function completeOne(model, doc, res, fields, self, pop, callback) {
+ var opts = pop ?
+ {populated: pop}
+ : undefined;
- if (selected) {
- // they passed $elemMatch, cast em
- try {
- out = this.cast(this.model, selected);
- } catch (err) {
- return err;
+ var casted = helpers.createModel(model, doc, fields);
+ casted.init(doc, opts, function(err) {
+ if (err) {
+ return callback(err);
}
-
- // apply the casted field args
- i = elemMatchKeys.length;
- while (i--) {
- key = elemMatchKeys[i];
- fields[key] = out[key];
+ if (res) {
+ return callback(null, casted, res);
}
- }
-
- return fields;
+ callback(null, casted);
+ });
}
-/**
- * Executes this query as a remove() operation.
- *
- * ####Example
- *
- * Cassette.where('artist').equals('Anne Murray').remove(callback)
- *
- * @param {Function} callback
- * @api public
- * @see remove http://docs.mongodb.org/manual/reference/method/db.collection.remove/
+/*!
+ * If the model is a discriminator type and not root, then add the key & value to the criteria.
*/
-Query.prototype.remove = function (callback) {
- this.op = 'remove';
-
- var model = this.model
- , options = this._optionsForExec(model)
- , cb = 'function' == typeof callback
-
- try {
- this.cast(model);
- } catch (err) {
- if (cb) return callback(err);
- throw err;
+function prepareDiscriminatorCriteria(query) {
+ if (!query || !query.model || !query.model.schema) {
+ return;
}
- if (!cb) {
- options.safe = { w: 0 };
- }
+ var schema = query.model.schema;
- var castQuery = this._conditions;
- model.collection.remove(castQuery, options, tick(callback));
- return this;
-};
+ if (schema && schema.discriminatorMapping && !schema.discriminatorMapping.isRoot) {
+ query._conditions[schema.discriminatorMapping.key] = schema.discriminatorMapping.value;
+ }
+}
/**
* Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) update command.
*
- * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
+ * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed.
*
* ####Available options
*
- * - `new`: bool - true to return the modified document rather than the original. defaults to true
+ * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
* - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
+ * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
+ * - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * - `context` (string) if set to 'query' and `runValidators` is on, `this` will refer to the query in custom validator functions that update validation runs. Does nothing if `runValidators` is false.
+ *
+ * ####Callback Signature
+ * function(error, doc) {
+ * // error: any errors that occurred
+ * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
+ * }
*
* ####Examples
*
@@ -2292,10 +1592,14 @@ Query.prototype.remove = function (callback) {
* query.findOneAndUpdate(conditions, update, options) // returns Query
* query.findOneAndUpdate(conditions, update, callback) // executes
* query.findOneAndUpdate(conditions, update) // returns Query
+ * query.findOneAndUpdate(update, callback) // returns Query
+ * query.findOneAndUpdate(update) // returns Query
* query.findOneAndUpdate(callback) // executes
* query.findOneAndUpdate() // returns Query
*
- * @param {Object} [query]
+ * @method findOneAndUpdate
+ * @memberOf Query
+ * @param {Object|Query} [query]
* @param {Object} [doc]
* @param {Object} [options]
* @param {Function} [callback]
@@ -2304,66 +1608,96 @@ Query.prototype.remove = function (callback) {
* @api public
*/
-Query.prototype.findOneAndUpdate = function (query, doc, options, callback) {
+Query.prototype.findOneAndUpdate = function(criteria, doc, options, callback) {
this.op = 'findOneAndUpdate';
+ this._validate();
switch (arguments.length) {
case 3:
- if ('function' == typeof options)
- callback = options, options = {};
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
break;
case 2:
- if ('function' == typeof doc) {
+ if (typeof doc === 'function') {
callback = doc;
- doc = query;
- query = undefined;
+ doc = criteria;
+ criteria = undefined;
}
options = undefined;
break;
case 1:
- if ('function' == typeof query) {
- callback = query;
- query = options = doc = undefined;
+ if (typeof criteria === 'function') {
+ callback = criteria;
+ criteria = options = doc = undefined;
} else {
- doc = query;
- query = options = undefined;
+ doc = criteria;
+ criteria = options = undefined;
}
}
- // apply query
- if (query) {
- if ('Object' === query.constructor.name) {
- merge(this._conditions, query);
- } else if (query instanceof Query) {
- merge(this._conditions, query._conditions);
- } else if (query instanceof Document) {
- merge(this._conditions, query.toObject());
- }
+ if (mquery.canMerge(criteria)) {
+ this.merge(criteria);
}
// apply doc
if (doc) {
- merge(this._updateArg, doc);
+ this._mergeUpdate(doc);
}
- // apply options
- options && this.setOptions(options);
+ if (options) {
+ options = utils.clone(options, { retainKeyOrder: true });
+ if (options.projection) {
+ this.select(options.projection);
+ delete options.projection;
+ }
+ if (options.fields) {
+ this.select(options.fields);
+ delete options.fields;
+ }
- if (!callback) return this;
+ this.setOptions(options);
+ }
- return this._findAndModify('update', callback);
-}
+ if (!callback) {
+ return this;
+ }
+
+ return this._findOneAndUpdate(callback);
+};
/**
- * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) remove command.
- *
- * Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if `callback` is passed else a Query object is returned.
- *
- * ####Available options
- *
- * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
- *
- * ####Examples
+ * Thunk around findOneAndUpdate()
+ *
+ * @param {Function} [callback]
+ * @api private
+ */
+
+Query.prototype._findOneAndUpdate = function(callback) {
+ this._findAndModify('update', callback);
+ return this;
+};
+
+/**
+ * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) remove command.
+ *
+ * Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if `callback` is passed.
+ *
+ * ####Available options
+ *
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
+ * - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ *
+ * ####Callback Signature
+ * function(error, doc, result) {
+ * // error: any errors that occurred
+ * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
+ * // result: [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
+ * }
+ *
+ * ####Examples
*
* A.where().findOneAndRemove(conditions, options, callback) // executes
* A.where().findOneAndRemove(conditions, options) // return Query
@@ -2372,6 +1706,8 @@ Query.prototype.findOneAndUpdate = function (query, doc, options, callback) {
* A.where().findOneAndRemove(callback) // executes
* A.where().findOneAndRemove() // returns Query
*
+ * @method findOneAndRemove
+ * @memberOf Query
* @param {Object} [conditions]
* @param {Object} [options]
* @param {Function} [callback]
@@ -2380,244 +1716,1599 @@ Query.prototype.findOneAndUpdate = function (query, doc, options, callback) {
* @api public
*/
-Query.prototype.findOneAndRemove = function (conditions, options, callback) {
+Query.prototype.findOneAndRemove = function(conditions, options, callback) {
this.op = 'findOneAndRemove';
+ this._validate();
- if ('function' == typeof options) {
- callback = options;
- options = undefined;
- } else if ('function' == typeof conditions) {
- callback = conditions;
- conditions = undefined;
+ switch (arguments.length) {
+ case 2:
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ break;
+ case 1:
+ if (typeof conditions === 'function') {
+ callback = conditions;
+ conditions = undefined;
+ options = undefined;
+ }
+ break;
}
- // apply conditions
- if (conditions) {
- if ('Object' === conditions.constructor.name) {
- merge(this._conditions, conditions);
- } else if (conditions instanceof Query) {
- merge(this._conditions, conditions._conditions);
- } else if (conditions instanceof Document) {
- merge(this._conditions, conditions.toObject());
- }
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
}
- // apply options
options && this.setOptions(options);
- if (!callback) return this;
+ if (!callback) {
+ return this;
+ }
- return this._findAndModify('remove', callback);
-}
+ this._findOneAndRemove(callback);
+
+ return this;
+};
+
+/**
+ * Thunk around findOneAndRemove()
+ *
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api private
+ */
+Query.prototype._findOneAndRemove = function(callback) {
+ Query.base.findOneAndRemove.call(this, callback);
+};
/**
- * _findAndModify
+ * Override mquery.prototype._findAndModify to provide casting etc.
*
* @param {String} type - either "remove" or "update"
* @param {Function} callback
* @api private
*/
-Query.prototype._findAndModify = function (type, callback) {
- var model = this.model
- , promise = new Promise(callback)
- , self = this
- , castedQuery
- , castedDoc
- , fields
- , sort
- , opts
+Query.prototype._findAndModify = function(type, callback) {
+ if (typeof callback !== 'function') {
+ throw new Error('Expected callback in _findAndModify');
+ }
+
+ var model = this.model;
+ var schema = model.schema;
+ var _this = this;
+ var castedQuery;
+ var castedDoc;
+ var fields;
+ var opts;
+ var doValidate;
castedQuery = castQuery(this);
if (castedQuery instanceof Error) {
- process.nextTick(promise.error.bind(promise, castedQuery));
- return promise;
+ return callback(castedQuery);
}
opts = this._optionsForExec(model);
- if ('remove' == type) {
+ if ('strict' in opts) {
+ this._mongooseOptions.strict = opts.strict;
+ }
+
+ if (type === 'remove') {
opts.remove = true;
} else {
- if (!('new' in opts)) opts.new = true;
- if (!('upsert' in opts)) opts.upsert = false;
+ if (!('new' in opts)) {
+ opts.new = false;
+ }
+ if (!('upsert' in opts)) {
+ opts.upsert = false;
+ }
+ if (opts.upsert || opts['new']) {
+ opts.remove = false;
+ }
- castedDoc = castDoc(this);
+ castedDoc = castDoc(this, opts.overwrite);
+ castedDoc = setDefaultsOnInsert(this, schema, castedDoc, opts);
if (!castedDoc) {
if (opts.upsert) {
// still need to do the upsert to empty doc
- castedDoc = { $set: {} };
+ var doc = utils.clone(castedQuery);
+ delete doc._id;
+ castedDoc = {$set: doc};
} else {
return this.findOne(callback);
}
} else if (castedDoc instanceof Error) {
- process.nextTick(promise.error.bind(promise, castedDoc));
- return promise;
+ return callback(castedDoc);
+ } else {
+ // In order to make MongoDB 2.6 happy (see
+ // https://jira.mongodb.org/browse/SERVER-12266 and related issues)
+ // if we have an actual update document but $set is empty, junk the $set.
+ if (castedDoc.$set && Object.keys(castedDoc.$set).length === 0) {
+ delete castedDoc.$set;
+ }
}
+
+ doValidate = updateValidators(this, schema, castedDoc, opts);
}
this._applyPaths();
+ var options = this._mongooseOptions;
+
if (this._fields) {
- fields = utils.clone(this._fields)
+ fields = utils.clone(this._fields);
opts.fields = this._castFields(fields);
if (opts.fields instanceof Error) {
- process.nextTick(promise.error.bind(promise, opts.fields));
- return promise;
+ return callback(opts.fields);
}
}
- // the driver needs a default
- sort = opts.sort || [];
+ if (opts.sort) convertSortToArray(opts);
+
+ var cb = function(err, doc, res) {
+ if (err) {
+ return callback(err);
+ }
- model
- .collection
- .findAndModify(castedQuery, sort, castedDoc, opts, tick(function (err, doc) {
- if (err) return promise.error(err);
if (!doc || (utils.isObject(doc) && Object.keys(doc).length === 0)) {
- return promise.complete(null);
+ if (opts.passRawResult) {
+ return callback(null, null, res);
+ }
+ return callback(null, null);
}
- if (!self.options.populate) {
- return true === opts.lean
- ? promise.complete(doc)
- : completeOne(model, doc, fields, self, null, promise);
+ if (!opts.passRawResult) {
+ res = null;
}
- var pop = helpers.preparePopulationOptions(self, opts);
- model.populate(doc, pop, function (err, doc) {
- if (err) return promise.error(err);
- return true === opts.lean
- ? promise.complete(doc)
- : completeOne(model, doc, fields, self, pop, promise);
- })
- }));
+ if (!options.populate) {
+ return options.lean === true
+ ? callback(null, doc)
+ : completeOne(_this.model, doc, res, fields, _this, null, callback);
+ }
- return promise;
-}
+ var pop = helpers.preparePopulationOptionsMQ(_this, options);
+ pop.__noPromise = true;
+ _this.model.populate(doc, pop, function(err, doc) {
+ if (err) {
+ return callback(err);
+ }
+
+ return options.lean === true
+ ? callback(null, doc)
+ : completeOne(_this.model, doc, res, fields, _this, pop, callback);
+ });
+ };
+
+ if (opts.runValidators && doValidate) {
+ var _callback = function(error) {
+ if (error) {
+ return callback(error);
+ }
+ _this._collection.findAndModify(castedQuery, castedDoc, opts, utils.tick(function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+ };
+
+ try {
+ doValidate(_callback);
+ } catch (error) {
+ callback(error);
+ }
+ } else {
+ this._collection.findAndModify(castedQuery, castedDoc, opts, utils.tick(function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+ }
+
+ return this;
+};
/**
- * Specifies paths which should be populated with other documents.
- *
- * ####Example:
- *
- * Kitten.findOne().populate('owner').exec(function (err, kitten) {
- * console.log(kitten.owner.name) // Max
- * })
- *
- * Kitten.find().populate({
- * path: 'owner'
- * , select: 'name'
- * , match: { color: 'black' }
- * , options: { sort: { name: -1 }}
- * }).exec(function (err, kittens) {
- * console.log(kittens[0].owner.name) // Zoopa
- * })
- *
- * // alternatively
- * Kitten.find().populate('owner', 'name', null, {sort: { name: -1 }}).exec(function (err, kittens) {
- * console.log(kittens[0].owner.name) // Zoopa
- * })
+ * Override mquery.prototype._mergeUpdate to handle mongoose objects in
+ * updates.
*
- * Paths are populated after the query executes and a response is received. A separate query is then executed for each path specified for population. After a response for each query has also been returned, the results are passed to the callback.
+ * @param {Object} doc
+ * @api private
+ */
+
+Query.prototype._mergeUpdate = function(doc) {
+ if (!this._update) this._update = {};
+ if (doc instanceof Query) {
+ if (doc._update) {
+ utils.mergeClone(this._update, doc._update);
+ }
+ } else {
+ utils.mergeClone(this._update, doc);
+ }
+};
+
+/*!
+ * The mongodb driver 1.3.23 only supports the nested array sort
+ * syntax. We must convert it or sorting findAndModify will not work.
+ */
+
+function convertSortToArray(opts) {
+ if (Array.isArray(opts.sort)) {
+ return;
+ }
+ if (!utils.isObject(opts.sort)) {
+ return;
+ }
+
+ var sort = [];
+
+ for (var key in opts.sort) {
+ if (utils.object.hasOwnProperty(opts.sort, key)) {
+ sort.push([key, opts.sort[key]]);
+ }
+ }
+
+ opts.sort = sort;
+}
+
+/**
+ * Internal thunk for .update()
*
- * @param {Object|String} path either the path to populate or an object specifying all parameters
- * @param {Object|String} [select] Field selection for the population query
- * @param {Model} [model] The name of the model you wish to use for population. If not specified, the name is looked up from the Schema ref.
- * @param {Object} [match] Conditions for the population query
- * @param {Object} [options] Options for the population query (sort, etc)
- * @see population ./populate.html
- * @see Query#select #query_Query-select
- * @see Model.populate #model_Model.populate
- * @return {Query} this
- * @api public
+ * @param {Function} callback
+ * @see Model.update #model_Model.update
+ * @api private
*/
+Query.prototype._execUpdate = function(callback) {
+ var schema = this.model.schema;
+ var doValidate;
+ var _this;
-Query.prototype.populate = function populate () {
- var res = utils.populate.apply(null, arguments);
- var opts = this.options;
+ var castedQuery = this._conditions;
+ var castedDoc = this._update;
+ var options = this.options;
- if (!utils.isObject(opts.populate)) {
- opts.populate = {};
+ if (this._castError) {
+ callback(this._castError);
+ return this;
}
- for (var i = 0; i < res.length; ++i) {
- opts.populate[res[i].path] = res[i];
+ if (this.options.runValidators) {
+ _this = this;
+ doValidate = updateValidators(this, schema, castedDoc, options);
+ var _callback = function(err) {
+ if (err) {
+ return callback(err);
+ }
+
+ Query.base.update.call(_this, castedQuery, castedDoc, options, callback);
+ };
+ try {
+ doValidate(_callback);
+ } catch (err) {
+ process.nextTick(function() {
+ callback(err);
+ });
+ }
+ return this;
}
+ Query.base.update.call(this, castedQuery, castedDoc, options, callback);
return this;
-}
+};
/**
- * Returns a Node.js 0.8 style [read stream](http://nodejs.org/docs/v0.8.21/api/stream.html#stream_readable_stream) interface.
+ * Declare and/or execute this query as an update() operation.
+ *
+ * _All paths passed that are not $atomic operations will become $set ops._
*
* ####Example
*
- * // follows the nodejs 0.8 stream api
- * Thing.find({ name: /^hello/ }).stream().pipe(res)
+ * Model.where({ _id: id }).update({ title: 'words' })
*
- * // manual streaming
- * var stream = Thing.find({ name: /^hello/ }).stream();
+ * // becomes
*
- * stream.on('data', function (doc) {
- * // do something with the mongoose document
- * }).on('error', function (err) {
- * // handle the error
- * }).on('close', function () {
- * // the stream is closed
- * });
+ * Model.where({ _id: id }).update({ $set: { title: 'words' }})
*
- * ####Valid options
+ * ####Valid options:
*
- * - `transform`: optional function which accepts a mongoose document. The return value of the function will be emitted on `data`.
+ * - `safe` (boolean) safe mode (defaults to value set in schema (true))
+ * - `upsert` (boolean) whether to create the doc if it doesn't match (false)
+ * - `multi` (boolean) whether multiple documents should be updated (false)
+ * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
+ * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
+ * - `strict` (boolean) overrides the `strict` option for this update
+ * - `overwrite` (boolean) disables update-only mode, allowing you to overwrite the doc (false)
+ * - `context` (string) if set to 'query' and `runValidators` is on, `this` will refer to the query in custom validator functions that update validation runs. Does nothing if `runValidators` is false.
*
- * ####Example
+ * ####Note
*
- * // JSON.stringify all documents before emitting
- * var stream = Thing.find().stream({ transform: JSON.stringify });
- * stream.pipe(writeStream);
+ * Passing an empty object `{}` as the doc will result in a no-op unless the `overwrite` option is passed. Without the `overwrite` option set, the update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting documents in the collection.
*
- * @return {QueryStream}
+ * ####Note
+ *
+ * The operation is only executed when a callback is passed. To force execution without a callback, we must first call update() and then execute it by using the `exec()` method.
+ *
+ * var q = Model.where({ _id: id });
+ * q.update({ $set: { name: 'bob' }}).update(); // not executed
+ *
+ * q.update({ $set: { name: 'bob' }}).exec(); // executed
+ *
+ * // keys that are not $atomic ops become $set.
+ * // this executes the same command as the previous example.
+ * q.update({ name: 'bob' }).exec();
+ *
+ * // overwriting with empty docs
+ * var q = Model.where({ _id: id }).setOptions({ overwrite: true })
+ * q.update({ }, callback); // executes
+ *
+ * // multi update with overwrite to empty doc
+ * var q = Model.where({ _id: id });
+ * q.setOptions({ multi: true, overwrite: true })
+ * q.update({ });
+ * q.update(callback); // executed
+ *
+ * // multi updates
+ * Model.where()
+ * .update({ name: /^match/ }, { $set: { arr: [] }}, { multi: true }, callback)
+ *
+ * // more multi updates
+ * Model.where()
+ * .setOptions({ multi: true })
+ * .update({ $set: { arr: [] }}, callback)
+ *
+ * // single update by default
+ * Model.where({ email: 'address@example.com' })
+ * .update({ $inc: { counter: 1 }}, callback)
+ *
+ * API summary
+ *
+ * update(criteria, doc, options, cb) // executes
+ * update(criteria, doc, options)
+ * update(criteria, doc, cb) // executes
+ * update(criteria, doc)
+ * update(doc, cb) // executes
+ * update(doc)
+ * update(cb) // executes
+ * update(true) // executes
+ * update()
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
* @param {Object} [options]
- * @see QueryStream
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @see Model.update #model_Model.update
+ * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
* @api public
*/
-Query.prototype.stream = function stream (opts) {
- return new QueryStream(this, opts);
-}
+Query.prototype.update = function(conditions, doc, options, callback) {
+ if (typeof options === 'function') {
+ // .update(conditions, doc, callback)
+ callback = options;
+ options = null;
+ } else if (typeof doc === 'function') {
+ // .update(doc, callback);
+ callback = doc;
+ doc = conditions;
+ conditions = {};
+ options = null;
+ } else if (typeof conditions === 'function') {
+ // .update(callback)
+ callback = conditions;
+ conditions = undefined;
+ doc = undefined;
+ options = undefined;
+ } else if (typeof conditions === 'object' && !doc && !options && !callback) {
+ // .update(doc)
+ doc = conditions;
+ conditions = undefined;
+ options = undefined;
+ callback = undefined;
+ }
-// helpers
+ // make sure we don't send in the whole Document to merge()
+ conditions = utils.toObject(conditions);
-/*!
- * castDoc
- * @api private
- */
+ var oldCb = callback;
+ if (oldCb) {
+ if (typeof oldCb === 'function') {
+ callback = function(error, result) {
+ oldCb(error, result ? result.result : {ok: 0, n: 0, nModified: 0});
+ };
+ } else {
+ throw new Error('Invalid callback() argument.');
+ }
+ }
+
+ // strict is an option used in the update checking, make sure it gets set
+ if (options) {
+ if ('strict' in options) {
+ this._mongooseOptions.strict = options.strict;
+ }
+ }
+
+ // if doc is undefined at this point, this means this function is being
+ // executed by exec(not always see below). Grab the update doc from here in
+ // order to validate
+ // This could also be somebody calling update() or update({}). Probably not a
+ // common use case, check for _update to make sure we don't do anything bad
+ if (!doc && this._update) {
+ doc = this._updateForExec();
+ }
-function castDoc (query) {
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ // validate the selector part of the query
+ var castedQuery = castQuery(this);
+ if (castedQuery instanceof Error) {
+ this._castError = castedQuery;
+ if (callback) {
+ callback(castedQuery);
+ return this;
+ } else if (!options || !options.dontThrowCastError) {
+ throw castedQuery;
+ }
+ }
+
+ // validate the update part of the query
+ var castedDoc;
try {
- return query._castUpdate(query._updateArg);
+ var $options = {retainKeyOrder: true};
+ if (options && options.minimize) {
+ $options.minimize = true;
+ }
+ castedDoc = this._castUpdate(utils.clone(doc, $options),
+ options && options.overwrite);
} catch (err) {
- return err;
+ this._castError = castedQuery;
+ if (callback) {
+ callback(err);
+ return this;
+ } else if (!options || !options.dontThrowCastError) {
+ throw err;
+ }
}
-}
-/*!
- * castQuery
- * @api private
+ castedDoc = setDefaultsOnInsert(this, this.schema, castedDoc, options);
+ if (!castedDoc) {
+ // Make sure promises know that this is still an update, see gh-2796
+ this.op = 'update';
+ callback && callback(null);
+ return this;
+ }
+
+ if (utils.isObject(options)) {
+ this.setOptions(options);
+ }
+
+ if (!this._update) this._update = castedDoc;
+
+ // Hooks
+ if (callback) {
+ return this._execUpdate(callback);
+ }
+
+ return Query.base.update.call(this, castedQuery, castedDoc, options, callback);
+};
+
+/**
+ * Executes the query
+ *
+ * ####Examples:
+ *
+ * var promise = query.exec();
+ * var promise = query.exec('update');
+ *
+ * query.exec(callback);
+ * query.exec('find', callback);
+ *
+ * @param {String|Function} [operation]
+ * @param {Function} [callback]
+ * @return {Promise}
+ * @api public
*/
-function castQuery (query) {
- try {
- return query.cast(query.model);
- } catch (err) {
- return err;
+Query.prototype.exec = function exec(op, callback) {
+ var Promise = PromiseProvider.get();
+ var _this = this;
+
+ if (typeof op === 'function') {
+ callback = op;
+ op = null;
+ } else if (typeof op === 'string') {
+ this.op = op;
}
-}
+
+ return new Promise.ES6(function(resolve, reject) {
+ if (!_this.op) {
+ callback && callback(null, undefined);
+ resolve();
+ return;
+ }
+
+ _this[_this.op].call(_this, function(error, res) {
+ if (error) {
+ callback && callback(error);
+ reject(error);
+ return;
+ }
+ callback && callback.apply(null, arguments);
+ resolve(res);
+ });
+ });
+};
+
+/**
+ * Executes the query returning a `Promise` which will be
+ * resolved with either the doc(s) or rejected with the error.
+ *
+ * @param {Function} [resolve]
+ * @param {Function} [reject]
+ * @return {Promise}
+ * @api public
+ */
+
+Query.prototype.then = function(resolve, reject) {
+ return this.exec().then(resolve, reject);
+};
+
+/**
+ * Executes the query returning a `Promise` which will be
+ * resolved with either the doc(s) or rejected with the error.
+ * Like `.then()`, but only takes a rejection handler.
+ *
+ * @param {Function} [reject]
+ * @return {Promise}
+ * @api public
+ */
+
+Query.prototype.catch = function(reject) {
+ return this.exec().then(null, reject);
+};
+
+/**
+ * Finds the schema for `path`. This is different than
+ * calling `schema.path` as it also resolves paths with
+ * positional selectors (something.$.another.$.path).
+ *
+ * @param {String} path
+ * @api private
+ */
+
+Query.prototype._getSchema = function _getSchema(path) {
+ return this.model._getSchema(path);
+};
+
+/*!
+ * These operators require casting docs
+ * to real Documents for Update operations.
+ */
+
+var castOps = {
+ $push: 1,
+ $pushAll: 1,
+ $addToSet: 1,
+ $set: 1
+};
+
+/*!
+ * These operators should be cast to numbers instead
+ * of their path schema type.
+ */
+
+var numberOps = {
+ $pop: 1,
+ $unset: 1,
+ $inc: 1
+};
+
+/**
+ * Casts obj for an update command.
+ *
+ * @param {Object} obj
+ * @return {Object} obj after casting its values
+ * @api private
+ */
+
+Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
+ if (!obj) {
+ return undefined;
+ }
+
+ var ops = Object.keys(obj);
+ var i = ops.length;
+ var ret = {};
+ var hasKeys;
+ var val;
+ var hasDollarKey = false;
+
+ while (i--) {
+ var op = ops[i];
+ // if overwrite is set, don't do any of the special $set stuff
+ if (op[0] !== '$' && !overwrite) {
+ // fix up $set sugar
+ if (!ret.$set) {
+ if (obj.$set) {
+ ret.$set = obj.$set;
+ } else {
+ ret.$set = {};
+ }
+ }
+ ret.$set[op] = obj[op];
+ ops.splice(i, 1);
+ if (!~ops.indexOf('$set')) ops.push('$set');
+ } else if (op === '$set') {
+ if (!ret.$set) {
+ ret[op] = obj[op];
+ }
+ } else {
+ ret[op] = obj[op];
+ }
+ }
+
+ // cast each value
+ i = ops.length;
+
+ // if we get passed {} for the update, we still need to respect that when it
+ // is an overwrite scenario
+ if (overwrite) {
+ hasKeys = true;
+ }
+
+ while (i--) {
+ op = ops[i];
+ val = ret[op];
+ hasDollarKey = hasDollarKey || op.charAt(0) === '$';
+ if (val &&
+ val.constructor.name === 'Object' &&
+ (!overwrite || hasDollarKey)) {
+ hasKeys |= this._walkUpdatePath(val, op);
+ } else if (overwrite && ret.constructor.name === 'Object') {
+ // if we are just using overwrite, cast the query and then we will
+ // *always* return the value, even if it is an empty object. We need to
+ // set hasKeys above because we need to account for the case where the
+ // user passes {} and wants to clobber the whole document
+ // Also, _walkUpdatePath expects an operation, so give it $set since that
+ // is basically what we're doing
+ this._walkUpdatePath(ret, '$set');
+ } else {
+ var msg = 'Invalid atomic update value for ' + op + '. '
+ + 'Expected an object, received ' + typeof val;
+ throw new Error(msg);
+ }
+ }
+
+ return hasKeys && ret;
+};
+
+/**
+ * Walk each path of obj and cast its values
+ * according to its schema.
+ *
+ * @param {Object} obj - part of a query
+ * @param {String} op - the atomic operator ($pull, $set, etc)
+ * @param {String} pref - path prefix (internal only)
+ * @return {Bool} true if this path has keys to update
+ * @api private
+ */
+
+Query.prototype._walkUpdatePath = function _walkUpdatePath(obj, op, pref) {
+ var prefix = pref ? pref + '.' : '',
+ keys = Object.keys(obj),
+ i = keys.length,
+ hasKeys = false,
+ schema,
+ key,
+ val;
+
+ var useNestedStrict = this.schema.options.useNestedStrict;
+
+ while (i--) {
+ key = keys[i];
+ val = obj[key];
+
+ if (val && val.constructor.name === 'Object') {
+ // watch for embedded doc schemas
+ schema = this._getSchema(prefix + key);
+ if (schema && schema.caster && op in castOps) {
+ // embedded doc schema
+ hasKeys = true;
+
+ if ('$each' in val) {
+ obj[key] = {
+ $each: this._castUpdateVal(schema, val.$each, op)
+ };
+
+ if (val.$slice != null) {
+ obj[key].$slice = val.$slice | 0;
+ }
+
+ if (val.$sort) {
+ obj[key].$sort = val.$sort;
+ }
+
+ if (!!val.$position || val.$position === 0) {
+ obj[key].$position = val.$position;
+ }
+ } else {
+ obj[key] = this._castUpdateVal(schema, val, op);
+ }
+ } else if (op === '$currentDate') {
+ // $currentDate can take an object
+ obj[key] = this._castUpdateVal(schema, val, op);
+ hasKeys = true;
+ } else if (op === '$set' && schema) {
+ obj[key] = this._castUpdateVal(schema, val, op);
+ hasKeys = true;
+ } else {
+ var pathToCheck = (prefix + key);
+ var v = this.model.schema._getPathType(pathToCheck);
+ var _strict = 'strict' in this._mongooseOptions ?
+ this._mongooseOptions.strict :
+ ((useNestedStrict && v.schema) || this.schema).options.strict;
+ if (v.pathType === 'undefined') {
+ if (_strict === 'throw') {
+ throw new StrictModeError(pathToCheck);
+ } else if (_strict) {
+ delete obj[key];
+ continue;
+ }
+ }
+
+ // gh-2314
+ // we should be able to set a schema-less field
+ // to an empty object literal
+ hasKeys |= this._walkUpdatePath(val, op, prefix + key) ||
+ (utils.isObject(val) && Object.keys(val).length === 0);
+ }
+ } else {
+ var checkPath = (key === '$each' || key === '$or' || key === '$and') ?
+ pref : prefix + key;
+ schema = this._getSchema(checkPath);
+
+ var pathDetails = this.model.schema._getPathType(checkPath);
+ var isStrict = 'strict' in this._mongooseOptions ?
+ this._mongooseOptions.strict :
+ ((useNestedStrict && pathDetails.schema) || this.schema).options.strict;
+
+ var skip = isStrict &&
+ !schema &&
+ !/real|nested/.test(pathDetails.pathType);
+
+ if (skip) {
+ if (isStrict === 'throw') {
+ throw new StrictModeError(prefix + key);
+ } else {
+ delete obj[key];
+ }
+ } else {
+ // gh-1845 temporary fix: ignore $rename. See gh-3027 for tracking
+ // improving this.
+ if (op === '$rename') {
+ hasKeys = true;
+ continue;
+ }
+
+ hasKeys = true;
+ obj[key] = this._castUpdateVal(schema, val, op, key);
+ }
+ }
+ }
+ return hasKeys;
+};
+
+/**
+ * Casts `val` according to `schema` and atomic `op`.
+ *
+ * @param {Schema} schema
+ * @param {Object} val
+ * @param {String} op - the atomic operator ($pull, $set, etc)
+ * @param {String} [$conditional]
+ * @api private
+ */
+
+Query.prototype._castUpdateVal = function _castUpdateVal(schema, val, op, $conditional) {
+ if (!schema) {
+ // non-existing schema path
+ return op in numberOps
+ ? Number(val)
+ : val;
+ }
+
+ var cond = schema.caster && op in castOps &&
+ (utils.isObject(val) || Array.isArray(val));
+ if (cond) {
+ // Cast values for ops that add data to MongoDB.
+ // Ensures embedded documents get ObjectIds etc.
+ var tmp = schema.cast(val);
+ if (Array.isArray(val)) {
+ val = tmp;
+ } else if (schema.caster.$isSingleNested) {
+ val = tmp;
+ } else {
+ val = tmp[0];
+ }
+ }
+
+ if (op in numberOps) {
+ if (op === '$inc') {
+ return schema.castForQuery(val);
+ }
+ return Number(val);
+ }
+ if (op === '$currentDate') {
+ if (typeof val === 'object') {
+ return {$type: val.$type};
+ }
+ return Boolean(val);
+ }
+ if (/^\$/.test($conditional)) {
+ return schema.castForQuery($conditional, val);
+ }
+
+ return schema.castForQuery(val);
+};
+
+/*!
+ * castQuery
+ * @api private
+ */
+
+function castQuery(query) {
+ try {
+ return query.cast(query.model);
+ } catch (err) {
+ return err;
+ }
+}
+
+/*!
+ * castDoc
+ * @api private
+ */
+
+function castDoc(query, overwrite) {
+ try {
+ return query._castUpdate(query._update, overwrite);
+ } catch (err) {
+ return err;
+ }
+}
+
+/**
+ * Specifies paths which should be populated with other documents.
+ *
+ * ####Example:
+ *
+ * Kitten.findOne().populate('owner').exec(function (err, kitten) {
+ * console.log(kitten.owner.name) // Max
+ * })
+ *
+ * Kitten.find().populate({
+ * path: 'owner'
+ * , select: 'name'
+ * , match: { color: 'black' }
+ * , options: { sort: { name: -1 }}
+ * }).exec(function (err, kittens) {
+ * console.log(kittens[0].owner.name) // Zoopa
+ * })
+ *
+ * // alternatively
+ * Kitten.find().populate('owner', 'name', null, {sort: { name: -1 }}).exec(function (err, kittens) {
+ * console.log(kittens[0].owner.name) // Zoopa
+ * })
+ *
+ * Paths are populated after the query executes and a response is received. A separate query is then executed for each path specified for population. After a response for each query has also been returned, the results are passed to the callback.
+ *
+ * @param {Object|String} path either the path to populate or an object specifying all parameters
+ * @param {Object|String} [select] Field selection for the population query
+ * @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field.
+ * @param {Object} [match] Conditions for the population query
+ * @param {Object} [options] Options for the population query (sort, etc)
+ * @see population ./populate.html
+ * @see Query#select #query_Query-select
+ * @see Model.populate #model_Model.populate
+ * @return {Query} this
+ * @api public
+ */
+
+Query.prototype.populate = function() {
+ var res = utils.populate.apply(null, arguments);
+ var opts = this._mongooseOptions;
+
+ if (!utils.isObject(opts.populate)) {
+ opts.populate = {};
+ }
+
+ var pop = opts.populate;
+
+ for (var i = 0; i < res.length; ++i) {
+ var path = res[i].path;
+ if (pop[path] && pop[path].populate && res[i].populate) {
+ res[i].populate = pop[path].populate.concat(res[i].populate);
+ }
+ pop[res[i].path] = res[i];
+ }
+
+ return this;
+};
+
+/**
+ * Casts this query to the schema of `model`
+ *
+ * ####Note
+ *
+ * If `obj` is present, it is cast instead of this query.
+ *
+ * @param {Model} model
+ * @param {Object} [obj]
+ * @return {Object}
+ * @api public
+ */
+
+Query.prototype.cast = function(model, obj) {
+ obj || (obj = this._conditions);
+
+ return cast(model.schema, obj);
+};
+
+/**
+ * Casts selected field arguments for field selection with mongo 2.2
+ *
+ * query.select({ ids: { $elemMatch: { $in: [hexString] }})
+ *
+ * @param {Object} fields
+ * @see https://github.com/Automattic/mongoose/issues/1091
+ * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
+ * @api private
+ */
+
+Query.prototype._castFields = function _castFields(fields) {
+ var selected,
+ elemMatchKeys,
+ keys,
+ key,
+ out,
+ i;
+
+ if (fields) {
+ keys = Object.keys(fields);
+ elemMatchKeys = [];
+ i = keys.length;
+
+ // collect $elemMatch args
+ while (i--) {
+ key = keys[i];
+ if (fields[key].$elemMatch) {
+ selected || (selected = {});
+ selected[key] = fields[key];
+ elemMatchKeys.push(key);
+ }
+ }
+ }
+
+ if (selected) {
+ // they passed $elemMatch, cast em
+ try {
+ out = this.cast(this.model, selected);
+ } catch (err) {
+ return err;
+ }
+
+ // apply the casted field args
+ i = elemMatchKeys.length;
+ while (i--) {
+ key = elemMatchKeys[i];
+ fields[key] = out[key];
+ }
+ }
+
+ return fields;
+};
+
+/**
+ * Applies schematype selected options to this query.
+ * @api private
+ */
+
+Query.prototype._applyPaths = function applyPaths() {
+ // determine if query is selecting or excluding fields
+
+ var fields = this._fields,
+ exclude,
+ keys,
+ ki;
+
+ if (fields) {
+ keys = Object.keys(fields);
+ ki = keys.length;
+
+ while (ki--) {
+ if (keys[ki][0] === '+') continue;
+ exclude = fields[keys[ki]] === 0;
+ break;
+ }
+ }
+
+ // if selecting, apply default schematype select:true fields
+ // if excluding, apply schematype select:false fields
+
+ var selected = [],
+ excluded = [],
+ seen = [];
+
+ var analyzePath = function(path, type) {
+ if (typeof type.selected !== 'boolean') return;
+
+ var plusPath = '+' + path;
+ if (fields && plusPath in fields) {
+ // forced inclusion
+ delete fields[plusPath];
+
+ // if there are other fields being included, add this one
+ // if no other included fields, leave this out (implied inclusion)
+ if (exclude === false && keys.length > 1 && !~keys.indexOf(path)) {
+ fields[path] = 1;
+ }
+
+ return;
+ }
+
+ // check for parent exclusions
+ var root = path.split('.')[0];
+ if (~excluded.indexOf(root)) return;
+
+ (type.selected ? selected : excluded).push(path);
+ };
+
+ var analyzeSchema = function(schema, prefix) {
+ prefix || (prefix = '');
+
+ // avoid recursion
+ if (~seen.indexOf(schema)) return;
+ seen.push(schema);
+
+ schema.eachPath(function(path, type) {
+ if (prefix) path = prefix + '.' + path;
+
+ analyzePath(path, type);
+
+ // array of subdocs?
+ if (type.schema) {
+ analyzeSchema(type.schema, path);
+ }
+ });
+ };
+
+ analyzeSchema(this.model.schema);
+
+ switch (exclude) {
+ case true:
+ excluded.length && this.select('-' + excluded.join(' -'));
+ break;
+ case false:
+ if (this.model.schema && this.model.schema.paths['_id'] &&
+ this.model.schema.paths['_id'].options && this.model.schema.paths['_id'].options.select === false) {
+ selected.push('-_id');
+ }
+ selected.length && this.select(selected.join(' '));
+ break;
+ case undefined:
+ // user didn't specify fields, implies returning all fields.
+ // only need to apply excluded fields
+ excluded.length && this.select('-' + excluded.join(' -'));
+ break;
+ }
+ seen = excluded = selected = keys = fields = null;
+};
+
+/**
+ * Returns a Node.js 0.8 style [read stream](http://nodejs.org/docs/v0.8.21/api/stream.html#stream_readable_stream) interface.
+ *
+ * ####Example
+ *
+ * // follows the nodejs 0.8 stream api
+ * Thing.find({ name: /^hello/ }).stream().pipe(res)
+ *
+ * // manual streaming
+ * var stream = Thing.find({ name: /^hello/ }).stream();
+ *
+ * stream.on('data', function (doc) {
+ * // do something with the mongoose document
+ * }).on('error', function (err) {
+ * // handle the error
+ * }).on('close', function () {
+ * // the stream is closed
+ * });
+ *
+ * ####Valid options
+ *
+ * - `transform`: optional function which accepts a mongoose document. The return value of the function will be emitted on `data`.
+ *
+ * ####Example
+ *
+ * // JSON.stringify all documents before emitting
+ * var stream = Thing.find().stream({ transform: JSON.stringify });
+ * stream.pipe(writeStream);
+ *
+ * @return {QueryStream}
+ * @param {Object} [options]
+ * @see QueryStream
+ * @api public
+ */
+
+Query.prototype.stream = function stream(opts) {
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
+ return new QueryStream(this, opts);
+};
+Query.prototype.stream = util.deprecate(Query.prototype.stream, 'Mongoose: ' +
+ 'Query.prototype.stream() is deprecated in mongoose >= 4.5.0, ' +
+ 'use Query.prototype.cursor() instead');
+
+/**
+ * Returns a wrapper around a [mongodb driver cursor](http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html).
+ * A QueryCursor exposes a [Streams3](https://strongloop.com/strongblog/whats-new-io-js-beta-streams3/)-compatible
+ * interface, as well as a `.next()` function.
+ *
+ * ####Example
+ *
+ * // There are 2 ways to use a cursor. First, as a stream:
+ * Thing.
+ * find({ name: /^hello/ }).
+ * cursor().
+ * on('data', function(doc) { console.log(doc); }).
+ * on('end', function() { console.log('Done!'); });
+ *
+ * // Or you can use `.next()` to manually get the next doc in the stream.
+ * // `.next()` returns a promise, so you can use promises or callbacks.
+ * var cursor = Thing.find({ name: /^hello/ }).cursor();
+ * cursor.next(function(error, doc) {
+ * console.log(doc);
+ * });
+ *
+ * // Because `.next()` returns a promise, you can use co
+ * // to easily iterate through all documents without loading them
+ * // all into memory.
+ * co(function*() {
+ * const cursor = Thing.find({ name: /^hello/ }).cursor();
+ * for (let doc = yield cursor.next(); doc != null; doc = yield cursor.next()) {
+ * console.log(doc);
+ * }
+ * });
+ *
+ *
+ * @return {QueryCursor}
+ * @param {Object} [options]
+ * @see QueryCursor
+ * @api public
+ */
+
+Query.prototype.cursor = function cursor(opts) {
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
+ this.setOptions({ fields: this._fieldsForExec() });
+ if (opts) {
+ this.setOptions(opts);
+ }
+
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ return (new QueryCursor(this, this.options))._markError(err);
+ }
+
+ return new QueryCursor(this, this.options);
+};
+
+// the rest of these are basically to support older Mongoose syntax with mquery
+
+/**
+ * _DEPRECATED_ Alias of `maxScan`
+ *
+ * @deprecated
+ * @see maxScan #query_Query-maxScan
+ * @method maxscan
+ * @memberOf Query
+ */
+
+Query.prototype.maxscan = Query.base.maxScan;
+
+/**
+ * Sets the tailable option (for use with capped collections).
+ *
+ * ####Example
+ *
+ * query.tailable() // true
+ * query.tailable(true)
+ * query.tailable(false)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @param {Boolean} bool defaults to true
+ * @param {Object} [opts] options to set
+ * @param {Number} [opts.numberOfRetries] if cursor is exhausted, retry this many times before giving up
+ * @param {Number} [opts.tailableRetryInterval] if cursor is exhausted, wait this many milliseconds before retrying
+ * @see tailable http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/
+ * @api public
+ */
+
+Query.prototype.tailable = function(val, opts) {
+ // we need to support the tailable({ awaitdata : true }) as well as the
+ // tailable(true, {awaitdata :true}) syntax that mquery does not support
+ if (val && val.constructor.name === 'Object') {
+ opts = val;
+ val = true;
+ }
+
+ if (val === undefined) {
+ val = true;
+ }
+
+ if (opts && typeof opts === 'object') {
+ for (var key in opts) {
+ if (key === 'awaitdata') {
+ // For backwards compatibility
+ this.options[key] = !!opts[key];
+ } else {
+ this.options[key] = opts[key];
+ }
+ }
+ }
+
+ return Query.base.tailable.call(this, val);
+};
+
+/**
+ * Declares an intersects query for `geometry()`.
+ *
+ * ####Example
+ *
+ * query.where('path').intersects().geometry({
+ * type: 'LineString'
+ * , coordinates: [[180.0, 11.0], [180, 9.0]]
+ * })
+ *
+ * query.where('path').intersects({
+ * type: 'LineString'
+ * , coordinates: [[180.0, 11.0], [180, 9.0]]
+ * })
+ *
+ * ####NOTE:
+ *
+ * **MUST** be used after `where()`.
+ *
+ * ####NOTE:
+ *
+ * In Mongoose 3.7, `intersects` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within).
+ *
+ * @method intersects
+ * @memberOf Query
+ * @param {Object} [arg]
+ * @return {Query} this
+ * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
+ * @see geoIntersects http://docs.mongodb.org/manual/reference/operator/geoIntersects/
+ * @api public
+ */
+
+/**
+ * Specifies a `$geometry` condition
+ *
+ * ####Example
+ *
+ * var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
+ * query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA })
+ *
+ * // or
+ * var polyB = [[ 0, 0 ], [ 1, 1 ]]
+ * query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB })
+ *
+ * // or
+ * var polyC = [ 0, 0 ]
+ * query.where('loc').within().geometry({ type: 'Point', coordinates: polyC })
+ *
+ * // or
+ * query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC })
+ *
+ * The argument is assigned to the most recent path passed to `where()`.
+ *
+ * ####NOTE:
+ *
+ * `geometry()` **must** come after either `intersects()` or `within()`.
+ *
+ * The `object` argument must contain `type` and `coordinates` properties.
+ * - type {String}
+ * - coordinates {Array}
+ *
+ * @method geometry
+ * @memberOf Query
+ * @param {Object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the examples.
+ * @return {Query} this
+ * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
+ * @see http://docs.mongodb.org/manual/release-notes/2.4/#new-geospatial-indexes-with-geojson-and-improved-spherical-geometry
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+/**
+ * Specifies a `$near` or `$nearSphere` condition
+ *
+ * These operators return documents sorted by distance.
+ *
+ * ####Example
+ *
+ * query.where('loc').near({ center: [10, 10] });
+ * query.where('loc').near({ center: [10, 10], maxDistance: 5 });
+ * query.where('loc').near({ center: [10, 10], maxDistance: 5, spherical: true });
+ * query.near('loc', { center: [10, 10], maxDistance: 5 });
+ *
+ * @method near
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Object} val
+ * @return {Query} this
+ * @see $near http://docs.mongodb.org/manual/reference/operator/near/
+ * @see $nearSphere http://docs.mongodb.org/manual/reference/operator/nearSphere/
+ * @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+/*!
+ * Overwriting mquery is needed to support a couple different near() forms found in older
+ * versions of mongoose
+ * near([1,1])
+ * near(1,1)
+ * near(field, [1,2])
+ * near(field, 1, 2)
+ * In addition to all of the normal forms supported by mquery
+ */
+
+Query.prototype.near = function() {
+ var params = [];
+ var sphere = this._mongooseOptions.nearSphere;
+
+ // TODO refactor
+
+ if (arguments.length === 1) {
+ if (Array.isArray(arguments[0])) {
+ params.push({center: arguments[0], spherical: sphere});
+ } else if (typeof arguments[0] === 'string') {
+ // just passing a path
+ params.push(arguments[0]);
+ } else if (utils.isObject(arguments[0])) {
+ if (typeof arguments[0].spherical !== 'boolean') {
+ arguments[0].spherical = sphere;
+ }
+ params.push(arguments[0]);
+ } else {
+ throw new TypeError('invalid argument');
+ }
+ } else if (arguments.length === 2) {
+ if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
+ params.push({center: [arguments[0], arguments[1]], spherical: sphere});
+ } else if (typeof arguments[0] === 'string' && Array.isArray(arguments[1])) {
+ params.push(arguments[0]);
+ params.push({center: arguments[1], spherical: sphere});
+ } else if (typeof arguments[0] === 'string' && utils.isObject(arguments[1])) {
+ params.push(arguments[0]);
+ if (typeof arguments[1].spherical !== 'boolean') {
+ arguments[1].spherical = sphere;
+ }
+ params.push(arguments[1]);
+ } else {
+ throw new TypeError('invalid argument');
+ }
+ } else if (arguments.length === 3) {
+ if (typeof arguments[0] === 'string' && typeof arguments[1] === 'number'
+ && typeof arguments[2] === 'number') {
+ params.push(arguments[0]);
+ params.push({center: [arguments[1], arguments[2]], spherical: sphere});
+ } else {
+ throw new TypeError('invalid argument');
+ }
+ } else {
+ throw new TypeError('invalid argument');
+ }
+
+ return Query.base.near.apply(this, params);
+};
+
+/**
+ * _DEPRECATED_ Specifies a `$nearSphere` condition
+ *
+ * ####Example
+ *
+ * query.where('loc').nearSphere({ center: [10, 10], maxDistance: 5 });
+ *
+ * **Deprecated.** Use `query.near()` instead with the `spherical` option set to `true`.
+ *
+ * ####Example
+ *
+ * query.where('loc').near({ center: [10, 10], spherical: true });
+ *
+ * @deprecated
+ * @see near() #query_Query-near
+ * @see $near http://docs.mongodb.org/manual/reference/operator/near/
+ * @see $nearSphere http://docs.mongodb.org/manual/reference/operator/nearSphere/
+ * @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
+ */
+
+Query.prototype.nearSphere = function() {
+ this._mongooseOptions.nearSphere = true;
+ this.near.apply(this, arguments);
+ return this;
+};
+
+/**
+ * Specifies a $polygon condition
+ *
+ * ####Example
+ *
+ * query.where('loc').within().polygon([10,20], [13, 25], [7,15])
+ * query.polygon('loc', [10,20], [13, 25], [7,15])
+ *
+ * @method polygon
+ * @memberOf Query
+ * @param {String|Array} [path]
+ * @param {Array|Object} [coordinatePairs...]
+ * @return {Query} this
+ * @see $polygon http://docs.mongodb.org/manual/reference/operator/polygon/
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+/**
+ * Specifies a $box condition
+ *
+ * ####Example
+ *
+ * var lowerLeft = [40.73083, -73.99756]
+ * var upperRight= [40.741404, -73.988135]
+ *
+ * query.where('loc').within().box(lowerLeft, upperRight)
+ * query.box({ ll : lowerLeft, ur : upperRight })
+ *
+ * @method box
+ * @memberOf Query
+ * @see $box http://docs.mongodb.org/manual/reference/operator/box/
+ * @see within() Query#within #query_Query-within
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @param {Object} val
+ * @param [Array] Upper Right Coords
+ * @return {Query} this
+ * @api public
+ */
+
+/*!
+ * this is needed to support the mongoose syntax of:
+ * box(field, { ll : [x,y], ur : [x2,y2] })
+ * box({ ll : [x,y], ur : [x2,y2] })
+ */
+
+Query.prototype.box = function(ll, ur) {
+ if (!Array.isArray(ll) && utils.isObject(ll)) {
+ ur = ll.ur;
+ ll = ll.ll;
+ }
+ return Query.base.box.call(this, ll, ur);
+};
+
+/**
+ * Specifies a $center or $centerSphere condition.
+ *
+ * ####Example
+ *
+ * var area = { center: [50, 50], radius: 10, unique: true }
+ * query.where('loc').within().circle(area)
+ * // alternatively
+ * query.circle('loc', area);
+ *
+ * // spherical calculations
+ * var area = { center: [50, 50], radius: 10, unique: true, spherical: true }
+ * query.where('loc').within().circle(area)
+ * // alternatively
+ * query.circle('loc', area);
+ *
+ * New in 3.7.0
+ *
+ * @method circle
+ * @memberOf Query
+ * @param {String} [path]
+ * @param {Object} area
+ * @return {Query} this
+ * @see $center http://docs.mongodb.org/manual/reference/operator/center/
+ * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
+ * @see $geoWithin http://docs.mongodb.org/manual/reference/operator/geoWithin/
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @api public
+ */
+
+/**
+ * _DEPRECATED_ Alias for [circle](#query_Query-circle)
+ *
+ * **Deprecated.** Use [circle](#query_Query-circle) instead.
+ *
+ * @deprecated
+ * @method center
+ * @memberOf Query
+ * @api public
+ */
+
+Query.prototype.center = Query.base.circle;
+
+/**
+ * _DEPRECATED_ Specifies a $centerSphere condition
+ *
+ * **Deprecated.** Use [circle](#query_Query-circle) instead.
+ *
+ * ####Example
+ *
+ * var area = { center: [50, 50], radius: 10 };
+ * query.where('loc').within().centerSphere(area);
+ *
+ * @deprecated
+ * @param {String} [path]
+ * @param {Object} val
+ * @return {Query} this
+ * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
+ * @api public
+ */
+
+Query.prototype.centerSphere = function() {
+ if (arguments[0] && arguments[0].constructor.name === 'Object') {
+ arguments[0].spherical = true;
+ }
+
+ if (arguments[1] && arguments[1].constructor.name === 'Object') {
+ arguments[1].spherical = true;
+ }
+
+ Query.base.circle.apply(this, arguments);
+};
+
+/**
+ * Determines if field selection has been made.
+ *
+ * @method selected
+ * @memberOf Query
+ * @return {Boolean}
+ * @api public
+ */
+
+/**
+ * Determines if inclusive field selection has been made.
+ *
+ * query.selectedInclusively() // false
+ * query.select('name')
+ * query.selectedInclusively() // true
+ *
+ * @method selectedInclusively
+ * @memberOf Query
+ * @return {Boolean}
+ * @api public
+ */
+
+/**
+ * Determines if exclusive field selection has been made.
+ *
+ * query.selectedExclusively() // false
+ * query.select('-name')
+ * query.selectedExclusively() // true
+ * query.selectedInclusively() // false
+ *
+ * @method selectedExclusively
+ * @memberOf Query
+ * @return {Boolean}
+ * @api public
+ */
/*!
- * Exports.
+ * Export
*/
module.exports = Query;
-module.exports.QueryStream = QueryStream;
diff --git a/node_modules/mongoose/lib/querycursor.js b/node_modules/mongoose/lib/querycursor.js
new file mode 100644
index 0000000..3779df0
--- /dev/null
+++ b/node_modules/mongoose/lib/querycursor.js
@@ -0,0 +1,262 @@
+/*!
+ * Module dependencies.
+ */
+
+var PromiseProvider = require('./promise_provider');
+var Readable = require('stream').Readable;
+var helpers = require('./queryhelpers');
+var util = require('util');
+
+/**
+ * A QueryCursor is a concurrency primitive for processing query results
+ * one document at a time. A QueryCursor fulfills the [Node.js streams3 API](https://strongloop.com/strongblog/whats-new-io-js-beta-streams3/),
+ * in addition to several other mechanisms for loading documents from MongoDB
+ * one at a time.
+ *
+ * Unless you're an advanced user, do **not** instantiate this class directly.
+ * Use [`Query#cursor()`](/api.html#query_Query-cursor) instead.
+ *
+ * @param {Query} query
+ * @param {Object} options query options passed to `.find()`
+ * @inherits Readable
+ * @event `cursor`: Emitted when the cursor is created
+ * @event `error`: Emitted when an error occurred
+ * @event `data`: Emitted when the stream is flowing and the next doc is ready
+ * @event `end`: Emitted when the stream is exhausted
+ * @api public
+ */
+
+function QueryCursor(query, options) {
+ Readable.call(this, { objectMode: true });
+
+ this.cursor = null;
+ this.query = query;
+ var _this = this;
+ var model = query.model;
+ model.collection.find(query._conditions, options, function(err, cursor) {
+ if (_this._error) {
+ cursor.close(function() {});
+ _this.listeners('error').length > 0 && _this.emit('error', _this._error);
+ }
+ if (err) {
+ return _this.emit('error', err);
+ }
+ _this.cursor = cursor;
+ _this.emit('cursor', cursor);
+ });
+}
+
+util.inherits(QueryCursor, Readable);
+
+/*!
+ * Necessary to satisfy the Readable API
+ */
+
+QueryCursor.prototype._read = function() {
+ var _this = this;
+ _next(this, function(error, doc) {
+ if (error) {
+ return _this.emit('error', error);
+ }
+ if (!doc) {
+ _this.push(null);
+ return _this.cursor.close(function(error) {
+ if (error) {
+ return _this.emit('error', error);
+ }
+ _this.emit('close');
+ });
+ }
+ _this.push(doc);
+ });
+};
+
+/*!
+ * Marks this cursor as errored
+ */
+
+QueryCursor.prototype._markError = function(error) {
+ this._error = error;
+ return this;
+};
+
+/**
+ * Marks this cursor as closed. Will stop streaming and subsequent calls to
+ * `next()` will error.
+ *
+ * @param {Function} callback
+ * @return {Promise}
+ * @api public
+ * @method close
+ * @emits close
+ * @see MongoDB driver cursor#close http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#close
+ */
+
+QueryCursor.prototype.close = function(callback) {
+ var Promise = PromiseProvider.get();
+ var _this = this;
+ return new Promise.ES6(function(resolve, reject) {
+ _this.cursor.close(function(error) {
+ if (error) {
+ callback && callback(error);
+ reject(error);
+ return _this.listeners('error').length > 0 &&
+ _this.emit('error', error);
+ }
+ _this.emit('close');
+ resolve();
+ callback && callback();
+ });
+ });
+};
+
+/**
+ * Get the next document from this cursor. Will return `null` when there are
+ * no documents left.
+ *
+ * @param {Function} callback
+ * @return {Promise}
+ * @api public
+ * @method next
+ */
+
+QueryCursor.prototype.next = function(callback) {
+ var Promise = PromiseProvider.get();
+ var _this = this;
+ return new Promise.ES6(function(resolve, reject) {
+ _next(_this, function(error, doc) {
+ if (error) {
+ callback && callback(error);
+ return reject(error);
+ }
+ callback && callback(null, doc);
+ resolve(doc);
+ });
+ });
+};
+
+/**
+ * Execute `fn` for every document in the cursor. If `fn` returns a promise,
+ * will wait for the promise to resolve before iterating on to the next one.
+ * Returns a promise that resolves when done.
+ *
+ * @param {Function} fn
+ * @param {Function} [callback] executed when all docs have been processed
+ * @return {Promise}
+ * @api public
+ * @method eachAsync
+ */
+
+QueryCursor.prototype.eachAsync = function(fn, callback) {
+ var Promise = PromiseProvider.get();
+ var _this = this;
+
+ var handleNextResult = function(doc, callback) {
+ var promise = fn(doc);
+ if (promise && typeof promise.then === 'function') {
+ promise.then(
+ function() { callback(null); },
+ function(error) { callback(error); });
+ } else {
+ callback(null);
+ }
+ };
+
+ var iterate = function(callback) {
+ return _next(_this, function(error, doc) {
+ if (error) {
+ return callback(error);
+ }
+ if (!doc) {
+ return callback(null);
+ }
+ handleNextResult(doc, function(error) {
+ if (error) {
+ return callback(error);
+ }
+ iterate(callback);
+ });
+ });
+ };
+
+ return new Promise.ES6(function(resolve, reject) {
+ iterate(function(error) {
+ if (error) {
+ callback && callback(error);
+ return reject(error);
+ }
+ callback && callback(null);
+ return resolve();
+ });
+ });
+};
+
+/*!
+ * Get the next doc from the underlying cursor and mongooseify it
+ * (populate, etc.)
+ */
+
+function _next(ctx, callback) {
+ if (ctx._error) {
+ return process.nextTick(function() {
+ callback(ctx._error);
+ });
+ }
+
+ if (ctx.cursor) {
+ ctx.cursor.next(function(error, doc) {
+ if (error) {
+ return callback(error);
+ }
+ if (!doc) {
+ return callback(null, null);
+ }
+
+ var opts = ctx.query._mongooseOptions;
+ if (!opts.populate) {
+ return opts.lean === true ?
+ callback(null, doc) :
+ _create(ctx, doc, null, callback);
+ }
+
+ var pop = helpers.preparePopulationOptionsMQ(ctx.query,
+ ctx.query._mongooseOptions);
+ pop.forEach(function(option) {
+ delete option.model;
+ });
+ pop.__noPromise = true;
+ ctx.query.model.populate(doc, pop, function(err, doc) {
+ if (err) {
+ return callback(err);
+ }
+ return opts.lean === true ?
+ callback(null, doc) :
+ _create(ctx, doc, pop, callback);
+ });
+ });
+ } else {
+ ctx.once('cursor', function() {
+ _next(ctx, callback);
+ });
+ }
+}
+
+/*!
+ * Convert a raw doc into a full mongoose doc.
+ */
+
+function _create(ctx, doc, populatedIds, cb) {
+ var instance = helpers.createModel(ctx.query.model, doc, ctx.query._fields);
+ var opts = populatedIds ?
+ { populated: populatedIds } :
+ undefined;
+
+ instance.init(doc, opts, function(err) {
+ if (err) {
+ return cb(err);
+ }
+ cb(null, instance);
+ });
+}
+
+module.exports = QueryCursor;
diff --git a/node_modules/mongoose/lib/queryhelpers.js b/node_modules/mongoose/lib/queryhelpers.js
index 4637fad..3e0b82d 100644
--- a/node_modules/mongoose/lib/queryhelpers.js
+++ b/node_modules/mongoose/lib/queryhelpers.js
@@ -3,7 +3,7 @@
* Module dependencies
*/
-var utils = require('./utils')
+var utils = require('./utils');
/*!
* Prepare a set of path options for query population.
@@ -13,14 +13,58 @@ var utils = require('./utils')
* @return {Array}
*/
-exports.preparePopulationOptions = function preparePopulationOptions (query, options) {
+exports.preparePopulationOptions = function preparePopulationOptions(query, options) {
var pop = utils.object.vals(query.options.populate);
// lean options should trickle through all queries
if (options.lean) pop.forEach(makeLean);
return pop;
-}
+};
+
+/*!
+ * Prepare a set of path options for query population. This is the MongooseQuery
+ * version
+ *
+ * @param {Query} query
+ * @param {Object} options
+ * @return {Array}
+ */
+
+exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query, options) {
+ var pop = utils.object.vals(query._mongooseOptions.populate);
+
+ // lean options should trickle through all queries
+ if (options.lean) pop.forEach(makeLean);
+
+ return pop;
+};
+
+/*!
+ * If the document is a mapped discriminator type, it returns a model instance for that type, otherwise,
+ * it returns an instance of the given model.
+ *
+ * @param {Model} model
+ * @param {Object} doc
+ * @param {Object} fields
+ *
+ * @return {Model}
+ */
+exports.createModel = function createModel(model, doc, fields) {
+ var discriminatorMapping = model.schema
+ ? model.schema.discriminatorMapping
+ : null;
+
+ var key = discriminatorMapping && discriminatorMapping.isRoot
+ ? discriminatorMapping.key
+ : null;
+
+ if (key && doc[key] && model.discriminators && model.discriminators[doc[key]]) {
+ return new model.discriminators[doc[key]](undefined, fields, true);
+ }
+
+ return new model(undefined, fields, true);
+};
/*!
* Set each path query option to lean
@@ -28,8 +72,7 @@ exports.preparePopulationOptions = function preparePopulationOptions (query, opt
* @param {Object} option
*/
-function makeLean (option) {
+function makeLean(option) {
option.options || (option.options = {});
option.options.lean = true;
}
-
diff --git a/node_modules/mongoose/lib/querystream.js b/node_modules/mongoose/lib/querystream.js
index 6db7570..2918e25 100644
--- a/node_modules/mongoose/lib/querystream.js
+++ b/node_modules/mongoose/lib/querystream.js
@@ -1,12 +1,15 @@
+/* eslint no-empty: 1 */
/*!
* Module dependencies.
*/
-var Stream = require('stream').Stream
-var utils = require('./utils')
-var helpers = require('./queryhelpers')
-var K = function(k){ return k }
+var Stream = require('stream').Stream;
+var utils = require('./utils');
+var helpers = require('./queryhelpers');
+var K = function(k) {
+ return k;
+};
/**
* Provides a Node.js 0.8 style [ReadStream](http://nodejs.org/docs/v0.8.21/api/stream.html#stream_readable_stream) interface for Queries.
@@ -49,7 +52,7 @@ var K = function(k){ return k }
* @api public
*/
-function QueryStream (query, options) {
+function QueryStream(query, options) {
Stream.call(this);
this.query = query;
@@ -61,14 +64,14 @@ function QueryStream (query, options) {
this._buffer = null;
this._inline = T_INIT;
this._running = false;
- this._transform = options && 'function' == typeof options.transform
- ? options.transform
- : K;
+ this._transform = options && typeof options.transform === 'function'
+ ? options.transform
+ : K;
// give time to hook up events
- var self = this;
- process.nextTick(function () {
- self._init();
+ var _this = this;
+ process.nextTick(function() {
+ _this._init();
});
}
@@ -107,29 +110,33 @@ var T_CONT = 2;
* @api private
*/
-QueryStream.prototype._init = function () {
- if (this._destroyed) return;
+QueryStream.prototype._init = function() {
+ if (this._destroyed) {
+ return;
+ }
- var query = this.query
- , model = query.model
- , options = query._optionsForExec(model)
- , self = this
+ var query = this.query,
+ model = query.model,
+ options = query._optionsForExec(model),
+ _this = this;
try {
query.cast(model);
} catch (err) {
- return self.destroy(err);
+ return _this.destroy(err);
}
- self._fields = utils.clone(query._fields);
- options.fields = query._castFields(self._fields);
+ _this._fields = utils.clone(query._fields);
+ options.fields = query._castFields(_this._fields);
- model.collection.find(query._conditions, options, function (err, cursor) {
- if (err) return self.destroy(err);
- self._cursor = cursor;
- self._next();
+ model.collection.find(query._conditions, options, function(err, cursor) {
+ if (err) {
+ return _this.destroy(err);
+ }
+ _this._cursor = cursor;
+ _this._next();
});
-}
+};
/**
* Trampoline for pulling the next doc from cursor.
@@ -138,24 +145,26 @@ QueryStream.prototype._init = function () {
* @api private
*/
-QueryStream.prototype._next = function _next () {
+QueryStream.prototype._next = function _next() {
if (this.paused || this._destroyed) {
- return this._running = false;
+ this._running = false;
+ return this._running;
}
this._running = true;
if (this._buffer && this._buffer.length) {
var arg;
- while (!this.paused && !this._destroyed && (arg = this._buffer.shift())) {
+ while (!this.paused && !this._destroyed && (arg = this._buffer.shift())) { // eslint-disable-line no-cond-assign
this._onNextObject.apply(this, arg);
}
}
// avoid stack overflows with large result sets.
// trampoline instead of recursion.
- while (this.__next()) {}
-}
+ while (this.__next()) {
+ }
+};
/**
* Pulls the next doc from the cursor.
@@ -164,28 +173,29 @@ QueryStream.prototype._next = function _next () {
* @api private
*/
-QueryStream.prototype.__next = function () {
- if (this.paused || this._destroyed)
- return this._running = false;
+QueryStream.prototype.__next = function() {
+ if (this.paused || this._destroyed) {
+ this._running = false;
+ return this._running;
+ }
- var self = this;
- self._inline = T_INIT;
+ var _this = this;
+ _this._inline = T_INIT;
- self._cursor.nextObject(function cursorcb (err, doc) {
- self._onNextObject(err, doc);
+ _this._cursor.nextObject(function cursorcb(err, doc) {
+ _this._onNextObject(err, doc);
});
// if onNextObject() was already called in this tick
// return ourselves to the trampoline.
if (T_CONT === this._inline) {
return true;
- } else {
- // onNextObject() hasn't fired yet. tell onNextObject
- // that its ok to call _next b/c we are not within
- // the trampoline anymore.
- this._inline = T_IDLE;
}
-}
+ // onNextObject() hasn't fired yet. tell onNextObject
+ // that its ok to call _next b/c we are not within
+ // the trampoline anymore.
+ this._inline = T_IDLE;
+};
/**
* Transforms raw `doc`s returned from the cursor into a model instance.
@@ -195,16 +205,21 @@ QueryStream.prototype.__next = function () {
* @api private
*/
-QueryStream.prototype._onNextObject = function _onNextObject (err, doc) {
- if (this._destroyed) return;
+QueryStream.prototype._onNextObject = function _onNextObject(err, doc) {
+ if (this._destroyed) {
+ return;
+ }
if (this.paused) {
this._buffer || (this._buffer = []);
this._buffer.push([err, doc]);
- return this._running = false;
+ this._running = false;
+ return this._running;
}
- if (err) return this.destroy(err);
+ if (err) {
+ return this.destroy(err);
+ }
// when doc is null we hit the end of the cursor
if (!doc) {
@@ -212,29 +227,43 @@ QueryStream.prototype._onNextObject = function _onNextObject (err, doc) {
return this.destroy();
}
- var opts = this.query.options;
+ var opts = this.query._mongooseOptions;
if (!opts.populate) {
- return true === opts.lean
- ? emit(this, doc)
- : createAndEmit(this, doc);
+ return opts.lean === true ?
+ emit(this, doc) :
+ createAndEmit(this, null, doc);
}
- var self = this;
- var pop = helpers.preparePopulationOptions(self.query, self.query.options);
+ var _this = this;
+ var pop = helpers.preparePopulationOptionsMQ(_this.query, _this.query._mongooseOptions);
- self.query.model.populate(doc, pop, function (err, doc) {
- if (err) return self.destroy(err);
- return true === opts.lean
- ? emit(self, doc)
- : createAndEmit(self, doc);
- })
-}
+ // Hack to work around gh-3108
+ pop.forEach(function(option) {
+ delete option.model;
+ });
+
+ pop.__noPromise = true;
+ _this.query.model.populate(doc, pop, function(err, doc) {
+ if (err) {
+ return _this.destroy(err);
+ }
+ return opts.lean === true ?
+ emit(_this, doc) :
+ createAndEmit(_this, pop, doc);
+ });
+};
+
+function createAndEmit(self, populatedIds, doc) {
+ var instance = helpers.createModel(self.query.model, doc, self._fields);
+ var opts = populatedIds ?
+ {populated: populatedIds} :
+ undefined;
-function createAndEmit (self, doc) {
- var instance = new self.query.model(undefined, self._fields, true);
- instance.init(doc, function (err) {
- if (err) return self.destroy(err);
+ instance.init(doc, opts, function(err) {
+ if (err) {
+ return self.destroy(err);
+ }
emit(self, instance);
});
}
@@ -243,7 +272,7 @@ function createAndEmit (self, doc) {
* Emit a data event and manage the trampoline state
*/
-function emit (self, doc) {
+function emit(self, doc) {
self.emit('data', self._transform(doc));
// trampoline management
@@ -263,9 +292,9 @@ function emit (self, doc) {
* @api public
*/
-QueryStream.prototype.pause = function () {
+QueryStream.prototype.pause = function() {
this.paused = true;
-}
+};
/**
* Resumes this stream.
@@ -273,7 +302,7 @@ QueryStream.prototype.pause = function () {
* @api public
*/
-QueryStream.prototype.resume = function () {
+QueryStream.prototype.resume = function() {
this.paused = false;
if (!this._cursor) {
@@ -290,17 +319,19 @@ QueryStream.prototype.resume = function () {
// outside QueryStream control, need manual restart
return this._next();
}
-}
+};
/**
- * Destroys the stream, closing the underlying cursor. No more events will be emitted.
+ * Destroys the stream, closing the underlying cursor, which emits the close event. No more events will be emitted after the close event.
*
* @param {Error} [err]
* @api public
*/
-QueryStream.prototype.destroy = function (err) {
- if (this._destroyed) return;
+QueryStream.prototype.destroy = function(err) {
+ if (this._destroyed) {
+ return;
+ }
this._destroyed = true;
this._running = false;
this.readable = false;
@@ -314,7 +345,7 @@ QueryStream.prototype.destroy = function (err) {
}
this.emit('close');
-}
+};
/**
* Pipes this query stream into another stream. This method is inherited from NodeJS Streams.
diff --git a/node_modules/mongoose/lib/schema.js b/node_modules/mongoose/lib/schema.js
index 53effd4..d77edbc 100644
--- a/node_modules/mongoose/lib/schema.js
+++ b/node_modules/mongoose/lib/schema.js
@@ -2,12 +2,23 @@
* Module dependencies.
*/
-var EventEmitter = require('events').EventEmitter
- , VirtualType = require('./virtualtype')
- , utils = require('./utils')
- , NamedScope
- , Query
- , Types
+var readPref = require('./drivers').ReadPreference;
+var EventEmitter = require('events').EventEmitter;
+var VirtualType = require('./virtualtype');
+var utils = require('./utils');
+var MongooseTypes;
+var Kareem = require('kareem');
+var async = require('async');
+
+var IS_KAREEM_HOOK = {
+ count: true,
+ find: true,
+ findOne: true,
+ findOneAndUpdate: true,
+ findOneAndRemove: true,
+ insertMany: true,
+ update: true
+};
/**
* Schema constructor.
@@ -23,10 +34,11 @@ var EventEmitter = require('events').EventEmitter
*
* ####Options:
*
- * - [autoIndex](/docs/guide.html#autoIndex): bool - defaults to true
+ * - [autoIndex](/docs/guide.html#autoIndex): bool - defaults to null (which means use the connection's autoIndex option)
* - [bufferCommands](/docs/guide.html#bufferCommands): bool - defaults to true
* - [capped](/docs/guide.html#capped): bool - defaults to false
* - [collection](/docs/guide.html#collection): string - no default
+ * - [emitIndexErrors](/docs/guide.html#emitIndexErrors): bool - defaults to false.
* - [id](/docs/guide.html#id): bool - defaults to true
* - [_id](/docs/guide.html#_id): bool - defaults to true
* - `minimize`: bool - controls [document#toObject](#document_Document-toObject) behavior when called manually - defaults to true
@@ -36,25 +48,31 @@ var EventEmitter = require('events').EventEmitter
* - [strict](/docs/guide.html#strict): bool - defaults to true
* - [toJSON](/docs/guide.html#toJSON) - object - no default
* - [toObject](/docs/guide.html#toObject) - object - no default
- * - [versionKey](/docs/guide.html#versionKey): bool - defaults to "__v"
+ * - [typeKey](/docs/guide.html#typeKey) - string - defaults to 'type'
+ * - [useNestedStrict](/docs/guide.html#useNestedStrict) - boolean - defaults to false
+ * - [validateBeforeSave](/docs/guide.html#validateBeforeSave) - bool - defaults to `true`
+ * - [versionKey](/docs/guide.html#versionKey): string - defaults to "__v"
*
* ####Note:
*
- * _When nesting schemas, (`children` in the example above), always declare the child schema first before passing it into is parent._
+ * _When nesting schemas, (`children` in the example above), always declare the child schema first before passing it into its parent._
*
* @param {Object} definition
+ * @param {Object} [options]
* @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
* @event `init`: Emitted after the schema is compiled into a `Model`.
* @api public
*/
-function Schema (obj, options) {
- if (!(this instanceof Schema))
+function Schema(obj, options) {
+ if (!(this instanceof Schema)) {
return new Schema(obj, options);
+ }
this.paths = {};
this.subpaths = {};
this.virtuals = {};
+ this.singleNestedPaths = {};
this.nested = {};
this.inherits = {};
this.callQueue = [];
@@ -63,6 +81,15 @@ function Schema (obj, options) {
this.statics = {};
this.tree = {};
this._requiredpaths = undefined;
+ this.discriminatorMapping = undefined;
+ this._indexedpaths = undefined;
+ this.query = {};
+ this.childSchemas = [];
+
+ this.s = {
+ hooks: new Kareem(),
+ kareemHooks: IS_KAREEM_HOOK
+ };
this.options = this.defaultOptions(options);
@@ -71,38 +98,194 @@ function Schema (obj, options) {
this.add(obj);
}
+ // check if _id's value is a subdocument (gh-2276)
+ var _idSubDoc = obj && obj._id && utils.isObject(obj._id);
+
// ensure the documents get an auto _id unless disabled
- var auto_id = !this.paths['_id'] && (!this.options.noId && this.options._id);
+ var auto_id = !this.paths['_id'] &&
+ (!this.options.noId && this.options._id) && !_idSubDoc;
+
if (auto_id) {
- this.add({ _id: {type: Schema.ObjectId, auto: true} });
+ obj = {_id: {auto: true}};
+ obj._id[this.options.typeKey] = Schema.ObjectId;
+ this.add(obj);
}
// ensure the documents receive an id getter unless disabled
- var autoid = !this.paths['id'] && (!this.options.noVirtualId && this.options.id);
+ var autoid = !this.paths['id'] &&
+ (!this.options.noVirtualId && this.options.id);
if (autoid) {
this.virtual('id').get(idGetter);
}
+
+ for (var i = 0; i < this._defaultMiddleware.length; ++i) {
+ var m = this._defaultMiddleware[i];
+ this[m.kind](m.hook, !!m.isAsync, m.fn);
+ }
+
+ if (this.options.timestamps) {
+ this.setupTimestamp(this.options.timestamps);
+ }
}
/*!
* Returns this documents _id cast to a string.
*/
-function idGetter () {
+function idGetter() {
if (this.$__._id) {
return this.$__._id;
}
- return this.$__._id = null == this._id
- ? null
- : String(this._id);
+ this.$__._id = this._id == null
+ ? null
+ : String(this._id);
+ return this.$__._id;
}
/*!
* Inherit from EventEmitter.
*/
+Schema.prototype = Object.create(EventEmitter.prototype);
+Schema.prototype.constructor = Schema;
+Schema.prototype.instanceOfSchema = true;
+
+/**
+ * Default middleware attached to a schema. Cannot be changed.
+ *
+ * This field is used to make sure discriminators don't get multiple copies of
+ * built-in middleware. Declared as a constant because changing this at runtime
+ * may lead to instability with Model.prototype.discriminator().
+ *
+ * @api private
+ * @property _defaultMiddleware
+ */
+Object.defineProperty(Schema.prototype, '_defaultMiddleware', {
+ configurable: false,
+ enumerable: false,
+ writable: false,
+ value: [
+ {
+ kind: 'pre',
+ hook: 'save',
+ fn: function(next, options) {
+ var _this = this;
+ // Nested docs have their own presave
+ if (this.ownerDocument) {
+ return next();
+ }
+
+ var hasValidateBeforeSaveOption = options &&
+ (typeof options === 'object') &&
+ ('validateBeforeSave' in options);
-Schema.prototype.__proto__ = EventEmitter.prototype;
+ var shouldValidate;
+ if (hasValidateBeforeSaveOption) {
+ shouldValidate = !!options.validateBeforeSave;
+ } else {
+ shouldValidate = this.schema.options.validateBeforeSave;
+ }
+
+ // Validate
+ if (shouldValidate) {
+ // HACK: use $__original_validate to avoid promises so bluebird doesn't
+ // complain
+ if (this.$__original_validate) {
+ this.$__original_validate({__noPromise: true}, function(error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
+ next(error);
+ });
+ });
+ } else {
+ this.validate({__noPromise: true}, function(error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [ _this], { error: error }, function(error) {
+ next(error);
+ });
+ });
+ }
+ } else {
+ next();
+ }
+ }
+ },
+ {
+ kind: 'pre',
+ hook: 'save',
+ isAsync: true,
+ fn: function(next, done) {
+ var _this = this;
+ var subdocs = this.$__getAllSubdocs();
+
+ if (!subdocs.length || this.$__preSavingFromParent) {
+ done();
+ next();
+ return;
+ }
+
+ async.each(subdocs, function(subdoc, cb) {
+ subdoc.$__preSavingFromParent = true;
+ subdoc.save(function(err) {
+ cb(err);
+ });
+ }, function(error) {
+ for (var i = 0; i < subdocs.length; ++i) {
+ delete subdocs[i].$__preSavingFromParent;
+ }
+ if (error) {
+ return _this.schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
+ done(error);
+ });
+ }
+ next();
+ done();
+ });
+ }
+ },
+ {
+ kind: 'pre',
+ hook: 'validate',
+ isAsync: true,
+ fn: function(next, done) {
+ // Hack to ensure that we always wrap validate() in a promise
+ next();
+ done();
+ }
+ },
+ {
+ kind: 'pre',
+ hook: 'remove',
+ isAsync: true,
+ fn: function(next, done) {
+ if (this.ownerDocument) {
+ done();
+ next();
+ return;
+ }
+
+ var subdocs = this.$__getAllSubdocs();
+
+ if (!subdocs.length || this.$__preSavingFromParent) {
+ done();
+ next();
+ return;
+ }
+
+ async.each(subdocs, function(subdoc, cb) {
+ subdoc.remove({ noop: true }, function(err) {
+ cb(err);
+ });
+ }, function(error) {
+ if (error) {
+ done(error);
+ return;
+ }
+ next();
+ done();
+ });
+ }
+ }
+ ]
+});
/**
* Schema as flat paths
@@ -144,32 +327,41 @@ Schema.prototype.tree;
* @api private
*/
-Schema.prototype.defaultOptions = function (options) {
- if (options && false === options.safe) {
- options.safe = { w: 0 };
+Schema.prototype.defaultOptions = function(options) {
+ if (options && options.safe === false) {
+ options.safe = {w: 0};
+ }
+
+ if (options && options.safe && options.safe.w === 0) {
+ // if you turn off safe writes, then versioning goes off as well
+ options.versionKey = false;
}
options = utils.options({
- strict: true
- , bufferCommands: true
- , capped: false // { size, max, autoIndexId }
- , versionKey: '__v'
- , minimize: true
- , autoIndex: true
- , shardKey: null
- , read: null
+ strict: true,
+ bufferCommands: true,
+ capped: false, // { size, max, autoIndexId }
+ versionKey: '__v',
+ discriminatorKey: '__t',
+ minimize: true,
+ autoIndex: null,
+ shardKey: null,
+ read: null,
+ validateBeforeSave: true,
// the following are only applied at construction time
- , noId: false // deprecated, use { _id: false }
- , _id: true
- , noVirtualId: false // deprecated, use { id: false }
- , id: true
+ noId: false, // deprecated, use { _id: false }
+ _id: true,
+ noVirtualId: false, // deprecated, use { id: false }
+ id: true,
+ typeKey: 'type'
}, options);
- if (options.read)
- options.read = utils.readPref(options.read);
+ if (options.read) {
+ options.read = readPref(options.read);
+ }
return options;
-}
+};
/**
* Adds key path / schema type pairs to this schema.
@@ -184,26 +376,38 @@ Schema.prototype.defaultOptions = function (options) {
* @api public
*/
-Schema.prototype.add = function add (obj, prefix) {
+Schema.prototype.add = function add(obj, prefix) {
prefix = prefix || '';
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i];
- if (null == obj[key]) {
- throw new TypeError('Invalid value for schema path `'+ prefix + key +'`');
+ if (obj[key] == null) {
+ throw new TypeError('Invalid value for schema path `' + prefix + key + '`');
}
- if (utils.isObject(obj[key]) && (!obj[key].constructor || 'Object' == obj[key].constructor.name) && (!obj[key].type || obj[key].type.type)) {
+ if (Array.isArray(obj[key]) && obj[key].length === 1 && obj[key][0] == null) {
+ throw new TypeError('Invalid value for schema Array path `' + prefix + key + '`');
+ }
+
+ if (utils.isObject(obj[key]) &&
+ (!obj[key].constructor || utils.getFunctionName(obj[key].constructor) === 'Object') &&
+ (!obj[key][this.options.typeKey] || (this.options.typeKey === 'type' && obj[key].type.type))) {
if (Object.keys(obj[key]).length) {
// nested object { last: { name: String }}
this.nested[prefix + key] = true;
this.add(obj[key], prefix + key + '.');
} else {
+ if (prefix) {
+ this.nested[prefix.substr(0, prefix.length - 1)] = true;
+ }
this.path(prefix + key, obj[key]); // mixed type
}
} else {
+ if (prefix) {
+ this.nested[prefix.substr(0, prefix.length - 1)] = true;
+ }
this.path(prefix + key, obj[key]);
}
}
@@ -214,7 +418,7 @@ Schema.prototype.add = function add (obj, prefix) {
*
* Keys in this object are names that are rejected in schema declarations b/c they conflict with mongoose functionality. Using these key name will throw an error.
*
- * on, emit, _events, db, init, isNew, errors, schema, options, modelName, collection, _pres, _posts, toObject
+ * on, emit, _events, db, get, set, init, isNew, errors, schema, options, modelName, collection, _pres, _posts, toObject
*
* _NOTE:_ Use of these terms as method names is permitted, but play at your own risk, as they may be existing mongoose document methods you are stomping on.
*
@@ -224,19 +428,36 @@ Schema.prototype.add = function add (obj, prefix) {
Schema.reserved = Object.create(null);
var reserved = Schema.reserved;
+// EventEmitter
+reserved.emit =
reserved.on =
+reserved.once =
+reserved.listeners =
+reserved.removeListener =
+// document properties and functions
+reserved.collection =
reserved.db =
+reserved.errors =
reserved.init =
+reserved.isModified =
reserved.isNew =
-reserved.errors =
-reserved.schema =
-reserved.options =
+reserved.get =
reserved.modelName =
-reserved.collection =
+reserved.save =
+reserved.schema =
+reserved.set =
reserved.toObject =
-reserved.emit = // EventEmitter
-reserved._events = // EventEmitter
-reserved._pres = reserved._posts = 1 // hooks.js
+reserved.validate =
+// hooks.js
+reserved._pres = reserved._posts = 1;
+
+/*!
+ * Document keys to print warnings for
+ */
+
+var warnings = {};
+warnings.increment = '`increment` should not be used as a schema path name ' +
+ 'unless you have disabled versioning.';
/**
* Gets/sets schema paths.
@@ -254,35 +475,48 @@ reserved._pres = reserved._posts = 1 // hooks.js
* @api public
*/
-Schema.prototype.path = function (path, obj) {
- if (obj == undefined) {
- if (this.paths[path]) return this.paths[path];
- if (this.subpaths[path]) return this.subpaths[path];
+Schema.prototype.path = function(path, obj) {
+ if (obj === undefined) {
+ if (this.paths[path]) {
+ return this.paths[path];
+ }
+ if (this.subpaths[path]) {
+ return this.subpaths[path];
+ }
+ if (this.singleNestedPaths[path]) {
+ return this.singleNestedPaths[path];
+ }
// subpaths?
return /\.\d+\.?.*$/.test(path)
- ? getPositionalPath(this, path)
- : undefined;
+ ? getPositionalPath(this, path)
+ : undefined;
}
// some path names conflict with document methods
if (reserved[path]) {
- throw new Error("`" + path + "` may not be used as a schema pathname");
+ throw new Error('`' + path + '` may not be used as a schema pathname');
+ }
+
+ if (warnings[path]) {
+ console.log('WARN: ' + warnings[path]);
}
// update the tree
- var subpaths = path.split(/\./)
- , last = subpaths.pop()
- , branch = this.tree;
+ var subpaths = path.split(/\./),
+ last = subpaths.pop(),
+ branch = this.tree;
subpaths.forEach(function(sub, i) {
- if (!branch[sub]) branch[sub] = {};
- if ('object' != typeof branch[sub]) {
+ if (!branch[sub]) {
+ branch[sub] = {};
+ }
+ if (typeof branch[sub] !== 'object') {
var msg = 'Cannot set nested path `' + path + '`. '
- + 'Parent path `'
- + subpaths.slice(0, i).concat([sub]).join('.')
- + '` already set to type ' + branch[sub].name
- + '.';
+ + 'Parent path `'
+ + subpaths.slice(0, i).concat([sub]).join('.')
+ + '` already set to type ' + branch[sub].name
+ + '.';
throw new Error(msg);
}
branch = branch[sub];
@@ -290,7 +524,22 @@ Schema.prototype.path = function (path, obj) {
branch[last] = utils.clone(obj);
- this.paths[path] = Schema.interpretAsType(path, obj);
+ this.paths[path] = Schema.interpretAsType(path, obj, this.options);
+
+ if (this.paths[path].$isSingleNested) {
+ for (var key in this.paths[path].schema.paths) {
+ this.singleNestedPaths[path + '.' + key] =
+ this.paths[path].schema.paths[key];
+ }
+ for (key in this.paths[path].schema.singleNestedPaths) {
+ this.singleNestedPaths[path + '.' + key] =
+ this.paths[path].schema.singleNestedPaths[key];
+ }
+
+ this.childSchemas.push(this.paths[path].schema);
+ } else if (this.paths[path].$isMongooseDocumentArray) {
+ this.childSchemas.push(this.paths[path].schema);
+ }
return this;
};
@@ -302,57 +551,86 @@ Schema.prototype.path = function (path, obj) {
* @api private
*/
-Schema.interpretAsType = function (path, obj) {
- if (obj.constructor && obj.constructor.name != 'Object')
- obj = { type: obj };
+Schema.interpretAsType = function(path, obj, options) {
+ if (obj.constructor) {
+ var constructorName = utils.getFunctionName(obj.constructor);
+ if (constructorName !== 'Object') {
+ var oldObj = obj;
+ obj = {};
+ obj[options.typeKey] = oldObj;
+ }
+ }
// Get the type making sure to allow keys named "type"
// and default to mixed if not specified.
// { type: { type: String, default: 'freshcut' } }
- var type = obj.type && !obj.type.type
- ? obj.type
- : {};
+ var type = obj[options.typeKey] && (options.typeKey !== 'type' || !obj.type.type)
+ ? obj[options.typeKey]
+ : {};
- if ('Object' == type.constructor.name || 'mixed' == type) {
- return new Types.Mixed(path, obj);
+ if (utils.getFunctionName(type.constructor) === 'Object' || type === 'mixed') {
+ return new MongooseTypes.Mixed(path, obj);
}
- if (Array.isArray(type) || Array == type || 'array' == type) {
+ if (Array.isArray(type) || Array === type || type === 'array') {
// if it was specified through { type } look for `cast`
- var cast = (Array == type || 'array' == type)
- ? obj.cast
- : type[0];
+ var cast = (Array === type || type === 'array')
+ ? obj.cast
+ : type[0];
- if (cast instanceof Schema) {
- return new Types.DocumentArray(path, cast, obj);
+ if (cast && cast.instanceOfSchema) {
+ return new MongooseTypes.DocumentArray(path, cast, obj);
}
- if ('string' == typeof cast) {
- cast = Types[cast.charAt(0).toUpperCase() + cast.substring(1)];
- } else if (cast && (!cast.type || cast.type.type)
- && 'Object' == cast.constructor.name
- && Object.keys(cast).length) {
- return new Types.DocumentArray(path, new Schema(cast), obj);
+ if (Array.isArray(cast)) {
+ return new MongooseTypes.Array(path, Schema.interpretAsType(path, cast, options), obj);
}
- return new Types.Array(path, cast || Types.Mixed, obj);
+ if (typeof cast === 'string') {
+ cast = MongooseTypes[cast.charAt(0).toUpperCase() + cast.substring(1)];
+ } else if (cast && (!cast[options.typeKey] || (options.typeKey === 'type' && cast.type.type))
+ && utils.getFunctionName(cast.constructor) === 'Object'
+ && Object.keys(cast).length) {
+ // The `minimize` and `typeKey` options propagate to child schemas
+ // declared inline, like `{ arr: [{ val: { $type: String } }] }`.
+ // See gh-3560
+ var childSchemaOptions = {minimize: options.minimize};
+ if (options.typeKey) {
+ childSchemaOptions.typeKey = options.typeKey;
+ }
+ var childSchema = new Schema(cast, childSchemaOptions);
+ return new MongooseTypes.DocumentArray(path, childSchema, obj);
+ }
+
+ return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj);
+ }
+
+ if (type && type.instanceOfSchema) {
+ return new MongooseTypes.Embedded(type, path, obj);
}
- var name = 'string' == typeof type
- ? type
- : type.name;
+ var name;
+ if (Buffer.isBuffer(type)) {
+ name = 'Buffer';
+ } else {
+ name = typeof type === 'string'
+ ? type
+ // If not string, `type` is a function. Outside of IE, function.name
+ // gives you the function name. In IE, you need to compute it
+ : type.schemaName || utils.getFunctionName(type);
+ }
if (name) {
name = name.charAt(0).toUpperCase() + name.substring(1);
}
- if (undefined == Types[name]) {
- throw new TypeError('Undefined type at `' + path +
+ if (undefined == MongooseTypes[name]) {
+ throw new TypeError('Undefined type `' + name + '` at `' + path +
'`\n Did you try nesting Schemas? ' +
'You can only nest using refs or arrays.');
}
- return new Types[name](path, obj);
+ return new MongooseTypes[name](path, obj);
};
/**
@@ -365,9 +643,9 @@ Schema.interpretAsType = function (path, obj) {
* @api public
*/
-Schema.prototype.eachPath = function (fn) {
- var keys = Object.keys(this.paths)
- , len = keys.length;
+Schema.prototype.eachPath = function(fn) {
+ var keys = Object.keys(this.paths),
+ len = keys.length;
for (var i = 0; i < len; ++i) {
fn(keys[i], this.paths[keys[i]]);
@@ -380,23 +658,43 @@ Schema.prototype.eachPath = function (fn) {
* Returns an Array of path strings that are required by this schema.
*
* @api public
+ * @param {Boolean} invalidate refresh the cache
* @return {Array}
*/
-Schema.prototype.requiredPaths = function requiredPaths () {
- if (this._requiredpaths) return this._requiredpaths;
+Schema.prototype.requiredPaths = function requiredPaths(invalidate) {
+ if (this._requiredpaths && !invalidate) {
+ return this._requiredpaths;
+ }
- var paths = Object.keys(this.paths)
- , i = paths.length
- , ret = [];
+ var paths = Object.keys(this.paths),
+ i = paths.length,
+ ret = [];
while (i--) {
var path = paths[i];
- if (this.paths[path].isRequired) ret.push(path);
+ if (this.paths[path].isRequired) {
+ ret.push(path);
+ }
}
+ this._requiredpaths = ret;
+ return this._requiredpaths;
+};
- return this._requiredpaths = ret;
-}
+/**
+ * Returns indexes from fields and schema-level indexes (cached).
+ *
+ * @api private
+ * @return {Array}
+ */
+
+Schema.prototype.indexedPaths = function indexedPaths() {
+ if (this._indexedpaths) {
+ return this._indexedpaths;
+ }
+ this._indexedpaths = this.indexes();
+ return this._indexedpaths;
+};
/**
* Returns the pathType of `path` for this schema.
@@ -408,16 +706,116 @@ Schema.prototype.requiredPaths = function requiredPaths () {
* @api public
*/
-Schema.prototype.pathType = function (path) {
- if (path in this.paths) return 'real';
- if (path in this.virtuals) return 'virtual';
- if (path in this.nested) return 'nested';
- if (path in this.subpaths) return 'real';
-
- if (/\.\d+\.|\.\d+$/.test(path) && getPositionalPath(this, path)) {
+Schema.prototype.pathType = function(path) {
+ if (path in this.paths) {
return 'real';
- } else {
- return 'adhocOrUndefined'
+ }
+ if (path in this.virtuals) {
+ return 'virtual';
+ }
+ if (path in this.nested) {
+ return 'nested';
+ }
+ if (path in this.subpaths) {
+ return 'real';
+ }
+ if (path in this.singleNestedPaths) {
+ return 'real';
+ }
+
+ if (/\.\d+\.|\.\d+$/.test(path)) {
+ return getPositionalPathType(this, path);
+ }
+ return 'adhocOrUndefined';
+};
+
+/**
+ * Returns true iff this path is a child of a mixed schema.
+ *
+ * @param {String} path
+ * @return {Boolean}
+ * @api private
+ */
+
+Schema.prototype.hasMixedParent = function(path) {
+ var subpaths = path.split(/\./g);
+ path = '';
+ for (var i = 0; i < subpaths.length; ++i) {
+ path = i > 0 ? path + '.' + subpaths[i] : subpaths[i];
+ if (path in this.paths &&
+ this.paths[path] instanceof MongooseTypes.Mixed) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/**
+ * Setup updatedAt and createdAt timestamps to documents if enabled
+ *
+ * @param {Boolean|Object} timestamps timestamps options
+ * @api private
+ */
+Schema.prototype.setupTimestamp = function(timestamps) {
+ if (timestamps) {
+ var createdAt = timestamps.createdAt || 'createdAt';
+ var updatedAt = timestamps.updatedAt || 'updatedAt';
+ var schemaAdditions = {};
+
+ schemaAdditions[updatedAt] = Date;
+
+ if (!this.paths[createdAt]) {
+ schemaAdditions[createdAt] = Date;
+ }
+
+ this.add(schemaAdditions);
+
+ this.pre('save', function(next) {
+ var defaultTimestamp = new Date();
+ var auto_id = this._id && this._id.auto;
+
+ if (!this[createdAt] && this.isSelected(createdAt)) {
+ this[createdAt] = auto_id ? this._id.getTimestamp() : defaultTimestamp;
+ }
+
+ if (this.isNew || this.isModified()) {
+ this[updatedAt] = this.isNew ? this[createdAt] : defaultTimestamp;
+ }
+
+ next();
+ });
+
+ var genUpdates = function() {
+ var now = new Date();
+ var updates = { $set: {}, $setOnInsert: {} };
+ updates.$set[updatedAt] = now;
+ updates.$setOnInsert[createdAt] = now;
+
+ return updates;
+ };
+
+ this.methods.initializeTimestamps = function() {
+ if (!this[createdAt]) {
+ this[createdAt] = new Date();
+ }
+ if (!this[updatedAt]) {
+ this[updatedAt] = new Date();
+ }
+ return this;
+ };
+
+ this.pre('findOneAndUpdate', function(next) {
+ this.findOneAndUpdate({}, genUpdates());
+ applyTimestampsToChildren(this);
+ next();
+ });
+
+ this.pre('update', function(next) {
+ this.update({}, genUpdates());
+ applyTimestampsToChildren(this);
+ next();
+ });
}
};
@@ -425,24 +823,83 @@ Schema.prototype.pathType = function (path) {
* ignore
*/
-function getPositionalPath (self, path) {
+function applyTimestampsToChildren(query) {
+ var now = new Date();
+ var update = query.getUpdate();
+ var keys = Object.keys(update);
+ var key;
+ var schema = query.model.schema;
+ var len;
+ var createdAt;
+ var updatedAt;
+ var timestamps;
+
+ var hasDollarKey = keys.length && keys[0].charAt(0) === '$';
+
+ if (hasDollarKey) {
+ if (update.$push) {
+ for (key in update.$push) {
+ if (update.$push[key] &&
+ schema.path(key).$isMongooseDocumentArray &&
+ schema.path(key).schema.options.timestamps) {
+ timestamps = schema.path(key).schema.options.timestamps;
+ createdAt = timestamps.createdAt || 'createdAt';
+ updatedAt = timestamps.updatedAt || 'updatedAt';
+ update.$push[key][updatedAt] = now;
+ update.$push[key][createdAt] = now;
+ }
+ }
+ }
+ if (update.$set) {
+ for (key in update.$set) {
+ if (Array.isArray(update.$set[key]) &&
+ schema.path(key).$isMongooseDocumentArray) {
+ len = update.$set[key].length;
+ timestamps = schema.path(key).schema.options.timestamps;
+ createdAt = timestamps.createdAt || 'createdAt';
+ updatedAt = timestamps.updatedAt || 'updatedAt';
+ for (var i = 0; i < len; ++i) {
+ update.$set[key][i][updatedAt] = now;
+ update.$set[key][i][createdAt] = now;
+ }
+ } else if (update.$set[key] && schema.path(key).$isSingleNested) {
+ timestamps = schema.path(key).schema.options.timestamps;
+ createdAt = timestamps.createdAt || 'createdAt';
+ updatedAt = timestamps.updatedAt || 'updatedAt';
+ update.$set[key][updatedAt] = now;
+ update.$set[key][createdAt] = now;
+ }
+ }
+ }
+ }
+}
+
+/*!
+ * ignore
+ */
+
+function getPositionalPathType(self, path) {
var subpaths = path.split(/\.(\d+)\.|\.(\d+)$/).filter(Boolean);
if (subpaths.length < 2) {
return self.paths[subpaths[0]];
}
var val = self.path(subpaths[0]);
- if (!val) return val;
+ var isNested = false;
+ if (!val) {
+ return val;
+ }
- var last = subpaths.length - 1
- , subpath
- , i = 1;
+ var last = subpaths.length - 1,
+ subpath,
+ i = 1;
for (; i < subpaths.length; ++i) {
+ isNested = false;
subpath = subpaths[i];
if (i === last && val && !val.schema && !/\D/.test(subpath)) {
- if (val instanceof Types.Array) {
+ if (val instanceof MongooseTypes.Array) {
// StringSchema, NumberSchema, etc
val = val.caster;
} else {
@@ -452,17 +909,38 @@ function getPositionalPath (self, path) {
}
// ignore if its just a position segment: path.0.subpath
- if (!/\D/.test(subpath)) continue;
+ if (!/\D/.test(subpath)) {
+ continue;
+ }
if (!(val && val.schema)) {
val = undefined;
break;
}
+ var type = val.schema.pathType(subpath);
+ isNested = (type === 'nested');
val = val.schema.path(subpath);
}
- return self.subpaths[path] = val;
+ self.subpaths[path] = val;
+ if (val) {
+ return 'real';
+ }
+ if (isNested) {
+ return 'nested';
+ }
+ return 'adhocOrUndefined';
+}
+
+
+/*!
+ * ignore
+ */
+
+function getPositionalPath(self, path) {
+ getPositionalPathType(self, path);
+ return self.subpaths[path];
}
/**
@@ -470,10 +948,10 @@ function getPositionalPath (self, path) {
*
* @param {String} name name of the document method to call later
* @param {Array} args arguments to pass to the method
- * @api private
+ * @api public
*/
-Schema.prototype.queue = function(name, args){
+Schema.prototype.queue = function(name, args) {
this.callQueue.push([name, args]);
return this;
};
@@ -491,7 +969,7 @@ Schema.prototype.queue = function(name, args){
* })
*
* toySchema.pre('validate', function (next) {
- * if (this.name != 'Woody') this.name = 'Woody';
+ * if (this.name !== 'Woody') this.name = 'Woody';
* next();
* })
*
@@ -501,47 +979,84 @@ Schema.prototype.queue = function(name, args){
* @api public
*/
-Schema.prototype.pre = function(){
+Schema.prototype.pre = function() {
+ var name = arguments[0];
+ if (IS_KAREEM_HOOK[name]) {
+ this.s.hooks.pre.apply(this.s.hooks, arguments);
+ return this;
+ }
return this.queue('pre', arguments);
};
/**
- * Defines a post for the document
- *
- * Post hooks fire `on` the event emitted from document instances of Models compiled from this schema.
+ * Defines a post hook for the document
*
* var schema = new Schema(..);
* schema.post('save', function (doc) {
* console.log('this fired after a document was saved');
* });
*
+ * shema.post('find', function(docs) {
+ * console.log('this fired after you run a find query');
+ * });
+ *
* var Model = mongoose.model('Model', schema);
*
* var m = new Model(..);
- * m.save(function (err) {
+ * m.save(function(err) {
* console.log('this fires after the `post` hook');
* });
*
+ * m.find(function(err, docs) {
+ * console.log('this fires after the post find hook');
+ * });
+ *
* @param {String} method name of the method to hook
* @param {Function} fn callback
- * @see hooks.js https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3
+ * @see middleware http://mongoosejs.com/docs/middleware.html
+ * @see hooks.js https://www.npmjs.com/package/hooks-fixed
+ * @see kareem http://npmjs.org/package/kareem
* @api public
*/
-Schema.prototype.post = function(method, fn){
- return this.queue('on', arguments);
+Schema.prototype.post = function(method, fn) {
+ if (IS_KAREEM_HOOK[method]) {
+ this.s.hooks.post.apply(this.s.hooks, arguments);
+ return this;
+ }
+ // assuming that all callbacks with arity < 2 are synchronous post hooks
+ if (fn.length < 2) {
+ return this.queue('on', [arguments[0], function(doc) {
+ return fn.call(doc, doc);
+ }]);
+ }
+
+ if (fn.length === 3) {
+ this.s.hooks.post(method + ':error', fn);
+ return this;
+ }
+
+ return this.queue('post', [arguments[0], function(next) {
+ // wrap original function so that the callback goes last,
+ // for compatibility with old code that is using synchronous post hooks
+ var _this = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ fn.call(this, this, function(err) {
+ return next.apply(_this, [err].concat(args));
+ });
+ }]);
};
/**
* Registers a plugin for this schema.
*
* @param {Function} plugin callback
- * @param {Object} opts
+ * @param {Object} [opts]
* @see plugins
* @api public
*/
-Schema.prototype.plugin = function (fn, opts) {
+Schema.prototype.plugin = function(fn, opts) {
fn(this, opts);
return this;
};
@@ -578,12 +1093,14 @@ Schema.prototype.plugin = function (fn, opts) {
* @api public
*/
-Schema.prototype.method = function (name, fn) {
- if ('string' != typeof name)
- for (var i in name)
+Schema.prototype.method = function(name, fn) {
+ if (typeof name !== 'string') {
+ for (var i in name) {
this.methods[i] = name[i];
- else
+ }
+ } else {
this.methods[name] = fn;
+ }
return this;
};
@@ -604,17 +1121,19 @@ Schema.prototype.method = function (name, fn) {
*
* If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as statics.
*
- * @param {String} name
- * @param {Function} fn
+ * @param {String|Object} name
+ * @param {Function} [fn]
* @api public
*/
Schema.prototype.static = function(name, fn) {
- if ('string' != typeof name)
- for (var i in name)
+ if (typeof name !== 'string') {
+ for (var i in name) {
this.statics[i] = name[i];
- else
+ }
+ } else {
this.statics[name] = fn;
+ }
return this;
};
@@ -626,15 +1145,17 @@ Schema.prototype.static = function(name, fn) {
* schema.index({ first: 1, last: -1 })
*
* @param {Object} fields
- * @param {Object} [options]
+ * @param {Object} [options] Options to pass to [MongoDB driver's `createIndex()` function](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#createIndex)
+ * @param {String} [options.expires=null] Mongoose-specific syntactic sugar, uses [ms](https://www.npmjs.com/package/ms) to convert `expires` option into seconds for the `expireAfterSeconds` in the above link.
* @api public
*/
-Schema.prototype.index = function (fields, options) {
+Schema.prototype.index = function(fields, options) {
options || (options = {});
- if (options.expires)
+ if (options.expires) {
utils.expires(options);
+ }
this._indexes.push([fields, options]);
return this;
@@ -643,31 +1164,42 @@ Schema.prototype.index = function (fields, options) {
/**
* Sets/gets a schema option.
*
+ * ####Example
+ *
+ * schema.set('strict'); // 'true' by default
+ * schema.set('strict', false); // Sets 'strict' to false
+ * schema.set('strict'); // 'false'
+ *
* @param {String} key option name
* @param {Object} [value] if not passed, the current option value is returned
+ * @see Schema ./
* @api public
*/
-Schema.prototype.set = function (key, value, _tags) {
- if (1 === arguments.length) {
+Schema.prototype.set = function(key, value, _tags) {
+ if (arguments.length === 1) {
return this.options[key];
}
switch (key) {
case 'read':
- this.options[key] = utils.readPref(value, _tags)
+ this.options[key] = readPref(value, _tags);
break;
case 'safe':
- this.options[key] = false === value
- ? { w: 0 }
- : value
+ this.options[key] = value === false
+ ? {w: 0}
+ : value;
+ break;
+ case 'timestamps':
+ this.setupTimestamp(value);
+ this.options[key] = value;
break;
default:
this.options[key] = value;
}
return this;
-}
+};
/**
* Gets a schema option.
@@ -676,9 +1208,9 @@ Schema.prototype.set = function (key, value, _tags) {
* @api public
*/
-Schema.prototype.get = function (key) {
+Schema.prototype.get = function(key) {
return this.options[key];
-}
+};
/**
* The allowed index types
@@ -691,9 +1223,13 @@ Schema.prototype.get = function (key) {
var indexTypes = '2d 2dsphere hashed text'.split(' ');
Object.defineProperty(Schema, 'indexTypes', {
- get: function () { return indexTypes }
- , set: function () { throw new Error('Cannot overwrite Schema.indexTypes') }
-})
+ get: function() {
+ return indexTypes;
+ },
+ set: function() {
+ throw new Error('Cannot overwrite Schema.indexTypes');
+ }
+});
/**
* Compiles indexes from fields and schema-level indexes
@@ -701,20 +1237,19 @@ Object.defineProperty(Schema, 'indexTypes', {
* @api public
*/
-Schema.prototype.indexes = function () {
+Schema.prototype.indexes = function() {
'use strict';
- var indexes = []
- , seenSchemas = []
- collectIndexes(this);
- return indexes;
+ var indexes = [];
+ var seenPrefix = {};
- function collectIndexes (schema, prefix) {
- if (~seenSchemas.indexOf(schema)) return;
- seenSchemas.push(schema);
+ var collectIndexes = function(schema, prefix) {
+ if (seenPrefix[prefix]) {
+ return;
+ }
+ seenPrefix[prefix] = true;
prefix = prefix || '';
-
var key, path, index, field, isObject, options, type;
var keys = Object.keys(schema.paths);
@@ -722,21 +1257,24 @@ Schema.prototype.indexes = function () {
key = keys[i];
path = schema.paths[key];
- if (path instanceof Types.DocumentArray) {
+ if ((path instanceof MongooseTypes.DocumentArray) || path.$isSingleNested) {
collectIndexes(path.schema, key + '.');
} else {
index = path._index;
- if (false !== index && null != index) {
+ if (index !== false && index !== null && index !== undefined) {
field = {};
isObject = utils.isObject(index);
options = isObject ? index : {};
- type = 'string' == typeof index ? index :
- isObject ? index.type :
- false;
+ type = typeof index === 'string' ? index :
+ isObject ? index.type :
+ false;
if (type && ~Schema.indexTypes.indexOf(type)) {
field[prefix + key] = type;
+ } else if (options.text) {
+ field[prefix + key] = 'text';
+ delete options.text;
} else {
field[prefix + key] = 1;
}
@@ -754,12 +1292,17 @@ Schema.prototype.indexes = function () {
if (prefix) {
fixSubIndexPaths(schema, prefix);
} else {
- schema._indexes.forEach(function (index) {
- if (!('background' in index[1])) index[1].background = true;
+ schema._indexes.forEach(function(index) {
+ if (!('background' in index[1])) {
+ index[1].background = true;
+ }
});
indexes = indexes.concat(schema._indexes);
}
- }
+ };
+
+ collectIndexes(this);
+ return indexes;
/*!
* Checks for indexes added to subdocs using Schema.index().
@@ -768,16 +1311,16 @@ Schema.prototype.indexes = function () {
* schema._indexes = [ [indexObj, options], [indexObj, options] ..]
*/
- function fixSubIndexPaths (schema, prefix) {
- var subindexes = schema._indexes
- , len = subindexes.length
- , indexObj
- , newindex
- , klen
- , keys
- , key
- , i = 0
- , j
+ function fixSubIndexPaths(schema, prefix) {
+ var subindexes = schema._indexes,
+ len = subindexes.length,
+ indexObj,
+ newindex,
+ klen,
+ keys,
+ key,
+ i = 0,
+ j;
for (i = 0; i < len; ++i) {
indexObj = subindexes[i][0];
@@ -794,7 +1337,7 @@ Schema.prototype.indexes = function () {
indexes.push([newindex, subindexes[i][1]]);
}
}
-}
+};
/**
* Creates a virtual type with the given name.
@@ -804,15 +1347,66 @@ Schema.prototype.indexes = function () {
* @return {VirtualType}
*/
-Schema.prototype.virtual = function (name, options) {
+Schema.prototype.virtual = function(name, options) {
+ if (options && options.ref) {
+ if (!options.localField) {
+ throw new Error('Reference virtuals require `localField` option');
+ }
+
+ if (!options.foreignField) {
+ throw new Error('Reference virtuals require `foreignField` option');
+ }
+
+ this.pre('init', function(next, obj) {
+ if (name in obj) {
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+
+ if (options.justOne) {
+ this.$$populatedVirtuals[name] = Array.isArray(obj[name]) ?
+ obj[name][0] :
+ obj[name];
+ } else {
+ this.$$populatedVirtuals[name] = Array.isArray(obj[name]) ?
+ obj[name] :
+ obj[name] == null ? [] : [obj[name]];
+ }
+
+ delete obj[name];
+ }
+ next();
+ });
+
+ var virtual = this.virtual(name);
+ virtual.options = options;
+ return virtual.
+ get(function() {
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+ if (name in this.$$populatedVirtuals) {
+ return this.$$populatedVirtuals[name];
+ }
+ return null;
+ }).
+ set(function(v) {
+ if (!this.$$populatedVirtuals) {
+ this.$$populatedVirtuals = {};
+ }
+ this.$$populatedVirtuals[name] = v;
+ });
+ }
+
var virtuals = this.virtuals;
var parts = name.split('.');
- return virtuals[name] = parts.reduce(function (mem, part, i) {
- mem[part] || (mem[part] = (i === parts.length-1)
- ? new VirtualType(options, name)
- : {});
+ virtuals[name] = parts.reduce(function(mem, part, i) {
+ mem[part] || (mem[part] = (i === parts.length - 1)
+ ? new VirtualType(options, name)
+ : {});
return mem[part];
}, this.tree);
+ return virtuals[name];
};
/**
@@ -822,46 +1416,151 @@ Schema.prototype.virtual = function (name, options) {
* @return {VirtualType}
*/
-Schema.prototype.virtualpath = function (name) {
+Schema.prototype.virtualpath = function(name) {
return this.virtuals[name];
};
/**
- * These still haven't been fixed. Once they're working we'll make them public again.
- * @api private
+ * Removes the given `path` (or [`paths`]).
+ *
+ * @param {String|Array} path
+ *
+ * @api public
*/
+Schema.prototype.remove = function(path) {
+ if (typeof path === 'string') {
+ path = [path];
+ }
+ if (Array.isArray(path)) {
+ path.forEach(function(name) {
+ if (this.path(name)) {
+ delete this.paths[name];
+
+ var pieces = name.split('.');
+ var last = pieces.pop();
+ var branch = this.tree;
+ for (var i = 0; i < pieces.length; ++i) {
+ branch = branch[pieces[i]];
+ }
+ delete branch[last];
+ }
+ }, this);
+ }
+};
-Schema.prototype.namedScope = function (name, fn) {
- var namedScopes = this.namedScopes || (this.namedScopes = new NamedScope)
- , newScope = Object.create(namedScopes)
- , allScopes = namedScopes.scopesByName || (namedScopes.scopesByName = {});
- allScopes[name] = newScope;
- newScope.name = name;
- newScope.block = fn;
- newScope.query = new Query();
- newScope.decorate(namedScopes, {
- block0: function (block) {
- return function () {
- block.call(this.query);
- return this;
- };
- },
- blockN: function (block) {
- return function () {
- block.apply(this.query, arguments);
- return this;
- };
- },
- basic: function (query) {
- return function () {
- this.query.find(query);
- return this;
- };
+/*!
+ * ignore
+ */
+
+Schema.prototype._getSchema = function(path) {
+ var _this = this;
+ var pathschema = _this.path(path);
+
+ if (pathschema) {
+ return pathschema;
+ }
+
+ function search(parts, schema) {
+ var p = parts.length + 1,
+ foundschema,
+ trypath;
+
+ while (p--) {
+ trypath = parts.slice(0, p).join('.');
+ foundschema = schema.path(trypath);
+ if (foundschema) {
+ if (foundschema.caster) {
+ // array of Mixed?
+ if (foundschema.caster instanceof MongooseTypes.Mixed) {
+ return foundschema.caster;
+ }
+
+ // Now that we found the array, we need to check if there
+ // are remaining document paths to look up for casting.
+ // Also we need to handle array.$.path since schema.path
+ // doesn't work for that.
+ // If there is no foundschema.schema we are dealing with
+ // a path like array.$
+ if (p !== parts.length && foundschema.schema) {
+ if (parts[p] === '$') {
+ // comments.$.comments.$.title
+ return search(parts.slice(p + 1), foundschema.schema);
+ }
+ // this is the last path of the selector
+ return search(parts.slice(p), foundschema.schema);
+ }
+ }
+ return foundschema;
+ }
}
- });
- return newScope;
+ }
+
+ // look for arrays
+ return search(path.split('.'), _this);
};
+/*!
+ * ignore
+ */
+
+Schema.prototype._getPathType = function(path) {
+ var _this = this;
+ var pathschema = _this.path(path);
+
+ if (pathschema) {
+ return 'real';
+ }
+
+ function search(parts, schema) {
+ var p = parts.length + 1,
+ foundschema,
+ trypath;
+
+ while (p--) {
+ trypath = parts.slice(0, p).join('.');
+ foundschema = schema.path(trypath);
+ if (foundschema) {
+ if (foundschema.caster) {
+ // array of Mixed?
+ if (foundschema.caster instanceof MongooseTypes.Mixed) {
+ return { schema: foundschema, pathType: 'mixed' };
+ }
+
+ // Now that we found the array, we need to check if there
+ // are remaining document paths to look up for casting.
+ // Also we need to handle array.$.path since schema.path
+ // doesn't work for that.
+ // If there is no foundschema.schema we are dealing with
+ // a path like array.$
+ if (p !== parts.length && foundschema.schema) {
+ if (parts[p] === '$') {
+ if (p === parts.length - 1) {
+ return { schema: foundschema, pathType: 'nested' };
+ }
+ // comments.$.comments.$.title
+ return search(parts.slice(p + 1), foundschema.schema);
+ }
+ // this is the last path of the selector
+ return search(parts.slice(p), foundschema.schema);
+ }
+ return {
+ schema: foundschema,
+ pathType: foundschema.$isSingleNested ? 'nested' : 'array'
+ };
+ }
+ return { schema: foundschema, pathType: 'real' };
+ } else if (p === parts.length && schema.nested[trypath]) {
+ return { schema: schema, pathType: 'nested' };
+ }
+ }
+ return { schema: foundschema || schema, pathType: 'undefined' };
+ }
+
+ // look for arrays
+ return search(path.split('.'), _this);
+};
+
+
/*!
* Module exports.
*/
@@ -897,14 +1596,10 @@ module.exports = exports = Schema;
* @api public
*/
-Schema.Types = require('./schema/index');
+Schema.Types = MongooseTypes = require('./schema/index');
/*!
* ignore
*/
-Types = Schema.Types;
-NamedScope = require('./namedscope')
-Query = require('./query');
-var ObjectId = exports.ObjectId = Types.ObjectId;
-
+exports.ObjectId = MongooseTypes.ObjectId;
diff --git a/node_modules/mongoose/lib/schema/array.js b/node_modules/mongoose/lib/schema/array.js
index 3bdbbce..bc4fe3b 100644
--- a/node_modules/mongoose/lib/schema/array.js
+++ b/node_modules/mongoose/lib/schema/array.js
@@ -2,23 +2,25 @@
* Module dependencies.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , NumberSchema = require('./number')
- , Types = {
- Boolean: require('./boolean')
- , Date: require('./date')
- , Number: require('./number')
- , String: require('./string')
- , ObjectId: require('./objectid')
- , Buffer: require('./buffer')
- }
- , MongooseArray = require('../types').Array
- , EmbeddedDoc = require('../types').Embedded
- , Mixed = require('./mixed')
- , Query = require('../query')
- , utils = require('../utils')
- , isMongooseObject = utils.isMongooseObject
+var SchemaType = require('../schematype');
+var CastError = SchemaType.CastError;
+var Types = {
+ Boolean: require('./boolean'),
+ Date: require('./date'),
+ Number: require('./number'),
+ String: require('./string'),
+ ObjectId: require('./objectid'),
+ Buffer: require('./buffer')
+};
+var MongooseArray = require('../types').Array;
+var EmbeddedDoc = require('../types').Embedded;
+var Mixed = require('./mixed');
+var cast = require('../cast');
+var util = require('util');
+var utils = require('../utils');
+var isMongooseObject = utils.isMongooseObject;
+var castToNumber = require('./operators/helpers').castToNumber;
+var geospatial = require('./operators/geospatial');
/**
* Array SchemaType constructor
@@ -27,14 +29,14 @@ var SchemaType = require('../schematype')
* @param {SchemaType} cast
* @param {Object} options
* @inherits SchemaType
- * @api private
+ * @api public
*/
-function SchemaArray (key, cast, options) {
+function SchemaArray(key, cast, options) {
if (cast) {
var castOptions = {};
- if ('Object' === cast.constructor.name) {
+ if (utils.getFunctionName(cast.constructor) === 'Object') {
if (cast.type) {
// support { type: Woot }
castOptions = utils.clone(cast); // do not alter user arguments
@@ -46,52 +48,69 @@ function SchemaArray (key, cast, options) {
}
// support { type: 'String' }
- var name = 'string' == typeof cast
- ? cast
- : cast.name;
+ var name = typeof cast === 'string'
+ ? cast
+ : utils.getFunctionName(cast);
var caster = name in Types
- ? Types[name]
- : cast;
+ ? Types[name]
+ : cast;
this.casterConstructor = caster;
- this.caster = new caster(null, castOptions);
+ if (typeof caster === 'function') {
+ this.caster = new caster(null, castOptions);
+ } else {
+ this.caster = caster;
+ }
+
if (!(this.caster instanceof EmbeddedDoc)) {
this.caster.path = key;
}
}
- SchemaType.call(this, key, options);
+ SchemaType.call(this, key, options, 'Array');
- var self = this
- , defaultArr
- , fn;
+ var _this = this;
+ var defaultArr;
+ var fn;
if (this.defaultValue) {
defaultArr = this.defaultValue;
- fn = 'function' == typeof defaultArr;
+ fn = typeof defaultArr === 'function';
}
- this.default(function(){
- var arr = fn ? defaultArr() : defaultArr || [];
- return new MongooseArray(arr, self.path, this);
- });
-};
+ if (!('defaultValue' in this) || this.defaultValue !== void 0) {
+ this.default(function() {
+ var arr = fn ? defaultArr() : defaultArr || [];
+ return new MongooseArray(arr, _this.path, this);
+ });
+ }
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaArray.schemaName = 'Array';
/*!
* Inherits from SchemaType.
*/
-
-SchemaArray.prototype.__proto__ = SchemaType.prototype;
+SchemaArray.prototype = Object.create(SchemaType.prototype);
+SchemaArray.prototype.constructor = SchemaArray;
/**
- * Check required
+ * Check if the given value satisfies a required validator. The given value
+ * must be not null nor undefined, and have a non-zero length.
*
- * @param {Array} value
- * @api private
+ * @param {Any} value
+ * @return {Boolean}
+ * @api public
*/
-SchemaArray.prototype.checkRequired = function (value) {
+SchemaArray.prototype.checkRequired = function(value) {
return !!(value && value.length);
};
@@ -103,7 +122,7 @@ SchemaArray.prototype.checkRequired = function (value) {
* @api private
*/
-SchemaArray.prototype.applyGetters = function (value, scope) {
+SchemaArray.prototype.applyGetters = function(value, scope) {
if (this.caster.options && this.caster.options.ref) {
// means the object id was populated
return value;
@@ -113,7 +132,7 @@ SchemaArray.prototype.applyGetters = function (value, scope) {
};
/**
- * Casts contents
+ * Casts values for set().
*
* @param {Object} value
* @param {Document} doc document that triggers the casting
@@ -121,194 +140,164 @@ SchemaArray.prototype.applyGetters = function (value, scope) {
* @api private
*/
-SchemaArray.prototype.cast = function (value, doc, init) {
+SchemaArray.prototype.cast = function(value, doc, init) {
if (Array.isArray(value)) {
- if (!(value instanceof MongooseArray)) {
+ if (!value.length && doc) {
+ var indexes = doc.schema.indexedPaths();
+
+ for (var i = 0, l = indexes.length; i < l; ++i) {
+ var pathIndex = indexes[i][0][this.path];
+ if (pathIndex === '2dsphere' || pathIndex === '2d') {
+ return;
+ }
+ }
+ }
+
+ if (!(value && value.isMongooseArray)) {
value = new MongooseArray(value, this.path, doc);
}
if (this.caster) {
try {
- for (var i = 0, l = value.length; i < l; i++) {
+ for (i = 0, l = value.length; i < l; i++) {
value[i] = this.caster.cast(value[i], doc, init);
}
} catch (e) {
// rethrow
- throw new CastError(e.type, value, this.path);
+ throw new CastError('[' + e.kind + ']', util.inspect(value), this.path, e);
}
}
return value;
- } else {
- return this.cast([value], doc, init);
}
+ // gh-2442: if we're loading this from the db and its not an array, mark
+ // the whole array as modified.
+ if (!!doc && !!init) {
+ doc.markModified(this.path);
+ }
+ return this.cast([value], doc, init);
};
/**
- * Casts contents for queries.
+ * Casts values for queries.
*
* @param {String} $conditional
* @param {any} [value]
* @api private
*/
-SchemaArray.prototype.castForQuery = function ($conditional, value) {
- var handler
- , val;
+SchemaArray.prototype.castForQuery = function($conditional, value) {
+ var handler,
+ val;
+
if (arguments.length === 2) {
handler = this.$conditionalHandlers[$conditional];
- if (!handler)
- throw new Error("Can't use " + $conditional + " with Array.");
+
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with Array.');
+ }
+
val = handler.call(this, value);
} else {
val = $conditional;
var proto = this.casterConstructor.prototype;
var method = proto.castForQuery || proto.cast;
-
var caster = this.caster;
- if (Array.isArray(val)) {
- val = val.map(function (v) {
- if (method) v = method.call(caster, v);
- return isMongooseObject(v)
- ? v.toObject()
- : v;
+ if (Array.isArray(val)) {
+ val = val.map(function(v) {
+ if (utils.isObject(v) && v.$elemMatch) {
+ return v;
+ }
+ if (method) {
+ v = method.call(caster, v);
+ }
+ return isMongooseObject(v) ?
+ v.toObject({virtuals: false}) :
+ v;
});
} else if (method) {
val = method.call(caster, val);
}
}
- return val && isMongooseObject(val)
- ? val.toObject()
- : val;
-};
-
-/*!
- * @ignore
- */
-function castToNumber (val) {
- return Types.Number.prototype.cast.call(this, val);
-}
+ return val && isMongooseObject(val) ?
+ val.toObject({virtuals: false}) :
+ val;
+};
-function castArray (arr, self) {
- self || (self = this);
+function cast$all(val) {
+ if (!Array.isArray(val)) {
+ val = [val];
+ }
- arr.forEach(function (v, i) {
- if (Array.isArray(v)) {
- castArray(v, self);
- } else {
- arr[i] = castToNumber.call(self, v);
+ val = val.map(function(v) {
+ if (utils.isObject(v)) {
+ var o = {};
+ o[this.path] = v;
+ return cast(this.casterConstructor.schema, o)[this.path];
}
- });
-}
+ return v;
+ }, this);
-SchemaArray.prototype.$conditionalHandlers = {
- '$all': function handle$all (val) {
- if (!Array.isArray(val)) {
- val = [val];
- }
-
- val = val.map(function (v) {
- if (v && 'Object' === v.constructor.name) {
- var o = {};
- o[this.path] = v;
- var query = new Query(o);
- query.cast(this.casterConstructor);
- return query._conditions[this.path];
- }
- return v;
- }, this);
+ return this.castForQuery(val);
+}
- return this.castForQuery(val);
+function cast$elemMatch(val) {
+ var keys = Object.keys(val);
+ var numKeys = keys.length;
+ var key;
+ var value;
+ for (var i = 0; i < numKeys; ++i) {
+ key = keys[i];
+ value = val[key];
+ if (key.indexOf('$') === 0 && value) {
+ val[key] = this.castForQuery(key, value);
}
- , '$elemMatch': function (val) {
- if (val.$in) {
- val.$in = this.castForQuery('$in', val.$in);
- return val;
- }
+ }
- var query = new Query(val);
- query.cast(this.casterConstructor);
- return query._conditions;
- }
- , '$size': castToNumber
- , '$ne': SchemaArray.prototype.castForQuery
- , '$in': SchemaArray.prototype.castForQuery
- , '$nin': SchemaArray.prototype.castForQuery
- , '$regex': SchemaArray.prototype.castForQuery
- , '$options': String
- , '$near': SchemaArray.prototype.castForQuery
- , '$nearSphere': SchemaArray.prototype.castForQuery
- , '$gt': SchemaArray.prototype.castForQuery
- , '$gte': SchemaArray.prototype.castForQuery
- , '$lt': SchemaArray.prototype.castForQuery
- , '$lte': SchemaArray.prototype.castForQuery
- , '$within': function (val) {
- var self = this;
-
- if (val.$maxDistance) {
- val.$maxDistance = castToNumber.call(this, val.$maxDistance);
- }
+ return cast(this.casterConstructor.schema, val);
+}
- if (val.$box || val.$polygon) {
- var type = val.$box ? '$box' : '$polygon';
- val[type].forEach(function (arr) {
- if (!Array.isArray(arr)) {
- var msg = 'Invalid $within $box argument. '
- + 'Expected an array, received ' + arr;
- throw new TypeError(msg);
- }
- arr.forEach(function (v, i) {
- arr[i] = castToNumber.call(this, v);
- });
- })
- } else if (val.$center || val.$centerSphere) {
- var type = val.$center ? '$center' : '$centerSphere';
- val[type].forEach(function (item, i) {
- if (Array.isArray(item)) {
- item.forEach(function (v, j) {
- item[j] = castToNumber.call(this, v);
- });
- } else {
- val[type][i] = castToNumber.call(this, item);
- }
- })
- } else if (val.$geometry) {
- switch (val.$geometry.type) {
- case 'Polygon':
- case 'LineString':
- case 'Point':
- val.$geometry.coordinates.forEach(castArray);
- break;
- default:
- // ignore unknowns
- break;
- }
- }
+var handle = SchemaArray.prototype.$conditionalHandlers = {};
- return val;
- }
- , '$geoIntersects': function (val) {
- var geo = val.$geometry;
- if (!geo) return;
-
- switch (val.$geometry.type) {
- case 'Polygon':
- case 'LineString':
- case 'Point':
- val.$geometry.coordinates.forEach(castArray);
- break;
- default:
- // ignore unknowns
- break;
- }
+handle.$all = cast$all;
+handle.$options = String;
+handle.$elemMatch = cast$elemMatch;
+handle.$geoIntersects = geospatial.cast$geoIntersects;
+handle.$or = handle.$and = function(val) {
+ if (!Array.isArray(val)) {
+ throw new TypeError('conditional $or/$and require array');
+ }
- return val;
- }
- , '$maxDistance': castToNumber
+ var ret = [];
+ for (var i = 0; i < val.length; ++i) {
+ ret.push(cast(this.casterConstructor.schema, val[i]));
+ }
+
+ return ret;
};
+handle.$near =
+handle.$nearSphere = geospatial.cast$near;
+
+handle.$within =
+handle.$geoWithin = geospatial.cast$within;
+
+handle.$size =
+handle.$minDistance =
+handle.$maxDistance = castToNumber;
+
+handle.$eq =
+handle.$gt =
+handle.$gte =
+handle.$in =
+handle.$lt =
+handle.$lte =
+handle.$ne =
+handle.$nin =
+handle.$regex = SchemaArray.prototype.castForQuery;
+
/*!
* Module exports.
*/
diff --git a/node_modules/mongoose/lib/schema/boolean.js b/node_modules/mongoose/lib/schema/boolean.js
index cc16b7a..206d9fd 100644
--- a/node_modules/mongoose/lib/schema/boolean.js
+++ b/node_modules/mongoose/lib/schema/boolean.js
@@ -2,6 +2,8 @@
* Module dependencies.
*/
+var utils = require('../utils');
+
var SchemaType = require('../schematype');
/**
@@ -10,25 +12,38 @@ var SchemaType = require('../schematype');
* @param {String} path
* @param {Object} options
* @inherits SchemaType
- * @api private
+ * @api public
*/
-function SchemaBoolean (path, options) {
- SchemaType.call(this, path, options);
-};
+function SchemaBoolean(path, options) {
+ SchemaType.call(this, path, options, 'Boolean');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaBoolean.schemaName = 'Boolean';
/*!
* Inherits from SchemaType.
*/
-SchemaBoolean.prototype.__proto__ = SchemaType.prototype;
+SchemaBoolean.prototype = Object.create(SchemaType.prototype);
+SchemaBoolean.prototype.constructor = SchemaBoolean;
/**
- * Required validator
+ * Check if the given value satisfies a required validator. For a boolean
+ * to satisfy a required validator, it must be strictly equal to true or to
+ * false.
*
- * @api private
+ * @param {Any} value
+ * @return {Boolean}
+ * @api public
*/
-SchemaBoolean.prototype.checkRequired = function (value) {
+SchemaBoolean.prototype.checkRequired = function(value) {
return value === true || value === false;
};
@@ -39,28 +54,24 @@ SchemaBoolean.prototype.checkRequired = function (value) {
* @api private
*/
-SchemaBoolean.prototype.cast = function (value) {
- if (null === value) return value;
- if ('0' === value) return false;
- if ('true' === value) return true;
- if ('false' === value) return false;
- return !! value;
-}
-
-/*!
- * ignore
- */
-
-function handleArray (val) {
- var self = this;
- return val.map(function (m) {
- return self.cast(m);
- });
-}
+SchemaBoolean.prototype.cast = function(value) {
+ if (value === null) {
+ return value;
+ }
+ if (value === '0') {
+ return false;
+ }
+ if (value === 'true') {
+ return true;
+ }
+ if (value === 'false') {
+ return false;
+ }
+ return !!value;
+};
-SchemaBoolean.$conditionalHandlers = {
- '$in': handleArray
-}
+SchemaBoolean.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {});
/**
* Casts contents for queries.
@@ -70,9 +81,9 @@ SchemaBoolean.$conditionalHandlers = {
* @api private
*/
-SchemaBoolean.prototype.castForQuery = function ($conditional, val) {
+SchemaBoolean.prototype.castForQuery = function($conditional, val) {
var handler;
- if (2 === arguments.length) {
+ if (arguments.length === 2) {
handler = SchemaBoolean.$conditionalHandlers[$conditional];
if (handler) {
diff --git a/node_modules/mongoose/lib/schema/buffer.js b/node_modules/mongoose/lib/schema/buffer.js
index e699c10..035f127 100644
--- a/node_modules/mongoose/lib/schema/buffer.js
+++ b/node_modules/mongoose/lib/schema/buffer.js
@@ -2,45 +2,59 @@
* Module dependencies.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , MongooseBuffer = require('../types').Buffer
- , Binary = MongooseBuffer.Binary
- , Query = require('../query')
- , utils = require('../utils')
- , Document
+var handleBitwiseOperator = require('./operators/bitwise');
+var utils = require('../utils');
+
+var MongooseBuffer = require('../types').Buffer;
+var SchemaType = require('../schematype');
+
+var Binary = MongooseBuffer.Binary;
+var CastError = SchemaType.CastError;
+var Document;
/**
* Buffer SchemaType constructor
*
* @param {String} key
- * @param {SchemaType} cast
+ * @param {Object} options
* @inherits SchemaType
- * @api private
+ * @api public
*/
-function SchemaBuffer (key, options) {
+function SchemaBuffer(key, options) {
SchemaType.call(this, key, options, 'Buffer');
-};
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaBuffer.schemaName = 'Buffer';
/*!
* Inherits from SchemaType.
*/
-
-SchemaBuffer.prototype.__proto__ = SchemaType.prototype;
+SchemaBuffer.prototype = Object.create(SchemaType.prototype);
+SchemaBuffer.prototype.constructor = SchemaBuffer;
/**
- * Check required
+ * Check if the given value satisfies a required validator. To satisfy a
+ * required validator, a buffer must not be null or undefined and have
+ * non-zero length.
*
- * @api private
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
*/
-SchemaBuffer.prototype.checkRequired = function (value, doc) {
+SchemaBuffer.prototype.checkRequired = function(value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
- return null != value;
- } else {
- return !!(value && value.length);
+ return !!value;
}
+ return !!(value && value.length);
};
/**
@@ -52,11 +66,12 @@ SchemaBuffer.prototype.checkRequired = function (value, doc) {
* @api private
*/
-SchemaBuffer.prototype.cast = function (value, doc, init) {
+SchemaBuffer.prototype.cast = function(value, doc, init) {
+ var ret;
if (SchemaType._isRef(this, value, doc, init)) {
// wait! we may need to cast this to a document
- if (null == value) {
+ if (value === null || value === undefined) {
return value;
}
@@ -81,7 +96,7 @@ SchemaBuffer.prototype.cast = function (value, doc, init) {
var path = doc.$__fullPath(this.path);
var owner = doc.ownerDocument ? doc.ownerDocument() : doc;
var pop = owner.populated(path, true);
- var ret = new pop.options.model(value);
+ ret = new pop.options.model(value);
ret.$__.wasPopulated = true;
return ret;
}
@@ -91,25 +106,35 @@ SchemaBuffer.prototype.cast = function (value, doc, init) {
value = value._id;
}
+ if (value && value.isMongooseBuffer) {
+ return value;
+ }
+
if (Buffer.isBuffer(value)) {
- if (!(value instanceof MongooseBuffer)) {
+ if (!value || !value.isMongooseBuffer) {
value = new MongooseBuffer(value, [this.path, doc]);
}
return value;
} else if (value instanceof Binary) {
- var ret = new MongooseBuffer(value.value(true), [this.path, doc]);
+ ret = new MongooseBuffer(value.value(true), [this.path, doc]);
+ if (typeof value.sub_type !== 'number') {
+ throw new CastError('buffer', value, this.path);
+ }
ret._subtype = value.sub_type;
- // do not override Binary subtypes. users set this
- // to whatever they want.
return ret;
}
- if (null === value) return value;
+ if (value === null) {
+ return value;
+ }
var type = typeof value;
- if ('string' == type || 'number' == type || Array.isArray(value)) {
- var ret = new MongooseBuffer(value, [this.path, doc]);
+ if (type === 'string' || type === 'number' || Array.isArray(value)) {
+ if (type === 'number') {
+ value = [value];
+ }
+ ret = new MongooseBuffer(value, [this.path, doc]);
return ret;
}
@@ -119,26 +144,21 @@ SchemaBuffer.prototype.cast = function (value, doc, init) {
/*!
* ignore
*/
-function handleSingle (val) {
+function handleSingle(val) {
return this.castForQuery(val);
}
-function handleArray (val) {
- var self = this;
- return val.map( function (m) {
- return self.castForQuery(m);
- });
-}
-
-SchemaBuffer.prototype.$conditionalHandlers = {
- '$ne' : handleSingle
- , '$in' : handleArray
- , '$nin': handleArray
- , '$gt' : handleSingle
- , '$lt' : handleSingle
- , '$gte': handleSingle
- , '$lte': handleSingle
-};
+SchemaBuffer.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $bitsAllClear: handleBitwiseOperator,
+ $bitsAnyClear: handleBitwiseOperator,
+ $bitsAllSet: handleBitwiseOperator,
+ $bitsAnySet: handleBitwiseOperator,
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle
+ });
/**
* Casts contents for queries.
@@ -148,17 +168,17 @@ SchemaBuffer.prototype.$conditionalHandlers = {
* @api private
*/
-SchemaBuffer.prototype.castForQuery = function ($conditional, val) {
+SchemaBuffer.prototype.castForQuery = function($conditional, val) {
var handler;
if (arguments.length === 2) {
handler = this.$conditionalHandlers[$conditional];
- if (!handler)
- throw new Error("Can't use " + $conditional + " with Buffer.");
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with Buffer.');
+ }
return handler.call(this, val);
- } else {
- val = $conditional;
- return this.cast(val).toObject();
}
+ val = $conditional;
+ return this.cast(val).toObject();
};
/*!
diff --git a/node_modules/mongoose/lib/schema/date.js b/node_modules/mongoose/lib/schema/date.js
index 86e4062..361b87b 100644
--- a/node_modules/mongoose/lib/schema/date.js
+++ b/node_modules/mongoose/lib/schema/date.js
@@ -2,9 +2,12 @@
* Module requirements.
*/
+var MongooseError = require('../error');
+var utils = require('../utils');
+
var SchemaType = require('../schematype');
+
var CastError = SchemaType.CastError;
-var utils = require('../utils');
/**
* Date SchemaType constructor.
@@ -12,23 +15,31 @@ var utils = require('../utils');
* @param {String} key
* @param {Object} options
* @inherits SchemaType
- * @api private
+ * @api public
*/
-function SchemaDate (key, options) {
- SchemaType.call(this, key, options);
-};
+function SchemaDate(key, options) {
+ SchemaType.call(this, key, options, 'Date');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaDate.schemaName = 'Date';
/*!
* Inherits from SchemaType.
*/
-
-SchemaDate.prototype.__proto__ = SchemaType.prototype;
+SchemaDate.prototype = Object.create(SchemaType.prototype);
+SchemaDate.prototype.constructor = SchemaDate;
/**
* Declares a TTL index (rounded to the nearest second) for _Date_ types only.
*
- * This sets the `expiresAfterSeconds` index option available in MongoDB >= 2.1.2.
+ * This sets the `expireAfterSeconds` index option available in MongoDB >= 2.1.2.
* This index type is only compatible with Date types.
*
* ####Example:
@@ -56,8 +67,8 @@ SchemaDate.prototype.__proto__ = SchemaType.prototype;
* @api public
*/
-SchemaDate.prototype.expires = function (when) {
- if (!this._index || 'Object' !== this._index.constructor.name) {
+SchemaDate.prototype.expires = function(when) {
+ if (!this._index || this._index.constructor.name !== 'Object') {
this._index = {};
}
@@ -67,15 +78,131 @@ SchemaDate.prototype.expires = function (when) {
};
/**
- * Required validator for date
+ * Check if the given value satisfies a required validator. To satisfy
+ * a required validator, the given value must be an instance of `Date`.
*
- * @api private
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
*/
-SchemaDate.prototype.checkRequired = function (value) {
+SchemaDate.prototype.checkRequired = function(value) {
return value instanceof Date;
};
+/**
+ * Sets a minimum date validator.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ d: { type: Date, min: Date('1970-01-01') })
+ * var M = db.model('M', s)
+ * var m = new M({ d: Date('1969-12-31') })
+ * m.save(function (err) {
+ * console.error(err) // validator error
+ * m.d = Date('2014-12-08');
+ * m.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MIN} token which will be replaced with the invalid value
+ * var min = [Date('1970-01-01'), 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).'];
+ * var schema = new Schema({ d: { type: Date, min: min })
+ * var M = mongoose.model('M', schema);
+ * var s= new M({ d: Date('1969-12-31') });
+ * s.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `d` (1969-12-31) is before the limit (1970-01-01).
+ * })
+ *
+ * @param {Date} value minimum date
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaDate.prototype.min = function(value, message) {
+ if (this.minValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.minValidator;
+ }, this);
+ }
+
+ if (value) {
+ var msg = message || MongooseError.messages.Date.min;
+ msg = msg.replace(/{MIN}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString()));
+ var _this = this;
+ this.validators.push({
+ validator: this.minValidator = function(val) {
+ var min = (value === Date.now ? value() : _this.cast(value));
+ return val === null || val.valueOf() >= min.valueOf();
+ },
+ message: msg,
+ type: 'min',
+ min: value
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Sets a maximum date validator.
+ *
+ * ####Example:
+ *
+ * var s = new Schema({ d: { type: Date, max: Date('2014-01-01') })
+ * var M = db.model('M', s)
+ * var m = new M({ d: Date('2014-12-08') })
+ * m.save(function (err) {
+ * console.error(err) // validator error
+ * m.d = Date('2013-12-31');
+ * m.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MAX} token which will be replaced with the invalid value
+ * var max = [Date('2014-01-01'), 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).'];
+ * var schema = new Schema({ d: { type: Date, max: max })
+ * var M = mongoose.model('M', schema);
+ * var s= new M({ d: Date('2014-12-08') });
+ * s.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `d` (2014-12-08) exceeds the limit (2014-01-01).
+ * })
+ *
+ * @param {Date} maximum date
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaDate.prototype.max = function(value, message) {
+ if (this.maxValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.maxValidator;
+ }, this);
+ }
+
+ if (value) {
+ var msg = message || MongooseError.messages.Date.max;
+ msg = msg.replace(/{MAX}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString()));
+ var _this = this;
+ this.validators.push({
+ validator: this.maxValidator = function(val) {
+ var max = (value === Date.now ? value() : _this.cast(value));
+ return val === null || val.valueOf() <= max.valueOf();
+ },
+ message: msg,
+ type: 'max',
+ max: value
+ });
+ }
+
+ return this;
+};
+
/**
* Casts to date
*
@@ -83,26 +210,34 @@ SchemaDate.prototype.checkRequired = function (value) {
* @api private
*/
-SchemaDate.prototype.cast = function (value) {
- if (value === null || value === '')
+SchemaDate.prototype.cast = function(value) {
+ // If null or undefined
+ if (value === null || value === void 0 || value === '') {
return null;
+ }
- if (value instanceof Date)
+ if (value instanceof Date) {
return value;
+ }
var date;
- // support for timestamps
- if (value instanceof Number || 'number' == typeof value
- || String(value) == Number(value))
- date = new Date(Number(value));
+ if (typeof value === 'boolean') {
+ throw new CastError('date', value, this.path);
+ }
- // support for date strings
- else if (value.toString)
- date = new Date(value.toString());
+ if (value instanceof Number || typeof value === 'number'
+ || String(value) == Number(value)) {
+ // support for timestamps
+ date = new Date(Number(value));
+ } else if (value.valueOf) {
+ // support for moment.js
+ date = new Date(value.valueOf());
+ }
- if (date.toString() != 'Invalid Date')
+ if (!isNaN(date.valueOf())) {
return date;
+ }
throw new CastError('date', value, this.path);
};
@@ -113,27 +248,17 @@ SchemaDate.prototype.cast = function (value) {
* @api private
*/
-function handleSingle (val) {
+function handleSingle(val) {
return this.cast(val);
}
-function handleArray (val) {
- var self = this;
- return val.map( function (m) {
- return self.cast(m);
- });
-}
-
-SchemaDate.prototype.$conditionalHandlers = {
- '$lt': handleSingle
- , '$lte': handleSingle
- , '$gt': handleSingle
- , '$gte': handleSingle
- , '$ne': handleSingle
- , '$in': handleArray
- , '$nin': handleArray
- , '$all': handleArray
-};
+SchemaDate.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle
+ });
/**
@@ -144,17 +269,17 @@ SchemaDate.prototype.$conditionalHandlers = {
* @api private
*/
-SchemaDate.prototype.castForQuery = function ($conditional, val) {
+SchemaDate.prototype.castForQuery = function($conditional, val) {
var handler;
- if (2 !== arguments.length) {
+ if (arguments.length !== 2) {
return this.cast($conditional);
}
handler = this.$conditionalHandlers[$conditional];
if (!handler) {
- throw new Error("Can't use " + $conditional + " with Date.");
+ throw new Error('Can\'t use ' + $conditional + ' with Date.');
}
return handler.call(this, val);
diff --git a/node_modules/mongoose/lib/schema/documentarray.js b/node_modules/mongoose/lib/schema/documentarray.js
index 3b02887..821facd 100644
--- a/node_modules/mongoose/lib/schema/documentarray.js
+++ b/node_modules/mongoose/lib/schema/documentarray.js
@@ -1,13 +1,15 @@
+/* eslint no-empty: 1 */
/*!
* Module dependencies.
*/
-var SchemaType = require('../schematype')
- , ArrayType = require('./array')
- , MongooseDocumentArray = require('../types/documentarray')
- , Subdocument = require('../types/embedded')
- , Document = require('../document');
+var ArrayType = require('./array');
+var CastError = require('../error/cast');
+var MongooseDocumentArray = require('../types/documentarray');
+var SchemaType = require('../schematype');
+var Subdocument = require('../types/embedded');
+var util = require('util');
/**
* SubdocsArray SchemaType constructor
@@ -16,17 +18,16 @@ var SchemaType = require('../schematype')
* @param {Schema} schema
* @param {Object} options
* @inherits SchemaArray
- * @api private
+ * @api public
*/
-function DocumentArray (key, schema, options) {
-
+function DocumentArray(key, schema, options) {
// compile an embedded document for this schema
- function EmbeddedDocument () {
+ function EmbeddedDocument() {
Subdocument.apply(this, arguments);
}
- EmbeddedDocument.prototype.__proto__ = Subdocument.prototype;
+ EmbeddedDocument.prototype = Object.create(Subdocument.prototype);
EmbeddedDocument.prototype.$__setSchema(schema);
EmbeddedDocument.schema = schema;
@@ -36,30 +37,43 @@ function DocumentArray (key, schema, options) {
}
// apply statics
- for (var i in schema.statics)
+ for (i in schema.statics) {
EmbeddedDocument[i] = schema.statics[i];
+ }
EmbeddedDocument.options = options;
- this.schema = schema;
ArrayType.call(this, key, EmbeddedDocument, options);
this.schema = schema;
+ this.$isMongooseDocumentArray = true;
var path = this.path;
var fn = this.defaultValue;
- this.default(function(){
- var arr = fn.call(this);
- if (!Array.isArray(arr)) arr = [arr];
- return new MongooseDocumentArray(arr, path, this);
- });
-};
+ if (!('defaultValue' in this) || fn !== void 0) {
+ this.default(function() {
+ var arr = fn.call(this);
+ if (!Array.isArray(arr)) {
+ arr = [arr];
+ }
+ return new MongooseDocumentArray(arr, path, this);
+ });
+ }
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+DocumentArray.schemaName = 'DocumentArray';
/*!
* Inherits from ArrayType.
*/
-
-DocumentArray.prototype.__proto__ = ArrayType.prototype;
+DocumentArray.prototype = Object.create(ArrayType.prototype);
+DocumentArray.prototype.constructor = DocumentArray;
/**
* Performs local validations first, then validations on each embedded doc
@@ -67,43 +81,109 @@ DocumentArray.prototype.__proto__ = ArrayType.prototype;
* @api private
*/
-DocumentArray.prototype.doValidate = function (array, fn, scope) {
- var self = this;
-
- SchemaType.prototype.doValidate.call(this, array, function (err) {
- if (err) return fn(err);
+DocumentArray.prototype.doValidate = function(array, fn, scope, options) {
+ var _this = this;
+ SchemaType.prototype.doValidate.call(this, array, function(err) {
+ if (err) {
+ return fn(err);
+ }
- var count = array && array.length
- , error;
+ var count = array && array.length;
+ var error;
- if (!count) return fn();
+ if (!count) {
+ return fn();
+ }
+ if (options && options.updateValidator) {
+ return fn();
+ }
// handle sparse arrays, do not use array.forEach which does not
// iterate over sparse elements yet reports array.length including
// them :(
+ function callback(err) {
+ if (err) {
+ error = err;
+ }
+ --count || fn(error);
+ }
+
for (var i = 0, len = count; i < len; ++i) {
// sidestep sparse entries
var doc = array[i];
if (!doc) {
- --count || fn();
+ --count || fn(error);
continue;
}
- ;(function (i) {
- doc.validate(function (err) {
- if (err && !error) {
- // rewrite the key
- err.key = self.key + '.' + i + '.' + err.key;
- return fn(error = err);
- }
- --count || fn();
- });
- })(i);
+ // If you set the array index directly, the doc might not yet be
+ // a full fledged mongoose subdoc, so make it into one.
+ if (!(doc instanceof Subdocument)) {
+ doc = array[i] = new _this.casterConstructor(doc, array, undefined,
+ undefined, i);
+ }
+
+ // HACK: use $__original_validate to avoid promises so bluebird doesn't
+ // complain
+ if (doc.$__original_validate) {
+ doc.$__original_validate({__noPromise: true}, callback);
+ } else {
+ doc.validate({__noPromise: true}, callback);
+ }
}
}, scope);
};
+/**
+ * Performs local validations first, then validations on each embedded doc.
+ *
+ * ####Note:
+ *
+ * This method ignores the asynchronous validators.
+ *
+ * @return {MongooseError|undefined}
+ * @api private
+ */
+
+DocumentArray.prototype.doValidateSync = function(array, scope) {
+ var schemaTypeError = SchemaType.prototype.doValidateSync.call(this, array, scope);
+ if (schemaTypeError) {
+ return schemaTypeError;
+ }
+
+ var count = array && array.length,
+ resultError = null;
+
+ if (!count) {
+ return;
+ }
+
+ // handle sparse arrays, do not use array.forEach which does not
+ // iterate over sparse elements yet reports array.length including
+ // them :(
+
+ for (var i = 0, len = count; i < len; ++i) {
+ // only first error
+ if (resultError) {
+ break;
+ }
+ // sidestep sparse entries
+ var doc = array[i];
+ if (!doc) {
+ continue;
+ }
+
+ var subdocValidateError = doc.validateSync();
+
+ if (subdocValidateError) {
+ resultError = subdocValidateError;
+ }
+ }
+
+ return resultError;
+};
+
/**
* Casts contents
*
@@ -112,45 +192,78 @@ DocumentArray.prototype.doValidate = function (array, fn, scope) {
* @api private
*/
-DocumentArray.prototype.cast = function (value, doc, init, prev) {
- var selected
- , subdoc
- , i
+DocumentArray.prototype.cast = function(value, doc, init, prev, options) {
+ var selected,
+ subdoc,
+ i;
if (!Array.isArray(value)) {
+ // gh-2442 mark whole array as modified if we're initializing a doc from
+ // the db and the path isn't an array in the document
+ if (!!doc && init) {
+ doc.markModified(this.path);
+ }
return this.cast([value], doc, init, prev);
}
- if (!(value instanceof MongooseDocumentArray)) {
+ if (!(value && value.isMongooseDocumentArray) &&
+ (!options || !options.skipDocumentArrayCast)) {
value = new MongooseDocumentArray(value, this.path, doc);
+ if (prev && prev._handlers) {
+ for (var key in prev._handlers) {
+ doc.removeListener(key, prev._handlers[key]);
+ }
+ }
}
i = value.length;
while (i--) {
+ if (!value[i]) {
+ continue;
+ }
+ // Check if the document has a different schema (re gh-3701)
+ if ((value[i] instanceof Subdocument) &&
+ value[i].schema !== this.casterConstructor.schema) {
+ value[i] = value[i].toObject({virtuals: false});
+ }
if (!(value[i] instanceof Subdocument) && value[i]) {
if (init) {
selected || (selected = scopePaths(this, doc.$__.selected, init));
- subdoc = new this.casterConstructor(null, value, true, selected);
+ subdoc = new this.casterConstructor(null, value, true, selected, i);
value[i] = subdoc.init(value[i]);
} else {
- if (prev && (subdoc = prev.id(value[i]._id))) {
+ try {
+ subdoc = prev.id(value[i]._id);
+ } catch (e) {
+ }
+
+ if (prev && subdoc) {
// handle resetting doc with existing id but differing data
// doc.array = [{ doc: 'val' }]
subdoc.set(value[i]);
+ // if set() is hooked it will have no return value
+ // see gh-746
+ value[i] = subdoc;
} else {
- subdoc = new this.casterConstructor(value[i], value);
+ try {
+ subdoc = new this.casterConstructor(value[i], value, undefined,
+ undefined, i);
+ // if set() is hooked it will have no return value
+ // see gh-746
+ value[i] = subdoc;
+ } catch (error) {
+ var valueInErrorMessage = util.inspect(value[i]);
+ throw new CastError('embedded', valueInErrorMessage,
+ value._path, error);
+ }
}
-
- // if set() is hooked it will have no return value
- // see gh-746
- value[i] = subdoc;
}
}
}
return value;
-}
+};
/*!
* Scopes paths selected in a query to this array.
@@ -161,19 +274,21 @@ DocumentArray.prototype.cast = function (value, doc, init, prev) {
* @param {Boolean|undefined} init - if we are being created part of a query result
*/
-function scopePaths (array, fields, init) {
- if (!(init && fields)) return undefined;
+function scopePaths(array, fields, init) {
+ if (!(init && fields)) {
+ return undefined;
+ }
- var path = array.path + '.'
- , keys = Object.keys(fields)
- , i = keys.length
- , selected = {}
- , hasKeys
- , key
+ var path = array.path + '.',
+ keys = Object.keys(fields),
+ i = keys.length,
+ selected = {},
+ hasKeys,
+ key;
while (i--) {
key = keys[i];
- if (0 === key.indexOf(path)) {
+ if (key.indexOf(path) === 0) {
hasKeys || (hasKeys = true);
selected[key.substring(path.length)] = fields[key];
}
diff --git a/node_modules/mongoose/lib/schema/embedded.js b/node_modules/mongoose/lib/schema/embedded.js
index d88b40d..3f8a157 100644
--- a/node_modules/mongoose/lib/schema/embedded.js
+++ b/node_modules/mongoose/lib/schema/embedded.js
@@ -1,41 +1,170 @@
+'use strict';
/*!
* Module dependencies.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , errorMessages = require('../error').messages
- , utils = require('../utils')
- , Document
+var SchemaType = require('../schematype');
+var Subdocument = require('../types/subdocument');
+var castToNumber = require('./operators/helpers').castToNumber;
+var geospatial = require('./operators/geospatial');
+
+module.exports = Embedded;
/**
- * EmbeddedDocument SchemaType constructor.
+ * Sub-schema schematype constructor
*
+ * @param {Schema} schema
* @param {String} key
* @param {Object} options
* @inherits SchemaType
+ * @api public
+ */
+
+function Embedded(schema, path, options) {
+ var _embedded = function(value, path, parent) {
+ var _this = this;
+ Subdocument.apply(this, arguments);
+ this.$parent = parent;
+ if (parent) {
+ parent.on('save', function() {
+ _this.emit('save', _this);
+ });
+
+ parent.on('isNew', function(val) {
+ _this.isNew = val;
+ _this.emit('isNew', val);
+ });
+ }
+ };
+ _embedded.prototype = Object.create(Subdocument.prototype);
+ _embedded.prototype.$__setSchema(schema);
+ _embedded.schema = schema;
+ _embedded.$isSingleNested = true;
+ _embedded.prototype.$basePath = path;
+
+ // apply methods
+ for (var i in schema.methods) {
+ _embedded.prototype[i] = schema.methods[i];
+ }
+
+ // apply statics
+ for (i in schema.statics) {
+ _embedded[i] = schema.statics[i];
+ }
+
+ this.caster = _embedded;
+ this.schema = schema;
+ this.$isSingleNested = true;
+ SchemaType.call(this, path, options, 'Embedded');
+}
+
+Embedded.prototype = Object.create(SchemaType.prototype);
+
+/**
+ * Special case for when users use a common location schema to represent
+ * locations for use with $geoWithin.
+ * https://docs.mongodb.org/manual/reference/operator/query/geoWithin/
+ *
+ * @param {Object} val
* @api private
*/
-function SchemaEmbedded (key, options, EmbeddedDoc, parentArray) {
- SchemaType.call(this, key, options, 'EmbeddedDocument');
- this.EmbeddedDoc = EmbeddedDoc;
- this.parentArray = parentArray;
+Embedded.prototype.$conditionalHandlers.$geoWithin = function(val) {
+ return { $geometry: this.castForQuery(val.$geometry) };
};
/*!
- * Inherits from SchemaType.
+ * ignore
*/
-SchemaEmbedded.prototype.__proto__ = SchemaType.prototype;
+Embedded.prototype.$conditionalHandlers.$near =
+Embedded.prototype.$conditionalHandlers.$nearSphere = geospatial.cast$near;
-SchemaEmbedded.prototype.cast = function (value, doc, init) {
- return new this.EmbeddedDoc(value, this.parentArray);
-}
+Embedded.prototype.$conditionalHandlers.$within =
+Embedded.prototype.$conditionalHandlers.$geoWithin = geospatial.cast$within;
-/*!
- * Module exports.
+Embedded.prototype.$conditionalHandlers.$geoIntersects =
+ geospatial.cast$geoIntersects;
+
+Embedded.prototype.$conditionalHandlers.$minDistance = castToNumber;
+Embedded.prototype.$conditionalHandlers.$maxDistance = castToNumber;
+
+/**
+ * Casts contents
+ *
+ * @param {Object} value
+ * @api private
*/
-module.exports = SchemaEmbedded;
+Embedded.prototype.cast = function(val, doc, init) {
+ if (val && val.$isSingleNested) {
+ return val;
+ }
+ var subdoc = new this.caster(void 0, doc ? doc.$__.selected : void 0, doc);
+ if (init) {
+ subdoc.init(val);
+ } else {
+ subdoc.set(val, undefined, true);
+ }
+ return subdoc;
+};
+
+/**
+ * Casts contents for query
+ *
+ * @param {string} [$conditional] optional query operator (like `$eq` or `$in`)
+ * @param {any} value
+ * @api private
+ */
+
+Embedded.prototype.castForQuery = function($conditional, val) {
+ var handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional);
+ }
+ return handler.call(this, val);
+ }
+ val = $conditional;
+ if (val == null) {
+ return val;
+ }
+ return new this.caster(val).toObject({virtuals: false});
+};
+
+/**
+ * Async validation on this single nested doc.
+ *
+ * @api private
+ */
+
+Embedded.prototype.doValidate = function(value, fn) {
+ SchemaType.prototype.doValidate.call(this, value, function(error) {
+ if (error) {
+ return fn(error);
+ }
+ if (!value) {
+ return fn(null);
+ }
+ value.validate(fn, {__noPromise: true});
+ });
+};
+
+/**
+ * Synchronously validate this single nested doc
+ *
+ * @api private
+ */
+
+Embedded.prototype.doValidateSync = function(value) {
+ var schemaTypeError = SchemaType.prototype.doValidateSync.call(this, value);
+ if (schemaTypeError) {
+ return schemaTypeError;
+ }
+ if (!value) {
+ return;
+ }
+ return value.validateSync();
+};
diff --git a/node_modules/mongoose/lib/schema/index.js b/node_modules/mongoose/lib/schema/index.js
index d1347ed..f2935c1 100644
--- a/node_modules/mongoose/lib/schema/index.js
+++ b/node_modules/mongoose/lib/schema/index.js
@@ -11,6 +11,8 @@ exports.Boolean = require('./boolean');
exports.DocumentArray = require('./documentarray');
+exports.Embedded = require('./embedded');
+
exports.Array = require('./array');
exports.Buffer = require('./buffer');
diff --git a/node_modules/mongoose/lib/schema/mixed.js b/node_modules/mongoose/lib/schema/mixed.js
index 6e2e4d3..e4c23ca 100644
--- a/node_modules/mongoose/lib/schema/mixed.js
+++ b/node_modules/mongoose/lib/schema/mixed.js
@@ -1,4 +1,3 @@
-
/*!
* Module dependencies.
*/
@@ -12,43 +11,39 @@ var utils = require('../utils');
* @param {String} path
* @param {Object} options
* @inherits SchemaType
- * @api private
+ * @api public
*/
-function Mixed (path, options) {
+function Mixed(path, options) {
if (options && options.default) {
var def = options.default;
- if (Array.isArray(def) && 0 === def.length) {
+ if (Array.isArray(def) && def.length === 0) {
// make sure empty array defaults are handled
options.default = Array;
- } else if (!options.shared &&
- utils.isObject(def) &&
- 0 === Object.keys(def).length) {
+ } else if (!options.shared && utils.isObject(def) && Object.keys(def).length === 0) {
// prevent odd "shared" objects between documents
- options.default = function () {
- return {}
- }
+ options.default = function() {
+ return {};
+ };
}
}
- SchemaType.call(this, path, options);
-};
-
-/*!
- * Inherits from SchemaType.
- */
-
-Mixed.prototype.__proto__ = SchemaType.prototype;
+ SchemaType.call(this, path, options, 'Mixed');
+}
/**
- * Required validator
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
*
- * @api private
+ * @api public
*/
+Mixed.schemaName = 'Mixed';
-Mixed.prototype.checkRequired = function (val) {
- return true;
-};
+/*!
+ * Inherits from SchemaType.
+ */
+Mixed.prototype = Object.create(SchemaType.prototype);
+Mixed.prototype.constructor = Mixed;
/**
* Casts `val` for Mixed.
@@ -59,7 +54,7 @@ Mixed.prototype.checkRequired = function (val) {
* @api private
*/
-Mixed.prototype.cast = function (val) {
+Mixed.prototype.cast = function(val) {
return val;
};
@@ -71,8 +66,10 @@ Mixed.prototype.cast = function (val) {
* @api private
*/
-Mixed.prototype.castForQuery = function ($cond, val) {
- if (arguments.length === 2) return val;
+Mixed.prototype.castForQuery = function($cond, val) {
+ if (arguments.length === 2) {
+ return val;
+ }
return $cond;
};
diff --git a/node_modules/mongoose/lib/schema/number.js b/node_modules/mongoose/lib/schema/number.js
index 5e127dd..3fec612 100644
--- a/node_modules/mongoose/lib/schema/number.js
+++ b/node_modules/mongoose/lib/schema/number.js
@@ -2,10 +2,12 @@
* Module requirements.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , utils = require('../utils')
- , Document
+var SchemaType = require('../schematype');
+var CastError = SchemaType.CastError;
+var handleBitwiseOperator = require('./operators/bitwise');
+var MongooseError = require('../error');
+var utils = require('../utils');
+var Document;
/**
* Number SchemaType constructor.
@@ -13,31 +15,41 @@ var SchemaType = require('../schematype')
* @param {String} key
* @param {Object} options
* @inherits SchemaType
- * @api private
+ * @api public
*/
-function SchemaNumber (key, options) {
+function SchemaNumber(key, options) {
SchemaType.call(this, key, options, 'Number');
-};
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaNumber.schemaName = 'Number';
/*!
* Inherits from SchemaType.
*/
-
-SchemaNumber.prototype.__proto__ = SchemaType.prototype;
+SchemaNumber.prototype = Object.create(SchemaType.prototype);
+SchemaNumber.prototype.constructor = SchemaNumber;
/**
- * Required validator for number
+ * Check if the given value satisfies a required validator.
*
- * @api private
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
*/
-SchemaNumber.prototype.checkRequired = function checkRequired (value, doc) {
+SchemaNumber.prototype.checkRequired = function checkRequired(value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
- return null != value;
- } else {
- return typeof value == 'number' || value instanceof Number;
+ return !!value;
}
+ return typeof value === 'number' || value instanceof Number;
};
/**
@@ -54,22 +66,41 @@ SchemaNumber.prototype.checkRequired = function checkRequired (value, doc) {
* m.save() // success
* })
*
+ * // custom error messages
+ * // We can also use the special {MIN} token which will be replaced with the invalid value
+ * var min = [10, 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).'];
+ * var schema = new Schema({ n: { type: Number, min: min })
+ * var M = mongoose.model('Measurement', schema);
+ * var s= new M({ n: 4 });
+ * s.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `n` (4) is beneath the limit (10).
+ * })
+ *
* @param {Number} value minimum number
+ * @param {String} [message] optional custom error message
* @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
* @api public
*/
-SchemaNumber.prototype.min = function (value) {
+SchemaNumber.prototype.min = function(value, message) {
if (this.minValidator) {
- this.validators = this.validators.filter(function (v) {
- return 'min' != v[1];
- });
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.minValidator;
+ }, this);
}
- if (value != null) {
- this.validators.push([this.minValidator = function (v) {
- return v === null || v >= value;
- }, 'min']);
+ if (value !== null && value !== undefined) {
+ var msg = message || MongooseError.messages.Number.min;
+ msg = msg.replace(/{MIN}/, value);
+ this.validators.push({
+ validator: this.minValidator = function(v) {
+ return v == null || v >= value;
+ },
+ message: msg,
+ type: 'min',
+ min: value
+ });
}
return this;
@@ -89,22 +120,41 @@ SchemaNumber.prototype.min = function (value) {
* m.save() // success
* })
*
+ * // custom error messages
+ * // We can also use the special {MAX} token which will be replaced with the invalid value
+ * var max = [10, 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).'];
+ * var schema = new Schema({ n: { type: Number, max: max })
+ * var M = mongoose.model('Measurement', schema);
+ * var s= new M({ n: 4 });
+ * s.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `n` (4) exceeds the limit (10).
+ * })
+ *
* @param {Number} maximum number
+ * @param {String} [message] optional custom error message
* @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
* @api public
*/
-SchemaNumber.prototype.max = function (value) {
+SchemaNumber.prototype.max = function(value, message) {
if (this.maxValidator) {
- this.validators = this.validators.filter(function(v){
- return 'max' != v[1];
- });
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.maxValidator;
+ }, this);
}
- if (value != null) {
- this.validators.push([this.maxValidator = function(v){
- return v === null || v <= value;
- }, 'max']);
+ if (value !== null && value !== undefined) {
+ var msg = message || MongooseError.messages.Number.max;
+ msg = msg.replace(/{MAX}/, value);
+ this.validators.push({
+ validator: this.maxValidator = function(v) {
+ return v == null || v <= value;
+ },
+ message: msg,
+ type: 'max',
+ max: value
+ });
}
return this;
@@ -119,11 +169,11 @@ SchemaNumber.prototype.max = function (value) {
* @api private
*/
-SchemaNumber.prototype.cast = function (value, doc, init) {
+SchemaNumber.prototype.cast = function(value, doc, init) {
if (SchemaType._isRef(this, value, doc, init)) {
// wait! we may need to cast this to a document
- if (null == value) {
+ if (value === null || value === undefined) {
return value;
}
@@ -136,7 +186,7 @@ SchemaNumber.prototype.cast = function (value, doc, init) {
}
// setting a populated path
- if ('number' == typeof value) {
+ if (typeof value === 'number') {
return value;
} else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
throw new CastError('number', value, this.path);
@@ -154,18 +204,27 @@ SchemaNumber.prototype.cast = function (value, doc, init) {
}
var val = value && value._id
- ? value._id // documents
- : value;
-
- if (!isNaN(val)){
- if (null === val) return val;
- if ('' === val) return null;
- if ('string' == typeof val) val = Number(val);
- if (val instanceof Number) return val
- if ('number' == typeof val) return val;
- if (val.toString && !Array.isArray(val) &&
- val.toString() == Number(val)) {
- return new Number(val)
+ ? value._id // documents
+ : value;
+
+ if (!isNaN(val)) {
+ if (val === null) {
+ return val;
+ }
+ if (val === '') {
+ return null;
+ }
+ if (typeof val === 'string' || typeof val === 'boolean') {
+ val = Number(val);
+ }
+ if (val instanceof Number) {
+ return val;
+ }
+ if (typeof val === 'number') {
+ return val;
+ }
+ if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) {
+ return new Number(val);
}
}
@@ -176,28 +235,32 @@ SchemaNumber.prototype.cast = function (value, doc, init) {
* ignore
*/
-function handleSingle (val) {
- return this.cast(val)
+function handleSingle(val) {
+ return this.cast(val);
}
-function handleArray (val) {
- var self = this;
- return val.map(function (m) {
- return self.cast(m)
+function handleArray(val) {
+ var _this = this;
+ if (!Array.isArray(val)) {
+ return [this.cast(val)];
+ }
+ return val.map(function(m) {
+ return _this.cast(m);
});
}
-SchemaNumber.prototype.$conditionalHandlers = {
- '$lt' : handleSingle
- , '$lte': handleSingle
- , '$gt' : handleSingle
- , '$gte': handleSingle
- , '$ne' : handleSingle
- , '$in' : handleArray
- , '$nin': handleArray
- , '$mod': handleArray
- , '$all': handleArray
-};
+SchemaNumber.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $bitsAllClear: handleBitwiseOperator,
+ $bitsAnyClear: handleBitwiseOperator,
+ $bitsAllSet: handleBitwiseOperator,
+ $bitsAnySet: handleBitwiseOperator,
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle,
+ $mod: handleArray
+ });
/**
* Casts contents for queries.
@@ -207,17 +270,17 @@ SchemaNumber.prototype.$conditionalHandlers = {
* @api private
*/
-SchemaNumber.prototype.castForQuery = function ($conditional, val) {
+SchemaNumber.prototype.castForQuery = function($conditional, val) {
var handler;
if (arguments.length === 2) {
handler = this.$conditionalHandlers[$conditional];
- if (!handler)
- throw new Error("Can't use " + $conditional + " with Number.");
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with Number.');
+ }
return handler.call(this, val);
- } else {
- val = this.cast($conditional);
- return val == null ? val : val
}
+ val = this.cast($conditional);
+ return val;
};
/*!
diff --git a/node_modules/mongoose/lib/schema/objectid.js b/node_modules/mongoose/lib/schema/objectid.js
index 99f9623..2885823 100644
--- a/node_modules/mongoose/lib/schema/objectid.js
+++ b/node_modules/mongoose/lib/schema/objectid.js
@@ -1,13 +1,14 @@
+/* eslint no-empty: 1 */
+
/*!
* Module dependencies.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , driver = global.MONGOOSE_DRIVER_PATH || './../drivers/node-mongodb-native'
- , oid = require('../types/objectid')
- , utils = require('../utils')
- , Document
+var SchemaType = require('../schematype'),
+ CastError = SchemaType.CastError,
+ oid = require('../types/objectid'),
+ utils = require('../utils'),
+ Document;
/**
* ObjectId SchemaType constructor.
@@ -15,18 +16,26 @@ var SchemaType = require('../schematype')
* @param {String} key
* @param {Object} options
* @inherits SchemaType
- * @api private
+ * @api public
*/
-function ObjectId (key, options) {
+function ObjectId(key, options) {
SchemaType.call(this, key, options, 'ObjectID');
-};
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+ObjectId.schemaName = 'ObjectId';
/*!
* Inherits from SchemaType.
*/
-
-ObjectId.prototype.__proto__ = SchemaType.prototype;
+ObjectId.prototype = Object.create(SchemaType.prototype);
+ObjectId.prototype.constructor = ObjectId;
/**
* Adds an auto-generated ObjectId default if turnOn is true.
@@ -35,27 +44,29 @@ ObjectId.prototype.__proto__ = SchemaType.prototype;
* @return {SchemaType} this
*/
-ObjectId.prototype.auto = function (turnOn) {
+ObjectId.prototype.auto = function(turnOn) {
if (turnOn) {
this.default(defaultId);
- this.set(resetId)
+ this.set(resetId);
}
return this;
};
/**
- * Check required
+ * Check if the given value satisfies a required validator.
*
- * @api private
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
*/
-ObjectId.prototype.checkRequired = function checkRequired (value, doc) {
+ObjectId.prototype.checkRequired = function checkRequired(value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
- return null != value;
- } else {
- return value instanceof oid;
+ return !!value;
}
+ return value instanceof oid;
};
/**
@@ -67,11 +78,11 @@ ObjectId.prototype.checkRequired = function checkRequired (value, doc) {
* @api private
*/
-ObjectId.prototype.cast = function (value, doc, init) {
+ObjectId.prototype.cast = function(value, doc, init) {
if (SchemaType._isRef(this, value, doc, init)) {
// wait! we may need to cast this to a document
- if (null == value) {
+ if (value === null || value === undefined) {
return value;
}
@@ -96,22 +107,42 @@ ObjectId.prototype.cast = function (value, doc, init) {
var path = doc.$__fullPath(this.path);
var owner = doc.ownerDocument ? doc.ownerDocument() : doc;
var pop = owner.populated(path, true);
- var ret = new pop.options.model(value);
- ret.$__.wasPopulated = true;
+ var ret = value;
+ if (!doc.$__.populated ||
+ !doc.$__.populated[path] ||
+ !doc.$__.populated[path].options ||
+ !doc.$__.populated[path].options.options ||
+ !doc.$__.populated[path].options.options.lean) {
+ ret = new pop.options.model(value);
+ ret.$__.wasPopulated = true;
+ }
+
return ret;
}
- if (value === null) return value;
+ if (value === null || value === undefined) {
+ return value;
+ }
- if (value instanceof oid)
+ if (value instanceof oid) {
return value;
+ }
- if (value._id && value._id instanceof oid)
- return value._id;
+ if (value._id) {
+ if (value._id instanceof oid) {
+ return value._id;
+ }
+ if (value._id.toString instanceof Function) {
+ try {
+ return new oid(value._id.toString());
+ } catch (e) {
+ }
+ }
+ }
- if (value.toString) {
+ if (value.toString instanceof Function) {
try {
- return oid.fromString(value.toString());
+ return new oid(value.toString());
} catch (err) {
throw new CastError('ObjectId', value, this.path);
}
@@ -124,27 +155,17 @@ ObjectId.prototype.cast = function (value, doc, init) {
* ignore
*/
-function handleSingle (val) {
+function handleSingle(val) {
return this.cast(val);
}
-function handleArray (val) {
- var self = this;
- return val.map(function (m) {
- return self.cast(m);
- });
-}
-
-ObjectId.prototype.$conditionalHandlers = {
- '$ne': handleSingle
- , '$in': handleArray
- , '$nin': handleArray
- , '$gt': handleSingle
- , '$lt': handleSingle
- , '$gte': handleSingle
- , '$lte': handleSingle
- , '$all': handleArray
-};
+ObjectId.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle
+ });
/**
* Casts contents for queries.
@@ -154,27 +175,27 @@ ObjectId.prototype.$conditionalHandlers = {
* @api private
*/
-ObjectId.prototype.castForQuery = function ($conditional, val) {
+ObjectId.prototype.castForQuery = function($conditional, val) {
var handler;
if (arguments.length === 2) {
handler = this.$conditionalHandlers[$conditional];
- if (!handler)
- throw new Error("Can't use " + $conditional + " with ObjectId.");
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with ObjectId.');
+ }
return handler.call(this, val);
- } else {
- return this.cast($conditional);
}
+ return this.cast($conditional);
};
/*!
* ignore
*/
-function defaultId () {
+function defaultId() {
return new oid();
-};
+}
-function resetId (v) {
+function resetId(v) {
this.$__._id = null;
return v;
}
diff --git a/node_modules/mongoose/lib/schema/operators/bitwise.js b/node_modules/mongoose/lib/schema/operators/bitwise.js
new file mode 100644
index 0000000..c1fdd34
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/bitwise.js
@@ -0,0 +1,36 @@
+/*!
+ * Module requirements.
+ */
+
+var CastError = require('../../error/cast');
+
+/*!
+ * ignore
+ */
+
+function handleBitwiseOperator(val) {
+ var _this = this;
+ if (Array.isArray(val)) {
+ return val.map(function(v) {
+ return _castNumber(_this.path, v);
+ });
+ } else if (Buffer.isBuffer(val)) {
+ return val;
+ }
+ // Assume trying to cast to number
+ return _castNumber(_this.path, val);
+}
+
+/*!
+ * ignore
+ */
+
+function _castNumber(path, num) {
+ var v = Number(num);
+ if (isNaN(v)) {
+ throw new CastError('number', num, path);
+ }
+ return v;
+}
+
+module.exports = handleBitwiseOperator;
diff --git a/node_modules/mongoose/lib/schema/operators/geospatial.js b/node_modules/mongoose/lib/schema/operators/geospatial.js
new file mode 100644
index 0000000..b490d2d
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/geospatial.js
@@ -0,0 +1,100 @@
+/*!
+ * Module requirements.
+ */
+
+var castArraysOfNumbers = require('./helpers').castArraysOfNumbers;
+var castToNumber = require('./helpers').castToNumber;
+
+/*!
+ * ignore
+ */
+
+exports.cast$geoIntersects = cast$geoIntersects;
+exports.cast$near = cast$near;
+exports.cast$within = cast$within;
+
+function cast$near(val) {
+ var SchemaArray = require('../array');
+
+ if (Array.isArray(val)) {
+ castArraysOfNumbers(val, this);
+ return val;
+ }
+
+ _castMinMaxDistance(this, val);
+
+ if (val && val.$geometry) {
+ return cast$geometry(val, this);
+ }
+
+ return SchemaArray.prototype.castForQuery.call(this, val);
+}
+
+function cast$geometry(val, self) {
+ switch (val.$geometry.type) {
+ case 'Polygon':
+ case 'LineString':
+ case 'Point':
+ castArraysOfNumbers(val.$geometry.coordinates, self);
+ break;
+ default:
+ // ignore unknowns
+ break;
+ }
+
+ _castMinMaxDistance(this, val);
+
+ return val;
+}
+
+function cast$within(val) {
+ _castMinMaxDistance(this, val);
+
+ if (val.$box || val.$polygon) {
+ var type = val.$box ? '$box' : '$polygon';
+ val[type].forEach(function(arr) {
+ if (!Array.isArray(arr)) {
+ var msg = 'Invalid $within $box argument. '
+ + 'Expected an array, received ' + arr;
+ throw new TypeError(msg);
+ }
+ arr.forEach(function(v, i) {
+ arr[i] = castToNumber.call(this, v);
+ });
+ });
+ } else if (val.$center || val.$centerSphere) {
+ type = val.$center ? '$center' : '$centerSphere';
+ val[type].forEach(function(item, i) {
+ if (Array.isArray(item)) {
+ item.forEach(function(v, j) {
+ item[j] = castToNumber.call(this, v);
+ });
+ } else {
+ val[type][i] = castToNumber.call(this, item);
+ }
+ });
+ } else if (val.$geometry) {
+ cast$geometry(val, this);
+ }
+
+ return val;
+}
+
+function cast$geoIntersects(val) {
+ var geo = val.$geometry;
+ if (!geo) {
+ return;
+ }
+
+ cast$geometry(val, this);
+ return val;
+}
+
+function _castMinMaxDistance(self, val) {
+ if (val.$maxDistance) {
+ val.$maxDistance = castToNumber.call(self, val.$maxDistance);
+ }
+ if (val.$minDistance) {
+ val.$minDistance = castToNumber.call(self, val.$minDistance);
+ }
+}
diff --git a/node_modules/mongoose/lib/schema/operators/helpers.js b/node_modules/mongoose/lib/schema/operators/helpers.js
new file mode 100644
index 0000000..850354a
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/operators/helpers.js
@@ -0,0 +1,34 @@
+'use strict';
+
+/*!
+ * Module requirements.
+ */
+
+var Types = {
+ Number: require('../number')
+};
+
+/*!
+ * @ignore
+ */
+
+exports.castToNumber = castToNumber;
+exports.castArraysOfNumbers = castArraysOfNumbers;
+
+/*!
+ * @ignore
+ */
+
+function castToNumber(val) {
+ return Types.Number.prototype.cast.call(this, val);
+}
+
+function castArraysOfNumbers(arr, self) {
+ arr.forEach(function(v, i) {
+ if (Array.isArray(v)) {
+ castArraysOfNumbers(v, self);
+ } else {
+ arr[i] = castToNumber.call(self, v);
+ }
+ });
+}
diff --git a/node_modules/mongoose/lib/schema/string.js b/node_modules/mongoose/lib/schema/string.js
index 235a12f..545e4bd 100644
--- a/node_modules/mongoose/lib/schema/string.js
+++ b/node_modules/mongoose/lib/schema/string.js
@@ -1,12 +1,12 @@
-
/*!
* Module dependencies.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , utils = require('../utils')
- , Document
+var SchemaType = require('../schematype');
+var CastError = SchemaType.CastError;
+var MongooseError = require('../error');
+var utils = require('../utils');
+var Document;
/**
* String SchemaType constructor.
@@ -14,68 +14,104 @@ var SchemaType = require('../schematype')
* @param {String} key
* @param {Object} options
* @inherits SchemaType
- * @api private
+ * @api public
*/
-function SchemaString (key, options) {
+function SchemaString(key, options) {
this.enumValues = [];
this.regExp = null;
SchemaType.call(this, key, options, 'String');
-};
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api public
+ */
+SchemaString.schemaName = 'String';
/*!
* Inherits from SchemaType.
*/
-
-SchemaString.prototype.__proto__ = SchemaType.prototype;
+SchemaString.prototype = Object.create(SchemaType.prototype);
+SchemaString.prototype.constructor = SchemaString;
/**
- * Adds enumeration values and a coinciding validator.
+ * Adds an enum validator
*
* ####Example:
*
* var states = 'opening open closing closed'.split(' ')
- * var s = new Schema({ state: { type: String, enum: states })
+ * var s = new Schema({ state: { type: String, enum: states }})
* var M = db.model('M', s)
* var m = new M({ state: 'invalid' })
* m.save(function (err) {
- * console.error(err) // validator error
+ * console.error(String(err)) // ValidationError: `invalid` is not a valid enum value for path `state`.
* m.state = 'open'
- * m.save() // success
+ * m.save(callback) // success
* })
*
- * @param {String} [args...] enumeration values
+ * // or with custom error messages
+ * var enu = {
+ * values: 'opening open closing closed'.split(' '),
+ * message: 'enum validator failed for path `{PATH}` with value `{VALUE}`'
+ * }
+ * var s = new Schema({ state: { type: String, enum: enu })
+ * var M = db.model('M', s)
+ * var m = new M({ state: 'invalid' })
+ * m.save(function (err) {
+ * console.error(String(err)) // ValidationError: enum validator failed for path `state` with value `invalid`
+ * m.state = 'open'
+ * m.save(callback) // success
+ * })
+ *
+ * @param {String|Object} [args...] enumeration values
* @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
* @api public
*/
-SchemaString.prototype.enum = function () {
- var len = arguments.length;
+SchemaString.prototype.enum = function() {
+ if (this.enumValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.enumValidator;
+ }, this);
+ this.enumValidator = false;
+ }
- if (!len || undefined === arguments[0] || false === arguments[0]) {
- if (this.enumValidator){
- this.enumValidator = false;
- this.validators = this.validators.filter(function(v){
- return v[1] != 'enum';
- });
- }
+ if (arguments[0] === void 0 || arguments[0] === false) {
return this;
}
- for (var i = 0; i < len; i++) {
- if (undefined !== arguments[i]) {
- this.enumValues.push(this.cast(arguments[i]));
- }
+ var values;
+ var errorMessage;
+
+ if (utils.isObject(arguments[0])) {
+ values = arguments[0].values;
+ errorMessage = arguments[0].message;
+ } else {
+ values = arguments;
+ errorMessage = MongooseError.messages.String.enum;
}
- if (!this.enumValidator) {
- var values = this.enumValues;
- this.enumValidator = function(v){
- return undefined === v || ~values.indexOf(v);
- };
- this.validators.push([this.enumValidator, 'enum']);
+ for (var i = 0; i < values.length; i++) {
+ if (undefined !== values[i]) {
+ this.enumValues.push(this.cast(values[i]));
+ }
}
+ var vals = this.enumValues;
+ this.enumValidator = function(v) {
+ return undefined === v || ~vals.indexOf(v);
+ };
+ this.validators.push({
+ validator: this.enumValidator,
+ message: errorMessage,
+ type: 'enum',
+ enumValues: vals
+ });
+
return this;
};
@@ -93,10 +129,14 @@ SchemaString.prototype.enum = function () {
* @return {SchemaType} this
*/
-SchemaString.prototype.lowercase = function () {
- return this.set(function (v, self) {
- if ('string' != typeof v) v = self.cast(v)
- if (v) return v.toLowerCase();
+SchemaString.prototype.lowercase = function() {
+ return this.set(function(v, self) {
+ if (typeof v !== 'string') {
+ v = self.cast(v);
+ }
+ if (v) {
+ return v.toLowerCase();
+ }
return v;
});
};
@@ -115,10 +155,14 @@ SchemaString.prototype.lowercase = function () {
* @return {SchemaType} this
*/
-SchemaString.prototype.uppercase = function () {
- return this.set(function (v, self) {
- if ('string' != typeof v) v = self.cast(v)
- if (v) return v.toUpperCase();
+SchemaString.prototype.uppercase = function() {
+ return this.set(function(v, self) {
+ if (typeof v !== 'string') {
+ v = self.cast(v);
+ }
+ if (v) {
+ return v.toUpperCase();
+ }
return v;
});
};
@@ -141,14 +185,126 @@ SchemaString.prototype.uppercase = function () {
* @return {SchemaType} this
*/
-SchemaString.prototype.trim = function () {
- return this.set(function (v, self) {
- if ('string' != typeof v) v = self.cast(v)
- if (v) return v.trim();
+SchemaString.prototype.trim = function() {
+ return this.set(function(v, self) {
+ if (typeof v !== 'string') {
+ v = self.cast(v);
+ }
+ if (v && self.options.trim) {
+ return v.trim();
+ }
return v;
});
};
+/**
+ * Sets a minimum length validator.
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ postalCode: { type: String, minlength: 5 })
+ * var Address = db.model('Address', schema)
+ * var address = new Address({ postalCode: '9512' })
+ * address.save(function (err) {
+ * console.error(err) // validator error
+ * address.postalCode = '95125';
+ * address.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MINLENGTH} token which will be replaced with the minimum allowed length
+ * var minlength = [5, 'The value of path `{PATH}` (`{VALUE}`) is shorter than the minimum allowed length ({MINLENGTH}).'];
+ * var schema = new Schema({ postalCode: { type: String, minlength: minlength })
+ * var Address = mongoose.model('Address', schema);
+ * var address = new Address({ postalCode: '9512' });
+ * address.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `postalCode` (`9512`) is shorter than the minimum length (5).
+ * })
+ *
+ * @param {Number} value minimum string length
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaString.prototype.minlength = function(value, message) {
+ if (this.minlengthValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.minlengthValidator;
+ }, this);
+ }
+
+ if (value !== null && value !== undefined) {
+ var msg = message || MongooseError.messages.String.minlength;
+ msg = msg.replace(/{MINLENGTH}/, value);
+ this.validators.push({
+ validator: this.minlengthValidator = function(v) {
+ return v === null || v.length >= value;
+ },
+ message: msg,
+ type: 'minlength',
+ minlength: value
+ });
+ }
+
+ return this;
+};
+
+/**
+ * Sets a maximum length validator.
+ *
+ * ####Example:
+ *
+ * var schema = new Schema({ postalCode: { type: String, maxlength: 9 })
+ * var Address = db.model('Address', schema)
+ * var address = new Address({ postalCode: '9512512345' })
+ * address.save(function (err) {
+ * console.error(err) // validator error
+ * address.postalCode = '95125';
+ * address.save() // success
+ * })
+ *
+ * // custom error messages
+ * // We can also use the special {MAXLENGTH} token which will be replaced with the maximum allowed length
+ * var maxlength = [9, 'The value of path `{PATH}` (`{VALUE}`) exceeds the maximum allowed length ({MAXLENGTH}).'];
+ * var schema = new Schema({ postalCode: { type: String, maxlength: maxlength })
+ * var Address = mongoose.model('Address', schema);
+ * var address = new Address({ postalCode: '9512512345' });
+ * address.validate(function (err) {
+ * console.log(String(err)) // ValidationError: The value of path `postalCode` (`9512512345`) exceeds the maximum allowed length (9).
+ * })
+ *
+ * @param {Number} value maximum string length
+ * @param {String} [message] optional custom error message
+ * @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @api public
+ */
+
+SchemaString.prototype.maxlength = function(value, message) {
+ if (this.maxlengthValidator) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.maxlengthValidator;
+ }, this);
+ }
+
+ if (value !== null && value !== undefined) {
+ var msg = message || MongooseError.messages.String.maxlength;
+ msg = msg.replace(/{MAXLENGTH}/, value);
+ this.validators.push({
+ validator: this.maxlengthValidator = function(v) {
+ return v === null || v.length <= value;
+ },
+ message: msg,
+ type: 'maxlength',
+ maxlength: value
+ });
+ }
+
+ return this;
+};
+
/**
* Sets a regexp validator.
*
@@ -158,43 +314,74 @@ SchemaString.prototype.trim = function () {
*
* var s = new Schema({ name: { type: String, match: /^a/ }})
* var M = db.model('M', s)
- * var m = new M({ name: 'invalid' })
+ * var m = new M({ name: 'I am invalid' })
* m.validate(function (err) {
- * console.error(err) // validation error
+ * console.error(String(err)) // "ValidationError: Path `name` is invalid (I am invalid)."
* m.name = 'apples'
* m.validate(function (err) {
* assert.ok(err) // success
* })
* })
*
+ * // using a custom error message
+ * var match = [ /\.html$/, "That file doesn't end in .html ({VALUE})" ];
+ * var s = new Schema({ file: { type: String, match: match }})
+ * var M = db.model('M', s);
+ * var m = new M({ file: 'invalid' });
+ * m.validate(function (err) {
+ * console.log(String(err)) // "ValidationError: That file doesn't end in .html (invalid)"
+ * })
+ *
+ * Empty strings, `undefined`, and `null` values always pass the match validator. If you require these values, enable the `required` validator also.
+ *
+ * var s = new Schema({ name: { type: String, match: /^a/, required: true }})
+ *
* @param {RegExp} regExp regular expression to test against
+ * @param {String} [message] optional custom error message
* @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
* @api public
*/
-SchemaString.prototype.match = function match (regExp) {
- this.validators.push([function(v){
- return null != v && '' !== v
- ? regExp.test(v)
- : true
- }, 'regexp']);
+SchemaString.prototype.match = function match(regExp, message) {
+ // yes, we allow multiple match validators
+
+ var msg = message || MongooseError.messages.String.match;
+
+ var matchValidator = function(v) {
+ if (!regExp) {
+ return false;
+ }
+
+ var ret = ((v != null && v !== '')
+ ? regExp.test(v)
+ : true);
+ return ret;
+ };
+ this.validators.push({
+ validator: matchValidator,
+ message: msg,
+ type: 'regexp',
+ regexp: regExp
+ });
return this;
};
/**
- * Check required
+ * Check if the given value satisfies a required validator.
*
- * @param {String|null|undefined} value
- * @api private
+ * @param {Any} value
+ * @param {Document} doc
+ * @return {Boolean}
+ * @api public
*/
-SchemaString.prototype.checkRequired = function checkRequired (value, doc) {
+SchemaString.prototype.checkRequired = function checkRequired(value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
- return null != value;
- } else {
- return (value instanceof String || typeof value == 'string') && value.length;
+ return !!value;
}
+ return (value instanceof String || typeof value === 'string') && value.length;
};
/**
@@ -203,11 +390,11 @@ SchemaString.prototype.checkRequired = function checkRequired (value, doc) {
* @api private
*/
-SchemaString.prototype.cast = function (value, doc, init) {
+SchemaString.prototype.cast = function(value, doc, init) {
if (SchemaType._isRef(this, value, doc, init)) {
// wait! we may need to cast this to a document
- if (null == value) {
+ if (value === null || value === undefined) {
return value;
}
@@ -220,7 +407,7 @@ SchemaString.prototype.cast = function (value, doc, init) {
}
// setting a populated path
- if ('string' == typeof value) {
+ if (typeof value === 'string') {
return value;
} else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
throw new CastError('string', value, this.path);
@@ -237,21 +424,25 @@ SchemaString.prototype.cast = function (value, doc, init) {
return ret;
}
- if (value === null) {
+ // If null or undefined
+ if (value === null || value === undefined) {
return value;
}
- if ('undefined' !== typeof value) {
+ if (typeof value !== 'undefined') {
// handle documents being passed
- if (value._id && 'string' == typeof value._id) {
+ if (value._id && typeof value._id === 'string') {
return value._id;
}
- if (value.toString) {
+
+ // Re: gh-647 and gh-3030, we're ok with casting using `toString()`
+ // **unless** its the default Object.toString, because "[object Object]"
+ // doesn't really qualify as useful data
+ if (value.toString && value.toString !== Object.prototype.toString) {
return value.toString();
}
}
-
throw new CastError('string', value, this.path);
};
@@ -259,29 +450,30 @@ SchemaString.prototype.cast = function (value, doc, init) {
* ignore
*/
-function handleSingle (val) {
+function handleSingle(val) {
return this.castForQuery(val);
}
-function handleArray (val) {
- var self = this;
- return val.map(function (m) {
- return self.castForQuery(m);
+function handleArray(val) {
+ var _this = this;
+ if (!Array.isArray(val)) {
+ return [this.castForQuery(val)];
+ }
+ return val.map(function(m) {
+ return _this.castForQuery(m);
});
}
-SchemaString.prototype.$conditionalHandlers = {
- '$ne' : handleSingle
- , '$in' : handleArray
- , '$nin': handleArray
- , '$gt' : handleSingle
- , '$lt' : handleSingle
- , '$gte': handleSingle
- , '$lte': handleSingle
- , '$all': handleArray
- , '$regex': handleSingle
- , '$options': handleSingle
-};
+SchemaString.prototype.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ $all: handleArray,
+ $gt: handleSingle,
+ $gte: handleSingle,
+ $lt: handleSingle,
+ $lte: handleSingle,
+ $options: handleSingle,
+ $regex: handleSingle
+ });
/**
* Casts contents for queries.
@@ -291,18 +483,20 @@ SchemaString.prototype.$conditionalHandlers = {
* @api private
*/
-SchemaString.prototype.castForQuery = function ($conditional, val) {
+SchemaString.prototype.castForQuery = function($conditional, val) {
var handler;
if (arguments.length === 2) {
handler = this.$conditionalHandlers[$conditional];
- if (!handler)
- throw new Error("Can't use " + $conditional + " with String.");
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional + ' with String.');
+ }
return handler.call(this, val);
- } else {
- val = $conditional;
- if (val instanceof RegExp) return val;
- return this.cast(val);
}
+ val = $conditional;
+ if (Object.prototype.toString.call(val) === '[object RegExp]') {
+ return val;
+ }
+ return this.cast(val);
};
/*!
diff --git a/node_modules/mongoose/lib/schemadefault.js b/node_modules/mongoose/lib/schemadefault.js
deleted file mode 100644
index aebcff5..0000000
--- a/node_modules/mongoose/lib/schemadefault.js
+++ /dev/null
@@ -1,34 +0,0 @@
-
-/*!
- * Module dependencies.
- */
-
-var Schema = require('./schema')
-
-/**
- * Default model for querying the system.profiles collection.
- *
- * @property system.profile
- * @receiver exports
- * @api private
- */
-
-exports['system.profile'] = new Schema({
- ts: Date
- , info: String // deprecated
- , millis: Number
- , op: String
- , ns: String
- , query: Schema.Types.Mixed
- , updateobj: Schema.Types.Mixed
- , ntoreturn: Number
- , nreturned: Number
- , nscanned: Number
- , responseLength: Number
- , client: String
- , user: String
- , idhack: Boolean
- , scanAndOrder: Boolean
- , keyUpdates: Number
- , cursorid: Number
-}, { noVirtualId: true, noId: true });
diff --git a/node_modules/mongoose/lib/schematype.js b/node_modules/mongoose/lib/schematype.js
index b396efa..a69be45 100644
--- a/node_modules/mongoose/lib/schematype.js
+++ b/node_modules/mongoose/lib/schematype.js
@@ -3,8 +3,9 @@
*/
var utils = require('./utils');
-var CastError = require('./error').CastError;
-var ValidatorError = require('./error').ValidatorError;
+var MongooseError = require('./error');
+var CastError = MongooseError.CastError;
+var ValidatorError = MongooseError.ValidatorError;
/**
* SchemaType constructor
@@ -15,7 +16,7 @@ var ValidatorError = require('./error').ValidatorError;
* @api public
*/
-function SchemaType (path, options, instance) {
+function SchemaType(path, options, instance) {
this.path = path;
this.instance = instance;
this.validators = [];
@@ -25,17 +26,21 @@ function SchemaType (path, options, instance) {
this._index = null;
this.selected;
- for (var i in options) if (this[i] && 'function' == typeof this[i]) {
- // { unique: true, index: true }
- if ('index' == i && this._index) continue;
+ for (var i in options) {
+ if (this[i] && typeof this[i] === 'function') {
+ // { unique: true, index: true }
+ if (i === 'index' && this._index) {
+ continue;
+ }
- var opts = Array.isArray(options[i])
- ? options[i]
- : [options[i]];
+ var opts = Array.isArray(options[i])
+ ? options[i]
+ : [options[i]];
- this[i].apply(this, opts);
+ this[i].apply(this, opts);
+ }
}
-};
+}
/**
* Sets a default value for this SchemaType.
@@ -52,22 +57,42 @@ function SchemaType (path, options, instance) {
* ####Example:
*
* // values are cast:
- * var schema = new Schema({ aNumber: Number, default: "4.815162342" })
+ * var schema = new Schema({ aNumber: { type: Number, default: 4.815162342 }})
* var M = db.model('M', schema)
* var m = new M;
- * console.log(m.aNumber, typeof m.aNumber) // 4.815162342 "number"
+ * console.log(m.aNumber) // 4.815162342
+ *
+ * // default unique objects for Mixed types:
+ * var schema = new Schema({ mixed: Schema.Types.Mixed });
+ * schema.path('mixed').default(function () {
+ * return {};
+ * });
+ *
+ * // if we don't use a function to return object literals for Mixed defaults,
+ * // each document will receive a reference to the same object literal creating
+ * // a "shared" object instance:
+ * var schema = new Schema({ mixed: Schema.Types.Mixed });
+ * schema.path('mixed').default({});
+ * var M = db.model('M', schema);
+ * var m1 = new M;
+ * m1.mixed.added = 1;
+ * console.log(m1.mixed); // { added: 1 }
+ * var m2 = new M;
+ * console.log(m2.mixed); // { added: 1 }
*
* @param {Function|any} val the default value
* @return {defaultValue}
* @api public
*/
-SchemaType.prototype.default = function (val) {
- if (1 === arguments.length) {
- this.defaultValue = typeof val === 'function'
- ? val
- : this.cast(val);
- return this;
+SchemaType.prototype.default = function(val) {
+ if (arguments.length === 1) {
+ if (val === void 0) {
+ this.defaultValue = void 0;
+ return void 0;
+ }
+ this.defaultValue = val;
+ return this.defaultValue;
} else if (arguments.length > 1) {
this.defaultValue = utils.args(arguments);
}
@@ -99,7 +124,7 @@ SchemaType.prototype.default = function (val) {
* @api public
*/
-SchemaType.prototype.index = function (options) {
+SchemaType.prototype.index = function(options) {
this._index = options;
utils.expires(this._index);
return this;
@@ -110,7 +135,7 @@ SchemaType.prototype.index = function (options) {
*
* ####Example:
*
- * var s = new Schema({ name: { type: String, unique: true })
+ * var s = new Schema({ name: { type: String, unique: true }});
* Schema.path('name').index({ unique: true });
*
* _NOTE: violating the constraint returns an `E11000` error from MongoDB when saving, not a Mongoose validation error._
@@ -120,17 +145,48 @@ SchemaType.prototype.index = function (options) {
* @api public
*/
-SchemaType.prototype.unique = function (bool) {
- if (null == this._index || 'boolean' == typeof this._index) {
+SchemaType.prototype.unique = function(bool) {
+ if (this._index === false) {
+ if (!bool) {
+ return;
+ }
+ throw new Error('Path "' + this.path + '" may not have `index` set to ' +
+ 'false and `unique` set to true');
+ }
+ if (this._index == null || this._index === true) {
this._index = {};
- } else if ('string' == typeof this._index) {
- this._index = { type: this._index };
+ } else if (typeof this._index === 'string') {
+ this._index = {type: this._index};
}
this._index.unique = bool;
return this;
};
+/**
+ * Declares a full text index.
+ *
+ * ###Example:
+ *
+ * var s = new Schema({name : {type: String, text : true })
+ * Schema.path('name').index({text : true});
+ * @param {Boolean} bool
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.text = function(bool) {
+ if (this._index === null || this._index === undefined ||
+ typeof this._index === 'boolean') {
+ this._index = {};
+ } else if (typeof this._index === 'string') {
+ this._index = {type: this._index};
+ }
+
+ this._index.text = bool;
+ return this;
+};
+
/**
* Declares a sparse index.
*
@@ -144,11 +200,12 @@ SchemaType.prototype.unique = function (bool) {
* @api public
*/
-SchemaType.prototype.sparse = function (bool) {
- if (null == this._index || 'boolean' == typeof this._index) {
+SchemaType.prototype.sparse = function(bool) {
+ if (this._index === null || this._index === undefined ||
+ typeof this._index === 'boolean') {
this._index = {};
- } else if ('string' == typeof this._index) {
- this._index = { type: this._index };
+ } else if (typeof this._index === 'string') {
+ this._index = {type: this._index};
}
this._index.sparse = bool;
@@ -161,7 +218,7 @@ SchemaType.prototype.sparse = function (bool) {
* ####Example:
*
* function capitalize (val) {
- * if ('string' != typeof val) val = '';
+ * if (typeof val !== 'string') val = '';
* return val.charAt(0).toUpperCase() + val.substring(1);
* }
*
@@ -228,9 +285,10 @@ SchemaType.prototype.sparse = function (bool) {
* @api public
*/
-SchemaType.prototype.set = function (fn) {
- if ('function' != typeof fn)
+SchemaType.prototype.set = function(fn) {
+ if (typeof fn !== 'function') {
throw new TypeError('A setter must be a function.');
+ }
this.setters.push(fn);
return this;
};
@@ -297,9 +355,10 @@ SchemaType.prototype.set = function (fn) {
* @api public
*/
-SchemaType.prototype.get = function (fn) {
- if ('function' != typeof fn)
+SchemaType.prototype.get = function(fn) {
+ if (typeof fn !== 'function') {
throw new TypeError('A getter must be a function.');
+ }
this.getters.push(fn);
return this;
};
@@ -307,43 +366,60 @@ SchemaType.prototype.get = function (fn) {
/**
* Adds validator(s) for this document path.
*
- * Validators always receive the value to validate as their first argument and must return `Boolean`. Returning false is interpreted as validation failure.
+ * Validators always receive the value to validate as their first argument and must return `Boolean`. Returning `false` means validation failed.
+ *
+ * The error message argument is optional. If not passed, the [default generic error message template](#error_messages_MongooseError-messages) will be used.
*
* ####Examples:
*
+ * // make sure every value is equal to "something"
* function validator (val) {
* return val == 'something';
* }
- *
* new Schema({ name: { type: String, validate: validator }});
*
* // with a custom error message
*
- * var custom = [validator, 'validation failed']
+ * var custom = [validator, 'Uh oh, {PATH} does not equal "something".']
* new Schema({ name: { type: String, validate: custom }});
*
+ * // adding many validators at a time
+ *
* var many = [
* { validator: validator, msg: 'uh oh' }
- * , { validator: fn, msg: 'failed' }
+ * , { validator: anotherValidator, msg: 'failed' }
* ]
* new Schema({ name: { type: String, validate: many }});
*
* // or utilizing SchemaType methods directly:
*
* var schema = new Schema({ name: 'string' });
- * schema.path('name').validate(validator, 'validation failed');
+ * schema.path('name').validate(validator, 'validation of `{PATH}` failed with value `{VALUE}`');
+ *
+ * ####Error message templates:
+ *
+ * From the examples above, you may have noticed that error messages support basic templating. There are a few other template keywords besides `{PATH}` and `{VALUE}` too. To find out more, details are available [here](#error_messages_MongooseError-messages)
*
* ####Asynchronous validation:
*
- * Passing a validator function that receives two arguments tells mongoose that the validator is an asynchronous validator. The second argument is an callback function that must be passed either `true` or `false` when validation is complete.
+ * Passing a validator function that receives two arguments tells mongoose that the validator is an asynchronous validator. The first argument passed to the validator function is the value being validated. The second argument is a callback function that must called when you finish validating the value and passed either `true` or `false` to communicate either success or failure respectively.
*
* schema.path('name').validate(function (value, respond) {
* doStuff(value, function () {
* ...
* respond(false); // validation failed
* })
-* }, 'my error type');
-*
+ * }, '{PATH} failed validation.');
+ *
+ * // or with dynamic message
+ *
+ * schema.path('name').validate(function (value, respond) {
+ * doStuff(value, function () {
+ * ...
+ * respond(false, 'this message gets to the validation error');
+ * });
+ * }, 'this message does not matter');
+ *
* You might use asynchronous validators to retreive other documents from the database to validate against or to meet other I/O bound validation needs.
*
* Validation occurs `pre('save')` or whenever you manually execute [document#validate](#document_Document-validate).
@@ -363,72 +439,134 @@ SchemaType.prototype.get = function (fn) {
* Product.on('error', handleError);
*
* @param {RegExp|Function|Object} obj validator
- * @param {String} [error] optional error message
+ * @param {String} [errorMsg] optional error message
+ * @param {String} [type] optional validator type
* @return {SchemaType} this
* @api public
*/
-SchemaType.prototype.validate = function (obj, error) {
- if ('function' == typeof obj || obj && 'RegExp' === obj.constructor.name) {
- this.validators.push([obj, error]);
+SchemaType.prototype.validate = function(obj, message, type) {
+ if (typeof obj === 'function' || obj && utils.getFunctionName(obj.constructor) === 'RegExp') {
+ var properties;
+ if (message instanceof Object && !type) {
+ properties = utils.clone(message);
+ if (!properties.message) {
+ properties.message = properties.msg;
+ }
+ properties.validator = obj;
+ properties.type = properties.type || 'user defined';
+ } else {
+ if (!message) {
+ message = MongooseError.messages.general.default;
+ }
+ if (!type) {
+ type = 'user defined';
+ }
+ properties = {message: message, type: type, validator: obj};
+ }
+ this.validators.push(properties);
return this;
}
- var i = arguments.length
- , arg
+ var i,
+ length,
+ arg;
- while (i--) {
+ for (i = 0, length = arguments.length; i < length; i++) {
arg = arguments[i];
- if (!(arg && 'Object' == arg.constructor.name)) {
+ if (!(arg && utils.getFunctionName(arg.constructor) === 'Object')) {
var msg = 'Invalid validator. Received (' + typeof arg + ') '
- + arg
- + '. See http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate';
+ + arg
+ + '. See http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate';
throw new Error(msg);
}
- this.validate(arg.validator, arg.msg);
+ this.validate(arg.validator, arg);
}
return this;
};
/**
- * Adds a required validator to this schematype.
+ * Adds a required validator to this SchemaType. The validator gets added
+ * to the front of this SchemaType's validators array using `unshift()`.
*
* ####Example:
*
* var s = new Schema({ born: { type: Date, required: true })
- * // or
+ *
+ * // or with custom error message
+ *
+ * var s = new Schema({ born: { type: Date, required: '{PATH} is required!' })
+ *
+ * // or through the path API
+ *
* Schema.path('name').required(true);
*
+ * // with custom error messaging
+ *
+ * Schema.path('name').required(true, 'grrr :( ');
+ *
+ * // or make a path conditionally required based on a function
+ * var isOver18 = function() { return this.age >= 18; };
+ * Schema.path('voterRegistrationId').required(isOver18);
+ *
+ * The required validator uses the SchemaType's `checkRequired` function to
+ * determine whether a given value satisfies the required validator. By default,
+ * a value satisfies the required validator if `val != null` (that is, if
+ * the value is not null nor undefined). However, most built-in mongoose schema
+ * types override the default `checkRequired` function:
*
* @param {Boolean} required enable/disable the validator
+ * @param {String} [message] optional custom error message
* @return {SchemaType} this
+ * @see Customized Error Messages #error_messages_MongooseError-messages
+ * @see SchemaArray#checkRequired #schema_array_SchemaArray.checkRequired
+ * @see SchemaBoolean#checkRequired #schema_boolean_SchemaBoolean-checkRequired
+ * @see SchemaBuffer#checkRequired #schema_buffer_SchemaBuffer.schemaName
+ * @see SchemaNumber#checkRequired #schema_number_SchemaNumber-min
+ * @see SchemaObjectId#checkRequired #schema_objectid_ObjectId-auto
+ * @see SchemaString#checkRequired #schema_string_SchemaString-checkRequired
* @api public
*/
-SchemaType.prototype.required = function (required) {
- var self = this;
+SchemaType.prototype.required = function(required, message) {
+ if (required === false) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator !== this.requiredValidator;
+ }, this);
+
+ this.isRequired = false;
+ return this;
+ }
+
+ var _this = this;
+ this.isRequired = true;
- function __checkRequired (v) {
+ this.requiredValidator = function(v) {
// in here, `this` refers to the validating document.
// no validation when this path wasn't selected in the query.
- if ('isSelected' in this &&
- !this.isSelected(self.path) &&
- !this.isModified(self.path)) return true;
- return self.checkRequired(v, this);
- }
+ if ('isSelected' in this && !this.isSelected(_this.path) && !this.isModified(_this.path)) {
+ return true;
+ }
- if (false === required) {
- this.isRequired = false;
- this.validators = this.validators.filter(function (v) {
- return v[0].name !== '__checkRequired';
- });
- } else {
- this.isRequired = true;
- this.validators.push([__checkRequired, 'required']);
+ return ((typeof required === 'function') && !required.apply(this)) ||
+ _this.checkRequired(v, this);
+ };
+ this.originalRequiredValue = required;
+
+ if (typeof required === 'string') {
+ message = required;
+ required = undefined;
}
+ var msg = message || MongooseError.messages.general.required;
+ this.validators.unshift({
+ validator: this.requiredValidator,
+ message: msg,
+ type: 'required'
+ });
+
return this;
};
@@ -440,16 +578,19 @@ SchemaType.prototype.required = function (required) {
* @api private
*/
-SchemaType.prototype.getDefault = function (scope, init) {
- var ret = 'function' === typeof this.defaultValue
- ? this.defaultValue.call(scope)
- : this.defaultValue;
+SchemaType.prototype.getDefault = function(scope, init) {
+ var ret = typeof this.defaultValue === 'function'
+ ? this.defaultValue.call(scope)
+ : this.defaultValue;
- if (null !== ret && undefined !== ret) {
- return this.cast(ret, scope, init);
- } else {
- return ret;
+ if (ret !== null && ret !== undefined) {
+ var casted = this.cast(ret, scope, init);
+ if (casted && casted.$isSingleNested) {
+ casted.$parent = scope;
+ }
+ return casted;
}
+ return ret;
};
/**
@@ -461,30 +602,30 @@ SchemaType.prototype.getDefault = function (scope, init) {
* @api private
*/
-SchemaType.prototype.applySetters = function (value, scope, init, priorVal) {
- if (SchemaType._isRef(this, value, scope, init)) {
- return init
- ? value
- : this.cast(value, scope, init, priorVal);
- }
-
- var v = value
- , setters = this.setters
- , len = setters.length
-
- if (!len) {
- if (null === v || undefined === v) return v;
- return this.cast(v, scope, init, priorVal)
- }
+SchemaType.prototype.applySetters = function(value, scope, init, priorVal, options) {
+ var v = value,
+ setters = this.setters,
+ len = setters.length,
+ caster = this.caster;
while (len--) {
v = setters[len].call(scope, v, this);
}
- if (null === v || undefined === v) return v;
+ if (Array.isArray(v) && caster && caster.setters) {
+ var newVal = [];
+ for (var i = 0; i < v.length; i++) {
+ newVal.push(caster.applySetters(v[i], scope, init, priorVal));
+ }
+ v = newVal;
+ }
+
+ if (v === null || v === undefined) {
+ return v;
+ }
// do not cast until all setters are applied #665
- v = this.cast(v, scope, init, priorVal);
+ v = this.cast(v, scope, init, priorVal, options);
return v;
};
@@ -497,12 +638,10 @@ SchemaType.prototype.applySetters = function (value, scope, init, priorVal) {
* @api private
*/
-SchemaType.prototype.applyGetters = function (value, scope) {
- if (SchemaType._isRef(this, value, scope, true)) return value;
-
- var v = value
- , getters = this.getters
- , len = getters.length;
+SchemaType.prototype.applyGetters = function(value, scope) {
+ var v = value,
+ getters = this.getters,
+ len = getters.length;
if (!len) {
return v;
@@ -532,10 +671,10 @@ SchemaType.prototype.applyGetters = function (value, scope) {
* @api public
*/
-SchemaType.prototype.select = function select (val) {
- this.selected = !! val;
+SchemaType.prototype.select = function select(val) {
+ this.selected = !!val;
return this;
-}
+};
/**
* Performs a validation of `value` using the validators declared for this SchemaType.
@@ -546,40 +685,125 @@ SchemaType.prototype.select = function select (val) {
* @api private
*/
-SchemaType.prototype.doValidate = function (value, fn, scope) {
- var err = false
- , path = this.path
- , count = this.validators.length;
+SchemaType.prototype.doValidate = function(value, fn, scope) {
+ var err = false,
+ path = this.path,
+ count = this.validators.length;
- if (!count) return fn(null);
+ if (!count) {
+ return fn(null);
+ }
- function validate (ok, msg, val) {
- if (err) return;
+ var validate = function(ok, validatorProperties) {
+ if (err) {
+ return;
+ }
if (ok === undefined || ok) {
--count || fn(null);
} else {
- fn(err = new ValidatorError(path, msg, val));
+ err = new ValidatorError(validatorProperties);
+ fn(err);
+ }
+ };
+
+ var _this = this;
+ this.validators.forEach(function(v) {
+ if (err) {
+ return;
}
- }
- this.validators.forEach(function (v) {
- var validator = v[0]
- , message = v[1];
+ var validator = v.validator;
+
+ var validatorProperties = utils.clone(v);
+ validatorProperties.path = path;
+ validatorProperties.value = value;
if (validator instanceof RegExp) {
- validate(validator.test(value), message, value);
- } else if ('function' === typeof validator) {
- if (2 === validator.length) {
- validator.call(scope, value, function (ok) {
- validate(ok, message, value);
+ validate(validator.test(value), validatorProperties);
+ } else if (typeof validator === 'function') {
+ if (value === undefined && !_this.isRequired) {
+ validate(true, validatorProperties);
+ return;
+ }
+ if (validator.length === 2) {
+ var returnVal = validator.call(scope, value, function(ok, customMsg) {
+ if (typeof returnVal === 'boolean') {
+ return;
+ }
+ if (customMsg) {
+ validatorProperties.message = customMsg;
+ }
+ validate(ok, validatorProperties);
});
+ if (typeof returnVal === 'boolean') {
+ validate(returnVal, validatorProperties);
+ }
} else {
- validate(validator.call(scope, value), message, value);
+ validate(validator.call(scope, value), validatorProperties);
}
}
});
};
+/**
+ * Performs a validation of `value` using the validators declared for this SchemaType.
+ *
+ * ####Note:
+ *
+ * This method ignores the asynchronous validators.
+ *
+ * @param {any} value
+ * @param {Object} scope
+ * @return {MongooseError|undefined}
+ * @api private
+ */
+
+SchemaType.prototype.doValidateSync = function(value, scope) {
+ var err = null,
+ path = this.path,
+ count = this.validators.length;
+
+ if (!count) {
+ return null;
+ }
+
+ var validate = function(ok, validatorProperties) {
+ if (err) {
+ return;
+ }
+ if (ok !== undefined && !ok) {
+ err = new ValidatorError(validatorProperties);
+ }
+ };
+
+ var _this = this;
+ if (value === undefined && !_this.isRequired) {
+ return null;
+ }
+
+ this.validators.forEach(function(v) {
+ if (err) {
+ return;
+ }
+
+ var validator = v.validator;
+ var validatorProperties = utils.clone(v);
+ validatorProperties.path = path;
+ validatorProperties.value = value;
+
+ if (validator instanceof RegExp) {
+ validate(validator.test(value), validatorProperties);
+ } else if (typeof validator === 'function') {
+ // if not async validators
+ if (validator.length !== 2) {
+ validate(validator.call(scope, value), validatorProperties);
+ }
+ }
+ });
+
+ return err;
+};
+
/**
* Determines if value is a valid Reference.
*
@@ -591,7 +815,7 @@ SchemaType.prototype.doValidate = function (value, fn, scope) {
* @api private
*/
-SchemaType._isRef = function (self, value, doc, init) {
+SchemaType._isRef = function(self, value, doc, init) {
// fast path
var ref = init && self.options && self.options.ref;
@@ -605,18 +829,86 @@ SchemaType._isRef = function (self, value, doc, init) {
}
if (ref) {
- if (null == value) return true;
+ if (value == null) {
+ return true;
+ }
if (!Buffer.isBuffer(value) && // buffers are objects too
- 'Binary' != value._bsontype // raw binary value from the db
+ value._bsontype !== 'Binary' // raw binary value from the db
&& utils.isObject(value) // might have deselected _id in population query
- ) {
+ ) {
return true;
}
}
return false;
+};
+
+/*!
+ * ignore
+ */
+
+function handleSingle(val) {
+ return this.castForQuery(val);
+}
+
+/*!
+ * ignore
+ */
+
+function handleArray(val) {
+ var _this = this;
+ if (!Array.isArray(val)) {
+ return [this.castForQuery(val)];
+ }
+ return val.map(function(m) {
+ return _this.castForQuery(m);
+ });
}
+/*!
+ * ignore
+ */
+
+SchemaType.prototype.$conditionalHandlers = {
+ $all: handleArray,
+ $eq: handleSingle,
+ $in: handleArray,
+ $ne: handleSingle,
+ $nin: handleArray
+};
+
+/**
+ * Cast the given value with the given optional query operator.
+ *
+ * @param {String} [$conditional] query operator, like `$eq` or `$in`
+ * @param {any} val
+ * @api private
+ */
+
+SchemaType.prototype.castForQuery = function($conditional, val) {
+ var handler;
+ if (arguments.length === 2) {
+ handler = this.$conditionalHandlers[$conditional];
+ if (!handler) {
+ throw new Error('Can\'t use ' + $conditional);
+ }
+ return handler.call(this, val);
+ }
+ val = $conditional;
+ return this.cast(val);
+};
+
+/**
+ * Default check for if this path satisfies the `required` validator.
+ *
+ * @param {any} val
+ * @api private
+ */
+
+SchemaType.prototype.checkRequired = function(val) {
+ return val != null;
+};
+
/*!
* Module exports.
*/
diff --git a/node_modules/mongoose/lib/services/common.js b/node_modules/mongoose/lib/services/common.js
new file mode 100644
index 0000000..956aee5
--- /dev/null
+++ b/node_modules/mongoose/lib/services/common.js
@@ -0,0 +1,83 @@
+'use strict';
+
+/*!
+ * Module dependencies.
+ */
+
+var ObjectId = require('../types/objectid');
+var utils = require('../utils');
+
+exports.flatten = flatten;
+exports.modifiedPaths = modifiedPaths;
+
+/*!
+ * ignore
+ */
+
+function flatten(update, path, options) {
+ var keys = Object.keys(update || {});
+ var numKeys = keys.length;
+ var result = {};
+ path = path ? path + '.' : '';
+
+ for (var i = 0; i < numKeys; ++i) {
+ var key = keys[i];
+ var val = update[key];
+ if (utils.isMongooseObject(val) && !Buffer.isBuffer(val)) {
+ val = val.toObject({ virtuals: false });
+ }
+ if (shouldFlatten(val)) {
+ result[path + key] = val;
+ if (options && options.skipArrays && Array.isArray(val)) {
+ continue;
+ }
+ var flat = flatten(val, path + key);
+ for (var k in flat) {
+ result[k] = flat[k];
+ }
+ if (Array.isArray(val)) {
+ result[path + key] = val;
+ }
+ } else {
+ result[path + key] = val;
+ }
+ }
+
+ return result;
+}
+
+/*!
+ * ignore
+ */
+
+function modifiedPaths(update, path, result) {
+ var keys = Object.keys(update || {});
+ var numKeys = keys.length;
+ result = result || {};
+ path = path ? path + '.' : '';
+
+ for (var i = 0; i < numKeys; ++i) {
+ var key = keys[i];
+ var val = update[key];
+
+ result[path + key] = true;
+ if (shouldFlatten(val)) {
+ modifiedPaths(val, path + key, result);
+ }
+ }
+
+ return result;
+}
+
+/*!
+ * ignore
+ */
+
+function shouldFlatten(val) {
+ return val &&
+ typeof val === 'object' &&
+ !(val instanceof Date) &&
+ !(val instanceof ObjectId) &&
+ (!Array.isArray(val) || val.length > 0) &&
+ !(val instanceof Buffer);
+}
diff --git a/node_modules/mongoose/lib/services/setDefaultsOnInsert.js b/node_modules/mongoose/lib/services/setDefaultsOnInsert.js
new file mode 100644
index 0000000..f122f40
--- /dev/null
+++ b/node_modules/mongoose/lib/services/setDefaultsOnInsert.js
@@ -0,0 +1,112 @@
+'use strict';
+
+var flatten = require('./common').flatten;
+var modifiedPaths = require('./common').modifiedPaths;
+
+/**
+ * Applies defaults to update and findOneAndUpdate operations.
+ *
+ * @param {Query} query
+ * @param {Schema} schema
+ * @param {Object} castedDoc
+ * @param {Object} options
+ * @method setDefaultsOnInsert
+ * @api private
+ */
+
+module.exports = function(query, schema, castedDoc, options) {
+ var keys = Object.keys(castedDoc || {});
+ var updatedKeys = {};
+ var updatedValues = {};
+ var numKeys = keys.length;
+ var hasDollarUpdate = false;
+ var modified = {};
+
+ for (var i = 0; i < numKeys; ++i) {
+ if (keys[i].charAt(0) === '$') {
+ modifiedPaths(castedDoc[keys[i]], '', modified);
+ var flat = flatten(castedDoc[keys[i]]);
+ var paths = Object.keys(flat);
+ var numPaths = paths.length;
+ for (var j = 0; j < numPaths; ++j) {
+ var updatedPath = paths[j].replace('.$.', '.0.');
+ updatedPath = updatedPath.replace(/\.\$$/, '.0');
+ if (keys[i] === '$set' || keys[i] === '$setOnInsert') {
+ updatedValues[updatedPath] = flat[paths[j]];
+ } else if (keys[i] === '$unset') {
+ updatedValues[updatedPath] = undefined;
+ }
+ updatedKeys[updatedPath] = true;
+ }
+ hasDollarUpdate = true;
+ }
+ }
+
+ if (!hasDollarUpdate) {
+ modifiedPaths(castedDoc, '', modified);
+ updatedValues = flatten(castedDoc);
+ updatedKeys = Object.keys(updatedValues);
+ }
+
+ if (options && options.upsert) {
+ paths = Object.keys(query._conditions);
+ numPaths = keys.length;
+ for (i = 0; i < numPaths; ++i) {
+ var path = paths[i];
+ var condition = query._conditions[path];
+ if (condition && typeof condition === 'object') {
+ var conditionKeys = Object.keys(condition);
+ var numConditionKeys = conditionKeys.length;
+ var hasDollarKey = false;
+ for (j = 0; j < numConditionKeys; ++j) {
+ if (conditionKeys[j].charAt(0) === '$') {
+ hasDollarKey = true;
+ break;
+ }
+ }
+ if (hasDollarKey) {
+ continue;
+ }
+ }
+ updatedKeys[path] = true;
+ modified[path] = true;
+ }
+
+ if (options.setDefaultsOnInsert) {
+ schema.eachPath(function(path, schemaType) {
+ if (path === '_id') {
+ // Ignore _id for now because it causes bugs in 2.4
+ return;
+ }
+ if (schemaType.$isSingleNested) {
+ // Only handle nested schemas 1-level deep to avoid infinite
+ // recursion re: https://github.com/mongodb-js/mongoose-autopopulate/issues/11
+ schemaType.schema.eachPath(function(_path, _schemaType) {
+ if (path === '_id') {
+ // Ignore _id for now because it causes bugs in 2.4
+ return;
+ }
+
+ var def = _schemaType.getDefault(null, true);
+ if (!modified[path + '.' + _path] && typeof def !== 'undefined') {
+ castedDoc = castedDoc || {};
+ castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
+ castedDoc.$setOnInsert[path + '.' + _path] = def;
+ updatedValues[path + '.' + _path] = def;
+ }
+ });
+ } else {
+ var def = schemaType.getDefault(null, true);
+ if (!modified[path] && typeof def !== 'undefined') {
+ castedDoc = castedDoc || {};
+ castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
+ castedDoc.$setOnInsert[path] = def;
+ updatedValues[path] = def;
+ }
+ }
+ });
+ }
+ }
+
+ return castedDoc;
+};
diff --git a/node_modules/mongoose/lib/services/updateValidators.js b/node_modules/mongoose/lib/services/updateValidators.js
new file mode 100644
index 0000000..8d7cd5c
--- /dev/null
+++ b/node_modules/mongoose/lib/services/updateValidators.js
@@ -0,0 +1,101 @@
+/*!
+ * Module dependencies.
+ */
+
+var Mixed = require('../schema/mixed');
+var ValidationError = require('../error/validation');
+var async = require('async');
+var flatten = require('./common').flatten;
+var modifiedPaths = require('./common').modifiedPaths;
+
+/**
+ * Applies validators and defaults to update and findOneAndUpdate operations,
+ * specifically passing a null doc as `this` to validators and defaults
+ *
+ * @param {Query} query
+ * @param {Schema} schema
+ * @param {Object} castedDoc
+ * @param {Object} options
+ * @method runValidatorsOnUpdate
+ * @api private
+ */
+
+module.exports = function(query, schema, castedDoc, options) {
+ var keys = Object.keys(castedDoc || {});
+ var updatedKeys = {};
+ var updatedValues = {};
+ var numKeys = keys.length;
+ var hasDollarUpdate = false;
+ var modified = {};
+
+ for (var i = 0; i < numKeys; ++i) {
+ if (keys[i].charAt(0) === '$') {
+ modifiedPaths(castedDoc[keys[i]], '', modified);
+ var flat = flatten(castedDoc[keys[i]]);
+ var paths = Object.keys(flat);
+ var numPaths = paths.length;
+ for (var j = 0; j < numPaths; ++j) {
+ var updatedPath = paths[j].replace('.$.', '.0.');
+ updatedPath = updatedPath.replace(/\.\$$/, '.0');
+ if (keys[i] === '$set' || keys[i] === '$setOnInsert') {
+ updatedValues[updatedPath] = flat[paths[j]];
+ } else if (keys[i] === '$unset') {
+ updatedValues[updatedPath] = undefined;
+ }
+ updatedKeys[updatedPath] = true;
+ }
+ hasDollarUpdate = true;
+ }
+ }
+
+ if (!hasDollarUpdate) {
+ modifiedPaths(castedDoc, '', modified);
+ updatedValues = flatten(castedDoc);
+ updatedKeys = Object.keys(updatedValues);
+ }
+
+ var updates = Object.keys(updatedValues);
+ var numUpdates = updates.length;
+ var validatorsToExecute = [];
+ var validationErrors = [];
+ function iter(i) {
+ var schemaPath = schema._getSchema(updates[i]);
+ if (schemaPath) {
+ // gh-4305: `_getSchema()` will report all sub-fields of a 'Mixed' path
+ // as 'Mixed', so avoid double validating them.
+ if (schemaPath instanceof Mixed && schemaPath.path !== updates[i]) {
+ return;
+ }
+
+ validatorsToExecute.push(function(callback) {
+ schemaPath.doValidate(
+ updatedValues[updates[i]],
+ function(err) {
+ if (err) {
+ err.path = updates[i];
+ validationErrors.push(err);
+ }
+ callback(null);
+ },
+ options && options.context === 'query' ? query : null,
+ {updateValidator: true});
+ });
+ }
+ }
+ for (i = 0; i < numUpdates; ++i) {
+ iter(i);
+ }
+
+ return function(callback) {
+ async.parallel(validatorsToExecute, function() {
+ if (validationErrors.length) {
+ var err = new ValidationError(null);
+ for (var i = 0; i < validationErrors.length; ++i) {
+ err.errors[validationErrors[i].path] = validationErrors[i];
+ }
+ return callback(err);
+ }
+ callback(null);
+ });
+ };
+};
diff --git a/node_modules/mongoose/lib/statemachine.js b/node_modules/mongoose/lib/statemachine.js
index 76005d8..3bba519 100644
--- a/node_modules/mongoose/lib/statemachine.js
+++ b/node_modules/mongoose/lib/statemachine.js
@@ -12,10 +12,8 @@ var utils = require('./utils');
* @api private
*/
-var StateMachine = module.exports = exports = function StateMachine () {
- this.paths = {};
- this.states = {};
-}
+var StateMachine = module.exports = exports = function StateMachine() {
+};
/*!
* StateMachine.ctor('state1', 'state2', ...)
@@ -31,15 +29,17 @@ var StateMachine = module.exports = exports = function StateMachine () {
* @private
*/
-StateMachine.ctor = function () {
+StateMachine.ctor = function() {
var states = utils.args(arguments);
- var ctor = function () {
+ var ctor = function() {
StateMachine.apply(this, arguments);
+ this.paths = {};
+ this.states = {};
this.stateNames = states;
- var i = states.length
- , state;
+ var i = states.length,
+ state;
while (i--) {
state = states[i];
@@ -47,13 +47,13 @@ StateMachine.ctor = function () {
}
};
- ctor.prototype.__proto__ = StateMachine.prototype;
+ ctor.prototype = new StateMachine();
- states.forEach(function (state) {
+ states.forEach(function(state) {
// Changes the `path`'s state to `state`.
- ctor.prototype[state] = function (path) {
+ ctor.prototype[state] = function(path) {
this._changeState(path, state);
- }
+ };
});
return ctor;
@@ -69,29 +69,29 @@ StateMachine.ctor = function () {
* @api private
*/
-StateMachine.prototype._changeState = function _changeState (path, nextState) {
+StateMachine.prototype._changeState = function _changeState(path, nextState) {
var prevBucket = this.states[this.paths[path]];
if (prevBucket) delete prevBucket[path];
this.paths[path] = nextState;
this.states[nextState][path] = true;
-}
+};
/*!
* ignore
*/
-StateMachine.prototype.clear = function clear (state) {
- var keys = Object.keys(this.states[state])
- , i = keys.length
- , path
+StateMachine.prototype.clear = function clear(state) {
+ var keys = Object.keys(this.states[state]),
+ i = keys.length,
+ path;
while (i--) {
path = keys[i];
delete this.states[state][path];
delete this.paths[path];
}
-}
+};
/*!
* Checks to see if at least one path is in the states passed in via `arguments`
@@ -101,13 +101,13 @@ StateMachine.prototype.clear = function clear (state) {
* @private
*/
-StateMachine.prototype.some = function some () {
- var self = this;
+StateMachine.prototype.some = function some() {
+ var _this = this;
var what = arguments.length ? arguments : this.stateNames;
- return Array.prototype.some.call(what, function (state) {
- return Object.keys(self.states[state]).length;
+ return Array.prototype.some.call(what, function(state) {
+ return Object.keys(_this.states[state]).length;
});
-}
+};
/*!
* This function builds the functions that get assigned to `forEach` and `map`,
@@ -118,25 +118,25 @@ StateMachine.prototype.some = function some () {
* @api private
*/
-StateMachine.prototype._iter = function _iter (iterMethod) {
- return function () {
- var numArgs = arguments.length
- , states = utils.args(arguments, 0, numArgs-1)
- , callback = arguments[numArgs-1];
+StateMachine.prototype._iter = function _iter(iterMethod) {
+ return function() {
+ var numArgs = arguments.length,
+ states = utils.args(arguments, 0, numArgs - 1),
+ callback = arguments[numArgs - 1];
if (!states.length) states = this.stateNames;
- var self = this;
+ var _this = this;
- var paths = states.reduce(function (paths, state) {
- return paths.concat(Object.keys(self.states[state]));
+ var paths = states.reduce(function(paths, state) {
+ return paths.concat(Object.keys(_this.states[state]));
}, []);
- return paths[iterMethod](function (path, i, paths) {
+ return paths[iterMethod](function(path, i, paths) {
return callback(path, i, paths);
});
};
-}
+};
/*!
* Iterates over the paths that belong to one of the parameter states.
@@ -152,10 +152,10 @@ StateMachine.prototype._iter = function _iter (iterMethod) {
* @private
*/
-StateMachine.prototype.forEach = function forEach () {
+StateMachine.prototype.forEach = function forEach() {
this.forEach = this._iter('forEach');
return this.forEach.apply(this, arguments);
-}
+};
/*!
* Maps over the paths that belong to one of the parameter states.
@@ -172,8 +172,7 @@ StateMachine.prototype.forEach = function forEach () {
* @private
*/
-StateMachine.prototype.map = function map () {
+StateMachine.prototype.map = function map() {
this.map = this._iter('map');
return this.map.apply(this, arguments);
-}
-
+};
diff --git a/node_modules/mongoose/lib/types/array.js b/node_modules/mongoose/lib/types/array.js
index e0d7700..044d5de 100644
--- a/node_modules/mongoose/lib/types/array.js
+++ b/node_modules/mongoose/lib/types/array.js
@@ -1,4 +1,3 @@
-
/*!
* Module dependencies.
*/
@@ -24,653 +23,746 @@ var isMongooseObject = utils.isMongooseObject;
* @see http://bit.ly/f6CnZU
*/
-function MongooseArray (values, path, doc) {
- var arr = [];
- arr.push.apply(arr, values);
- arr.__proto__ = MongooseArray.prototype;
+function MongooseArray(values, path, doc) {
+ var arr = [].concat(values);
+
+ var keysMA = Object.keys(MongooseArray.mixin);
+ var numKeys = keysMA.length;
+ for (var i = 0; i < numKeys; ++i) {
+ arr[keysMA[i]] = MongooseArray.mixin[keysMA[i]];
+ }
- arr._atomics = {};
- arr.validators = [];
arr._path = path;
+ arr.isMongooseArray = true;
+ arr.validators = [];
+ arr._atomics = {};
+ arr._schema = void 0;
- if (doc) {
+ // Because doc comes from the context of another function, doc === global
+ // can happen if there was a null somewhere up the chain (see #3020)
+ // RB Jun 17, 2015 updated to check for presence of expected paths instead
+ // to make more proof against unusual node environments
+ if (doc && doc instanceof Document) {
arr._parent = doc;
arr._schema = doc.schema.path(path);
}
return arr;
-};
-
-/*!
- * Inherit from Array
- */
-
-MongooseArray.prototype = new Array;
-
-/**
- * Stores a queue of atomic operations to perform
- *
- * @property _atomics
- * @api private
- */
-
-MongooseArray.prototype._atomics;
+}
-/**
- * Parent owner document
- *
- * @property _parent
- * @api private
- */
+MongooseArray.mixin = {
+
+ /**
+ * Stores a queue of atomic operations to perform
+ *
+ * @property _atomics
+ * @api private
+ */
+
+ _atomics: undefined,
+
+ /**
+ * Parent owner document
+ *
+ * @property _parent
+ * @api private
+ * @receiver MongooseArray
+ */
+
+ _parent: undefined,
+
+ /**
+ * Casts a member based on this arrays schema.
+ *
+ * @param {any} value
+ * @return value the casted value
+ * @method _cast
+ * @api private
+ * @receiver MongooseArray
+ */
+
+ _cast: function(value) {
+ var populated = false;
+ var Model;
+
+ if (this._parent) {
+ populated = this._parent.populated(this._path, true);
+ }
-MongooseArray.prototype._parent;
+ if (populated && value !== null && value !== undefined) {
+ // cast to the populated Models schema
+ Model = populated.options.model;
-/**
- * Casts a member based on this arrays schema.
- *
- * @param {any} value
- * @return value the casted value
- * @api private
- */
+ // only objects are permitted so we can safely assume that
+ // non-objects are to be interpreted as _id
+ if (Buffer.isBuffer(value) ||
+ value instanceof ObjectId || !utils.isObject(value)) {
+ value = {_id: value};
+ }
-MongooseArray.prototype._cast = function (value) {
- var owner = this._owner;
- var populated = false;
- var Model;
-
- if (this._parent) {
- // if a populated array, we must cast to the same model
- // instance as specified in the original query.
- if (!owner) {
- owner = this._owner = this._parent.ownerDocument
- ? this._parent.ownerDocument()
- : this._parent;
+ // gh-2399
+ // we should cast model only when it's not a discriminator
+ var isDisc = value.schema && value.schema.discriminatorMapping &&
+ value.schema.discriminatorMapping.key !== undefined;
+ if (!isDisc) {
+ value = new Model(value);
+ }
+ return this._schema.caster.cast(value, this._parent, true);
}
- populated = owner.populated(this._path, true);
- }
-
- if (populated && null != value) {
- // cast to the populated Models schema
- var Model = populated.options.model;
+ return this._schema.caster.cast(value, this._parent, false);
+ },
+
+ /**
+ * Marks this array as modified.
+ *
+ * If it bubbles up from an embedded document change, then it takes the following arguments (otherwise, takes 0 arguments)
+ *
+ * @param {EmbeddedDocument} embeddedDoc the embedded doc that invoked this method on the Array
+ * @param {String} embeddedPath the path which changed in the embeddedDoc
+ * @method _markModified
+ * @api private
+ * @receiver MongooseArray
+ */
+
+ _markModified: function(elem, embeddedPath) {
+ var parent = this._parent,
+ dirtyPath;
+
+ if (parent) {
+ dirtyPath = this._path;
+
+ if (arguments.length) {
+ if (embeddedPath != null) {
+ // an embedded doc bubbled up the change
+ dirtyPath = dirtyPath + '.' + this.indexOf(elem) + '.' + embeddedPath;
+ } else {
+ // directly set an index
+ dirtyPath = dirtyPath + '.' + elem;
+ }
+ }
- // only objects are permitted so we can safely assume that
- // non-objects are to be interpreted as _id
- if (Buffer.isBuffer(value) ||
- value instanceof ObjectId || !utils.isObject(value)) {
- value = { _id: value };
+ parent.markModified(dirtyPath);
}
- value = new Model(value);
- return this._schema.caster.cast(value, this._parent, true)
- }
-
- return this._schema.caster.cast(value, this._parent, false)
-}
+ return this;
+ },
+
+ /**
+ * Register an atomic operation with the parent.
+ *
+ * @param {Array} op operation
+ * @param {any} val
+ * @method _registerAtomic
+ * @api private
+ * @receiver MongooseArray
+ */
+
+ _registerAtomic: function(op, val) {
+ if (op === '$set') {
+ // $set takes precedence over all other ops.
+ // mark entire array modified.
+ this._atomics = {$set: val};
+ return this;
+ }
-/**
- * Marks this array as modified.
- *
- * If it bubbles up from an embedded document change, then it takes the following arguments (otherwise, takes 0 arguments)
- *
- * @param {EmbeddedDocument} embeddedDoc the embedded doc that invoked this method on the Array
- * @param {String} embeddedPath the path which changed in the embeddedDoc
- * @api private
- */
+ var atomics = this._atomics;
-MongooseArray.prototype._markModified = function (elem, embeddedPath) {
- var parent = this._parent
- , dirtyPath;
+ // reset pop/shift after save
+ if (op === '$pop' && !('$pop' in atomics)) {
+ var _this = this;
+ this._parent.once('save', function() {
+ _this._popped = _this._shifted = null;
+ });
+ }
- if (parent) {
- dirtyPath = this._path;
+ // check for impossible $atomic combos (Mongo denies more than one
+ // $atomic op on a single path
+ if (this._atomics.$set ||
+ Object.keys(atomics).length && !(op in atomics)) {
+ // a different op was previously registered.
+ // save the entire thing.
+ this._atomics = {$set: this};
+ return this;
+ }
- if (arguments.length) {
- if (null != embeddedPath) {
- // an embedded doc bubbled up the change
- dirtyPath = dirtyPath + '.' + this.indexOf(elem) + '.' + embeddedPath;
+ var selector;
+
+ if (op === '$pullAll' || op === '$pushAll' || op === '$addToSet') {
+ atomics[op] || (atomics[op] = []);
+ atomics[op] = atomics[op].concat(val);
+ } else if (op === '$pullDocs') {
+ var pullOp = atomics['$pull'] || (atomics['$pull'] = {});
+ if (val[0] instanceof EmbeddedDocument) {
+ selector = pullOp['$or'] || (pullOp['$or'] = []);
+ Array.prototype.push.apply(selector, val.map(function(v) {
+ return v.toObject({virtuals: false});
+ }));
} else {
- // directly set an index
- dirtyPath = dirtyPath + '.' + elem;
+ selector = pullOp['_id'] || (pullOp['_id'] = {$in: []});
+ selector['$in'] = selector['$in'].concat(val);
}
+ } else {
+ atomics[op] = val;
}
- parent.markModified(dirtyPath);
- }
-
- return this;
-};
-
-/**
- * Register an atomic operation with the parent.
- *
- * @param {Array} op operation
- * @param {any} val
- * @api private
- */
-
-MongooseArray.prototype._registerAtomic = function (op, val) {
- if ('$set' == op) {
- // $set takes precedence over all other ops.
- // mark entire array modified.
- this._atomics = { $set: val };
- return this;
- }
-
- var atomics = this._atomics;
-
- // reset pop/shift after save
- if ('$pop' == op && !('$pop' in atomics)) {
- var self = this;
- this._parent.once('save', function () {
- self._popped = self._shifted = null;
- });
- }
- // check for impossible $atomic combos (Mongo denies more than one
- // $atomic op on a single path
- if (this._atomics.$set ||
- Object.keys(atomics).length && !(op in atomics)) {
- // a different op was previously registered.
- // save the entire thing.
- this._atomics = { $set: this };
return this;
- }
+ },
+
+ /**
+ * Depopulates stored atomic operation values as necessary for direct insertion to MongoDB.
+ *
+ * If no atomics exist, we return all array values after conversion.
+ *
+ * @return {Array}
+ * @method $__getAtomics
+ * @memberOf MongooseArray
+ * @api private
+ */
+
+ $__getAtomics: function() {
+ var ret = [];
+ var keys = Object.keys(this._atomics);
+ var i = keys.length;
+
+ if (i === 0) {
+ ret[0] = ['$set', this.toObject({depopulate: 1, transform: false})];
+ return ret;
+ }
- if (op === '$pullAll' || op === '$pushAll' || op === '$addToSet') {
- atomics[op] || (atomics[op] = []);
- atomics[op] = atomics[op].concat(val);
- } else if (op === '$pullDocs') {
- var pullOp = atomics['$pull'] || (atomics['$pull'] = {})
- , selector = pullOp['_id'] || (pullOp['_id'] = {'$in' : [] });
- selector['$in'] = selector['$in'].concat(val);
- } else {
- atomics[op] = val;
- }
+ while (i--) {
+ var op = keys[i];
+ var val = this._atomics[op];
+
+ // the atomic values which are arrays are not MongooseArrays. we
+ // need to convert their elements as if they were MongooseArrays
+ // to handle populated arrays versus DocumentArrays properly.
+ if (isMongooseObject(val)) {
+ val = val.toObject({depopulate: 1, transform: false});
+ } else if (Array.isArray(val)) {
+ val = this.toObject.call(val, {depopulate: 1, transform: false});
+ } else if (val.valueOf) {
+ val = val.valueOf();
+ }
- return this;
-};
+ if (op === '$addToSet') {
+ val = {$each: val};
+ }
-/**
- * Depopulates stored atomic operation values as necessary for direct insertion to MongoDB.
- *
- * If no atomics exist, we return all array values after conversion.
- *
- * @return {Array}
- * @method $__getAtomics
- * @memberOf MongooseArray
- * @api private
- */
+ ret.push([op, val]);
+ }
-MongooseArray.prototype.$__getAtomics = function () {
- var ret = [];
- var keys = Object.keys(this._atomics);
- var i = keys.length;
+ return ret;
+ },
+
+ /**
+ * Returns the number of pending atomic operations to send to the db for this array.
+ *
+ * @api private
+ * @return {Number}
+ * @method hasAtomics
+ * @receiver MongooseArray
+ */
+
+ hasAtomics: function hasAtomics() {
+ if (!(this._atomics && this._atomics.constructor.name === 'Object')) {
+ return 0;
+ }
- if (0 === i) {
- ret[0] = ['$set', this.toObject({ depopulate: 1 })];
+ return Object.keys(this._atomics).length;
+ },
+
+ /**
+ * Internal helper for .map()
+ *
+ * @api private
+ * @return {Number}
+ * @method _mapCast
+ * @receiver MongooseArray
+ */
+ _mapCast: function(val, index) {
+ return this._cast(val, this.length + index);
+ },
+
+ /**
+ * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
+ *
+ * @param {Object} [args...]
+ * @api public
+ * @method push
+ * @receiver MongooseArray
+ */
+
+ push: function() {
+ var values = [].map.call(arguments, this._mapCast, this);
+ values = this._schema.applySetters(values, this._parent, undefined,
+ undefined, {skipDocumentArrayCast: true});
+ var ret = [].push.apply(this, values);
+
+ // $pushAll might be fibbed (could be $push). But it makes it easier to
+ // handle what could have been $push, $pushAll combos
+ this._registerAtomic('$pushAll', values);
+ this._markModified();
return ret;
- }
+ },
+
+ /**
+ * Pushes items to the array non-atomically.
+ *
+ * ####NOTE:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @param {any} [args...]
+ * @api public
+ * @method nonAtomicPush
+ * @receiver MongooseArray
+ */
+
+ nonAtomicPush: function() {
+ var values = [].map.call(arguments, this._mapCast, this);
+ var ret = [].push.apply(this, values);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Pops the array atomically at most one time per document `save()`.
+ *
+ * #### NOTE:
+ *
+ * _Calling this mulitple times on an array before saving sends the same command as calling it once._
+ * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._
+ *
+ * doc.array = [1,2,3];
+ *
+ * var popped = doc.array.$pop();
+ * console.log(popped); // 3
+ * console.log(doc.array); // [1,2]
+ *
+ * // no affect
+ * popped = doc.array.$pop();
+ * console.log(doc.array); // [1,2]
+ *
+ * doc.save(function (err) {
+ * if (err) return handleError(err);
+ *
+ * // we saved, now $pop works again
+ * popped = doc.array.$pop();
+ * console.log(popped); // 2
+ * console.log(doc.array); // [1]
+ * })
+ *
+ * @api public
+ * @method $pop
+ * @memberOf MongooseArray
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop
+ * @method $pop
+ * @receiver MongooseArray
+ */
+
+ $pop: function() {
+ this._registerAtomic('$pop', 1);
+ this._markModified();
- while (i--) {
- var op = keys[i];
- var val = this._atomics[op];
-
- // the atomic values which are arrays are not MongooseArrays. we
- // need to convert their elements as if they were MongooseArrays
- // to handle populated arrays versus DocumentArrays properly.
- if (isMongooseObject(val)) {
- val = val.toObject({ depopulate: 1 });
- } else if (Array.isArray(val)) {
- val = this.toObject.call(val, { depopulate: 1 });
- } else if (val.valueOf) {
- val = val.valueOf();
+ // only allow popping once
+ if (this._popped) {
+ return;
}
+ this._popped = true;
+
+ return [].pop.call(this);
+ },
+
+ /**
+ * Wraps [`Array#pop`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/pop) with proper change tracking.
+ *
+ * ####Note:
+ *
+ * _marks the entire array as modified which will pass the entire thing to $set potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @see MongooseArray#$pop #types_array_MongooseArray-%24pop
+ * @api public
+ * @method pop
+ * @receiver MongooseArray
+ */
+
+ pop: function() {
+ var ret = [].pop.call(this);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Atomically shifts the array at most one time per document `save()`.
+ *
+ * ####NOTE:
+ *
+ * _Calling this mulitple times on an array before saving sends the same command as calling it once._
+ * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._
+ *
+ * doc.array = [1,2,3];
+ *
+ * var shifted = doc.array.$shift();
+ * console.log(shifted); // 1
+ * console.log(doc.array); // [2,3]
+ *
+ * // no affect
+ * shifted = doc.array.$shift();
+ * console.log(doc.array); // [2,3]
+ *
+ * doc.save(function (err) {
+ * if (err) return handleError(err);
+ *
+ * // we saved, now $shift works again
+ * shifted = doc.array.$shift();
+ * console.log(shifted ); // 2
+ * console.log(doc.array); // [3]
+ * })
+ *
+ * @api public
+ * @memberOf MongooseArray
+ * @method $shift
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop
+ */
+
+ $shift: function $shift() {
+ this._registerAtomic('$pop', -1);
+ this._markModified();
- if ('$addToSet' == op) {
- val = { $each: val }
+ // only allow shifting once
+ if (this._shifted) {
+ return;
}
-
- ret.push([op, val]);
- }
-
- return ret;
-}
-
-/**
- * Returns the number of pending atomic operations to send to the db for this array.
- *
- * @api private
- * @return {Number}
- */
-
-MongooseArray.prototype.hasAtomics = function hasAtomics () {
- if (!(this._atomics && 'Object' === this._atomics.constructor.name)) {
- return 0;
- }
-
- return Object.keys(this._atomics).length;
-}
-
-/**
- * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
- *
- * @param {Object} [args...]
- * @api public
- */
-
-MongooseArray.prototype.push = function () {
- var values = [].map.call(arguments, this._cast, this)
- , ret = [].push.apply(this, values);
-
- // $pushAll might be fibbed (could be $push). But it makes it easier to
- // handle what could have been $push, $pushAll combos
- this._registerAtomic('$pushAll', values);
- this._markModified();
- return ret;
-};
-
-/**
- * Pushes items to the array non-atomically.
- *
- * ####NOTE:
- *
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
- *
- * @param {any} [args...]
- * @api public
- */
-
-MongooseArray.prototype.nonAtomicPush = function () {
- var values = [].map.call(arguments, this._cast, this)
- , ret = [].push.apply(this, values);
- this._registerAtomic('$set', this);
- this._markModified();
- return ret;
-};
-
-/**
- * Pops the array atomically at most one time per document `save()`.
- *
- * #### NOTE:
- *
- * _Calling this mulitple times on an array before saving sends the same command as calling it once._
- * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._
- *
- * doc.array = [1,2,3];
- *
- * var popped = doc.array.$pop();
- * console.log(popped); // 3
- * console.log(doc.array); // [1,2]
- *
- * // no affect
- * popped = doc.array.$pop();
- * console.log(doc.array); // [1,2]
- *
- * doc.save(function (err) {
- * if (err) return handleError(err);
- *
- * // we saved, now $pop works again
- * popped = doc.array.$pop();
- * console.log(popped); // 2
- * console.log(doc.array); // [1]
- * })
- *
- * @api public
- * @method $pop
- * @memberOf MongooseArray
- * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop
- */
-
-MongooseArray.prototype.$pop = function () {
- this._registerAtomic('$pop', 1);
- this._markModified();
-
- // only allow popping once
- if (this._popped) return;
- this._popped = true;
-
- return [].pop.call(this);
-};
-
-/**
- * Wraps [`Array#pop`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/pop) with proper change tracking.
- *
- * ####Note:
- *
- * _marks the entire array as modified which will pass the entire thing to $set potentially overwritting any changes that happen between when you retrieved the object and when you save it._
- *
- * @see MongooseArray#$pop #types_array_MongooseArray-%24pop
- * @api public
- */
-
-MongooseArray.prototype.pop = function () {
- var ret = [].pop.call(this);
- this._registerAtomic('$set', this);
- this._markModified();
- return ret;
-};
-
-/**
- * Atomically shifts the array at most one time per document `save()`.
- *
- * ####NOTE:
- *
- * _Calling this mulitple times on an array before saving sends the same command as calling it once._
- * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._
- *
- * doc.array = [1,2,3];
- *
- * var shifted = doc.array.$shift();
- * console.log(shifted); // 1
- * console.log(doc.array); // [2,3]
- *
- * // no affect
- * shifted = doc.array.$shift();
- * console.log(doc.array); // [2,3]
- *
- * doc.save(function (err) {
- * if (err) return handleError(err);
- *
- * // we saved, now $shift works again
- * shifted = doc.array.$shift();
- * console.log(shifted ); // 2
- * console.log(doc.array); // [3]
- * })
- *
- * @api public
- * @memberOf MongooseArray
- * @method $shift
- * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop
- */
-
-MongooseArray.prototype.$shift = function $shift () {
- this._registerAtomic('$pop', -1);
- this._markModified();
-
- // only allow shifting once
- if (this._shifted) return;
- this._shifted = true;
-
- return [].shift.call(this);
-};
-
-/**
- * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
- *
- * ####Example:
- *
- * doc.array = [2,3];
- * var res = doc.array.shift();
- * console.log(res) // 2
- * console.log(doc.array) // [3]
- *
- * ####Note:
- *
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
- *
- * @api public
- */
-
-MongooseArray.prototype.shift = function () {
- var ret = [].shift.call(this);
- this._registerAtomic('$set', this);
- this._markModified();
- return ret;
-};
-
-/**
- * Pulls items from the array atomically.
- *
- * ####Examples:
- *
- * doc.array.pull(ObjectId)
- * doc.array.pull({ _id: 'someId' })
- * doc.array.pull(36)
- * doc.array.pull('tag 1', 'tag 2')
- *
- * To remove a document from a subdocument array we may pass an object with a matching `_id`.
- *
- * doc.subdocs.push({ _id: 4815162342 })
- * doc.subdocs.pull({ _id: 4815162342 }) // removed
- *
- * Or we may passing the _id directly and let mongoose take care of it.
- *
- * doc.subdocs.push({ _id: 4815162342 })
- * doc.subdocs.pull(4815162342); // works
- *
- * @param {any} [args...]
- * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
- * @api public
- */
-
-MongooseArray.prototype.pull = function () {
- var values = [].map.call(arguments, this._cast, this)
- , cur = this._parent.get(this._path)
- , i = cur.length
- , mem;
-
- while (i--) {
- mem = cur[i];
- if (mem instanceof EmbeddedDocument) {
- if (values.some(function (v) { return v.equals(mem); } )) {
+ this._shifted = true;
+
+ return [].shift.call(this);
+ },
+
+ /**
+ * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
+ *
+ * ####Example:
+ *
+ * doc.array = [2,3];
+ * var res = doc.array.shift();
+ * console.log(res) // 2
+ * console.log(doc.array) // [3]
+ *
+ * ####Note:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @api public
+ * @method shift
+ * @receiver MongooseArray
+ */
+
+ shift: function() {
+ var ret = [].shift.call(this);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Pulls items from the array atomically. Equality is determined by casting
+ * the provided value to an embedded document and comparing using
+ * [the `Document.equals()` function.](./api.html#document_Document-equals)
+ *
+ * ####Examples:
+ *
+ * doc.array.pull(ObjectId)
+ * doc.array.pull({ _id: 'someId' })
+ * doc.array.pull(36)
+ * doc.array.pull('tag 1', 'tag 2')
+ *
+ * To remove a document from a subdocument array we may pass an object with a matching `_id`.
+ *
+ * doc.subdocs.push({ _id: 4815162342 })
+ * doc.subdocs.pull({ _id: 4815162342 }) // removed
+ *
+ * Or we may passing the _id directly and let mongoose take care of it.
+ *
+ * doc.subdocs.push({ _id: 4815162342 })
+ * doc.subdocs.pull(4815162342); // works
+ *
+ * The first pull call will result in a atomic operation on the database, if pull is called repeatedly without saving the document, a $set operation is used on the complete array instead, overwriting possible changes that happened on the database in the meantime.
+ *
+ * @param {any} [args...]
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
+ * @api public
+ * @method pull
+ * @receiver MongooseArray
+ */
+
+ pull: function() {
+ var values = [].map.call(arguments, this._cast, this),
+ cur = this._parent.get(this._path),
+ i = cur.length,
+ mem;
+
+ while (i--) {
+ mem = cur[i];
+ if (mem instanceof Document) {
+ var some = values.some(function(v) {
+ return mem.equals(v);
+ });
+ if (some) {
+ [].splice.call(cur, i, 1);
+ }
+ } else if (~cur.indexOf.call(values, mem)) {
[].splice.call(cur, i, 1);
}
- } else if (~cur.indexOf.call(values, mem)) {
- [].splice.call(cur, i, 1);
}
- }
-
- if (values[0] instanceof EmbeddedDocument) {
- this._registerAtomic('$pullDocs', values.map( function (v) { return v._id; } ));
- } else {
- this._registerAtomic('$pullAll', values);
- }
-
- this._markModified();
- return this;
-};
-
-/**
- * Alias of [pull](#types_array_MongooseArray-pull)
- *
- * @see MongooseArray#pull #types_array_MongooseArray-pull
- * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
- * @api public
- * @memberOf MongooseArray
- * @method remove
- */
-
-MongooseArray.prototype.remove = MongooseArray.prototype.pull;
-/**
- * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
- *
- * ####Note:
- *
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
- *
- * @api public
- */
+ if (values[0] instanceof EmbeddedDocument) {
+ this._registerAtomic('$pullDocs', values.map(function(v) {
+ return v._id || v;
+ }));
+ } else {
+ this._registerAtomic('$pullAll', values);
+ }
-MongooseArray.prototype.splice = function splice () {
- var ret, vals, i;
+ this._markModified();
+ return this;
+ },
+
+ /**
+ * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
+ *
+ * ####Note:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @api public
+ * @method splice
+ * @receiver MongooseArray
+ */
+
+ splice: function splice() {
+ var ret, vals, i;
- if (arguments.length) {
- vals = [];
- for (i = 0; i < arguments.length; ++i) {
- vals[i] = i < 2
- ? arguments[i]
- : this._cast(arguments[i]);
+ if (arguments.length) {
+ vals = [];
+ for (i = 0; i < arguments.length; ++i) {
+ vals[i] = i < 2
+ ? arguments[i]
+ : this._cast(arguments[i], arguments[0] + (i - 2));
+ }
+ ret = [].splice.apply(this, vals);
+ this._registerAtomic('$set', this);
+ this._markModified();
}
- ret = [].splice.apply(this, vals);
+
+ return ret;
+ },
+
+ /**
+ * Wraps [`Array#unshift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
+ *
+ * ####Note:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @api public
+ * @method unshift
+ * @receiver MongooseArray
+ */
+
+ unshift: function() {
+ var values = [].map.call(arguments, this._cast, this);
+ values = this._schema.applySetters(values, this._parent);
+ [].unshift.apply(this, values);
this._registerAtomic('$set', this);
this._markModified();
- }
-
- return ret;
-}
-
-/**
- * Wraps [`Array#unshift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
- *
- * ####Note:
- *
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
- *
- * @api public
- */
-
-MongooseArray.prototype.unshift = function () {
- var values = [].map.call(arguments, this._cast, this);
- [].unshift.apply(this, values);
- this._registerAtomic('$set', this);
- this._markModified();
- return this.length;
-};
-
-/**
- * Wraps [`Array#sort`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort) with proper change tracking.
- *
- * ####NOTE:
- *
- * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
- *
- * @api public
- */
-
-MongooseArray.prototype.sort = function () {
- var ret = [].sort.apply(this, arguments);
- this._registerAtomic('$set', this);
- this._markModified();
- return ret;
-}
+ return this.length;
+ },
+
+ /**
+ * Wraps [`Array#sort`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort) with proper change tracking.
+ *
+ * ####NOTE:
+ *
+ * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._
+ *
+ * @api public
+ * @method sort
+ * @receiver MongooseArray
+ */
+
+ sort: function() {
+ var ret = [].sort.apply(this, arguments);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
+
+ /**
+ * Adds values to the array if not already present.
+ *
+ * ####Example:
+ *
+ * console.log(doc.array) // [2,3,4]
+ * var added = doc.array.addToSet(4,5);
+ * console.log(doc.array) // [2,3,4,5]
+ * console.log(added) // [5]
+ *
+ * @param {any} [args...]
+ * @return {Array} the values that were added
+ * @receiver MongooseArray
+ * @api public
+ * @method addToSet
+ */
+
+ addToSet: function addToSet() {
+ var values = [].map.call(arguments, this._mapCast, this);
+ values = this._schema.applySetters(values, this._parent);
+ var added = [];
+ var type = '';
+ if (values[0] instanceof EmbeddedDocument) {
+ type = 'doc';
+ } else if (values[0] instanceof Date) {
+ type = 'date';
+ }
-/**
- * Adds values to the array if not already present.
- *
- * ####Example:
- *
- * console.log(doc.array) // [2,3,4]
- * var added = doc.array.addToSet(4,5);
- * console.log(doc.array) // [2,3,4,5]
- * console.log(added) // [5]
- *
- * @param {any} [args...]
- * @return {Array} the values that were added
- * @api public
- */
+ values.forEach(function(v) {
+ var found;
+ switch (type) {
+ case 'doc':
+ found = this.some(function(doc) {
+ return doc.equals(v);
+ });
+ break;
+ case 'date':
+ var val = +v;
+ found = this.some(function(d) {
+ return +d === val;
+ });
+ break;
+ default:
+ found = ~this.indexOf(v);
+ }
-MongooseArray.prototype.addToSet = function addToSet () {
- var values = [].map.call(arguments, this._cast, this)
- , added = []
- , type = values[0] instanceof EmbeddedDocument ? 'doc' :
- values[0] instanceof Date ? 'date' :
- '';
-
- values.forEach(function (v) {
- var found;
- switch (type) {
- case 'doc':
- found = this.some(function(doc){ return doc.equals(v) });
- break;
- case 'date':
- var val = +v;
- found = this.some(function(d){ return +d === val });
- break;
- default:
- found = ~this.indexOf(v);
+ if (!found) {
+ [].push.call(this, v);
+ this._registerAtomic('$addToSet', v);
+ this._markModified();
+ [].push.call(added, v);
+ }
+ }, this);
+
+ return added;
+ },
+
+ /**
+ * Sets the casted `val` at index `i` and marks the array modified.
+ *
+ * ####Example:
+ *
+ * // given documents based on the following
+ * var Doc = mongoose.model('Doc', new Schema({ array: [Number] }));
+ *
+ * var doc = new Doc({ array: [2,3,4] })
+ *
+ * console.log(doc.array) // [2,3,4]
+ *
+ * doc.array.set(1,"5");
+ * console.log(doc.array); // [2,5,4] // properly cast to number
+ * doc.save() // the change is saved
+ *
+ * // VS not using array#set
+ * doc.array[1] = "5";
+ * console.log(doc.array); // [2,"5",4] // no casting
+ * doc.save() // change is not saved
+ *
+ * @return {Array} this
+ * @api public
+ * @method set
+ * @receiver MongooseArray
+ */
+
+ set: function set(i, val) {
+ var value = this._cast(val, i);
+ value = this._schema.caster instanceof EmbeddedDocument ?
+ value :
+ this._schema.caster.applySetters(val, this._parent)
+ ;
+ this[i] = value;
+ this._markModified(i);
+ return this;
+ },
+
+ /**
+ * Returns a native js Array.
+ *
+ * @param {Object} options
+ * @return {Array}
+ * @api public
+ * @method toObject
+ * @receiver MongooseArray
+ */
+
+ toObject: function(options) {
+ if (options && options.depopulate) {
+ return this.map(function(doc) {
+ return doc instanceof Document
+ ? doc.toObject(options)
+ : doc;
+ });
}
- if (!found) {
- [].push.call(this, v);
- this._registerAtomic('$addToSet', v);
- this._markModified();
- [].push.call(added, v);
+ return this.slice();
+ },
+
+ /**
+ * Helper for console.log
+ *
+ * @api public
+ * @method inspect
+ * @receiver MongooseArray
+ */
+
+ inspect: function() {
+ return JSON.stringify(this);
+ },
+
+ /**
+ * Return the index of `obj` or `-1` if not found.
+ *
+ * @param {Object} obj the item to look for
+ * @return {Number}
+ * @api public
+ * @method indexOf
+ * @receiver MongooseArray
+ */
+
+ indexOf: function indexOf(obj) {
+ if (obj instanceof ObjectId) {
+ obj = obj.toString();
}
- }, this);
-
- return added;
-};
-
-/**
- * Sets the casted `val` at index `i` and marks the array modified.
- *
- * ####Example:
- *
- * // given documents based on the following
- * var Doc = mongoose.model('Doc', new Schema({ array: [Number] }));
- *
- * var doc = new Doc({ array: [2,3,4] })
- *
- * console.log(doc.array) // [2,3,4]
- *
- * doc.array.set(1,"5");
- * console.log(doc.array); // [2,5,4] // properly cast to number
- * doc.save() // the change is saved
- *
- * // VS not using array#set
- * doc.array[1] = "5";
- * console.log(doc.array); // [2,"5",4] // no casting
- * doc.save() // change is not saved
- *
- * @return {Array} this
- * @api public
- */
-
-MongooseArray.prototype.set = function set (i, val) {
- this[i] = this._cast(val);
- this._markModified(i);
- return this;
-}
-
-/**
- * Returns a native js Array.
- *
- * @param {Object} options
- * @return {Array}
- * @api public
- */
-
-MongooseArray.prototype.toObject = function (options) {
- if (options && options.depopulate) {
- return this.map(function (doc) {
- return doc instanceof Document
- ? doc.toObject(options)
- : doc
- });
+ for (var i = 0, len = this.length; i < len; ++i) {
+ if (obj == this[i]) {
+ return i;
+ }
+ }
+ return -1;
}
-
- return this.slice();
-}
-
-/**
- * Helper for console.log
- *
- * @api public
- */
-
-MongooseArray.prototype.inspect = function () {
- return '[' + this.map(function (doc) {
- return ' ' + doc;
- }) + ' ]';
};
/**
- * Return the index of `obj` or `-1` if not found.
+ * Alias of [pull](#types_array_MongooseArray-pull)
*
- * @param {Object} obj the item to look for
- * @return {Number}
+ * @see MongooseArray#pull #types_array_MongooseArray-pull
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
* @api public
+ * @memberOf MongooseArray
+ * @method remove
*/
-MongooseArray.prototype.indexOf = function indexOf (obj) {
- if (obj instanceof ObjectId) obj = obj.toString();
- for (var i = 0, len = this.length; i < len; ++i) {
- if (obj == this[i])
- return i;
- }
- return -1;
-};
+MongooseArray.mixin.remove = MongooseArray.mixin.pull;
/*!
* Module exports.
diff --git a/node_modules/mongoose/lib/types/buffer.js b/node_modules/mongoose/lib/types/buffer.js
index b409772..8be67c8 100644
--- a/node_modules/mongoose/lib/types/buffer.js
+++ b/node_modules/mongoose/lib/types/buffer.js
@@ -1,15 +1,9 @@
-
-/*!
- * Access driver.
- */
-
-var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native';
-
/*!
* Module dependencies.
*/
-var Binary = require(driver + '/binary');
+var Binary = require('../drivers').Binary,
+ utils = require('../utils');
/**
* Mongoose Buffer constructor.
@@ -24,11 +18,11 @@ var Binary = require(driver + '/binary');
* @see http://bit.ly/f6CnZU
*/
-function MongooseBuffer (value, encode, offset) {
+function MongooseBuffer(value, encode, offset) {
var length = arguments.length;
var val;
- if (0 === length || null === arguments[0] || undefined === arguments[0]) {
+ if (length === 0 || arguments[0] === null || arguments[0] === undefined) {
val = 0;
} else {
val = value;
@@ -47,111 +41,136 @@ function MongooseBuffer (value, encode, offset) {
}
var buf = new Buffer(val, encoding, offset);
- buf.__proto__ = MongooseBuffer.prototype;
+ utils.decorate(buf, MongooseBuffer.mixin);
+ buf.isMongooseBuffer = true;
// make sure these internal props don't show up in Object.keys()
Object.defineProperties(buf, {
- validators: { value: [] }
- , _path: { value: path }
- , _parent: { value: doc }
+ validators: {value: []},
+ _path: {value: path},
+ _parent: {value: doc}
});
- if (doc && "string" === typeof path) {
+ if (doc && typeof path === 'string') {
Object.defineProperty(buf, '_schema', {
- value: doc.schema.path(path)
+ value: doc.schema.path(path)
});
}
buf._subtype = 0;
return buf;
-};
+}
/*!
* Inherit from Buffer.
*/
-MongooseBuffer.prototype = new Buffer(0);
-
-/**
- * Parent owner document
- *
- * @api private
- * @property _parent
- */
-
-MongooseBuffer.prototype._parent;
-
-/**
- * Marks this buffer as modified.
- *
- * @api private
- */
-
-MongooseBuffer.prototype._markModified = function () {
- var parent = this._parent;
-
- if (parent) {
- parent.markModified(this._path);
+// MongooseBuffer.prototype = new Buffer(0);
+
+MongooseBuffer.mixin = {
+
+ /**
+ * Parent owner document
+ *
+ * @api private
+ * @property _parent
+ * @receiver MongooseBuffer
+ */
+
+ _parent: undefined,
+
+ /**
+ * Default subtype for the Binary representing this Buffer
+ *
+ * @api private
+ * @property _subtype
+ * @receiver MongooseBuffer
+ */
+
+ _subtype: undefined,
+
+ /**
+ * Marks this buffer as modified.
+ *
+ * @api private
+ * @method _markModified
+ * @receiver MongooseBuffer
+ */
+
+ _markModified: function() {
+ var parent = this._parent;
+
+ if (parent) {
+ parent.markModified(this._path);
+ }
+ return this;
+ },
+
+ /**
+ * Writes the buffer.
+ *
+ * @api public
+ * @method write
+ * @receiver MongooseBuffer
+ */
+
+ write: function() {
+ var written = Buffer.prototype.write.apply(this, arguments);
+
+ if (written > 0) {
+ this._markModified();
+ }
+
+ return written;
+ },
+
+ /**
+ * Copies the buffer.
+ *
+ * ####Note:
+ *
+ * `Buffer#copy` does not mark `target` as modified so you must copy from a `MongooseBuffer` for it to work as expected. This is a work around since `copy` modifies the target, not this.
+ *
+ * @return {Number} The number of bytes copied.
+ * @param {Buffer} target
+ * @method copy
+ * @receiver MongooseBuffer
+ */
+
+ copy: function(target) {
+ var ret = Buffer.prototype.copy.apply(this, arguments);
+
+ if (target && target.isMongooseBuffer) {
+ target._markModified();
+ }
+
+ return ret;
}
- return this;
-};
-
-/**
-* Writes the buffer.
-*/
-
-MongooseBuffer.prototype.write = function () {
- var written = Buffer.prototype.write.apply(this, arguments);
-
- if (written > 0) {
- this._markModified();
- }
-
- return written;
-};
-
-/**
- * Copies the buffer.
- *
- * ####Note:
- *
- * `Buffer#copy` does not mark `target` as modified so you must copy from a `MongooseBuffer` for it to work as expected. This is a work around since `copy` modifies the target, not this.
- *
- * @return {MongooseBuffer}
- * @param {Buffer} target
- */
-
-MongooseBuffer.prototype.copy = function (target) {
- var ret = Buffer.prototype.copy.apply(this, arguments);
-
- if (target instanceof MongooseBuffer) {
- target._markModified();
- }
-
- return ret;
};
/*!
* Compile other Buffer methods marking this buffer as modified.
*/
-;(
+(
// node < 0.5
-'writeUInt8 writeUInt16 writeUInt32 writeInt8 writeInt16 writeInt32 ' +
-'writeFloat writeDouble fill ' +
-'utf8Write binaryWrite asciiWrite set ' +
+ 'writeUInt8 writeUInt16 writeUInt32 writeInt8 writeInt16 writeInt32 ' +
+ 'writeFloat writeDouble fill ' +
+ 'utf8Write binaryWrite asciiWrite set ' +
// node >= 0.5
-'writeUInt16LE writeUInt16BE writeUInt32LE writeUInt32BE ' +
-'writeInt16LE writeInt16BE writeInt32LE writeInt32BE ' +
-'writeFloatLE writeFloatBE writeDoubleLE writeDoubleBE'
-).split(' ').forEach(function (method) {
- if (!Buffer.prototype[method]) return;
- MongooseBuffer.prototype[method] = new Function(
- 'var ret = Buffer.prototype.'+method+'.apply(this, arguments);' +
- 'this._markModified();' +
- 'return ret;'
- )
+ 'writeUInt16LE writeUInt16BE writeUInt32LE writeUInt32BE ' +
+ 'writeInt16LE writeInt16BE writeInt32LE writeInt32BE ' +
+ 'writeFloatLE writeFloatBE writeDoubleLE writeDoubleBE'
+).split(' ').forEach(function(method) {
+ if (!Buffer.prototype[method]) {
+ return;
+ }
+ MongooseBuffer.mixin[method] = function() {
+ var ret = Buffer.prototype[method].apply(this, arguments);
+ this._markModified();
+ return ret;
+ };
});
/**
@@ -173,12 +192,14 @@ MongooseBuffer.prototype.copy = function (target) {
* @param {Hex} [subtype]
* @return {Binary}
* @api public
+ * @method toObject
+ * @receiver MongooseBuffer
*/
-MongooseBuffer.prototype.toObject = function (options) {
- var subtype = 'number' == typeof options
- ? options
- : (this._subtype || 0x00);
+MongooseBuffer.mixin.toObject = function(options) {
+ var subtype = typeof options === 'number'
+ ? options
+ : (this._subtype || 0);
return new Binary(this, subtype);
};
@@ -187,9 +208,11 @@ MongooseBuffer.prototype.toObject = function (options) {
*
* @param {Buffer} other
* @return {Boolean}
+ * @method equals
+ * @receiver MongooseBuffer
*/
-MongooseBuffer.prototype.equals = function (other) {
+MongooseBuffer.mixin.equals = function(other) {
if (!Buffer.isBuffer(other)) {
return false;
}
@@ -199,11 +222,47 @@ MongooseBuffer.prototype.equals = function (other) {
}
for (var i = 0; i < this.length; ++i) {
- if (this[i] !== other[i]) return false;
+ if (this[i] !== other[i]) {
+ return false;
+ }
}
return true;
-}
+};
+
+/**
+ * Sets the subtype option and marks the buffer modified.
+ *
+ * ####SubTypes:
+ *
+ * var bson = require('bson')
+ * bson.BSON_BINARY_SUBTYPE_DEFAULT
+ * bson.BSON_BINARY_SUBTYPE_FUNCTION
+ * bson.BSON_BINARY_SUBTYPE_BYTE_ARRAY
+ * bson.BSON_BINARY_SUBTYPE_UUID
+ * bson.BSON_BINARY_SUBTYPE_MD5
+ * bson.BSON_BINARY_SUBTYPE_USER_DEFINED
+ *
+ * doc.buffer.subtype(bson.BSON_BINARY_SUBTYPE_UUID);
+ *
+ * @see http://bsonspec.org/#/specification
+ * @param {Hex} subtype
+ * @api public
+ * @method subtype
+ * @receiver MongooseBuffer
+ */
+
+MongooseBuffer.mixin.subtype = function(subtype) {
+ if (typeof subtype !== 'number') {
+ throw new TypeError('Invalid subtype. Expected a number');
+ }
+
+ if (this._subtype !== subtype) {
+ this._markModified();
+ }
+
+ this._subtype = subtype;
+};
/*!
* Module exports.
diff --git a/node_modules/mongoose/lib/types/documentarray.js b/node_modules/mongoose/lib/types/documentarray.js
index 0cbe6ca..f18bde1 100644
--- a/node_modules/mongoose/lib/types/documentarray.js
+++ b/node_modules/mongoose/lib/types/documentarray.js
@@ -1,15 +1,12 @@
-
/*!
* Module dependencies.
*/
-var MongooseArray = require('./array')
- , driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native'
- , ObjectId = require(driver + '/objectid')
- , ObjectIdSchema = require('../schema/objectid')
- , utils = require('../utils')
- , util = require('util')
- , Document = require('../document')
+var MongooseArray = require('./array'),
+ ObjectId = require('./objectid'),
+ ObjectIdSchema = require('../schema/objectid'),
+ utils = require('../utils'),
+ Document = require('../document');
/**
* DocumentArray constructor
@@ -23,167 +20,228 @@ var MongooseArray = require('./array')
* @see http://bit.ly/f6CnZU
*/
-function MongooseDocumentArray (values, path, doc) {
- var arr = [];
+function MongooseDocumentArray(values, path, doc) {
+ var arr = [].concat(values);
+ arr._path = path;
+
+ var props = {
+ isMongooseArray: true,
+ isMongooseDocumentArray: true,
+ validators: [],
+ _atomics: {},
+ _schema: void 0,
+ _handlers: void 0
+ };
// Values always have to be passed to the constructor to initialize, since
// otherwise MongooseArray#push will mark the array as modified to the parent.
- arr.push.apply(arr, values);
- arr.__proto__ = MongooseDocumentArray.prototype;
+ var keysMA = Object.keys(MongooseArray.mixin);
+ var numKeys = keysMA.length;
+ for (var j = 0; j < numKeys; ++j) {
+ arr[keysMA[j]] = MongooseArray.mixin[keysMA[j]];
+ }
- arr._atomics = {};
- arr.validators = [];
- arr._path = path;
+ var keysMDA = Object.keys(MongooseDocumentArray.mixin);
+ numKeys = keysMDA.length;
+ for (var i = 0; i < numKeys; ++i) {
+ arr[keysMDA[i]] = MongooseDocumentArray.mixin[keysMDA[i]];
+ }
+
+ var keysP = Object.keys(props);
+ numKeys = keysP.length;
+ for (var k = 0; k < numKeys; ++k) {
+ arr[keysP[k]] = props[keysP[k]];
+ }
- if (doc) {
+ // Because doc comes from the context of another function, doc === global
+ // can happen if there was a null somewhere up the chain (see #3020 && #3034)
+ // RB Jun 17, 2015 updated to check for presence of expected paths instead
+ // to make more proof against unusual node environments
+ if (doc && doc instanceof Document) {
arr._parent = doc;
arr._schema = doc.schema.path(path);
- doc.on('save', arr.notify('save'));
- doc.on('isNew', arr.notify('isNew'));
+ arr._handlers = {
+ isNew: arr.notify('isNew'),
+ save: arr.notify('save')
+ };
+
+ doc.on('save', arr._handlers.save);
+ doc.on('isNew', arr._handlers.isNew);
}
return arr;
-};
+}
/*!
* Inherits from MongooseArray
*/
-
-MongooseDocumentArray.prototype.__proto__ = MongooseArray.prototype;
-
-/**
- * Overrides MongooseArray#cast
- *
- * @api private
- */
-
-MongooseDocumentArray.prototype._cast = function (value) {
- if (value instanceof this._schema.casterConstructor) {
- if (!(value.__parent && value.__parentArray)) {
- // value may have been created using array.create()
- value.__parent = this._parent;
- value.__parentArray = this;
+// MongooseDocumentArray.mixin = Object.create( MongooseArray.mixin );
+MongooseDocumentArray.mixin = {
+
+ /**
+ * Overrides MongooseArray#cast
+ *
+ * @method _cast
+ * @api private
+ * @receiver MongooseDocumentArray
+ */
+
+ _cast: function(value, index) {
+ if (value instanceof this._schema.casterConstructor) {
+ if (!(value.__parent && value.__parentArray)) {
+ // value may have been created using array.create()
+ value.__parent = this._parent;
+ value.__parentArray = this;
+ }
+ value.__index = index;
+ return value;
}
- return value;
- }
-
- // handle cast('string') or cast(ObjectId) etc.
- // only objects are permitted so we can safely assume that
- // non-objects are to be interpreted as _id
- if (Buffer.isBuffer(value) ||
- value instanceof ObjectId || !utils.isObject(value)) {
- value = { _id: value };
- }
-
- return new this._schema.casterConstructor(value, this);
-};
-
-/**
- * Searches array items for the first document with a matching _id.
- *
- * ####Example:
- *
- * var embeddedDoc = m.array.id(some_id);
- *
- * @return {EmbeddedDocument|null} the subdocuent or null if not found.
- * @param {ObjectId|String|Number|Buffer} id
- * @api public
- */
-MongooseDocumentArray.prototype.id = function (id) {
- var casted
- , sid
- , _id
-
- try {
- casted = ObjectId.toString(ObjectIdSchema.prototype.cast.call({}, id));
- } catch (e) {
- casted = null;
- }
-
- for (var i = 0, l = this.length; i < l; i++) {
- _id = this[i].get('_id');
-
- if (_id instanceof Document) {
- sid || (sid = String(id));
- if (sid == _id._id) return this[i];
- } else if (!(_id instanceof ObjectId)) {
- sid || (sid = String(id));
- if (sid == _id) return this[i];
- } else if (casted == _id) {
- return this[i];
+ if (value === undefined || value === null) {
+ return null;
}
- }
- return null;
-};
-
-/**
- * Returns a native js Array of plain js objects
- *
- * ####NOTE:
- *
- * _Each sub-document is converted to a plain object by calling its `#toObject` method._
- *
- * @param {Object} [options] optional options to pass to each documents `toObject` method call during conversion
- * @return {Array}
- * @api public
- */
-
-MongooseDocumentArray.prototype.toObject = function (options) {
- return this.map(function (doc) {
- return doc && doc.toObject(options) || null;
- });
-};
-
-/**
- * Helper for console.log
- *
- * @api public
- */
-
-MongooseDocumentArray.prototype.inspect = function () {
- return '[' + this.map(function (doc) {
- if (doc) {
- return doc.inspect
- ? doc.inspect()
- : util.inspect(doc)
+ // handle cast('string') or cast(ObjectId) etc.
+ // only objects are permitted so we can safely assume that
+ // non-objects are to be interpreted as _id
+ if (Buffer.isBuffer(value) ||
+ value instanceof ObjectId || !utils.isObject(value)) {
+ value = {_id: value};
+ }
+ return new this._schema.casterConstructor(value, this, undefined, undefined, index);
+ },
+
+ /**
+ * Searches array items for the first document with a matching _id.
+ *
+ * ####Example:
+ *
+ * var embeddedDoc = m.array.id(some_id);
+ *
+ * @return {EmbeddedDocument|null} the subdocument or null if not found.
+ * @param {ObjectId|String|Number|Buffer} id
+ * @TODO cast to the _id based on schema for proper comparison
+ * @method id
+ * @api public
+ * @receiver MongooseDocumentArray
+ */
+
+ id: function(id) {
+ var casted,
+ sid,
+ _id;
+
+ try {
+ var casted_ = ObjectIdSchema.prototype.cast.call({}, id);
+ if (casted_) {
+ casted = String(casted_);
+ }
+ } catch (e) {
+ casted = null;
}
- return 'null'
- }).join('\n') + ']';
-};
-
-/**
- * Creates a subdocument casted to this schema.
- *
- * This is the same subdocument constructor used for casting.
- *
- * @param {Object} obj the value to cast to this arrays SubDocument schema
- * @api public
- */
-
-MongooseDocumentArray.prototype.create = function (obj) {
- return new this._schema.casterConstructor(obj);
-}
-
-/**
- * Creates a fn that notifies all child docs of `event`.
- *
- * @param {String} event
- * @return {Function}
- * @api private
- */
-MongooseDocumentArray.prototype.notify = function notify (event) {
- var self = this;
- return function notify (val) {
- var i = self.length;
- while (i--) {
- if (!self[i]) continue;
- self[i].emit(event, val);
+ for (var i = 0, l = this.length; i < l; i++) {
+ _id = this[i].get('_id');
+
+ if (_id === null || typeof _id === 'undefined') {
+ continue;
+ } else if (_id instanceof Document) {
+ sid || (sid = String(id));
+ if (sid == _id._id) {
+ return this[i];
+ }
+ } else if (!(id instanceof ObjectId) && !(_id instanceof ObjectId)) {
+ if (utils.deepEqual(id, _id)) {
+ return this[i];
+ }
+ } else if (casted == _id) {
+ return this[i];
+ }
}
+
+ return null;
+ },
+
+ /**
+ * Returns a native js Array of plain js objects
+ *
+ * ####NOTE:
+ *
+ * _Each sub-document is converted to a plain object by calling its `#toObject` method._
+ *
+ * @param {Object} [options] optional options to pass to each documents `toObject` method call during conversion
+ * @return {Array}
+ * @method toObject
+ * @api public
+ * @receiver MongooseDocumentArray
+ */
+
+ toObject: function(options) {
+ return this.map(function(doc) {
+ return doc && doc.toObject(options) || null;
+ });
+ },
+
+ /**
+ * Helper for console.log
+ *
+ * @method inspect
+ * @api public
+ * @receiver MongooseDocumentArray
+ */
+
+ inspect: function() {
+ return Array.prototype.slice.call(this);
+ },
+
+ /**
+ * Creates a subdocument casted to this schema.
+ *
+ * This is the same subdocument constructor used for casting.
+ *
+ * @param {Object} obj the value to cast to this arrays SubDocument schema
+ * @method create
+ * @api public
+ * @receiver MongooseDocumentArray
+ */
+
+ create: function(obj) {
+ return new this._schema.casterConstructor(obj);
+ },
+
+ /**
+ * Creates a fn that notifies all child docs of `event`.
+ *
+ * @param {String} event
+ * @return {Function}
+ * @method notify
+ * @api private
+ * @receiver MongooseDocumentArray
+ */
+
+ notify: function notify(event) {
+ var _this = this;
+ return function notify(val) {
+ var i = _this.length;
+ while (i--) {
+ if (!_this[i]) {
+ continue;
+ }
+ switch (event) {
+ // only swap for save event for now, we may change this to all event types later
+ case 'save':
+ val = _this[i];
+ break;
+ default:
+ // NO-OP
+ break;
+ }
+ _this[i].emit(event, val);
+ }
+ };
}
-}
+
+};
/*!
* Module exports.
diff --git a/node_modules/mongoose/lib/types/embedded.js b/node_modules/mongoose/lib/types/embedded.js
index 6de36e6..811e932 100644
--- a/node_modules/mongoose/lib/types/embedded.js
+++ b/node_modules/mongoose/lib/types/embedded.js
@@ -1,9 +1,11 @@
+/* eslint no-func-assign: 1 */
+
/*!
* Module dependencies.
*/
-var Document = require('../document')
- , inspect = require('util').inspect;
+var Document = require('../document_provider')();
+var PromiseProvider = require('../promise_provider');
/**
* EmbeddedDocument constructor.
@@ -15,7 +17,7 @@ var Document = require('../document')
* @api private
*/
-function EmbeddedDocument (obj, parentArr, skipId, fields) {
+function EmbeddedDocument(obj, parentArr, skipId, fields, index) {
if (parentArr) {
this.__parentArray = parentArr;
this.__parent = parentArr._parent;
@@ -23,20 +25,21 @@ function EmbeddedDocument (obj, parentArr, skipId, fields) {
this.__parentArray = undefined;
this.__parent = undefined;
}
+ this.__index = index;
Document.call(this, obj, fields, skipId);
- var self = this;
- this.on('isNew', function (val) {
- self.isNew = val;
+ var _this = this;
+ this.on('isNew', function(val) {
+ _this.isNew = val;
});
-};
+}
/*!
* Inherit from Document
*/
-
-EmbeddedDocument.prototype.__proto__ = Document.prototype;
+EmbeddedDocument.prototype = Object.create(Document.prototype);
+EmbeddedDocument.prototype.constructor = EmbeddedDocument;
/**
* Marks the embedded doc modified.
@@ -49,20 +52,23 @@ EmbeddedDocument.prototype.__proto__ = Document.prototype;
*
* @param {String} path the path which changed
* @api public
+ * @receiver EmbeddedDocument
*/
-EmbeddedDocument.prototype.markModified = function (path) {
- if (!this.__parentArray) return;
-
+EmbeddedDocument.prototype.markModified = function(path) {
this.$__.activePaths.modify(path);
+ if (!this.__parentArray) {
+ return;
+ }
if (this.isNew) {
// Mark the WHOLE parent array as modified
// if this is a new document (i.e., we are initializing
// a document),
this.__parentArray._markModified();
- } else
+ } else {
this.__parentArray._markModified(this, path);
+ }
};
/**
@@ -73,39 +79,73 @@ EmbeddedDocument.prototype.markModified = function (path) {
* _This is a no-op. Does not actually save the doc to the db._
*
* @param {Function} [fn]
- * @return {EmbeddedDocument} this
+ * @return {Promise} resolved Promise
* @api private
*/
EmbeddedDocument.prototype.save = function(fn) {
- if (fn)
- fn(null);
- return this;
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve) {
+ fn && fn();
+ resolve();
+ });
};
+/*!
+ * Registers remove event listeners for triggering
+ * on subdocuments.
+ *
+ * @param {EmbeddedDocument} sub
+ * @api private
+ */
+
+function registerRemoveListener(sub) {
+ var owner = sub.ownerDocument();
+
+ function emitRemove() {
+ owner.removeListener('save', emitRemove);
+ owner.removeListener('remove', emitRemove);
+ sub.emit('remove', sub);
+ owner = sub = null;
+ }
+
+ owner.on('save', emitRemove);
+ owner.on('remove', emitRemove);
+}
+
/**
* Removes the subdocument from its parent array.
*
+ * @param {Object} [options]
* @param {Function} [fn]
* @api public
*/
-EmbeddedDocument.prototype.remove = function (fn) {
- if (!this.__parentArray) return this;
+EmbeddedDocument.prototype.remove = function(options, fn) {
+ if ( typeof options === 'function' && !fn ) {
+ fn = options;
+ options = undefined;
+ }
+ if (!this.__parentArray || (options && options.noop)) {
+ fn && fn(null);
+ return this;
+ }
var _id;
if (!this.willRemove) {
_id = this._doc._id;
if (!_id) {
throw new Error('For your own good, Mongoose does not know ' +
- 'how to remove an EmbeddedDocument that has no _id');
+ 'how to remove an EmbeddedDocument that has no _id');
}
- this.__parentArray.pull({ _id: _id });
+ this.__parentArray.pull({_id: _id});
this.willRemove = true;
+ registerRemoveListener(this);
}
- if (fn)
+ if (fn) {
fn(null);
+ }
return this;
};
@@ -115,9 +155,9 @@ EmbeddedDocument.prototype.remove = function (fn) {
* @api private
*/
-EmbeddedDocument.prototype.update = function () {
+EmbeddedDocument.prototype.update = function() {
throw new Error('The #update method is not available on EmbeddedDocuments');
-}
+};
/**
* Helper for console.log
@@ -125,8 +165,8 @@ EmbeddedDocument.prototype.update = function () {
* @api public
*/
-EmbeddedDocument.prototype.inspect = function () {
- return inspect(this.toObject());
+EmbeddedDocument.prototype.inspect = function() {
+ return this.toObject();
};
/**
@@ -138,29 +178,65 @@ EmbeddedDocument.prototype.inspect = function () {
* @api public
*/
-EmbeddedDocument.prototype.invalidate = function (path, err, val, first) {
+EmbeddedDocument.prototype.invalidate = function(path, err, val, first) {
if (!this.__parent) {
- var msg = 'Unable to invalidate a subdocument that has not been added to an array.'
- throw new Error(msg);
+ throw err;
}
- var index = this.__parentArray.indexOf(this);
- var parentPath = this.__parentArray._path;
- var fullPath = [parentPath, index, path].join('.');
-
- // sniffing arguments:
- // need to check if user passed a value to keep
- // our error message clean.
- if (2 < arguments.length) {
+ var index = this.__index;
+ if (typeof index !== 'undefined') {
+ var parentPath = this.__parentArray._path;
+ var fullPath = [parentPath, index, path].join('.');
this.__parent.invalidate(fullPath, err, val);
- } else {
- this.__parent.invalidate(fullPath, err);
}
- if (first)
+ if (first) {
this.$__.validationError = this.ownerDocument().$__.validationError;
+ }
+
return true;
-}
+};
+
+/**
+ * Marks a path as valid, removing existing validation errors.
+ *
+ * @param {String} path the field to mark as valid
+ * @api private
+ * @method $markValid
+ * @receiver EmbeddedDocument
+ */
+
+EmbeddedDocument.prototype.$markValid = function(path) {
+ if (!this.__parent) {
+ return;
+ }
+
+ var index = this.__index;
+ if (typeof index !== 'undefined') {
+ var parentPath = this.__parentArray._path;
+ var fullPath = [parentPath, index, path].join('.');
+ this.__parent.$markValid(fullPath);
+ }
+};
+
+/**
+ * Checks if a path is invalid
+ *
+ * @param {String} path the field to check
+ * @api private
+ * @method $isValid
+ * @receiver EmbeddedDocument
+ */
+
+EmbeddedDocument.prototype.$isValid = function(path) {
+ var index = this.__index;
+ if (typeof index !== 'undefined') {
+ return !this.__parent.$__.validationError ||
+ !this.__parent.$__.validationError.errors[this.$__fullPath(path)];
+ }
+
+ return true;
+};
/**
* Returns the top level document of this sub-document.
@@ -168,20 +244,23 @@ EmbeddedDocument.prototype.invalidate = function (path, err, val, first) {
* @return {Document}
*/
-EmbeddedDocument.prototype.ownerDocument = function () {
+EmbeddedDocument.prototype.ownerDocument = function() {
if (this.$__.ownerDocument) {
return this.$__.ownerDocument;
}
var parent = this.__parent;
- if (!parent) return this;
+ if (!parent) {
+ return this;
+ }
- while (parent.__parent) {
- parent = parent.__parent;
+ while (parent.__parent || parent.$parent) {
+ parent = parent.__parent || parent.$parent;
}
- return this.$__.ownerDocument = parent;
-}
+ this.$__.ownerDocument = parent;
+ return this.$__.ownerDocument;
+};
/**
* Returns the full path to this document. If optional `path` is passed, it is appended to the full path.
@@ -193,15 +272,21 @@ EmbeddedDocument.prototype.ownerDocument = function () {
* @memberOf EmbeddedDocument
*/
-EmbeddedDocument.prototype.$__fullPath = function (path) {
+EmbeddedDocument.prototype.$__fullPath = function(path) {
if (!this.$__.fullPath) {
- var parent = this;
- if (!parent.__parent) return path;
+ var parent = this; // eslint-disable-line consistent-this
+ if (!parent.__parent) {
+ return path;
+ }
var paths = [];
- while (parent.__parent) {
- paths.unshift(parent.__parentArray._path);
- parent = parent.__parent;
+ while (parent.__parent || parent.$parent) {
+ if (parent.__parent) {
+ paths.unshift(parent.__parentArray._path);
+ } else {
+ paths.unshift(parent.$basePath);
+ }
+ parent = parent.__parent || parent.$parent;
}
this.$__.fullPath = paths.join('.');
@@ -213,9 +298,9 @@ EmbeddedDocument.prototype.$__fullPath = function (path) {
}
return path
- ? this.$__.fullPath + '.' + path
- : this.$__.fullPath;
-}
+ ? this.$__.fullPath + '.' + path
+ : this.$__.fullPath;
+};
/**
* Returns this sub-documents parent document.
@@ -223,9 +308,9 @@ EmbeddedDocument.prototype.$__fullPath = function (path) {
* @api public
*/
-EmbeddedDocument.prototype.parent = function () {
+EmbeddedDocument.prototype.parent = function() {
return this.__parent;
-}
+};
/**
* Returns this sub-documents parent array.
@@ -233,9 +318,9 @@ EmbeddedDocument.prototype.parent = function () {
* @api public
*/
-EmbeddedDocument.prototype.parentArray = function () {
+EmbeddedDocument.prototype.parentArray = function() {
return this.__parentArray;
-}
+};
/*!
* Module exports.
diff --git a/node_modules/mongoose/lib/types/index.js b/node_modules/mongoose/lib/types/index.js
index e6a9901..0d01923 100644
--- a/node_modules/mongoose/lib/types/index.js
+++ b/node_modules/mongoose/lib/types/index.js
@@ -11,3 +11,5 @@ exports.Embedded = require('./embedded');
exports.DocumentArray = require('./documentarray');
exports.ObjectId = require('./objectid');
+
+exports.Subdocument = require('./subdocument');
diff --git a/node_modules/mongoose/lib/types/objectid.js b/node_modules/mongoose/lib/types/objectid.js
index 44ad43f..9fe0b97 100644
--- a/node_modules/mongoose/lib/types/objectid.js
+++ b/node_modules/mongoose/lib/types/objectid.js
@@ -1,10 +1,3 @@
-
-/*!
- * Access driver.
- */
-
-var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native';
-
/**
* ObjectId type constructor
*
@@ -15,29 +8,6 @@ var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native';
* @constructor ObjectId
*/
-var ObjectId = require(driver + '/objectid');
-module.exports = ObjectId;
+var ObjectId = require('../drivers').ObjectId;
-/**
- * Creates an ObjectId from `str`
- *
- * @param {ObjectId|HexString} str
- * @static fromString
- * @receiver ObjectId
- * @return {ObjectId}
- * @api private
- */
-
-ObjectId.fromString;
-
-/**
- * Converts `oid` to a string.
- *
- * @param {ObjectId} oid ObjectId instance
- * @static toString
- * @receiver ObjectId
- * @return {String}
- * @api private
- */
-
-ObjectId.toString;
+module.exports = ObjectId;
diff --git a/node_modules/mongoose/lib/types/subdocument.js b/node_modules/mongoose/lib/types/subdocument.js
new file mode 100644
index 0000000..cf295c2
--- /dev/null
+++ b/node_modules/mongoose/lib/types/subdocument.js
@@ -0,0 +1,133 @@
+var Document = require('../document');
+var PromiseProvider = require('../promise_provider');
+
+module.exports = Subdocument;
+
+/**
+ * Subdocument constructor.
+ *
+ * @inherits Document
+ * @api private
+ */
+
+function Subdocument(value, fields) {
+ this.$isSingleNested = true;
+ Document.call(this, value, fields);
+}
+
+Subdocument.prototype = Object.create(Document.prototype);
+
+/**
+ * Used as a stub for [hooks.js](https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3)
+ *
+ * ####NOTE:
+ *
+ * _This is a no-op. Does not actually save the doc to the db._
+ *
+ * @param {Function} [fn]
+ * @return {Promise} resolved Promise
+ * @api private
+ */
+
+Subdocument.prototype.save = function(fn) {
+ var Promise = PromiseProvider.get();
+ return new Promise.ES6(function(resolve) {
+ fn && fn();
+ resolve();
+ });
+};
+
+Subdocument.prototype.$isValid = function(path) {
+ if (this.$parent) {
+ return this.$parent.$isValid([this.$basePath, path].join('.'));
+ }
+};
+
+Subdocument.prototype.markModified = function(path) {
+ Document.prototype.markModified.call(this, path);
+ if (this.$parent) {
+ if (this.$parent.isModified(this.$basePath)) {
+ return;
+ }
+ this.$parent.markModified([this.$basePath, path].join('.'));
+ }
+};
+
+Subdocument.prototype.$markValid = function(path) {
+ if (this.$parent) {
+ this.$parent.$markValid([this.$basePath, path].join('.'));
+ }
+};
+
+Subdocument.prototype.invalidate = function(path, err, val) {
+ if (this.$parent) {
+ this.$parent.invalidate([this.$basePath, path].join('.'), err, val);
+ } else if (err.kind === 'cast' || err.name === 'CastError') {
+ throw err;
+ }
+};
+
+/**
+ * Returns the top level document of this sub-document.
+ *
+ * @return {Document}
+ */
+
+Subdocument.prototype.ownerDocument = function() {
+ if (this.$__.ownerDocument) {
+ return this.$__.ownerDocument;
+ }
+
+ var parent = this.$parent;
+ if (!parent) {
+ return this;
+ }
+
+ while (parent.$parent || parent.__parent) {
+ parent = parent.$parent || parent.__parent;
+ }
+ this.$__.ownerDocument = parent;
+ return this.$__.ownerDocument;
+};
+
+/**
+ * Null-out this subdoc
+ *
+ * @param {Object} [options]
+ * @param {Function} [callback] optional callback for compatibility with Document.prototype.remove
+ */
+
+Subdocument.prototype.remove = function(options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ this.$parent.set(this.$basePath, null);
+ registerRemoveListener(this);
+ if (typeof callback === 'function') {
+ callback(null);
+ }
+};
+
+/*!
+ * Registers remove event listeners for triggering
+ * on subdocuments.
+ *
+ * @param {EmbeddedDocument} sub
+ * @api private
+ */
+
+function registerRemoveListener(sub) {
+ var owner = sub.ownerDocument();
+
+ function emitRemove() {
+ owner.removeListener('save', emitRemove);
+ owner.removeListener('remove', emitRemove);
+ sub.emit('remove', sub);
+ owner = sub = null;
+ }
+
+ owner.on('save', emitRemove);
+ owner.on('remove', emitRemove);
+}
diff --git a/node_modules/mongoose/lib/utils.js b/node_modules/mongoose/lib/utils.js
index 678a7a8..5c40d84 100644
--- a/node_modules/mongoose/lib/utils.js
+++ b/node_modules/mongoose/lib/utils.js
@@ -2,15 +2,14 @@
* Module dependencies.
*/
-var ReadPref = require('mongodb').ReadPreference
- , ObjectId = require('./types/objectid')
- , cloneRegExp = require('regexp-clone')
- , sliced = require('sliced')
- , mpath = require('mpath')
- , ms = require('ms')
- , MongooseBuffer
- , MongooseArray
- , Document
+var ObjectId = require('./types/objectid');
+var cloneRegExp = require('regexp-clone');
+var sliced = require('sliced');
+var mpath = require('mpath');
+var ms = require('ms');
+var MongooseBuffer;
+var MongooseArray;
+var Document;
/*!
* Produces a collection name from model `name`.
@@ -20,9 +19,17 @@ var ReadPref = require('mongodb').ReadPreference
* @api private
*/
-exports.toCollectionName = function (name) {
- if ('system.profile' === name) return name;
- if ('system.indexes' === name) return name;
+exports.toCollectionName = function(name, options) {
+ options = options || {};
+ if (name === 'system.profile') {
+ return name;
+ }
+ if (name === 'system.indexes') {
+ return name;
+ }
+ if (options.pluralization === false) {
+ return name;
+ }
return pluralize(name.toLowerCase());
};
@@ -52,8 +59,10 @@ exports.pluralization = [
[/(x|ch|ss|sh)$/gi, '$1es'],
[/(matr|vert|ind)ix|ex$/gi, '$1ices'],
[/([m|l])ouse$/gi, '$1ice'],
+ [/(kn|w|l)ife$/gi, '$1ives'],
[/(quiz)$/gi, '$1zes'],
[/s$/gi, 's'],
+ [/([^a-z])$/, '$1'],
[/$/gi, 's']
];
var rules = exports.pluralization;
@@ -104,16 +113,18 @@ var uncountables = exports.uncountables;
* @api private
*/
-function pluralize (str) {
- var rule, found;
- if (!~uncountables.indexOf(str.toLowerCase())){
- found = rules.filter(function(rule){
+function pluralize(str) {
+ var found;
+ if (!~uncountables.indexOf(str.toLowerCase())) {
+ found = rules.filter(function(rule) {
return str.match(rule[0]);
});
- if (found[0]) return str.replace(found[0][0], found[0][1]);
+ if (found[0]) {
+ return str.replace(found[0][0], found[0][1]);
+ }
}
return str;
-};
+}
/*!
* Determines if `a` and `b` are deep equal.
@@ -126,30 +137,37 @@ function pluralize (str) {
* @api private
*/
-exports.deepEqual = function deepEqual (a, b) {
- if (a === b) return true;
+exports.deepEqual = function deepEqual(a, b) {
+ if (a === b) {
+ return true;
+ }
- if (a instanceof Date && b instanceof Date)
+ if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
+ }
if (a instanceof ObjectId && b instanceof ObjectId) {
return a.toString() === b.toString();
}
if (a instanceof RegExp && b instanceof RegExp) {
- return a.source == b.source &&
- a.ignoreCase == b.ignoreCase &&
- a.multiline == b.multiline &&
- a.global == b.global;
+ return a.source === b.source &&
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline &&
+ a.global === b.global;
}
- if (typeof a !== 'object' && typeof b !== 'object')
+ if (typeof a !== 'object' && typeof b !== 'object') {
return a == b;
+ }
- if (a === null || b === null || a === undefined || b === undefined)
- return false
+ if (a === null || b === null || a === undefined || b === undefined) {
+ return false;
+ }
- if (a.prototype !== b.prototype) return false;
+ if (a.prototype !== b.prototype) {
+ return false;
+ }
// Handle MongooseNumbers
if (a instanceof Number && b instanceof Number) {
@@ -157,45 +175,49 @@ exports.deepEqual = function deepEqual (a, b) {
}
if (Buffer.isBuffer(a)) {
- if (!Buffer.isBuffer(b)) return false;
- if (a.length !== b.length) return false;
- for (var i = 0, len = a.length; i < len; ++i) {
- if (a[i] !== b[i]) return false;
- }
- return true;
+ return exports.buffer.areEqual(a, b);
}
- if (isMongooseObject(a)) a = a.toObject();
- if (isMongooseObject(b)) b = b.toObject();
+ if (isMongooseObject(a)) {
+ a = a.toObject();
+ }
+ if (isMongooseObject(b)) {
+ b = b.toObject();
+ }
try {
var ka = Object.keys(a),
kb = Object.keys(b),
key, i;
- } catch (e) {//happens when one is a string literal and the other isn't
+ } catch (e) {
+ // happens when one is a string literal and the other isn't
return false;
}
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
- if (ka.length != kb.length)
+ if (ka.length !== kb.length) {
return false;
+ }
- //the same set of keys (although not necessarily the same order),
+ // the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
- //~~~cheap key test
+ // ~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
- if (ka[i] != kb[i])
+ if (ka[i] !== kb[i]) {
return false;
+ }
}
- //equivalent values for every corresponding key, and
- //~~~possibly expensive deep test
+ // equivalent values for every corresponding key, and
+ // ~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
- if (!deepEqual(a[key], b[key])) return false;
+ if (!deepEqual(a[key], b[key])) {
+ return false;
+ }
}
return true;
@@ -214,23 +236,24 @@ exports.deepEqual = function deepEqual (a, b) {
* @api private
*/
-exports.clone = function clone (obj, options) {
- if (obj === undefined || obj === null)
+exports.clone = function clone(obj, options) {
+ if (obj === undefined || obj === null) {
return obj;
+ }
- if (Array.isArray(obj))
+ if (Array.isArray(obj)) {
return cloneArray(obj, options);
+ }
if (isMongooseObject(obj)) {
- if (options && options.json && 'function' === typeof obj.toJSON) {
+ if (options && options.json && typeof obj.toJSON === 'function') {
return obj.toJSON(options);
- } else {
- return obj.toObject(options);
}
+ return obj.toObject(options);
}
if (obj.constructor) {
- switch (obj.constructor.name) {
+ switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
@@ -243,16 +266,18 @@ exports.clone = function clone (obj, options) {
}
}
- if (obj instanceof ObjectId)
+ if (obj instanceof ObjectId) {
return new ObjectId(obj.id);
+ }
if (!obj.constructor && exports.isObject(obj)) {
// object created with Object.create(null)
return cloneObject(obj, options);
}
- if (obj.valueOf)
+ if (obj.valueOf) {
return obj.valueOf();
+ }
};
var clone = exports.clone;
@@ -260,21 +285,21 @@ var clone = exports.clone;
* ignore
*/
-function cloneObject (obj, options) {
- var retainKeyOrder = options && options.retainKeyOrder
- , minimize = options && options.minimize
- , ret = {}
- , hasKeys
- , keys
- , val
- , k
- , i
+function cloneObject(obj, options) {
+ var retainKeyOrder = options && options.retainKeyOrder,
+ minimize = options && options.minimize,
+ ret = {},
+ hasKeys,
+ keys,
+ val,
+ k,
+ i;
if (retainKeyOrder) {
for (k in obj) {
val = clone(obj[k], options);
- if (!minimize || ('undefined' !== typeof val)) {
+ if (!minimize || (typeof val !== 'undefined')) {
hasKeys || (hasKeys = true);
ret[k] = val;
}
@@ -289,24 +314,27 @@ function cloneObject (obj, options) {
k = keys[i];
val = clone(obj[k], options);
- if (!minimize || ('undefined' !== typeof val)) {
- if (!hasKeys) hasKeys = true;
+ if (!minimize || (typeof val !== 'undefined')) {
+ if (!hasKeys) {
+ hasKeys = true;
+ }
ret[k] = val;
}
}
}
return minimize
- ? hasKeys && ret
- : ret;
-};
+ ? hasKeys && ret
+ : ret;
+}
-function cloneArray (arr, options) {
+function cloneArray(arr, options) {
var ret = [];
- for (var i = 0, l = arr.length; i < l; i++)
+ for (var i = 0, l = arr.length; i < l; i++) {
ret.push(clone(arr[i], options));
+ }
return ret;
-};
+}
/*!
* Shallow copies defaults into options.
@@ -317,10 +345,10 @@ function cloneArray (arr, options) {
* @api private
*/
-exports.options = function (defaults, options) {
- var keys = Object.keys(defaults)
- , i = keys.length
- , k ;
+exports.options = function(defaults, options) {
+ var keys = Object.keys(defaults),
+ i = keys.length,
+ k;
options = options || {};
@@ -340,7 +368,7 @@ exports.options = function (defaults, options) {
* @api private
*/
-exports.random = function () {
+exports.random = function() {
return Math.random().toString().substr(3);
};
@@ -352,21 +380,17 @@ exports.random = function () {
* @api private
*/
-exports.merge = function merge (to, from) {
- var keys = Object.keys(from)
- , i = keys.length
- , key
+exports.merge = function merge(to, from) {
+ var keys = Object.keys(from),
+ i = keys.length,
+ key;
while (i--) {
key = keys[i];
- if ('undefined' === typeof to[key]) {
+ if (typeof to[key] === 'undefined') {
to[key] = from[key];
- } else {
- if (exports.isObject(from[key])) {
- merge(to[key], from[key]);
- } else {
- to[key] = from[key];
- }
+ } else if (exports.isObject(from[key])) {
+ merge(to[key], from[key]);
}
}
};
@@ -377,6 +401,50 @@ exports.merge = function merge (to, from) {
var toString = Object.prototype.toString;
+/*!
+ * Applies toObject recursively.
+ *
+ * @param {Document|Array|Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+exports.toObject = function toObject(obj) {
+ Document || (Document = require('./document'));
+ var ret;
+
+ if (exports.isNullOrUndefined(obj)) {
+ return obj;
+ }
+
+ if (obj instanceof Document) {
+ return obj.toObject();
+ }
+
+ if (Array.isArray(obj)) {
+ ret = [];
+
+ for (var i = 0, len = obj.length; i < len; ++i) {
+ ret.push(toObject(obj[i]));
+ }
+
+ return ret;
+ }
+
+ if ((obj.constructor && exports.getFunctionName(obj.constructor) === 'Object') ||
+ (!obj.constructor && exports.isObject(obj))) {
+ ret = {};
+
+ for (var k in obj) {
+ ret[k] = toObject(obj[k]);
+ }
+
+ return ret;
+ }
+
+ return obj;
+};
+
/*!
* Determines if `arg` is an object.
*
@@ -385,9 +453,12 @@ var toString = Object.prototype.toString;
* @return {Boolean}
*/
-exports.isObject = function (arg) {
- return '[object Object]' == toString.call(arg);
-}
+exports.isObject = function(arg) {
+ if (Buffer.isBuffer(arg)) {
+ return true;
+ }
+ return toString.call(arg) === '[object Object]';
+};
/*!
* A faster Array.prototype.slice.call(arguments) alternative
@@ -407,20 +478,22 @@ exports.args = sliced;
* @api private
*/
-exports.tick = function tick (callback) {
- if ('function' !== typeof callback) return;
- return function () {
+exports.tick = function tick(callback) {
+ if (typeof callback !== 'function') {
+ return;
+ }
+ return function() {
try {
callback.apply(this, arguments);
} catch (err) {
// only nextTick on err to get out of
// the event loop and avoid state corruption.
- process.nextTick(function () {
+ process.nextTick(function() {
throw err;
});
}
- }
-}
+ };
+};
/*!
* Returns if `v` is a mongoose object that has a `toObject()` method we can use.
@@ -431,15 +504,15 @@ exports.tick = function tick (callback) {
* @api private
*/
-exports.isMongooseObject = function (v) {
+exports.isMongooseObject = function(v) {
Document || (Document = require('./document'));
MongooseArray || (MongooseArray = require('./types').Array);
MongooseBuffer || (MongooseBuffer = require('./types').Buffer);
return v instanceof Document ||
- v instanceof MongooseArray ||
- v instanceof MongooseBuffer
-}
+ (v && v.isMongooseArray) ||
+ (v && v.isMongooseBuffer);
+};
var isMongooseObject = exports.isMongooseObject;
/*!
@@ -449,66 +522,37 @@ var isMongooseObject = exports.isMongooseObject;
* @api private
*/
-exports.expires = function expires (object) {
- if (!(object && 'Object' == object.constructor.name)) return;
- if (!('expires' in object)) return;
+exports.expires = function expires(object) {
+ if (!(object && object.constructor.name === 'Object')) {
+ return;
+ }
+ if (!('expires' in object)) {
+ return;
+ }
var when;
- if ('string' != typeof object.expires) {
+ if (typeof object.expires !== 'string') {
when = object.expires;
} else {
when = Math.round(ms(object.expires) / 1000);
}
object.expireAfterSeconds = when;
delete object.expires;
-}
-
-/*!
- * Converts arguments to ReadPrefs the driver
- * can understand.
- *
- * @TODO move this into the driver layer
- * @param {String|Array} pref
- * @param {Array} [tags]
- */
-
-exports.readPref = function readPref (pref, tags) {
- if (Array.isArray(pref)) {
- tags = pref[1];
- pref = pref[0];
- }
-
- switch (pref) {
- case 'p':
- pref = 'primary';
- break;
- case 'pp':
- pref = 'primaryPreferred';
- break;
- case 's':
- pref = 'secondary';
- break;
- case 'sp':
- pref = 'secondaryPreferred';
- break;
- case 'n':
- pref = 'nearest';
- break;
- }
-
- return new ReadPref(pref, tags);
-}
+};
/*!
* Populate options constructor
*/
-function PopulateOptions (path, select, match, options, model) {
+function PopulateOptions(path, select, match, options, model, subPopulate) {
this.path = path;
this.match = match;
this.select = select;
this.options = options;
this.model = model;
+ if (typeof subPopulate === 'object') {
+ this.populate = subPopulate;
+ }
this._docs = {};
}
@@ -522,19 +566,19 @@ exports.PopulateOptions = PopulateOptions;
* populate helper
*/
-exports.populate = function populate (path, select, model, match, options) {
+exports.populate = function populate(path, select, model, match, options, subPopulate) {
// The order of select/conditions args is opposite Model.find but
// necessary to keep backward compatibility (select could be
// an array, string, or object literal).
// might have passed an object specifying all arguments
- if (1 === arguments.length) {
+ if (arguments.length === 1) {
if (path instanceof PopulateOptions) {
return [path];
}
if (Array.isArray(path)) {
- return path.map(function(o){
+ return path.map(function(o) {
return exports.populate(o)[0];
});
}
@@ -544,26 +588,32 @@ exports.populate = function populate (path, select, model, match, options) {
options = path.options;
select = path.select;
model = path.model;
+ subPopulate = path.populate;
path = path.path;
}
- } else if ('string' !== typeof model) {
+ } else if (typeof model !== 'string' && typeof model !== 'function') {
options = match;
match = model;
model = undefined;
}
- if ('string' != typeof path) {
+ if (typeof path !== 'string') {
throw new TypeError('utils.populate: invalid path. Expected string. Got typeof `' + typeof path + '`');
}
+ if (typeof subPopulate === 'object') {
+ subPopulate = exports.populate(subPopulate);
+ }
+
var ret = [];
var paths = path.split(' ');
+ options = exports.clone(options, { retainKeyOrder: true });
for (var i = 0; i < paths.length; ++i) {
- ret.push(new PopulateOptions(paths[i], select, match, options, model));
+ ret.push(new PopulateOptions(paths[i], select, match, options, model, subPopulate));
}
return ret;
-}
+};
/*!
* Return the value of `obj` at the given `path`.
@@ -572,9 +622,9 @@ exports.populate = function populate (path, select, model, match, options) {
* @param {Object} obj
*/
-exports.getValue = function (path, obj, map) {
+exports.getValue = function(path, obj, map) {
return mpath.get(path, obj, '_doc', map);
-}
+};
/*!
* Sets the value of `obj` at the given `path`.
@@ -584,9 +634,9 @@ exports.getValue = function (path, obj, map) {
* @param {Object} obj
*/
-exports.setValue = function (path, val, obj, map) {
+exports.setValue = function(path, val, obj, map) {
mpath.set(path, val, obj, '_doc', map);
-}
+};
/*!
* Returns an array of values from object `o`.
@@ -597,17 +647,17 @@ exports.setValue = function (path, val, obj, map) {
*/
exports.object = {};
-exports.object.vals = function vals (o) {
- var keys = Object.keys(o)
- , i = keys.length
- , ret = [];
+exports.object.vals = function vals(o) {
+ var keys = Object.keys(o),
+ i = keys.length,
+ ret = [];
while (i--) {
ret.push(o[keys[i]]);
}
return ret;
-}
+};
/*!
* @see exports.options
@@ -622,9 +672,10 @@ exports.object.shallowCopy = exports.options;
* @param {String} prop
*/
-exports.object.hasOwnProperty = function (obj, prop) {
- return Object.prototype.hasOwnProperty.call(obj, prop);
-}
+var hop = Object.prototype.hasOwnProperty;
+exports.object.hasOwnProperty = function(obj, prop) {
+ return hop.call(obj, prop);
+};
/*!
* Determine if `val` is null or undefined
@@ -632,9 +683,9 @@ exports.object.hasOwnProperty = function (obj, prop) {
* @return {Boolean}
*/
-exports.isNullOrUndefined = function (val) {
- return null == val
-}
+exports.isNullOrUndefined = function(val) {
+ return val === null || val === undefined;
+};
/*!
* ignore
@@ -653,10 +704,10 @@ exports.array = {};
* @private
*/
-exports.array.flatten = function flatten (arr, filter, ret) {
+exports.array.flatten = function flatten(arr, filter, ret) {
ret || (ret = []);
- arr.forEach(function (item) {
+ arr.forEach(function(item) {
if (Array.isArray(item)) {
flatten(item, filter, ret);
} else {
@@ -667,5 +718,130 @@ exports.array.flatten = function flatten (arr, filter, ret) {
});
return ret;
-}
+};
+
+/*!
+ * Removes duplicate values from an array
+ *
+ * [1, 2, 3, 3, 5] => [1, 2, 3, 5]
+ * [ ObjectId("550988ba0c19d57f697dc45e"), ObjectId("550988ba0c19d57f697dc45e") ]
+ * => [ObjectId("550988ba0c19d57f697dc45e")]
+ *
+ * @param {Array} arr
+ * @return {Array}
+ * @private
+ */
+
+exports.array.unique = function(arr) {
+ var primitives = {};
+ var ids = {};
+ var ret = [];
+ var length = arr.length;
+ for (var i = 0; i < length; ++i) {
+ if (typeof arr[i] === 'number' || typeof arr[i] === 'string') {
+ if (primitives[arr[i]]) {
+ continue;
+ }
+ ret.push(arr[i]);
+ primitives[arr[i]] = true;
+ } else if (arr[i] instanceof ObjectId) {
+ if (ids[arr[i].toString()]) {
+ continue;
+ }
+ ret.push(arr[i]);
+ ids[arr[i].toString()] = true;
+ } else {
+ ret.push(arr[i]);
+ }
+ }
+ return ret;
+};
+
+/*!
+ * Determines if two buffers are equal.
+ *
+ * @param {Buffer} a
+ * @param {Object} b
+ */
+
+exports.buffer = {};
+exports.buffer.areEqual = function(a, b) {
+ if (!Buffer.isBuffer(a)) {
+ return false;
+ }
+ if (!Buffer.isBuffer(b)) {
+ return false;
+ }
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (var i = 0, len = a.length; i < len; ++i) {
+ if (a[i] !== b[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+exports.getFunctionName = function(fn) {
+ if (fn.name) {
+ return fn.name;
+ }
+ return (fn.toString().trim().match(/^function\s*([^\s(]+)/) || [])[1];
+};
+
+exports.decorate = function(destination, source) {
+ for (var key in source) {
+ destination[key] = source[key];
+ }
+};
+
+/**
+ * merges to with a copy of from
+ *
+ * @param {Object} to
+ * @param {Object} fromObj
+ * @api private
+ */
+
+exports.mergeClone = function(to, fromObj) {
+ var keys = Object.keys(fromObj),
+ i = keys.length,
+ key;
+
+ while (i--) {
+ key = keys[i];
+ if (typeof to[key] === 'undefined') {
+ // make sure to retain key order here because of a bug handling the $each
+ // operator in mongodb 2.4.4
+ to[key] = exports.clone(fromObj[key], {retainKeyOrder: 1});
+ } else {
+ if (exports.isObject(fromObj[key])) {
+ var obj = fromObj[key];
+ if (isMongooseObject(fromObj[key]) && !fromObj[key].isMongooseBuffer) {
+ obj = obj.toObject({ virtuals: false });
+ }
+ exports.mergeClone(to[key], obj);
+ } else {
+ // make sure to retain key order here because of a bug handling the
+ // $each operator in mongodb 2.4.4
+ to[key] = exports.clone(fromObj[key], {retainKeyOrder: 1});
+ }
+ }
+ }
+};
+
+/**
+ * Executes a function on each element of an array (like _.each)
+ *
+ * @param {Array} arr
+ * @param {Function} fn
+ * @api private
+ */
+
+exports.each = function(arr, fn) {
+ for (var i = 0; i < arr.length; ++i) {
+ fn(arr[i]);
+ }
+};
diff --git a/node_modules/mongoose/lib/virtualtype.js b/node_modules/mongoose/lib/virtualtype.js
index 819ac10..f43cbe2 100644
--- a/node_modules/mongoose/lib/virtualtype.js
+++ b/node_modules/mongoose/lib/virtualtype.js
@@ -13,7 +13,7 @@
* @api public
*/
-function VirtualType (options, name) {
+function VirtualType(options, name) {
this.path = name;
this.getters = [];
this.setters = [];
@@ -35,7 +35,7 @@ function VirtualType (options, name) {
* @api public
*/
-VirtualType.prototype.get = function (fn) {
+VirtualType.prototype.get = function(fn) {
this.getters.push(fn);
return this;
};
@@ -57,7 +57,7 @@ VirtualType.prototype.get = function (fn) {
* @api public
*/
-VirtualType.prototype.set = function (fn) {
+VirtualType.prototype.set = function(fn) {
this.setters.push(fn);
return this;
};
@@ -71,7 +71,7 @@ VirtualType.prototype.set = function (fn) {
* @api public
*/
-VirtualType.prototype.applyGetters = function (value, scope) {
+VirtualType.prototype.applyGetters = function(value, scope) {
var v = value;
for (var l = this.getters.length - 1; l >= 0; l--) {
v = this.getters[l].call(scope, v, this);
@@ -88,7 +88,7 @@ VirtualType.prototype.applyGetters = function (value, scope) {
* @api public
*/
-VirtualType.prototype.applySetters = function (value, scope) {
+VirtualType.prototype.applySetters = function(value, scope) {
var v = value;
for (var l = this.setters.length - 1; l >= 0; l--) {
v = this.setters[l].call(scope, v, this);
diff --git a/node_modules/mongoose/node_modules/async/CHANGELOG.md b/node_modules/mongoose/node_modules/async/CHANGELOG.md
new file mode 100644
index 0000000..f15e081
--- /dev/null
+++ b/node_modules/mongoose/node_modules/async/CHANGELOG.md
@@ -0,0 +1,125 @@
+# v1.5.2
+- Allow using `"consructor"` as an argument in `memoize` (#998)
+- Give a better error messsage when `auto` dependency checking fails (#994)
+- Various doc updates (#936, #956, #979, #1002)
+
+# v1.5.1
+- Fix issue with `pause` in `queue` with concurrency enabled (#946)
+- `while` and `until` now pass the final result to callback (#963)
+- `auto` will properly handle concurrency when there is no callback (#966)
+- `auto` will now properly stop execution when an error occurs (#988, #993)
+- Various doc fixes (#971, #980)
+
+# v1.5.0
+
+- Added `transform`, analogous to [`_.transform`](http://lodash.com/docs#transform) (#892)
+- `map` now returns an object when an object is passed in, rather than array with non-numeric keys. `map` will begin always returning an array with numeric indexes in the next major release. (#873)
+- `auto` now accepts an optional `concurrency` argument to limit the number of running tasks (#637)
+- Added `queue#workersList()`, to retrieve the list of currently running tasks. (#891)
+- Various code simplifications (#896, #904)
+- Various doc fixes :scroll: (#890, #894, #903, #905, #912)
+
+# v1.4.2
+
+- Ensure coverage files don't get published on npm (#879)
+
+# v1.4.1
+
+- Add in overlooked `detectLimit` method (#866)
+- Removed unnecessary files from npm releases (#861)
+- Removed usage of a reserved word to prevent :boom: in older environments (#870)
+
+# v1.4.0
+
+- `asyncify` now supports promises (#840)
+- Added `Limit` versions of `filter` and `reject` (#836)
+- Add `Limit` versions of `detect`, `some` and `every` (#828, #829)
+- `some`, `every` and `detect` now short circuit early (#828, #829)
+- Improve detection of the global object (#804), enabling use in WebWorkers
+- `whilst` now called with arguments from iterator (#823)
+- `during` now gets called with arguments from iterator (#824)
+- Code simplifications and optimizations aplenty ([diff](https://github.com/caolan/async/compare/v1.3.0...v1.4.0))
+
+
+# v1.3.0
+
+New Features:
+- Added `constant`
+- Added `asyncify`/`wrapSync` for making sync functions work with callbacks. (#671, #806)
+- Added `during` and `doDuring`, which are like `whilst` with an async truth test. (#800)
+- `retry` now accepts an `interval` parameter to specify a delay between retries. (#793)
+- `async` should work better in Web Workers due to better `root` detection (#804)
+- Callbacks are now optional in `whilst`, `doWhilst`, `until`, and `doUntil` (#642)
+- Various internal updates (#786, #801, #802, #803)
+- Various doc fixes (#790, #794)
+
+Bug Fixes:
+- `cargo` now exposes the `payload` size, and `cargo.payload` can be changed on the fly after the `cargo` is created. (#740, #744, #783)
+
+
+# v1.2.1
+
+Bug Fix:
+
+- Small regression with synchronous iterator behavior in `eachSeries` with a 1-element array. Before 1.1.0, `eachSeries`'s callback was called on the same tick, which this patch restores. In 2.0.0, it will be called on the next tick. (#782)
+
+
+# v1.2.0
+
+New Features:
+
+- Added `timesLimit` (#743)
+- `concurrency` can be changed after initialization in `queue` by setting `q.concurrency`. The new concurrency will be reflected the next time a task is processed. (#747, #772)
+
+Bug Fixes:
+
+- Fixed a regression in `each` and family with empty arrays that have additional properties. (#775, #777)
+
+
+# v1.1.1
+
+Bug Fix:
+
+- Small regression with synchronous iterator behavior in `eachSeries` with a 1-element array. Before 1.1.0, `eachSeries`'s callback was called on the same tick, which this patch restores. In 2.0.0, it will be called on the next tick. (#782)
+
+
+# v1.1.0
+
+New Features:
+
+- `cargo` now supports all of the same methods and event callbacks as `queue`.
+- Added `ensureAsync` - A wrapper that ensures an async function calls its callback on a later tick. (#769)
+- Optimized `map`, `eachOf`, and `waterfall` families of functions
+- Passing a `null` or `undefined` array to `map`, `each`, `parallel` and families will be treated as an empty array (#667).
+- The callback is now optional for the composed results of `compose` and `seq`. (#618)
+- Reduced file size by 4kb, (minified version by 1kb)
+- Added code coverage through `nyc` and `coveralls` (#768)
+
+Bug Fixes:
+
+- `forever` will no longer stack overflow with a synchronous iterator (#622)
+- `eachLimit` and other limit functions will stop iterating once an error occurs (#754)
+- Always pass `null` in callbacks when there is no error (#439)
+- Ensure proper conditions when calling `drain()` after pushing an empty data set to a queue (#668)
+- `each` and family will properly handle an empty array (#578)
+- `eachSeries` and family will finish if the underlying array is modified during execution (#557)
+- `queue` will throw if a non-function is passed to `q.push()` (#593)
+- Doc fixes (#629, #766)
+
+
+# v1.0.0
+
+No known breaking changes, we are simply complying with semver from here on out.
+
+Changes:
+
+- Start using a changelog!
+- Add `forEachOf` for iterating over Objects (or to iterate Arrays with indexes available) (#168 #704 #321)
+- Detect deadlocks in `auto` (#663)
+- Better support for require.js (#527)
+- Throw if queue created with concurrency `0` (#714)
+- Fix unneeded iteration in `queue.resume()` (#758)
+- Guard against timer mocking overriding `setImmediate` (#609 #611)
+- Miscellaneous doc fixes (#542 #596 #615 #628 #631 #690 #729)
+- Use single noop function internally (#546)
+- Optimize internal `_each`, `_map` and `_keys` functions.
diff --git a/node_modules/mongoose/node_modules/async/LICENSE b/node_modules/mongoose/node_modules/async/LICENSE
new file mode 100644
index 0000000..8f29698
--- /dev/null
+++ b/node_modules/mongoose/node_modules/async/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010-2014 Caolan McMahon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/mongoose/node_modules/async/README.md b/node_modules/mongoose/node_modules/async/README.md
new file mode 100644
index 0000000..316c405
--- /dev/null
+++ b/node_modules/mongoose/node_modules/async/README.md
@@ -0,0 +1,1877 @@
+# Async.js
+
+[![Build Status via Travis CI](https://travis-ci.org/caolan/async.svg?branch=master)](https://travis-ci.org/caolan/async)
+[![NPM version](http://img.shields.io/npm/v/async.svg)](https://www.npmjs.org/package/async)
+[![Coverage Status](https://coveralls.io/repos/caolan/async/badge.svg?branch=master)](https://coveralls.io/r/caolan/async?branch=master)
+[![Join the chat at https://gitter.im/caolan/async](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/caolan/async?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+
+Async is a utility module which provides straight-forward, powerful functions
+for working with asynchronous JavaScript. Although originally designed for
+use with [Node.js](http://nodejs.org) and installable via `npm install async`,
+it can also be used directly in the browser.
+
+Async is also installable via:
+
+- [bower](http://bower.io/): `bower install async`
+- [component](https://github.com/component/component): `component install
+ caolan/async`
+- [jam](http://jamjs.org/): `jam install async`
+- [spm](http://spmjs.io/): `spm install async`
+
+Async provides around 20 functions that include the usual 'functional'
+suspects (`map`, `reduce`, `filter`, `each`…) as well as some common patterns
+for asynchronous control flow (`parallel`, `series`, `waterfall`…). All these
+functions assume you follow the Node.js convention of providing a single
+callback as the last argument of your `async` function.
+
+
+## Quick Examples
+
+```javascript
+async.map(['file1','file2','file3'], fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+
+async.filter(['file1','file2','file3'], fs.exists, function(results){
+ // results now equals an array of the existing files
+});
+
+async.parallel([
+ function(){ ... },
+ function(){ ... }
+], callback);
+
+async.series([
+ function(){ ... },
+ function(){ ... }
+]);
+```
+
+There are many more functions available so take a look at the docs below for a
+full list. This module aims to be comprehensive, so if you feel anything is
+missing please create a GitHub issue for it.
+
+## Common Pitfalls [(StackOverflow)](http://stackoverflow.com/questions/tagged/async.js)
+### Synchronous iteration functions
+
+If you get an error like `RangeError: Maximum call stack size exceeded.` or other stack overflow issues when using async, you are likely using a synchronous iterator. By *synchronous* we mean a function that calls its callback on the same tick in the javascript event loop, without doing any I/O or using any timers. Calling many callbacks iteratively will quickly overflow the stack. If you run into this issue, just defer your callback with `async.setImmediate` to start a new call stack on the next tick of the event loop.
+
+This can also arise by accident if you callback early in certain cases:
+
+```js
+async.eachSeries(hugeArray, function iterator(item, callback) {
+ if (inCache(item)) {
+ callback(null, cache[item]); // if many items are cached, you'll overflow
+ } else {
+ doSomeIO(item, callback);
+ }
+}, function done() {
+ //...
+});
+```
+
+Just change it to:
+
+```js
+async.eachSeries(hugeArray, function iterator(item, callback) {
+ if (inCache(item)) {
+ async.setImmediate(function () {
+ callback(null, cache[item]);
+ });
+ } else {
+ doSomeIO(item, callback);
+ //...
+```
+
+Async guards against synchronous functions in some, but not all, cases. If you are still running into stack overflows, you can defer as suggested above, or wrap functions with [`async.ensureAsync`](#ensureAsync) Functions that are asynchronous by their nature do not have this problem and don't need the extra callback deferral.
+
+If JavaScript's event loop is still a bit nebulous, check out [this article](http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/) or [this talk](http://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html) for more detailed information about how it works.
+
+
+### Multiple callbacks
+
+Make sure to always `return` when calling a callback early, otherwise you will cause multiple callbacks and unpredictable behavior in many cases.
+
+```js
+async.waterfall([
+ function (callback) {
+ getSomething(options, function (err, result) {
+ if (err) {
+ callback(new Error("failed getting something:" + err.message));
+ // we should return here
+ }
+ // since we did not return, this callback still will be called and
+ // `processData` will be called twice
+ callback(null, result);
+ });
+ },
+ processData
+], done)
+```
+
+It is always good practice to `return callback(err, result)` whenever a callback call is not the last statement of a function.
+
+
+### Binding a context to an iterator
+
+This section is really about `bind`, not about `async`. If you are wondering how to
+make `async` execute your iterators in a given context, or are confused as to why
+a method of another library isn't working as an iterator, study this example:
+
+```js
+// Here is a simple object with an (unnecessarily roundabout) squaring method
+var AsyncSquaringLibrary = {
+ squareExponent: 2,
+ square: function(number, callback){
+ var result = Math.pow(number, this.squareExponent);
+ setTimeout(function(){
+ callback(null, result);
+ }, 200);
+ }
+};
+
+async.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result){
+ // result is [NaN, NaN, NaN]
+ // This fails because the `this.squareExponent` expression in the square
+ // function is not evaluated in the context of AsyncSquaringLibrary, and is
+ // therefore undefined.
+});
+
+async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result){
+ // result is [1, 4, 9]
+ // With the help of bind we can attach a context to the iterator before
+ // passing it to async. Now the square function will be executed in its
+ // 'home' AsyncSquaringLibrary context and the value of `this.squareExponent`
+ // will be as expected.
+});
+```
+
+## Download
+
+The source is available for download from
+[GitHub](https://github.com/caolan/async/blob/master/lib/async.js).
+Alternatively, you can install using Node Package Manager (`npm`):
+
+ npm install async
+
+As well as using Bower:
+
+ bower install async
+
+__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed
+
+## In the Browser
+
+So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5.
+
+Usage:
+
+```html
+
+
+```
+
+## Documentation
+
+Some functions are also available in the following forms:
+* `Series` - the same as `` but runs only a single async operation at a time
+* `Limit` - the same as `` but runs a maximum of `limit` async operations at a time
+
+### Collections
+
+* [`each`](#each), `eachSeries`, `eachLimit`
+* [`forEachOf`](#forEachOf), `forEachOfSeries`, `forEachOfLimit`
+* [`map`](#map), `mapSeries`, `mapLimit`
+* [`filter`](#filter), `filterSeries`, `filterLimit`
+* [`reject`](#reject), `rejectSeries`, `rejectLimit`
+* [`reduce`](#reduce), [`reduceRight`](#reduceRight)
+* [`detect`](#detect), `detectSeries`, `detectLimit`
+* [`sortBy`](#sortBy)
+* [`some`](#some), `someLimit`
+* [`every`](#every), `everyLimit`
+* [`concat`](#concat), `concatSeries`
+
+### Control Flow
+
+* [`series`](#seriestasks-callback)
+* [`parallel`](#parallel), `parallelLimit`
+* [`whilst`](#whilst), [`doWhilst`](#doWhilst)
+* [`until`](#until), [`doUntil`](#doUntil)
+* [`during`](#during), [`doDuring`](#doDuring)
+* [`forever`](#forever)
+* [`waterfall`](#waterfall)
+* [`compose`](#compose)
+* [`seq`](#seq)
+* [`applyEach`](#applyEach), `applyEachSeries`
+* [`queue`](#queue), [`priorityQueue`](#priorityQueue)
+* [`cargo`](#cargo)
+* [`auto`](#auto)
+* [`retry`](#retry)
+* [`iterator`](#iterator)
+* [`times`](#times), `timesSeries`, `timesLimit`
+
+### Utils
+
+* [`apply`](#apply)
+* [`nextTick`](#nextTick)
+* [`memoize`](#memoize)
+* [`unmemoize`](#unmemoize)
+* [`ensureAsync`](#ensureAsync)
+* [`constant`](#constant)
+* [`asyncify`](#asyncify)
+* [`wrapSync`](#wrapSync)
+* [`log`](#log)
+* [`dir`](#dir)
+* [`noConflict`](#noConflict)
+
+## Collections
+
+
+
+### each(arr, iterator, [callback])
+
+Applies the function `iterator` to each item in `arr`, in parallel.
+The `iterator` is called with an item from the list, and a callback for when it
+has finished. If the `iterator` passes an error to its `callback`, the main
+`callback` (for the `each` function) is immediately called with the error.
+
+Note, that since this function applies `iterator` to each item in parallel,
+there is no guarantee that the iterator functions will complete in order.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err)` which must be called once it has
+ completed. If no error has occurred, the `callback` should be run without
+ arguments or with an explicit `null` argument. The array index is not passed
+ to the iterator. If you need the index, use [`forEachOf`](#forEachOf).
+* `callback(err)` - *Optional* A callback which is called when all `iterator` functions
+ have finished, or an error occurs.
+
+__Examples__
+
+
+```js
+// assuming openFiles is an array of file names and saveFile is a function
+// to save the modified contents of that file:
+
+async.each(openFiles, saveFile, function(err){
+ // if any of the saves produced an error, err would equal that error
+});
+```
+
+```js
+// assuming openFiles is an array of file names
+
+async.each(openFiles, function(file, callback) {
+
+ // Perform operation on file here.
+ console.log('Processing file ' + file);
+
+ if( file.length > 32 ) {
+ console.log('This file name is too long');
+ callback('File name too long');
+ } else {
+ // Do work to process file here
+ console.log('File processed');
+ callback();
+ }
+}, function(err){
+ // if any of the file processing produced an error, err would equal that error
+ if( err ) {
+ // One of the iterations produced an error.
+ // All processing will now stop.
+ console.log('A file failed to process');
+ } else {
+ console.log('All files have been processed successfully');
+ }
+});
+```
+
+__Related__
+
+* eachSeries(arr, iterator, [callback])
+* eachLimit(arr, limit, iterator, [callback])
+
+---------------------------------------
+
+
+
+
+### forEachOf(obj, iterator, [callback])
+
+Like `each`, except that it iterates over objects, and passes the key as the second argument to the iterator.
+
+__Arguments__
+
+* `obj` - An object or array to iterate over.
+* `iterator(item, key, callback)` - A function to apply to each item in `obj`.
+The `key` is the item's key, or index in the case of an array. The iterator is
+passed a `callback(err)` which must be called once it has completed. If no
+error has occurred, the callback should be run without arguments or with an
+explicit `null` argument.
+* `callback(err)` - *Optional* A callback which is called when all `iterator` functions have finished, or an error occurs.
+
+__Example__
+
+```js
+var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"};
+var configs = {};
+
+async.forEachOf(obj, function (value, key, callback) {
+ fs.readFile(__dirname + value, "utf8", function (err, data) {
+ if (err) return callback(err);
+ try {
+ configs[key] = JSON.parse(data);
+ } catch (e) {
+ return callback(e);
+ }
+ callback();
+ })
+}, function (err) {
+ if (err) console.error(err.message);
+ // configs is now a map of JSON data
+ doSomethingWith(configs);
+})
+```
+
+__Related__
+
+* forEachOfSeries(obj, iterator, [callback])
+* forEachOfLimit(obj, limit, iterator, [callback])
+
+---------------------------------------
+
+
+### map(arr, iterator, [callback])
+
+Produces a new array of values by mapping each value in `arr` through
+the `iterator` function. The `iterator` is called with an item from `arr` and a
+callback for when it has finished processing. Each of these callback takes 2 arguments:
+an `error`, and the transformed item from `arr`. If `iterator` passes an error to its
+callback, the main `callback` (for the `map` function) is immediately called with the error.
+
+Note, that since this function applies the `iterator` to each item in parallel,
+there is no guarantee that the `iterator` functions will complete in order.
+However, the results array will be in the same order as the original `arr`.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err, transformed)` which must be called once
+ it has completed with an error (which can be `null`) and a transformed item.
+* `callback(err, results)` - *Optional* A callback which is called when all `iterator`
+ functions have finished, or an error occurs. Results is an array of the
+ transformed items from the `arr`.
+
+__Example__
+
+```js
+async.map(['file1','file2','file3'], fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+```
+
+__Related__
+* mapSeries(arr, iterator, [callback])
+* mapLimit(arr, limit, iterator, [callback])
+
+---------------------------------------
+
+
+
+### filter(arr, iterator, [callback])
+
+__Alias:__ `select`
+
+Returns a new array of all the values in `arr` which pass an async truth test.
+_The callback for each `iterator` call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`. This operation is
+performed in parallel, but the results array will be in the same order as the
+original.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A truth test to apply to each item in `arr`.
+ The `iterator` is passed a `callback(truthValue)`, which must be called with a
+ boolean argument once it has completed.
+* `callback(results)` - *Optional* A callback which is called after all the `iterator`
+ functions have finished.
+
+__Example__
+
+```js
+async.filter(['file1','file2','file3'], fs.exists, function(results){
+ // results now equals an array of the existing files
+});
+```
+
+__Related__
+
+* filterSeries(arr, iterator, [callback])
+* filterLimit(arr, limit, iterator, [callback])
+
+---------------------------------------
+
+
+### reject(arr, iterator, [callback])
+
+The opposite of [`filter`](#filter). Removes values that pass an `async` truth test.
+
+__Related__
+
+* rejectSeries(arr, iterator, [callback])
+* rejectLimit(arr, limit, iterator, [callback])
+
+---------------------------------------
+
+
+### reduce(arr, memo, iterator, [callback])
+
+__Aliases:__ `inject`, `foldl`
+
+Reduces `arr` into a single value using an async `iterator` to return
+each successive step. `memo` is the initial state of the reduction.
+This function only operates in series.
+
+For performance reasons, it may make sense to split a call to this function into
+a parallel map, and then use the normal `Array.prototype.reduce` on the results.
+This function is for situations where each step in the reduction needs to be async;
+if you can get the data before reducing it, then it's probably a good idea to do so.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `memo` - The initial state of the reduction.
+* `iterator(memo, item, callback)` - A function applied to each item in the
+ array to produce the next step in the reduction. The `iterator` is passed a
+ `callback(err, reduction)` which accepts an optional error as its first
+ argument, and the state of the reduction as the second. If an error is
+ passed to the callback, the reduction is stopped and the main `callback` is
+ immediately called with the error.
+* `callback(err, result)` - *Optional* A callback which is called after all the `iterator`
+ functions have finished. Result is the reduced value.
+
+__Example__
+
+```js
+async.reduce([1,2,3], 0, function(memo, item, callback){
+ // pointless async:
+ process.nextTick(function(){
+ callback(null, memo + item)
+ });
+}, function(err, result){
+ // result is now equal to the last value of memo, which is 6
+});
+```
+
+---------------------------------------
+
+
+### reduceRight(arr, memo, iterator, [callback])
+
+__Alias:__ `foldr`
+
+Same as [`reduce`](#reduce), only operates on `arr` in reverse order.
+
+
+---------------------------------------
+
+
+### detect(arr, iterator, [callback])
+
+Returns the first value in `arr` that passes an async truth test. The
+`iterator` is applied in parallel, meaning the first iterator to return `true` will
+fire the detect `callback` with that result. That means the result might not be
+the first item in the original `arr` (in terms of order) that passes the test.
+
+If order within the original `arr` is important, then look at [`detectSeries`](#detectSeries).
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A truth test to apply to each item in `arr`.
+ The iterator is passed a `callback(truthValue)` which must be called with a
+ boolean argument once it has completed. **Note: this callback does not take an error as its first argument.**
+* `callback(result)` - *Optional* A callback which is called as soon as any iterator returns
+ `true`, or after all the `iterator` functions have finished. Result will be
+ the first item in the array that passes the truth test (iterator) or the
+ value `undefined` if none passed. **Note: this callback does not take an error as its first argument.**
+
+__Example__
+
+```js
+async.detect(['file1','file2','file3'], fs.exists, function(result){
+ // result now equals the first file in the list that exists
+});
+```
+
+__Related__
+
+* detectSeries(arr, iterator, [callback])
+* detectLimit(arr, limit, iterator, [callback])
+
+---------------------------------------
+
+
+### sortBy(arr, iterator, [callback])
+
+Sorts a list by the results of running each `arr` value through an async `iterator`.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err, sortValue)` which must be called once it
+ has completed with an error (which can be `null`) and a value to use as the sort
+ criteria.
+* `callback(err, results)` - *Optional* A callback which is called after all the `iterator`
+ functions have finished, or an error occurs. Results is the items from
+ the original `arr` sorted by the values returned by the `iterator` calls.
+
+__Example__
+
+```js
+async.sortBy(['file1','file2','file3'], function(file, callback){
+ fs.stat(file, function(err, stats){
+ callback(err, stats.mtime);
+ });
+}, function(err, results){
+ // results is now the original array of files sorted by
+ // modified date
+});
+```
+
+__Sort Order__
+
+By modifying the callback parameter the sorting order can be influenced:
+
+```js
+//ascending order
+async.sortBy([1,9,3,5], function(x, callback){
+ callback(null, x);
+}, function(err,result){
+ //result callback
+} );
+
+//descending order
+async.sortBy([1,9,3,5], function(x, callback){
+ callback(null, x*-1); //<- x*-1 instead of x, turns the order around
+}, function(err,result){
+ //result callback
+} );
+```
+
+---------------------------------------
+
+
+### some(arr, iterator, [callback])
+
+__Alias:__ `any`
+
+Returns `true` if at least one element in the `arr` satisfies an async test.
+_The callback for each iterator call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`. Once any iterator
+call returns `true`, the main `callback` is immediately called.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A truth test to apply to each item in the array
+ in parallel. The iterator is passed a `callback(truthValue)`` which must be
+ called with a boolean argument once it has completed.
+* `callback(result)` - *Optional* A callback which is called as soon as any iterator returns
+ `true`, or after all the iterator functions have finished. Result will be
+ either `true` or `false` depending on the values of the async tests.
+
+ **Note: the callbacks do not take an error as their first argument.**
+__Example__
+
+```js
+async.some(['file1','file2','file3'], fs.exists, function(result){
+ // if result is true then at least one of the files exists
+});
+```
+
+__Related__
+
+* someLimit(arr, limit, iterator, callback)
+
+---------------------------------------
+
+
+### every(arr, iterator, [callback])
+
+__Alias:__ `all`
+
+Returns `true` if every element in `arr` satisfies an async test.
+_The callback for each `iterator` call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A truth test to apply to each item in the array
+ in parallel. The iterator is passed a `callback(truthValue)` which must be
+ called with a boolean argument once it has completed.
+* `callback(result)` - *Optional* A callback which is called as soon as any iterator returns
+ `false`, or after all the iterator functions have finished. Result will be
+ either `true` or `false` depending on the values of the async tests.
+
+ **Note: the callbacks do not take an error as their first argument.**
+
+__Example__
+
+```js
+async.every(['file1','file2','file3'], fs.exists, function(result){
+ // if result is true then every file exists
+});
+```
+
+__Related__
+
+* everyLimit(arr, limit, iterator, callback)
+
+---------------------------------------
+
+
+### concat(arr, iterator, [callback])
+
+Applies `iterator` to each item in `arr`, concatenating the results. Returns the
+concatenated list. The `iterator`s are called in parallel, and the results are
+concatenated as they return. There is no guarantee that the results array will
+be returned in the original order of `arr` passed to the `iterator` function.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `iterator(item, callback)` - A function to apply to each item in `arr`.
+ The iterator is passed a `callback(err, results)` which must be called once it
+ has completed with an error (which can be `null`) and an array of results.
+* `callback(err, results)` - *Optional* A callback which is called after all the `iterator`
+ functions have finished, or an error occurs. Results is an array containing
+ the concatenated results of the `iterator` function.
+
+__Example__
+
+```js
+async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
+ // files is now a list of filenames that exist in the 3 directories
+});
+```
+
+__Related__
+
+* concatSeries(arr, iterator, [callback])
+
+
+## Control Flow
+
+
+### series(tasks, [callback])
+
+Run the functions in the `tasks` array in series, each one running once the previous
+function has completed. If any functions in the series pass an error to its
+callback, no more functions are run, and `callback` is immediately called with the value of the error.
+Otherwise, `callback` receives an array of results when `tasks` have completed.
+
+It is also possible to use an object instead of an array. Each property will be
+run as a function, and the results will be passed to the final `callback` as an object
+instead of an array. This can be a more readable way of handling results from
+[`series`](#series).
+
+**Note** that while many implementations preserve the order of object properties, the
+[ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6)
+explicitly states that
+
+> The mechanics and order of enumerating the properties is not specified.
+
+So if you rely on the order in which your series of functions are executed, and want
+this to work on all platforms, consider using an array.
+
+__Arguments__
+
+* `tasks` - An array or object containing functions to run, each function is passed
+ a `callback(err, result)` it must call on completion with an error `err` (which can
+ be `null`) and an optional `result` value.
+* `callback(err, results)` - An optional callback to run once all the functions
+ have completed. This function gets a results array (or object) containing all
+ the result arguments passed to the `task` callbacks.
+
+__Example__
+
+```js
+async.series([
+ function(callback){
+ // do some stuff ...
+ callback(null, 'one');
+ },
+ function(callback){
+ // do some more stuff ...
+ callback(null, 'two');
+ }
+],
+// optional callback
+function(err, results){
+ // results is now equal to ['one', 'two']
+});
+
+
+// an example using an object instead of an array
+async.series({
+ one: function(callback){
+ setTimeout(function(){
+ callback(null, 1);
+ }, 200);
+ },
+ two: function(callback){
+ setTimeout(function(){
+ callback(null, 2);
+ }, 100);
+ }
+},
+function(err, results) {
+ // results is now equal to: {one: 1, two: 2}
+});
+```
+
+---------------------------------------
+
+
+### parallel(tasks, [callback])
+
+Run the `tasks` array of functions in parallel, without waiting until the previous
+function has completed. If any of the functions pass an error to its
+callback, the main `callback` is immediately called with the value of the error.
+Once the `tasks` have completed, the results are passed to the final `callback` as an
+array.
+
+**Note:** `parallel` is about kicking-off I/O tasks in parallel, not about parallel execution of code. If your tasks do not use any timers or perform any I/O, they will actually be executed in series. Any synchronous setup sections for each task will happen one after the other. JavaScript remains single-threaded.
+
+It is also possible to use an object instead of an array. Each property will be
+run as a function and the results will be passed to the final `callback` as an object
+instead of an array. This can be a more readable way of handling results from
+[`parallel`](#parallel).
+
+
+__Arguments__
+
+* `tasks` - An array or object containing functions to run. Each function is passed
+ a `callback(err, result)` which it must call on completion with an error `err`
+ (which can be `null`) and an optional `result` value.
+* `callback(err, results)` - An optional callback to run once all the functions
+ have completed successfully. This function gets a results array (or object) containing all
+ the result arguments passed to the task callbacks.
+
+__Example__
+
+```js
+async.parallel([
+ function(callback){
+ setTimeout(function(){
+ callback(null, 'one');
+ }, 200);
+ },
+ function(callback){
+ setTimeout(function(){
+ callback(null, 'two');
+ }, 100);
+ }
+],
+// optional callback
+function(err, results){
+ // the results array will equal ['one','two'] even though
+ // the second function had a shorter timeout.
+});
+
+
+// an example using an object instead of an array
+async.parallel({
+ one: function(callback){
+ setTimeout(function(){
+ callback(null, 1);
+ }, 200);
+ },
+ two: function(callback){
+ setTimeout(function(){
+ callback(null, 2);
+ }, 100);
+ }
+},
+function(err, results) {
+ // results is now equals to: {one: 1, two: 2}
+});
+```
+
+__Related__
+
+* parallelLimit(tasks, limit, [callback])
+
+---------------------------------------
+
+
+### whilst(test, fn, callback)
+
+Repeatedly call `fn`, while `test` returns `true`. Calls `callback` when stopped,
+or an error occurs.
+
+__Arguments__
+
+* `test()` - synchronous truth test to perform before each execution of `fn`.
+* `fn(callback)` - A function which is called each time `test` passes. The function is
+ passed a `callback(err)`, which must be called once it has completed with an
+ optional `err` argument.
+* `callback(err, [results])` - A callback which is called after the test
+ function has failed and repeated execution of `fn` has stopped. `callback`
+ will be passed an error and any arguments passed to the final `fn`'s callback.
+
+__Example__
+
+```js
+var count = 0;
+
+async.whilst(
+ function () { return count < 5; },
+ function (callback) {
+ count++;
+ setTimeout(function () {
+ callback(null, count);
+ }, 1000);
+ },
+ function (err, n) {
+ // 5 seconds have passed, n = 5
+ }
+);
+```
+
+---------------------------------------
+
+
+### doWhilst(fn, test, callback)
+
+The post-check version of [`whilst`](#whilst). To reflect the difference in
+the order of operations, the arguments `test` and `fn` are switched.
+
+`doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
+
+---------------------------------------
+
+
+### until(test, fn, callback)
+
+Repeatedly call `fn` until `test` returns `true`. Calls `callback` when stopped,
+or an error occurs. `callback` will be passed an error and any arguments passed
+to the final `fn`'s callback.
+
+The inverse of [`whilst`](#whilst).
+
+---------------------------------------
+
+
+### doUntil(fn, test, callback)
+
+Like [`doWhilst`](#doWhilst), except the `test` is inverted. Note the argument ordering differs from `until`.
+
+---------------------------------------
+
+
+### during(test, fn, callback)
+
+Like [`whilst`](#whilst), except the `test` is an asynchronous function that is passed a callback in the form of `function (err, truth)`. If error is passed to `test` or `fn`, the main callback is immediately called with the value of the error.
+
+__Example__
+
+```js
+var count = 0;
+
+async.during(
+ function (callback) {
+ return callback(null, count < 5);
+ },
+ function (callback) {
+ count++;
+ setTimeout(callback, 1000);
+ },
+ function (err) {
+ // 5 seconds have passed
+ }
+);
+```
+
+---------------------------------------
+
+
+### doDuring(fn, test, callback)
+
+The post-check version of [`during`](#during). To reflect the difference in
+the order of operations, the arguments `test` and `fn` are switched.
+
+Also a version of [`doWhilst`](#doWhilst) with asynchronous `test` function.
+
+---------------------------------------
+
+
+### forever(fn, [errback])
+
+Calls the asynchronous function `fn` with a callback parameter that allows it to
+call itself again, in series, indefinitely.
+
+If an error is passed to the callback then `errback` is called with the
+error, and execution stops, otherwise it will never be called.
+
+```js
+async.forever(
+ function(next) {
+ // next is suitable for passing to things that need a callback(err [, whatever]);
+ // it will result in this function being called again.
+ },
+ function(err) {
+ // if next is called with a value in its first parameter, it will appear
+ // in here as 'err', and execution will stop.
+ }
+);
+```
+
+---------------------------------------
+
+
+### waterfall(tasks, [callback])
+
+Runs the `tasks` array of functions in series, each passing their results to the next in
+the array. However, if any of the `tasks` pass an error to their own callback, the
+next function is not executed, and the main `callback` is immediately called with
+the error.
+
+__Arguments__
+
+* `tasks` - An array of functions to run, each function is passed a
+ `callback(err, result1, result2, ...)` it must call on completion. The first
+ argument is an error (which can be `null`) and any further arguments will be
+ passed as arguments in order to the next task.
+* `callback(err, [results])` - An optional callback to run once all the functions
+ have completed. This will be passed the results of the last task's callback.
+
+
+
+__Example__
+
+```js
+async.waterfall([
+ function(callback) {
+ callback(null, 'one', 'two');
+ },
+ function(arg1, arg2, callback) {
+ // arg1 now equals 'one' and arg2 now equals 'two'
+ callback(null, 'three');
+ },
+ function(arg1, callback) {
+ // arg1 now equals 'three'
+ callback(null, 'done');
+ }
+], function (err, result) {
+ // result now equals 'done'
+});
+```
+Or, with named functions:
+
+```js
+async.waterfall([
+ myFirstFunction,
+ mySecondFunction,
+ myLastFunction,
+], function (err, result) {
+ // result now equals 'done'
+});
+function myFirstFunction(callback) {
+ callback(null, 'one', 'two');
+}
+function mySecondFunction(arg1, arg2, callback) {
+ // arg1 now equals 'one' and arg2 now equals 'two'
+ callback(null, 'three');
+}
+function myLastFunction(arg1, callback) {
+ // arg1 now equals 'three'
+ callback(null, 'done');
+}
+```
+
+Or, if you need to pass any argument to the first function:
+
+```js
+async.waterfall([
+ async.apply(myFirstFunction, 'zero'),
+ mySecondFunction,
+ myLastFunction,
+], function (err, result) {
+ // result now equals 'done'
+});
+function myFirstFunction(arg1, callback) {
+ // arg1 now equals 'zero'
+ callback(null, 'one', 'two');
+}
+function mySecondFunction(arg1, arg2, callback) {
+ // arg1 now equals 'one' and arg2 now equals 'two'
+ callback(null, 'three');
+}
+function myLastFunction(arg1, callback) {
+ // arg1 now equals 'three'
+ callback(null, 'done');
+}
+```
+
+---------------------------------------
+
+### compose(fn1, fn2...)
+
+Creates a function which is a composition of the passed asynchronous
+functions. Each function consumes the return value of the function that
+follows. Composing functions `f()`, `g()`, and `h()` would produce the result of
+`f(g(h()))`, only this version uses callbacks to obtain the return values.
+
+Each function is executed with the `this` binding of the composed function.
+
+__Arguments__
+
+* `functions...` - the asynchronous functions to compose
+
+
+__Example__
+
+```js
+function add1(n, callback) {
+ setTimeout(function () {
+ callback(null, n + 1);
+ }, 10);
+}
+
+function mul3(n, callback) {
+ setTimeout(function () {
+ callback(null, n * 3);
+ }, 10);
+}
+
+var add1mul3 = async.compose(mul3, add1);
+
+add1mul3(4, function (err, result) {
+ // result now equals 15
+});
+```
+
+---------------------------------------
+
+### seq(fn1, fn2...)
+
+Version of the compose function that is more natural to read.
+Each function consumes the return value of the previous function.
+It is the equivalent of [`compose`](#compose) with the arguments reversed.
+
+Each function is executed with the `this` binding of the composed function.
+
+__Arguments__
+
+* `functions...` - the asynchronous functions to compose
+
+
+__Example__
+
+```js
+// Requires lodash (or underscore), express3 and dresende's orm2.
+// Part of an app, that fetches cats of the logged user.
+// This example uses `seq` function to avoid overnesting and error
+// handling clutter.
+app.get('/cats', function(request, response) {
+ var User = request.models.User;
+ async.seq(
+ _.bind(User.get, User), // 'User.get' has signature (id, callback(err, data))
+ function(user, fn) {
+ user.getCats(fn); // 'getCats' has signature (callback(err, data))
+ }
+ )(req.session.user_id, function (err, cats) {
+ if (err) {
+ console.error(err);
+ response.json({ status: 'error', message: err.message });
+ } else {
+ response.json({ status: 'ok', message: 'Cats found', data: cats });
+ }
+ });
+});
+```
+
+---------------------------------------
+
+### applyEach(fns, args..., callback)
+
+Applies the provided arguments to each function in the array, calling
+`callback` after all functions have completed. If you only provide the first
+argument, then it will return a function which lets you pass in the
+arguments as if it were a single function call.
+
+__Arguments__
+
+* `fns` - the asynchronous functions to all call with the same arguments
+* `args...` - any number of separate arguments to pass to the function
+* `callback` - the final argument should be the callback, called when all
+ functions have completed processing
+
+
+__Example__
+
+```js
+async.applyEach([enableSearch, updateSchema], 'bucket', callback);
+
+// partial application example:
+async.each(
+ buckets,
+ async.applyEach([enableSearch, updateSchema]),
+ callback
+);
+```
+
+__Related__
+
+* applyEachSeries(tasks, args..., [callback])
+
+---------------------------------------
+
+
+### queue(worker, [concurrency])
+
+Creates a `queue` object with the specified `concurrency`. Tasks added to the
+`queue` are processed in parallel (up to the `concurrency` limit). If all
+`worker`s are in progress, the task is queued until one becomes available.
+Once a `worker` completes a `task`, that `task`'s callback is called.
+
+__Arguments__
+
+* `worker(task, callback)` - An asynchronous function for processing a queued
+ task, which must call its `callback(err)` argument when finished, with an
+ optional `error` as an argument. If you want to handle errors from an individual task, pass a callback to `q.push()`.
+* `concurrency` - An `integer` for determining how many `worker` functions should be
+ run in parallel. If omitted, the concurrency defaults to `1`. If the concurrency is `0`, an error is thrown.
+
+__Queue objects__
+
+The `queue` object returned by this function has the following properties and
+methods:
+
+* `length()` - a function returning the number of items waiting to be processed.
+* `started` - a function returning whether or not any items have been pushed and processed by the queue
+* `running()` - a function returning the number of items currently being processed.
+* `workersList()` - a function returning the array of items currently being processed.
+* `idle()` - a function returning false if there are items waiting or being processed, or true if not.
+* `concurrency` - an integer for determining how many `worker` functions should be
+ run in parallel. This property can be changed after a `queue` is created to
+ alter the concurrency on-the-fly.
+* `push(task, [callback])` - add a new task to the `queue`. Calls `callback` once
+ the `worker` has finished processing the task. Instead of a single task, a `tasks` array
+ can be submitted. The respective callback is used for every task in the list.
+* `unshift(task, [callback])` - add a new task to the front of the `queue`.
+* `saturated` - a callback that is called when the `queue` length hits the `concurrency` limit,
+ and further tasks will be queued.
+* `empty` - a callback that is called when the last item from the `queue` is given to a `worker`.
+* `drain` - a callback that is called when the last item from the `queue` has returned from the `worker`.
+* `paused` - a boolean for determining whether the queue is in a paused state
+* `pause()` - a function that pauses the processing of tasks until `resume()` is called.
+* `resume()` - a function that resumes the processing of queued tasks when the queue is paused.
+* `kill()` - a function that removes the `drain` callback and empties remaining tasks from the queue forcing it to go idle.
+
+__Example__
+
+```js
+// create a queue object with concurrency 2
+
+var q = async.queue(function (task, callback) {
+ console.log('hello ' + task.name);
+ callback();
+}, 2);
+
+
+// assign a callback
+q.drain = function() {
+ console.log('all items have been processed');
+}
+
+// add some items to the queue
+
+q.push({name: 'foo'}, function (err) {
+ console.log('finished processing foo');
+});
+q.push({name: 'bar'}, function (err) {
+ console.log('finished processing bar');
+});
+
+// add some items to the queue (batch-wise)
+
+q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
+ console.log('finished processing item');
+});
+
+// add some items to the front of the queue
+
+q.unshift({name: 'bar'}, function (err) {
+ console.log('finished processing bar');
+});
+```
+
+
+---------------------------------------
+
+
+### priorityQueue(worker, concurrency)
+
+The same as [`queue`](#queue) only tasks are assigned a priority and completed in ascending priority order. There are two differences between `queue` and `priorityQueue` objects:
+
+* `push(task, priority, [callback])` - `priority` should be a number. If an array of
+ `tasks` is given, all tasks will be assigned the same priority.
+* The `unshift` method was removed.
+
+---------------------------------------
+
+
+### cargo(worker, [payload])
+
+Creates a `cargo` object with the specified payload. Tasks added to the
+cargo will be processed altogether (up to the `payload` limit). If the
+`worker` is in progress, the task is queued until it becomes available. Once
+the `worker` has completed some tasks, each callback of those tasks is called.
+Check out [these](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) [animations](https://camo.githubusercontent.com/f4810e00e1c5f5f8addbe3e9f49064fd5d102699/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130312f38346339323036362d356632392d313165322d383134662d3964336430323431336266642e676966) for how `cargo` and `queue` work.
+
+While [queue](#queue) passes only one task to one of a group of workers
+at a time, cargo passes an array of tasks to a single worker, repeating
+when the worker is finished.
+
+__Arguments__
+
+* `worker(tasks, callback)` - An asynchronous function for processing an array of
+ queued tasks, which must call its `callback(err)` argument when finished, with
+ an optional `err` argument.
+* `payload` - An optional `integer` for determining how many tasks should be
+ processed per round; if omitted, the default is unlimited.
+
+__Cargo objects__
+
+The `cargo` object returned by this function has the following properties and
+methods:
+
+* `length()` - A function returning the number of items waiting to be processed.
+* `payload` - An `integer` for determining how many tasks should be
+ process per round. This property can be changed after a `cargo` is created to
+ alter the payload on-the-fly.
+* `push(task, [callback])` - Adds `task` to the `queue`. The callback is called
+ once the `worker` has finished processing the task. Instead of a single task, an array of `tasks`
+ can be submitted. The respective callback is used for every task in the list.
+* `saturated` - A callback that is called when the `queue.length()` hits the concurrency and further tasks will be queued.
+* `empty` - A callback that is called when the last item from the `queue` is given to a `worker`.
+* `drain` - A callback that is called when the last item from the `queue` has returned from the `worker`.
+* `idle()`, `pause()`, `resume()`, `kill()` - cargo inherits all of the same methods and event calbacks as [`queue`](#queue)
+
+__Example__
+
+```js
+// create a cargo object with payload 2
+
+var cargo = async.cargo(function (tasks, callback) {
+ for(var i=0; i
+### auto(tasks, [concurrency], [callback])
+
+Determines the best order for running the functions in `tasks`, based on their requirements. Each function can optionally depend on other functions being completed first, and each function is run as soon as its requirements are satisfied.
+
+If any of the functions pass an error to their callback, the `auto` sequence will stop. Further tasks will not execute (so any other functions depending on it will not run), and the main `callback` is immediately called with the error. Functions also receive an object containing the results of functions which have completed so far.
+
+Note, all functions are called with a `results` object as a second argument,
+so it is unsafe to pass functions in the `tasks` object which cannot handle the
+extra argument.
+
+For example, this snippet of code:
+
+```js
+async.auto({
+ readData: async.apply(fs.readFile, 'data.txt', 'utf-8')
+}, callback);
+```
+
+will have the effect of calling `readFile` with the results object as the last
+argument, which will fail:
+
+```js
+fs.readFile('data.txt', 'utf-8', cb, {});
+```
+
+Instead, wrap the call to `readFile` in a function which does not forward the
+`results` object:
+
+```js
+async.auto({
+ readData: function(cb, results){
+ fs.readFile('data.txt', 'utf-8', cb);
+ }
+}, callback);
+```
+
+__Arguments__
+
+* `tasks` - An object. Each of its properties is either a function or an array of
+ requirements, with the function itself the last item in the array. The object's key
+ of a property serves as the name of the task defined by that property,
+ i.e. can be used when specifying requirements for other tasks.
+ The function receives two arguments: (1) a `callback(err, result)` which must be
+ called when finished, passing an `error` (which can be `null`) and the result of
+ the function's execution, and (2) a `results` object, containing the results of
+ the previously executed functions.
+* `concurrency` - An optional `integer` for determining the maximum number of tasks that can be run in parallel. By default, as many as possible.
+* `callback(err, results)` - An optional callback which is called when all the
+ tasks have been completed. It receives the `err` argument if any `tasks`
+ pass an error to their callback. Results are always returned; however, if
+ an error occurs, no further `tasks` will be performed, and the results
+ object will only contain partial results.
+
+
+__Example__
+
+```js
+async.auto({
+ get_data: function(callback){
+ console.log('in get_data');
+ // async code to get some data
+ callback(null, 'data', 'converted to array');
+ },
+ make_folder: function(callback){
+ console.log('in make_folder');
+ // async code to create a directory to store a file in
+ // this is run at the same time as getting the data
+ callback(null, 'folder');
+ },
+ write_file: ['get_data', 'make_folder', function(callback, results){
+ console.log('in write_file', JSON.stringify(results));
+ // once there is some data and the directory exists,
+ // write the data to a file in the directory
+ callback(null, 'filename');
+ }],
+ email_link: ['write_file', function(callback, results){
+ console.log('in email_link', JSON.stringify(results));
+ // once the file is written let's email a link to it...
+ // results.write_file contains the filename returned by write_file.
+ callback(null, {'file':results.write_file, 'email':'user@example.com'});
+ }]
+}, function(err, results) {
+ console.log('err = ', err);
+ console.log('results = ', results);
+});
+```
+
+This is a fairly trivial example, but to do this using the basic parallel and
+series functions would look like this:
+
+```js
+async.parallel([
+ function(callback){
+ console.log('in get_data');
+ // async code to get some data
+ callback(null, 'data', 'converted to array');
+ },
+ function(callback){
+ console.log('in make_folder');
+ // async code to create a directory to store a file in
+ // this is run at the same time as getting the data
+ callback(null, 'folder');
+ }
+],
+function(err, results){
+ async.series([
+ function(callback){
+ console.log('in write_file', JSON.stringify(results));
+ // once there is some data and the directory exists,
+ // write the data to a file in the directory
+ results.push('filename');
+ callback(null);
+ },
+ function(callback){
+ console.log('in email_link', JSON.stringify(results));
+ // once the file is written let's email a link to it...
+ callback(null, {'file':results.pop(), 'email':'user@example.com'});
+ }
+ ]);
+});
+```
+
+For a complicated series of `async` tasks, using the [`auto`](#auto) function makes adding
+new tasks much easier (and the code more readable).
+
+
+---------------------------------------
+
+
+### retry([opts = {times: 5, interval: 0}| 5], task, [callback])
+
+Attempts to get a successful response from `task` no more than `times` times before
+returning an error. If the task is successful, the `callback` will be passed the result
+of the successful task. If all attempts fail, the callback will be passed the error and
+result (if any) of the final attempt.
+
+__Arguments__
+
+* `opts` - Can be either an object with `times` and `interval` or a number.
+ * `times` - The number of attempts to make before giving up. The default is `5`.
+ * `interval` - The time to wait between retries, in milliseconds. The default is `0`.
+ * If `opts` is a number, the number specifies the number of times to retry, with the default interval of `0`.
+* `task(callback, results)` - A function which receives two arguments: (1) a `callback(err, result)`
+ which must be called when finished, passing `err` (which can be `null`) and the `result` of
+ the function's execution, and (2) a `results` object, containing the results of
+ the previously executed functions (if nested inside another control flow).
+* `callback(err, results)` - An optional callback which is called when the
+ task has succeeded, or after the final failed attempt. It receives the `err` and `result` arguments of the last attempt at completing the `task`.
+
+The [`retry`](#retry) function can be used as a stand-alone control flow by passing a callback, as shown below:
+
+```js
+// try calling apiMethod 3 times
+async.retry(3, apiMethod, function(err, result) {
+ // do something with the result
+});
+```
+
+```js
+// try calling apiMethod 3 times, waiting 200 ms between each retry
+async.retry({times: 3, interval: 200}, apiMethod, function(err, result) {
+ // do something with the result
+});
+```
+
+```js
+// try calling apiMethod the default 5 times no delay between each retry
+async.retry(apiMethod, function(err, result) {
+ // do something with the result
+});
+```
+
+It can also be embedded within other control flow functions to retry individual methods
+that are not as reliable, like this:
+
+```js
+async.auto({
+ users: api.getUsers.bind(api),
+ payments: async.retry(3, api.getPayments.bind(api))
+}, function(err, results) {
+ // do something with the results
+});
+```
+
+
+---------------------------------------
+
+
+### iterator(tasks)
+
+Creates an iterator function which calls the next function in the `tasks` array,
+returning a continuation to call the next one after that. It's also possible to
+“peek” at the next iterator with `iterator.next()`.
+
+This function is used internally by the `async` module, but can be useful when
+you want to manually control the flow of functions in series.
+
+__Arguments__
+
+* `tasks` - An array of functions to run.
+
+__Example__
+
+```js
+var iterator = async.iterator([
+ function(){ sys.p('one'); },
+ function(){ sys.p('two'); },
+ function(){ sys.p('three'); }
+]);
+
+node> var iterator2 = iterator();
+'one'
+node> var iterator3 = iterator2();
+'two'
+node> iterator3();
+'three'
+node> var nextfn = iterator2.next();
+node> nextfn();
+'three'
+```
+
+---------------------------------------
+
+
+### apply(function, arguments..)
+
+Creates a continuation function with some arguments already applied.
+
+Useful as a shorthand when combined with other control flow functions. Any arguments
+passed to the returned function are added to the arguments originally passed
+to apply.
+
+__Arguments__
+
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to automatically apply when the
+ continuation is called.
+
+__Example__
+
+```js
+// using apply
+
+async.parallel([
+ async.apply(fs.writeFile, 'testfile1', 'test1'),
+ async.apply(fs.writeFile, 'testfile2', 'test2'),
+]);
+
+
+// the same process without using apply
+
+async.parallel([
+ function(callback){
+ fs.writeFile('testfile1', 'test1', callback);
+ },
+ function(callback){
+ fs.writeFile('testfile2', 'test2', callback);
+ }
+]);
+```
+
+It's possible to pass any number of additional arguments when calling the
+continuation:
+
+```js
+node> var fn = async.apply(sys.puts, 'one');
+node> fn('two', 'three');
+one
+two
+three
+```
+
+---------------------------------------
+
+
+### nextTick(callback), setImmediate(callback)
+
+Calls `callback` on a later loop around the event loop. In Node.js this just
+calls `process.nextTick`; in the browser it falls back to `setImmediate(callback)`
+if available, otherwise `setTimeout(callback, 0)`, which means other higher priority
+events may precede the execution of `callback`.
+
+This is used internally for browser-compatibility purposes.
+
+__Arguments__
+
+* `callback` - The function to call on a later loop around the event loop.
+
+__Example__
+
+```js
+var call_order = [];
+async.nextTick(function(){
+ call_order.push('two');
+ // call_order now equals ['one','two']
+});
+call_order.push('one')
+```
+
+
+### times(n, iterator, [callback])
+
+Calls the `iterator` function `n` times, and accumulates results in the same manner
+you would use with [`map`](#map).
+
+__Arguments__
+
+* `n` - The number of times to run the function.
+* `iterator` - The function to call `n` times.
+* `callback` - see [`map`](#map)
+
+__Example__
+
+```js
+// Pretend this is some complicated async factory
+var createUser = function(id, callback) {
+ callback(null, {
+ id: 'user' + id
+ })
+}
+// generate 5 users
+async.times(5, function(n, next){
+ createUser(n, function(err, user) {
+ next(err, user)
+ })
+}, function(err, users) {
+ // we should now have 5 users
+});
+```
+
+__Related__
+
+* timesSeries(n, iterator, [callback])
+* timesLimit(n, limit, iterator, [callback])
+
+
+## Utils
+
+
+### memoize(fn, [hasher])
+
+Caches the results of an `async` function. When creating a hash to store function
+results against, the callback is omitted from the hash and an optional hash
+function can be used.
+
+If no hash function is specified, the first argument is used as a hash key, which may work reasonably if it is a string or a data type that converts to a distinct string. Note that objects and arrays will not behave reasonably. Neither will cases where the other arguments are significant. In such cases, specify your own hash function.
+
+The cache of results is exposed as the `memo` property of the function returned
+by `memoize`.
+
+__Arguments__
+
+* `fn` - The function to proxy and cache results from.
+* `hasher` - An optional function for generating a custom hash for storing
+ results. It has all the arguments applied to it apart from the callback, and
+ must be synchronous.
+
+__Example__
+
+```js
+var slow_fn = function (name, callback) {
+ // do something
+ callback(null, result);
+};
+var fn = async.memoize(slow_fn);
+
+// fn can now be used as if it were slow_fn
+fn('some name', function () {
+ // callback
+});
+```
+
+
+### unmemoize(fn)
+
+Undoes a [`memoize`](#memoize)d function, reverting it to the original, unmemoized
+form. Handy for testing.
+
+__Arguments__
+
+* `fn` - the memoized function
+
+---------------------------------------
+
+
+### ensureAsync(fn)
+
+Wrap an async function and ensure it calls its callback on a later tick of the event loop. If the function already calls its callback on a next tick, no extra deferral is added. This is useful for preventing stack overflows (`RangeError: Maximum call stack size exceeded`) and generally keeping [Zalgo](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony) contained.
+
+__Arguments__
+
+* `fn` - an async function, one that expects a node-style callback as its last argument
+
+Returns a wrapped function with the exact same call signature as the function passed in.
+
+__Example__
+
+```js
+function sometimesAsync(arg, callback) {
+ if (cache[arg]) {
+ return callback(null, cache[arg]); // this would be synchronous!!
+ } else {
+ doSomeIO(arg, callback); // this IO would be asynchronous
+ }
+}
+
+// this has a risk of stack overflows if many results are cached in a row
+async.mapSeries(args, sometimesAsync, done);
+
+// this will defer sometimesAsync's callback if necessary,
+// preventing stack overflows
+async.mapSeries(args, async.ensureAsync(sometimesAsync), done);
+
+```
+
+---------------------------------------
+
+
+### constant(values...)
+
+Returns a function that when called, calls-back with the values provided. Useful as the first function in a `waterfall`, or for plugging values in to `auto`.
+
+__Example__
+
+```js
+async.waterfall([
+ async.constant(42),
+ function (value, next) {
+ // value === 42
+ },
+ //...
+], callback);
+
+async.waterfall([
+ async.constant(filename, "utf8"),
+ fs.readFile,
+ function (fileData, next) {
+ //...
+ }
+ //...
+], callback);
+
+async.auto({
+ hostname: async.constant("https://server.net/"),
+ port: findFreePort,
+ launchServer: ["hostname", "port", function (cb, options) {
+ startServer(options, cb);
+ }],
+ //...
+}, callback);
+
+```
+
+---------------------------------------
+
+
+
+### asyncify(func)
+
+__Alias:__ `wrapSync`
+
+Take a sync function and make it async, passing its return value to a callback. This is useful for plugging sync functions into a waterfall, series, or other async functions. Any arguments passed to the generated function will be passed to the wrapped function (except for the final callback argument). Errors thrown will be passed to the callback.
+
+__Example__
+
+```js
+async.waterfall([
+ async.apply(fs.readFile, filename, "utf8"),
+ async.asyncify(JSON.parse),
+ function (data, next) {
+ // data is the result of parsing the text.
+ // If there was a parsing error, it would have been caught.
+ }
+], callback)
+```
+
+If the function passed to `asyncify` returns a Promise, that promises's resolved/rejected state will be used to call the callback, rather than simply the synchronous return value. Example:
+
+```js
+async.waterfall([
+ async.apply(fs.readFile, filename, "utf8"),
+ async.asyncify(function (contents) {
+ return db.model.create(contents);
+ }),
+ function (model, next) {
+ // `model` is the instantiated model object.
+ // If there was an error, this function would be skipped.
+ }
+], callback)
+```
+
+This also means you can asyncify ES2016 `async` functions.
+
+```js
+var q = async.queue(async.asyncify(async function (file) {
+ var intermediateStep = await processFile(file);
+ return await somePromise(intermediateStep)
+}));
+
+q.push(files);
+```
+
+---------------------------------------
+
+
+### log(function, arguments)
+
+Logs the result of an `async` function to the `console`. Only works in Node.js or
+in browsers that support `console.log` and `console.error` (such as FF and Chrome).
+If multiple arguments are returned from the async function, `console.log` is
+called on each argument in order.
+
+__Arguments__
+
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to apply to the function.
+
+__Example__
+
+```js
+var hello = function(name, callback){
+ setTimeout(function(){
+ callback(null, 'hello ' + name);
+ }, 1000);
+};
+```
+```js
+node> async.log(hello, 'world');
+'hello world'
+```
+
+---------------------------------------
+
+
+### dir(function, arguments)
+
+Logs the result of an `async` function to the `console` using `console.dir` to
+display the properties of the resulting object. Only works in Node.js or
+in browsers that support `console.dir` and `console.error` (such as FF and Chrome).
+If multiple arguments are returned from the async function, `console.dir` is
+called on each argument in order.
+
+__Arguments__
+
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to apply to the function.
+
+__Example__
+
+```js
+var hello = function(name, callback){
+ setTimeout(function(){
+ callback(null, {hello: name});
+ }, 1000);
+};
+```
+```js
+node> async.dir(hello, 'world');
+{hello: 'world'}
+```
+
+---------------------------------------
+
+
+### noConflict()
+
+Changes the value of `async` back to its original value, returning a reference to the
+`async` object.
diff --git a/node_modules/mongoose/node_modules/async/dist/async.js b/node_modules/mongoose/node_modules/async/dist/async.js
new file mode 100644
index 0000000..31e7620
--- /dev/null
+++ b/node_modules/mongoose/node_modules/async/dist/async.js
@@ -0,0 +1,1265 @@
+/*!
+ * async
+ * https://github.com/caolan/async
+ *
+ * Copyright 2010-2014 Caolan McMahon
+ * Released under the MIT license
+ */
+(function () {
+
+ var async = {};
+ function noop() {}
+ function identity(v) {
+ return v;
+ }
+ function toBool(v) {
+ return !!v;
+ }
+ function notId(v) {
+ return !v;
+ }
+
+ // global on the server, window in the browser
+ var previous_async;
+
+ // Establish the root object, `window` (`self`) in the browser, `global`
+ // on the server, or `this` in some virtual machines. We use `self`
+ // instead of `window` for `WebWorker` support.
+ var root = typeof self === 'object' && self.self === self && self ||
+ typeof global === 'object' && global.global === global && global ||
+ this;
+
+ if (root != null) {
+ previous_async = root.async;
+ }
+
+ async.noConflict = function () {
+ root.async = previous_async;
+ return async;
+ };
+
+ function only_once(fn) {
+ return function() {
+ if (fn === null) throw new Error("Callback was already called.");
+ fn.apply(this, arguments);
+ fn = null;
+ };
+ }
+
+ function _once(fn) {
+ return function() {
+ if (fn === null) return;
+ fn.apply(this, arguments);
+ fn = null;
+ };
+ }
+
+ //// cross-browser compatiblity functions ////
+
+ var _toString = Object.prototype.toString;
+
+ var _isArray = Array.isArray || function (obj) {
+ return _toString.call(obj) === '[object Array]';
+ };
+
+ // Ported from underscore.js isObject
+ var _isObject = function(obj) {
+ var type = typeof obj;
+ return type === 'function' || type === 'object' && !!obj;
+ };
+
+ function _isArrayLike(arr) {
+ return _isArray(arr) || (
+ // has a positive integer length property
+ typeof arr.length === "number" &&
+ arr.length >= 0 &&
+ arr.length % 1 === 0
+ );
+ }
+
+ function _arrayEach(arr, iterator) {
+ var index = -1,
+ length = arr.length;
+
+ while (++index < length) {
+ iterator(arr[index], index, arr);
+ }
+ }
+
+ function _map(arr, iterator) {
+ var index = -1,
+ length = arr.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iterator(arr[index], index, arr);
+ }
+ return result;
+ }
+
+ function _range(count) {
+ return _map(Array(count), function (v, i) { return i; });
+ }
+
+ function _reduce(arr, iterator, memo) {
+ _arrayEach(arr, function (x, i, a) {
+ memo = iterator(memo, x, i, a);
+ });
+ return memo;
+ }
+
+ function _forEachOf(object, iterator) {
+ _arrayEach(_keys(object), function (key) {
+ iterator(object[key], key);
+ });
+ }
+
+ function _indexOf(arr, item) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] === item) return i;
+ }
+ return -1;
+ }
+
+ var _keys = Object.keys || function (obj) {
+ var keys = [];
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ keys.push(k);
+ }
+ }
+ return keys;
+ };
+
+ function _keyIterator(coll) {
+ var i = -1;
+ var len;
+ var keys;
+ if (_isArrayLike(coll)) {
+ len = coll.length;
+ return function next() {
+ i++;
+ return i < len ? i : null;
+ };
+ } else {
+ keys = _keys(coll);
+ len = keys.length;
+ return function next() {
+ i++;
+ return i < len ? keys[i] : null;
+ };
+ }
+ }
+
+ // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
+ // This accumulates the arguments passed into an array, after a given index.
+ // From underscore.js (https://github.com/jashkenas/underscore/pull/2140).
+ function _restParam(func, startIndex) {
+ startIndex = startIndex == null ? func.length - 1 : +startIndex;
+ return function() {
+ var length = Math.max(arguments.length - startIndex, 0);
+ var rest = Array(length);
+ for (var index = 0; index < length; index++) {
+ rest[index] = arguments[index + startIndex];
+ }
+ switch (startIndex) {
+ case 0: return func.call(this, rest);
+ case 1: return func.call(this, arguments[0], rest);
+ }
+ // Currently unused but handle cases outside of the switch statement:
+ // var args = Array(startIndex + 1);
+ // for (index = 0; index < startIndex; index++) {
+ // args[index] = arguments[index];
+ // }
+ // args[startIndex] = rest;
+ // return func.apply(this, args);
+ };
+ }
+
+ function _withoutIndex(iterator) {
+ return function (value, index, callback) {
+ return iterator(value, callback);
+ };
+ }
+
+ //// exported async module functions ////
+
+ //// nextTick implementation with browser-compatible fallback ////
+
+ // capture the global reference to guard against fakeTimer mocks
+ var _setImmediate = typeof setImmediate === 'function' && setImmediate;
+
+ var _delay = _setImmediate ? function(fn) {
+ // not a direct alias for IE10 compatibility
+ _setImmediate(fn);
+ } : function(fn) {
+ setTimeout(fn, 0);
+ };
+
+ if (typeof process === 'object' && typeof process.nextTick === 'function') {
+ async.nextTick = process.nextTick;
+ } else {
+ async.nextTick = _delay;
+ }
+ async.setImmediate = _setImmediate ? _delay : async.nextTick;
+
+
+ async.forEach =
+ async.each = function (arr, iterator, callback) {
+ return async.eachOf(arr, _withoutIndex(iterator), callback);
+ };
+
+ async.forEachSeries =
+ async.eachSeries = function (arr, iterator, callback) {
+ return async.eachOfSeries(arr, _withoutIndex(iterator), callback);
+ };
+
+
+ async.forEachLimit =
+ async.eachLimit = function (arr, limit, iterator, callback) {
+ return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback);
+ };
+
+ async.forEachOf =
+ async.eachOf = function (object, iterator, callback) {
+ callback = _once(callback || noop);
+ object = object || [];
+
+ var iter = _keyIterator(object);
+ var key, completed = 0;
+
+ while ((key = iter()) != null) {
+ completed += 1;
+ iterator(object[key], key, only_once(done));
+ }
+
+ if (completed === 0) callback(null);
+
+ function done(err) {
+ completed--;
+ if (err) {
+ callback(err);
+ }
+ // Check key is null in case iterator isn't exhausted
+ // and done resolved synchronously.
+ else if (key === null && completed <= 0) {
+ callback(null);
+ }
+ }
+ };
+
+ async.forEachOfSeries =
+ async.eachOfSeries = function (obj, iterator, callback) {
+ callback = _once(callback || noop);
+ obj = obj || [];
+ var nextKey = _keyIterator(obj);
+ var key = nextKey();
+ function iterate() {
+ var sync = true;
+ if (key === null) {
+ return callback(null);
+ }
+ iterator(obj[key], key, only_once(function (err) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ key = nextKey();
+ if (key === null) {
+ return callback(null);
+ } else {
+ if (sync) {
+ async.setImmediate(iterate);
+ } else {
+ iterate();
+ }
+ }
+ }
+ }));
+ sync = false;
+ }
+ iterate();
+ };
+
+
+
+ async.forEachOfLimit =
+ async.eachOfLimit = function (obj, limit, iterator, callback) {
+ _eachOfLimit(limit)(obj, iterator, callback);
+ };
+
+ function _eachOfLimit(limit) {
+
+ return function (obj, iterator, callback) {
+ callback = _once(callback || noop);
+ obj = obj || [];
+ var nextKey = _keyIterator(obj);
+ if (limit <= 0) {
+ return callback(null);
+ }
+ var done = false;
+ var running = 0;
+ var errored = false;
+
+ (function replenish () {
+ if (done && running <= 0) {
+ return callback(null);
+ }
+
+ while (running < limit && !errored) {
+ var key = nextKey();
+ if (key === null) {
+ done = true;
+ if (running <= 0) {
+ callback(null);
+ }
+ return;
+ }
+ running += 1;
+ iterator(obj[key], key, only_once(function (err) {
+ running -= 1;
+ if (err) {
+ callback(err);
+ errored = true;
+ }
+ else {
+ replenish();
+ }
+ }));
+ }
+ })();
+ };
+ }
+
+
+ function doParallel(fn) {
+ return function (obj, iterator, callback) {
+ return fn(async.eachOf, obj, iterator, callback);
+ };
+ }
+ function doParallelLimit(fn) {
+ return function (obj, limit, iterator, callback) {
+ return fn(_eachOfLimit(limit), obj, iterator, callback);
+ };
+ }
+ function doSeries(fn) {
+ return function (obj, iterator, callback) {
+ return fn(async.eachOfSeries, obj, iterator, callback);
+ };
+ }
+
+ function _asyncMap(eachfn, arr, iterator, callback) {
+ callback = _once(callback || noop);
+ arr = arr || [];
+ var results = _isArrayLike(arr) ? [] : {};
+ eachfn(arr, function (value, index, callback) {
+ iterator(value, function (err, v) {
+ results[index] = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+
+ async.map = doParallel(_asyncMap);
+ async.mapSeries = doSeries(_asyncMap);
+ async.mapLimit = doParallelLimit(_asyncMap);
+
+ // reduce only has a series version, as doing reduce in parallel won't
+ // work in many situations.
+ async.inject =
+ async.foldl =
+ async.reduce = function (arr, memo, iterator, callback) {
+ async.eachOfSeries(arr, function (x, i, callback) {
+ iterator(memo, x, function (err, v) {
+ memo = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, memo);
+ });
+ };
+
+ async.foldr =
+ async.reduceRight = function (arr, memo, iterator, callback) {
+ var reversed = _map(arr, identity).reverse();
+ async.reduce(reversed, memo, iterator, callback);
+ };
+
+ async.transform = function (arr, memo, iterator, callback) {
+ if (arguments.length === 3) {
+ callback = iterator;
+ iterator = memo;
+ memo = _isArray(arr) ? [] : {};
+ }
+
+ async.eachOf(arr, function(v, k, cb) {
+ iterator(memo, v, k, cb);
+ }, function(err) {
+ callback(err, memo);
+ });
+ };
+
+ function _filter(eachfn, arr, iterator, callback) {
+ var results = [];
+ eachfn(arr, function (x, index, callback) {
+ iterator(x, function (v) {
+ if (v) {
+ results.push({index: index, value: x});
+ }
+ callback();
+ });
+ }, function () {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ }
+
+ async.select =
+ async.filter = doParallel(_filter);
+
+ async.selectLimit =
+ async.filterLimit = doParallelLimit(_filter);
+
+ async.selectSeries =
+ async.filterSeries = doSeries(_filter);
+
+ function _reject(eachfn, arr, iterator, callback) {
+ _filter(eachfn, arr, function(value, cb) {
+ iterator(value, function(v) {
+ cb(!v);
+ });
+ }, callback);
+ }
+ async.reject = doParallel(_reject);
+ async.rejectLimit = doParallelLimit(_reject);
+ async.rejectSeries = doSeries(_reject);
+
+ function _createTester(eachfn, check, getResult) {
+ return function(arr, limit, iterator, cb) {
+ function done() {
+ if (cb) cb(getResult(false, void 0));
+ }
+ function iteratee(x, _, callback) {
+ if (!cb) return callback();
+ iterator(x, function (v) {
+ if (cb && check(v)) {
+ cb(getResult(true, x));
+ cb = iterator = false;
+ }
+ callback();
+ });
+ }
+ if (arguments.length > 3) {
+ eachfn(arr, limit, iteratee, done);
+ } else {
+ cb = iterator;
+ iterator = limit;
+ eachfn(arr, iteratee, done);
+ }
+ };
+ }
+
+ async.any =
+ async.some = _createTester(async.eachOf, toBool, identity);
+
+ async.someLimit = _createTester(async.eachOfLimit, toBool, identity);
+
+ async.all =
+ async.every = _createTester(async.eachOf, notId, notId);
+
+ async.everyLimit = _createTester(async.eachOfLimit, notId, notId);
+
+ function _findGetResult(v, x) {
+ return x;
+ }
+ async.detect = _createTester(async.eachOf, identity, _findGetResult);
+ async.detectSeries = _createTester(async.eachOfSeries, identity, _findGetResult);
+ async.detectLimit = _createTester(async.eachOfLimit, identity, _findGetResult);
+
+ async.sortBy = function (arr, iterator, callback) {
+ async.map(arr, function (x, callback) {
+ iterator(x, function (err, criteria) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ callback(null, {value: x, criteria: criteria});
+ }
+ });
+ }, function (err, results) {
+ if (err) {
+ return callback(err);
+ }
+ else {
+ callback(null, _map(results.sort(comparator), function (x) {
+ return x.value;
+ }));
+ }
+
+ });
+
+ function comparator(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }
+ };
+
+ async.auto = function (tasks, concurrency, callback) {
+ if (typeof arguments[1] === 'function') {
+ // concurrency is optional, shift the args.
+ callback = concurrency;
+ concurrency = null;
+ }
+ callback = _once(callback || noop);
+ var keys = _keys(tasks);
+ var remainingTasks = keys.length;
+ if (!remainingTasks) {
+ return callback(null);
+ }
+ if (!concurrency) {
+ concurrency = remainingTasks;
+ }
+
+ var results = {};
+ var runningTasks = 0;
+
+ var hasError = false;
+
+ var listeners = [];
+ function addListener(fn) {
+ listeners.unshift(fn);
+ }
+ function removeListener(fn) {
+ var idx = _indexOf(listeners, fn);
+ if (idx >= 0) listeners.splice(idx, 1);
+ }
+ function taskComplete() {
+ remainingTasks--;
+ _arrayEach(listeners.slice(0), function (fn) {
+ fn();
+ });
+ }
+
+ addListener(function () {
+ if (!remainingTasks) {
+ callback(null, results);
+ }
+ });
+
+ _arrayEach(keys, function (k) {
+ if (hasError) return;
+ var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
+ var taskCallback = _restParam(function(err, args) {
+ runningTasks--;
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ if (err) {
+ var safeResults = {};
+ _forEachOf(results, function(val, rkey) {
+ safeResults[rkey] = val;
+ });
+ safeResults[k] = args;
+ hasError = true;
+
+ callback(err, safeResults);
+ }
+ else {
+ results[k] = args;
+ async.setImmediate(taskComplete);
+ }
+ });
+ var requires = task.slice(0, task.length - 1);
+ // prevent dead-locks
+ var len = requires.length;
+ var dep;
+ while (len--) {
+ if (!(dep = tasks[requires[len]])) {
+ throw new Error('Has nonexistent dependency in ' + requires.join(', '));
+ }
+ if (_isArray(dep) && _indexOf(dep, k) >= 0) {
+ throw new Error('Has cyclic dependencies');
+ }
+ }
+ function ready() {
+ return runningTasks < concurrency && _reduce(requires, function (a, x) {
+ return (a && results.hasOwnProperty(x));
+ }, true) && !results.hasOwnProperty(k);
+ }
+ if (ready()) {
+ runningTasks++;
+ task[task.length - 1](taskCallback, results);
+ }
+ else {
+ addListener(listener);
+ }
+ function listener() {
+ if (ready()) {
+ runningTasks++;
+ removeListener(listener);
+ task[task.length - 1](taskCallback, results);
+ }
+ }
+ });
+ };
+
+
+
+ async.retry = function(times, task, callback) {
+ var DEFAULT_TIMES = 5;
+ var DEFAULT_INTERVAL = 0;
+
+ var attempts = [];
+
+ var opts = {
+ times: DEFAULT_TIMES,
+ interval: DEFAULT_INTERVAL
+ };
+
+ function parseTimes(acc, t){
+ if(typeof t === 'number'){
+ acc.times = parseInt(t, 10) || DEFAULT_TIMES;
+ } else if(typeof t === 'object'){
+ acc.times = parseInt(t.times, 10) || DEFAULT_TIMES;
+ acc.interval = parseInt(t.interval, 10) || DEFAULT_INTERVAL;
+ } else {
+ throw new Error('Unsupported argument type for \'times\': ' + typeof t);
+ }
+ }
+
+ var length = arguments.length;
+ if (length < 1 || length > 3) {
+ throw new Error('Invalid arguments - must be either (task), (task, callback), (times, task) or (times, task, callback)');
+ } else if (length <= 2 && typeof times === 'function') {
+ callback = task;
+ task = times;
+ }
+ if (typeof times !== 'function') {
+ parseTimes(opts, times);
+ }
+ opts.callback = callback;
+ opts.task = task;
+
+ function wrappedTask(wrappedCallback, wrappedResults) {
+ function retryAttempt(task, finalAttempt) {
+ return function(seriesCallback) {
+ task(function(err, result){
+ seriesCallback(!err || finalAttempt, {err: err, result: result});
+ }, wrappedResults);
+ };
+ }
+
+ function retryInterval(interval){
+ return function(seriesCallback){
+ setTimeout(function(){
+ seriesCallback(null);
+ }, interval);
+ };
+ }
+
+ while (opts.times) {
+
+ var finalAttempt = !(opts.times-=1);
+ attempts.push(retryAttempt(opts.task, finalAttempt));
+ if(!finalAttempt && opts.interval > 0){
+ attempts.push(retryInterval(opts.interval));
+ }
+ }
+
+ async.series(attempts, function(done, data){
+ data = data[data.length - 1];
+ (wrappedCallback || opts.callback)(data.err, data.result);
+ });
+ }
+
+ // If a callback is passed, run this as a controll flow
+ return opts.callback ? wrappedTask() : wrappedTask;
+ };
+
+ async.waterfall = function (tasks, callback) {
+ callback = _once(callback || noop);
+ if (!_isArray(tasks)) {
+ var err = new Error('First argument to waterfall must be an array of functions');
+ return callback(err);
+ }
+ if (!tasks.length) {
+ return callback();
+ }
+ function wrapIterator(iterator) {
+ return _restParam(function (err, args) {
+ if (err) {
+ callback.apply(null, [err].concat(args));
+ }
+ else {
+ var next = iterator.next();
+ if (next) {
+ args.push(wrapIterator(next));
+ }
+ else {
+ args.push(callback);
+ }
+ ensureAsync(iterator).apply(null, args);
+ }
+ });
+ }
+ wrapIterator(async.iterator(tasks))();
+ };
+
+ function _parallel(eachfn, tasks, callback) {
+ callback = callback || noop;
+ var results = _isArrayLike(tasks) ? [] : {};
+
+ eachfn(tasks, function (task, key, callback) {
+ task(_restParam(function (err, args) {
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ results[key] = args;
+ callback(err);
+ }));
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+
+ async.parallel = function (tasks, callback) {
+ _parallel(async.eachOf, tasks, callback);
+ };
+
+ async.parallelLimit = function(tasks, limit, callback) {
+ _parallel(_eachOfLimit(limit), tasks, callback);
+ };
+
+ async.series = function(tasks, callback) {
+ _parallel(async.eachOfSeries, tasks, callback);
+ };
+
+ async.iterator = function (tasks) {
+ function makeCallback(index) {
+ function fn() {
+ if (tasks.length) {
+ tasks[index].apply(null, arguments);
+ }
+ return fn.next();
+ }
+ fn.next = function () {
+ return (index < tasks.length - 1) ? makeCallback(index + 1): null;
+ };
+ return fn;
+ }
+ return makeCallback(0);
+ };
+
+ async.apply = _restParam(function (fn, args) {
+ return _restParam(function (callArgs) {
+ return fn.apply(
+ null, args.concat(callArgs)
+ );
+ });
+ });
+
+ function _concat(eachfn, arr, fn, callback) {
+ var result = [];
+ eachfn(arr, function (x, index, cb) {
+ fn(x, function (err, y) {
+ result = result.concat(y || []);
+ cb(err);
+ });
+ }, function (err) {
+ callback(err, result);
+ });
+ }
+ async.concat = doParallel(_concat);
+ async.concatSeries = doSeries(_concat);
+
+ async.whilst = function (test, iterator, callback) {
+ callback = callback || noop;
+ if (test()) {
+ var next = _restParam(function(err, args) {
+ if (err) {
+ callback(err);
+ } else if (test.apply(this, args)) {
+ iterator(next);
+ } else {
+ callback.apply(null, [null].concat(args));
+ }
+ });
+ iterator(next);
+ } else {
+ callback(null);
+ }
+ };
+
+ async.doWhilst = function (iterator, test, callback) {
+ var calls = 0;
+ return async.whilst(function() {
+ return ++calls <= 1 || test.apply(this, arguments);
+ }, iterator, callback);
+ };
+
+ async.until = function (test, iterator, callback) {
+ return async.whilst(function() {
+ return !test.apply(this, arguments);
+ }, iterator, callback);
+ };
+
+ async.doUntil = function (iterator, test, callback) {
+ return async.doWhilst(iterator, function() {
+ return !test.apply(this, arguments);
+ }, callback);
+ };
+
+ async.during = function (test, iterator, callback) {
+ callback = callback || noop;
+
+ var next = _restParam(function(err, args) {
+ if (err) {
+ callback(err);
+ } else {
+ args.push(check);
+ test.apply(this, args);
+ }
+ });
+
+ var check = function(err, truth) {
+ if (err) {
+ callback(err);
+ } else if (truth) {
+ iterator(next);
+ } else {
+ callback(null);
+ }
+ };
+
+ test(check);
+ };
+
+ async.doDuring = function (iterator, test, callback) {
+ var calls = 0;
+ async.during(function(next) {
+ if (calls++ < 1) {
+ next(null, true);
+ } else {
+ test.apply(this, arguments);
+ }
+ }, iterator, callback);
+ };
+
+ function _queue(worker, concurrency, payload) {
+ if (concurrency == null) {
+ concurrency = 1;
+ }
+ else if(concurrency === 0) {
+ throw new Error('Concurrency must not be zero');
+ }
+ function _insert(q, data, pos, callback) {
+ if (callback != null && typeof callback !== "function") {
+ throw new Error("task callback must be a function");
+ }
+ q.started = true;
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length === 0 && q.idle()) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ q.drain();
+ });
+ }
+ _arrayEach(data, function(task) {
+ var item = {
+ data: task,
+ callback: callback || noop
+ };
+
+ if (pos) {
+ q.tasks.unshift(item);
+ } else {
+ q.tasks.push(item);
+ }
+
+ if (q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ });
+ async.setImmediate(q.process);
+ }
+ function _next(q, tasks) {
+ return function(){
+ workers -= 1;
+
+ var removed = false;
+ var args = arguments;
+ _arrayEach(tasks, function (task) {
+ _arrayEach(workersList, function (worker, index) {
+ if (worker === task && !removed) {
+ workersList.splice(index, 1);
+ removed = true;
+ }
+ });
+
+ task.callback.apply(task, args);
+ });
+ if (q.tasks.length + workers === 0) {
+ q.drain();
+ }
+ q.process();
+ };
+ }
+
+ var workers = 0;
+ var workersList = [];
+ var q = {
+ tasks: [],
+ concurrency: concurrency,
+ payload: payload,
+ saturated: noop,
+ empty: noop,
+ drain: noop,
+ started: false,
+ paused: false,
+ push: function (data, callback) {
+ _insert(q, data, false, callback);
+ },
+ kill: function () {
+ q.drain = noop;
+ q.tasks = [];
+ },
+ unshift: function (data, callback) {
+ _insert(q, data, true, callback);
+ },
+ process: function () {
+ while(!q.paused && workers < q.concurrency && q.tasks.length){
+
+ var tasks = q.payload ?
+ q.tasks.splice(0, q.payload) :
+ q.tasks.splice(0, q.tasks.length);
+
+ var data = _map(tasks, function (task) {
+ return task.data;
+ });
+
+ if (q.tasks.length === 0) {
+ q.empty();
+ }
+ workers += 1;
+ workersList.push(tasks[0]);
+ var cb = only_once(_next(q, tasks));
+ worker(data, cb);
+ }
+ },
+ length: function () {
+ return q.tasks.length;
+ },
+ running: function () {
+ return workers;
+ },
+ workersList: function () {
+ return workersList;
+ },
+ idle: function() {
+ return q.tasks.length + workers === 0;
+ },
+ pause: function () {
+ q.paused = true;
+ },
+ resume: function () {
+ if (q.paused === false) { return; }
+ q.paused = false;
+ var resumeCount = Math.min(q.concurrency, q.tasks.length);
+ // Need to call q.process once per concurrent
+ // worker to preserve full concurrency after pause
+ for (var w = 1; w <= resumeCount; w++) {
+ async.setImmediate(q.process);
+ }
+ }
+ };
+ return q;
+ }
+
+ async.queue = function (worker, concurrency) {
+ var q = _queue(function (items, cb) {
+ worker(items[0], cb);
+ }, concurrency, 1);
+
+ return q;
+ };
+
+ async.priorityQueue = function (worker, concurrency) {
+
+ function _compareTasks(a, b){
+ return a.priority - b.priority;
+ }
+
+ function _binarySearch(sequence, item, compare) {
+ var beg = -1,
+ end = sequence.length - 1;
+ while (beg < end) {
+ var mid = beg + ((end - beg + 1) >>> 1);
+ if (compare(item, sequence[mid]) >= 0) {
+ beg = mid;
+ } else {
+ end = mid - 1;
+ }
+ }
+ return beg;
+ }
+
+ function _insert(q, data, priority, callback) {
+ if (callback != null && typeof callback !== "function") {
+ throw new Error("task callback must be a function");
+ }
+ q.started = true;
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length === 0) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ q.drain();
+ });
+ }
+ _arrayEach(data, function(task) {
+ var item = {
+ data: task,
+ priority: priority,
+ callback: typeof callback === 'function' ? callback : noop
+ };
+
+ q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
+
+ if (q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
+ }
+
+ // Start with a normal queue
+ var q = async.queue(worker, concurrency);
+
+ // Override push to accept second parameter representing priority
+ q.push = function (data, priority, callback) {
+ _insert(q, data, priority, callback);
+ };
+
+ // Remove unshift function
+ delete q.unshift;
+
+ return q;
+ };
+
+ async.cargo = function (worker, payload) {
+ return _queue(worker, 1, payload);
+ };
+
+ function _console_fn(name) {
+ return _restParam(function (fn, args) {
+ fn.apply(null, args.concat([_restParam(function (err, args) {
+ if (typeof console === 'object') {
+ if (err) {
+ if (console.error) {
+ console.error(err);
+ }
+ }
+ else if (console[name]) {
+ _arrayEach(args, function (x) {
+ console[name](x);
+ });
+ }
+ }
+ })]));
+ });
+ }
+ async.log = _console_fn('log');
+ async.dir = _console_fn('dir');
+ /*async.info = _console_fn('info');
+ async.warn = _console_fn('warn');
+ async.error = _console_fn('error');*/
+
+ async.memoize = function (fn, hasher) {
+ var memo = {};
+ var queues = {};
+ var has = Object.prototype.hasOwnProperty;
+ hasher = hasher || identity;
+ var memoized = _restParam(function memoized(args) {
+ var callback = args.pop();
+ var key = hasher.apply(null, args);
+ if (has.call(memo, key)) {
+ async.setImmediate(function () {
+ callback.apply(null, memo[key]);
+ });
+ }
+ else if (has.call(queues, key)) {
+ queues[key].push(callback);
+ }
+ else {
+ queues[key] = [callback];
+ fn.apply(null, args.concat([_restParam(function (args) {
+ memo[key] = args;
+ var q = queues[key];
+ delete queues[key];
+ for (var i = 0, l = q.length; i < l; i++) {
+ q[i].apply(null, args);
+ }
+ })]));
+ }
+ });
+ memoized.memo = memo;
+ memoized.unmemoized = fn;
+ return memoized;
+ };
+
+ async.unmemoize = function (fn) {
+ return function () {
+ return (fn.unmemoized || fn).apply(null, arguments);
+ };
+ };
+
+ function _times(mapper) {
+ return function (count, iterator, callback) {
+ mapper(_range(count), iterator, callback);
+ };
+ }
+
+ async.times = _times(async.map);
+ async.timesSeries = _times(async.mapSeries);
+ async.timesLimit = function (count, limit, iterator, callback) {
+ return async.mapLimit(_range(count), limit, iterator, callback);
+ };
+
+ async.seq = function (/* functions... */) {
+ var fns = arguments;
+ return _restParam(function (args) {
+ var that = this;
+
+ var callback = args[args.length - 1];
+ if (typeof callback == 'function') {
+ args.pop();
+ } else {
+ callback = noop;
+ }
+
+ async.reduce(fns, args, function (newargs, fn, cb) {
+ fn.apply(that, newargs.concat([_restParam(function (err, nextargs) {
+ cb(err, nextargs);
+ })]));
+ },
+ function (err, results) {
+ callback.apply(that, [err].concat(results));
+ });
+ });
+ };
+
+ async.compose = function (/* functions... */) {
+ return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+ };
+
+
+ function _applyEach(eachfn) {
+ return _restParam(function(fns, args) {
+ var go = _restParam(function(args) {
+ var that = this;
+ var callback = args.pop();
+ return eachfn(fns, function (fn, _, cb) {
+ fn.apply(that, args.concat([cb]));
+ },
+ callback);
+ });
+ if (args.length) {
+ return go.apply(this, args);
+ }
+ else {
+ return go;
+ }
+ });
+ }
+
+ async.applyEach = _applyEach(async.eachOf);
+ async.applyEachSeries = _applyEach(async.eachOfSeries);
+
+
+ async.forever = function (fn, callback) {
+ var done = only_once(callback || noop);
+ var task = ensureAsync(fn);
+ function next(err) {
+ if (err) {
+ return done(err);
+ }
+ task(next);
+ }
+ next();
+ };
+
+ function ensureAsync(fn) {
+ return _restParam(function (args) {
+ var callback = args.pop();
+ args.push(function () {
+ var innerArgs = arguments;
+ if (sync) {
+ async.setImmediate(function () {
+ callback.apply(null, innerArgs);
+ });
+ } else {
+ callback.apply(null, innerArgs);
+ }
+ });
+ var sync = true;
+ fn.apply(this, args);
+ sync = false;
+ });
+ }
+
+ async.ensureAsync = ensureAsync;
+
+ async.constant = _restParam(function(values) {
+ var args = [null].concat(values);
+ return function (callback) {
+ return callback.apply(this, args);
+ };
+ });
+
+ async.wrapSync =
+ async.asyncify = function asyncify(func) {
+ return _restParam(function (args) {
+ var callback = args.pop();
+ var result;
+ try {
+ result = func.apply(this, args);
+ } catch (e) {
+ return callback(e);
+ }
+ // if result is Promise object
+ if (_isObject(result) && typeof result.then === "function") {
+ result.then(function(value) {
+ callback(null, value);
+ })["catch"](function(err) {
+ callback(err.message ? err : new Error(err));
+ });
+ } else {
+ callback(null, result);
+ }
+ });
+ };
+
+ // Node.js
+ if (typeof module === 'object' && module.exports) {
+ module.exports = async;
+ }
+ // AMD / RequireJS
+ else if (typeof define === 'function' && define.amd) {
+ define([], function () {
+ return async;
+ });
+ }
+ // included directly via
+
+
+
+
+