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 b/node_modules/mongoose/.eslintrc
new file mode 100644
index 0000000..99f448b
--- /dev/null
+++ b/node_modules/mongoose/.eslintrc
@@ -0,0 +1,31 @@
+extends: 'eslint:recommended'
+
+env:
+ node: true
+ mocha: true
+
+rules:
+ comma-style: 2
+ consistent-this:
+ - 1
+ - _this
+ indent:
+ - 2
+ - 2
+ - SwitchCase: 1
+ VariableDeclarator: 2
+ key-spacing: 1
+ no-console: 0
+ no-multi-spaces: 1
+ no-spaced-func: 2
+ no-trailing-spaces: 2
+ semi: 2
+ space-after-keywords: 2
+ space-before-blocks: 2
+ space-before-function-paren:
+ - 2
+ - never
+ space-before-keywords: 2
+ space-infix-ops: 2
+ space-return-throw-case: 2
+ space-unary-ops: 1
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..dcdb2c7 100644
--- a/node_modules/mongoose/.travis.yml
+++ b/node_modules/mongoose/.travis.yml
@@ -1,7 +1,10 @@
language: node_js
+sudo: false
node_js:
- - 0.6
- - 0.8
- - 0.10
+ - "5"
+ - "4"
+ - "0.12"
+ - "0.10"
+ - "iojs"
services:
- mongodb
diff --git a/node_modules/mongoose/CONTRIBUTING.md b/node_modules/mongoose/CONTRIBUTING.md
index daddb4a..514657e 100644
--- a/node_modules/mongoose/CONTRIBUTING.md
+++ b/node_modules/mongoose/CONTRIBUTING.md
@@ -1,7 +1,5 @@
## 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.
### Reporting bugs
@@ -41,7 +39,7 @@ If you have a question about Mongoose (not a bug report) please post it to eithe
- 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="..."
+ - any mocha flags can be specified with `T="..."`
### Documentation
@@ -49,7 +47,7 @@ To contribute to the [API documentation](http://mongoosejs.com/docs/api.html) ju
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.
-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
diff --git a/node_modules/mongoose/History.md b/node_modules/mongoose/History.md
index 2e3b58c..3463892 100644
--- a/node_modules/mongoose/History.md
+++ b/node_modules/mongoose/History.md
@@ -1,3 +1,1033 @@
+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 #3254 [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
+===================
+
+ * fixed; repopulating modified populated paths #1697
+ * fixed; doc.equals w/ _id false #1687
+ * fixed; strict mode warning #1686
+ * docs; near/nearSphere
+
+3.6.19 (stable) / 2013-09-04
+==================
+
+ * fixed; population field selection w/ strings #1669
+ * docs; Date method caveats #1598
+
+3.6.18 (stable) / 2013-08-22
+===================
+
+ * updated; warn when using an unstable version of mongoose
+ * updated; mocha to 1.12.0
+ * updated; mongodb driver to 1.3.19 (fix error swallowing behavior)
+ * fixed; setters not firing on null values #1445 [ebensing](https://github.com/ebensing)
+ * fixed; properly exclude subdocument fields #1280 [ebensing](https://github.com/ebensing)
+ * fixed; cast error in findAndModify #1643 [aheuermann](https://github.com/aheuermann)
+ * website; update guide
+ * website; added documentation for safe:false and versioning interaction
+ * docs; mention that middleware dont run on Models
+ * docs; fix indexes link
+ * make; suppress warning msg in test
+ * tests; moar
+
+3.6.17 / 2013-08-13
+===================
+
+ * updated; driver to 1.3.18 (fixes memory leak)
+ * fixed; casting ref docs on creation #1606
+ * docs; query options
+
+3.6.16 / 2013-08-08
+===================
+
+ * added; publicly expose connection states #1585
+ * fixed; limit applies to individual items on population #1490 [ebensing](https://github.com/ebensing)
+ * fixed; positional operator casting in updates #1572 [ebensing](https://github.com/ebensing)
+ * updated; MongoDB driver to 1.3.17
+ * updated; sliced to 0.0.5
+ * website; tweak homepage
+ * tests; fixed + added
+ * docs; fix some examples
+ * docs; multi-mongos support details
+ * docs; auto open browser after starting static server
3.6.15 / 2013-07-16
==================
@@ -309,6 +1339,20 @@
* updated; mpath to 0.1.1
* updated; docs
+3.5.16 / 2013-08-13
+===================
+
+ * updated; driver to 1.3.18
+
+3.5.15 / 2013-07-26
+==================
+
+ * updated; sliced to 0.0.5
+ * updated; driver to 1.3.12
+ * fixed; regression in Query#count() due to driver change
+ * tests; fixed timeouts
+ * tests; handle differing test uris
+
3.5.14 / 2013-05-15
===================
@@ -1383,7 +2427,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)
@@ -1492,7 +2536,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
@@ -1550,7 +2594,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`.
@@ -1585,28 +2629,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]
@@ -1615,7 +2659,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
@@ -1624,14 +2668,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]
@@ -1645,20 +2689,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]
@@ -1666,7 +2710,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 71af427..fd8077d 100644
--- a/node_modules/mongoose/README.md
+++ b/node_modules/mongoose/README.md
@@ -1,69 +1,83 @@
-## What's Mongoose?
+# Mongoose
Mongoose is a [MongoDB](http://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment.
+[](https://travis-ci.org/Automattic/mongoose)
+[](https://gitter.im/Automattic/mongoose?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](http://badge.fury.io/js/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)
- - [10gen support](http://www.mongodb.org/display/DOCS/Technical+Support)
+ - [MongoDB support](http://www.mongodb.org/display/DOCS/Technical+Support)
- (irc) #mongoosejs on freenode
-## Installation
-
-First install [node.js](http://nodejs.org/) and [mongodb](http://www.mongodb.org/downloads).
-
- $ npm install mongoose
-
## Plugins
Check out the [plugins search site](http://plugins.mongoosejs.com/) to see hundreds of related modules from the community.
+Build your own Mongoose plugin through [generator-mongoose-plugin](https://github.com/huei90/generator-mongoose-plugin).
+
## Contributors
-View all 90+ [contributors](https://github.com/learnboost/mongoose/graphs/contributors).
+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!
-## Get Involved
+## Live Examples
+
-Stand up and be counted as a [contributor](https://github.com/LearnBoost/mongoose/blob/master/CONTRIBUTING.md) too!
+## Installation
+
+First install [node.js](http://nodejs.org/) and [mongodb](http://www.mongodb.org/downloads). Then:
+
+```sh
+$ npm install mongoose
+```
+
+## Stability
+
+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 will continue to be actively maintained until September 1, 2015.
## 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 +94,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
- });
+```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);
- });
+// a setter
+Comment.path('name').set(function (v) {
+ return capitalize(v);
+});
- // middleware
- Comment.pre('save', function (next) {
- notify(this.get('email'));
- next();
- });
+// 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,73 +121,99 @@ 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!
@@ -187,55 +229,61 @@ 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);
- })
+```js
+.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();
- })
+// 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();
+});
+```
#### Schema gotcha
`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:
- new Schema({
- broken: { type: Boolean }
- , asset : {
- name: String
- , type: String // uh oh, it broke. asset will be interpreted as String
- }
- });
+```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
- }
- });
+new Schema({
+ works: { type: Boolean },
+ asset : {
+ name: String,
+ type: { type: String } // works. asset is an object with a type property
+ }
+});
+```
### Driver access
@@ -243,7 +291,8 @@ The driver being used defaults to [node-mongodb-native](https://github.com/mongo
## 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](http://github.com/visionmedia/dox)
+and [acquit](http://github.com/vkarpov15/acquit).
## License
diff --git a/node_modules/mongoose/contRun.sh b/node_modules/mongoose/contRun.sh
new file mode 100755
index 0000000..cd5610d
--- /dev/null
+++ b/node_modules/mongoose/contRun.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+make test
+
+ret=$?
+
+while [ $ret == 0 ]; do
+ make test
+ ret=$?
+done
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..dd55642
--- /dev/null
+++ b/node_modules/mongoose/examples/aggregate/aggregate.js
@@ -0,0 +1,101 @@
+
+// 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..f7c7cad 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..31e9b93
--- /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..5a70c18
--- /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..bd1ec5a
--- /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..d479595
--- /dev/null
+++ b/node_modules/mongoose/examples/geospatial/geoJSONexample.js
@@ -0,0 +1,49 @@
+
+// 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..5f24921
--- /dev/null
+++ b/node_modules/mongoose/examples/geospatial/geospatial.js
@@ -0,0 +1,95 @@
+
+// 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..d98434e
--- /dev/null
+++ b/node_modules/mongoose/examples/geospatial/person.js
@@ -0,0 +1,28 @@
+
+// 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..744d7bd
--- /dev/null
+++ b/node_modules/mongoose/examples/globalschemas/gs_example.js
@@ -0,0 +1,40 @@
+
+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..f9c9a27
--- /dev/null
+++ b/node_modules/mongoose/examples/globalschemas/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/hooks.js b/node_modules/mongoose/examples/hooks.js
deleted file mode 100644
index 01b2bb1..0000000
--- a/node_modules/mongoose/examples/hooks.js
+++ /dev/null
@@ -1,42 +0,0 @@
-
-var mongoose = require('mongoose')
-var Schema = mongoose.Schema;
-
-mongoose.connect('mongodb://localhost/example-hooks');
-
-var schema = Schema({ name: String });
-schema.post('save', function () {
- console.log('post save hook', arguments);
-})
-
-schema.pre('save', function (next) {
- console.log('pre save');
- next();
-})
-
-var M = mongoose.model('Hooks', schema);
-
-var doc = new M({ name: 'hooooks' });
-doc.save(function (err) {
- console.log('save callback');
- mongoose.disconnect();
-})
-
-
-////
-
-Model.on('init', cb);
-Model.on('remove', cb);
-Model.on('update', cb);
- // Model.update() and doc.save()
-Model.on('insert', cb);
-Model.on('save', cb);
-
-var promise = Model.save(doc, options, callback);
-
-//
-
-var schema = new Schema(..);
-schema.pre('save', function (next, done) {
-
-})
diff --git a/node_modules/mongoose/examples/lean/lean.js b/node_modules/mongoose/examples/lean/lean.js
new file mode 100644
index 0000000..0bf7c4f
--- /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..5046b1f
--- /dev/null
+++ b/node_modules/mongoose/examples/lean/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/mapreduce/mapreduce.js b/node_modules/mongoose/examples/mapreduce/mapreduce.js
new file mode 100644
index 0000000..53983fa
--- /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..6d476ec
--- /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 88%
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..5edd01a 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);
/**
@@ -71,7 +71,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 +79,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);
/**
@@ -113,18 +113,18 @@ mongoose.connection.on('open', function() {
path: 'author.friends',
select: 'name',
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 });
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..8b3c1cf
--- /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..3abbf0e
--- /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..5e1ecfa
--- /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..2ca0ce9
--- /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..f8b729c
--- /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..ed188c2
--- /dev/null
+++ b/node_modules/mongoose/examples/promises/promise.js
@@ -0,0 +1,88 @@
+
+// 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..c455d70
--- /dev/null
+++ b/node_modules/mongoose/examples/querybuilder/querybuilder.js
@@ -0,0 +1,80 @@
+
+// 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..2ff8621
--- /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..bb906df
--- /dev/null
+++ b/node_modules/mongoose/examples/schema/schema.js
@@ -0,0 +1,120 @@
+
+/**
+ * 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..e93cabc
--- /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..c0dee73
--- /dev/null
+++ b/node_modules/mongoose/examples/statics/person.js
@@ -0,0 +1,21 @@
+
+// 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..a318ca4
--- /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..295adb1
--- /dev/null
+++ b/node_modules/mongoose/lib/ES6Promise.js
@@ -0,0 +1,28 @@
+/* eslint no-unused-vars: 1 */
+
+/**
+ * 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(fn) {
+ throw '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..3686941
--- /dev/null
+++ b/node_modules/mongoose/lib/aggregate.js
@@ -0,0 +1,577 @@
+/* eslint no-unused-vars: 1 */
+
+/*!
+ * 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 (1 === arguments.length && 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 = 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 ('object' === typeof arg && !util.isArray(arg)) {
+ 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 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);
+
+ return this.append.apply(this, args.map(function(arg) {
+ return { $unwind: (arg && arg.charAt(0) === '$') ? arg : '$' + arg };
+ }));
+};
+
+/**
+ * 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 ('Object' === arg.constructor.name) {
+ var desc = ['desc', 'descending', -1];
+ Object.keys(arg).forEach(function(field) {
+ sort[field] = desc.indexOf(arg[field]) === -1 ? 1 : -1;
+ });
+ } 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);
+ 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) {
+ if (!this.options) this.options = {};
+ read.apply(this, arguments);
+ 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;
+};
+
+/**
+ * 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();
+
+ if (this.options && this.options.cursor) {
+ if (this.options.cursor.async) {
+ return new Promise.ES6(function(resolve, reject) {
+ if (!_this._model.collection.buffer) {
+ process.nextTick(function() {
+ var cursor = _this._model.collection.
+ aggregate(_this._pipeline, _this.options || {});
+ resolve(cursor);
+ callback && callback(cursor);
+ });
+ return;
+ } else {
+ _this._model.collection.emitter.once('queue', function() {
+ var cursor = _this._model.collection.
+ aggregate(_this._pipeline, _this.options || {});
+ resolve(cursor);
+ callback && callback(null, cursor);
+ });
+ }
+ });
+ } else {
+ 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);
+ });
+ });
+};
+
+/*!
+ * 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 ('object' !== typeof obj) {
+ return false;
+ }
+
+ k = Object.keys(obj);
+
+ return 1 === k.length && 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..d135f66
--- /dev/null
+++ b/node_modules/mongoose/lib/browser.js
@@ -0,0 +1,97 @@
+/**
+ * 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..ed23af3
--- /dev/null
+++ b/node_modules/mongoose/lib/browserDocument.js
@@ -0,0 +1,105 @@
+/*!
+ * 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 instanceof Schema)) {
+ 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 ('boolean' === typeof fields) {
+ 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..d91d675
--- /dev/null
+++ b/node_modules/mongoose/lib/cast.js
@@ -0,0 +1,211 @@
+/*!
+ * Module dependencies.
+ */
+
+var utils = require('./utils');
+var Types = require('./schema/index');
+
+/**
+ * Handles internal casting for queries
+ *
+ * @param {Schema} schema
+ * @param {Object obj Object to cast
+ * @api private
+ */
+
+var cast = module.exports = function(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 ('$or' === path || '$nor' === path || '$and' === path) {
+ var k = val.length;
+
+ while (k--) {
+ val[k] = cast(schema, val[k]);
+ }
+
+ } 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 (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 = 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 || '$nearSphere' == 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) {
+ obj[path] = null;
+ 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 {
+
+ var ks = Object.keys(val),
+ $cond;
+
+ k = ks.length;
+
+ 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) {
+ cast(schema, nested);
+ } else {
+ val[$cond] = schematype.castForQuery($cond, nested);
+ }
+ }
+ }
+ } else {
+ obj[path] = schematype.castForQuery(val);
+ }
+ }
+ }
+
+ return obj;
+};
diff --git a/node_modules/mongoose/lib/collection.js b/node_modules/mongoose/lib/collection.js
index 1c38286..b2d165d 100644
--- a/node_modules/mongoose/lib/collection.js
+++ b/node_modules/mongoose/lib/collection.js
@@ -3,7 +3,8 @@
* Module dependencies.
*/
-var STATES = require('./connectionstate')
+var EventEmitter = require('events').EventEmitter;
+var STATES = require('./connectionstate');
/**
* Abstract Collection constructor
@@ -16,7 +17,7 @@ var STATES = require('./connectionstate')
* @api public
*/
-function Collection (name, conn, opts) {
+function Collection(name, conn, opts) {
if (undefined === opts) opts = {};
if (undefined === opts.capped) opts.capped = {};
@@ -30,14 +31,16 @@ function Collection (name, conn, opts) {
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) {
this.onOpen();
}
-};
+}
/**
* The collection name
@@ -48,6 +51,15 @@ function Collection (name, conn, opts) {
Collection.prototype.name;
+/**
+ * The collection name
+ *
+ * @api public
+ * @property collectionName
+ */
+
+Collection.prototype.collectionName;
+
/**
* The Connection instance
*
@@ -63,7 +75,7 @@ Collection.prototype.conn;
* @api private
*/
-Collection.prototype.onOpen = function () {
+Collection.prototype.onOpen = function() {
var self = this;
this.buffer = false;
self.doQueue();
@@ -75,7 +87,7 @@ Collection.prototype.onOpen = function () {
* @api private
*/
-Collection.prototype.onClose = function () {
+Collection.prototype.onClose = function() {
if (this.opts.bufferCommands) {
this.buffer = true;
}
@@ -90,7 +102,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 +113,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 +129,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 +137,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 +145,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 +153,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 +161,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 +169,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 +177,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 +185,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 +193,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 ef4595f..b10293d 100644
--- a/node_modules/mongoose/lib/connection.js
+++ b/node_modules/mongoose/lib/connection.js
@@ -2,17 +2,14 @@
* 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'),
+ EventEmitter = require('events').EventEmitter,
+ driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native',
+ Schema = require('./schema'),
+ Collection = require(driver + '/collection'),
+ STATES = require('./connectionstate'),
+ MongooseError = require('./error'),
+ muri = require('muri');
/*!
* Protocol prefix regexp.
@@ -22,6 +19,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
*
@@ -41,10 +48,11 @@ var rgxProtocol = /^(?:.)+:\/\//;
* @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 +61,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 +93,25 @@ 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;
-
- if (STATES.connected === val)
- this._hasOpened = true;
-
- this.emit(STATES[val]);
- }
+ 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;
+ // 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;
+ }
+
+ if (STATES.connected === val)
+ this._hasOpened = true;
+
+ this.emit(STATES[val]);
+ }
+ }
});
/**
@@ -117,11 +130,20 @@ Connection.prototype.collections;
Connection.prototype.db;
+/**
+ * A hash of the global options that are associated with this connection
+ *
+ * @property global
+ */
+
+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,15 +169,14 @@ 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) {
switch (arguments.length) {
case 2:
port = 27017;
+ break;
case 3:
switch (typeof port) {
case 'function':
@@ -219,7 +240,7 @@ 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;
@@ -239,6 +260,17 @@ Connection.prototype.open = function (host, database, port, options, callback) {
this.user = this.pass = undefined;
}
+ // global configuration options
+ if (options && options.config) {
+ if (options.config.autoIndex === false) {
+ this.config.autoIndex = false;
+ }
+ else {
+ this.config.autoIndex = true;
+ }
+
+ }
+
this.name = database;
this.host = host;
this.port = port;
@@ -266,15 +298,22 @@ Connection.prototype.open = function (host, database, port, options, callback) {
* user - username for authentication
* pass - password for authentication
* 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._
+ *
+ * conn.open('mongodb://mongosA:27501,mongosB:27501', { mongos: true }, cb);
+ *
* Mongoose forces the db option `forceServerObjectId` false and cannot be overridden.
* Mongoose defaults the server `auto_reconnect` options to true which can be overridden.
* See the node-mongodb-native driver instance for options that it understands.
*
+ * _Options passed take precedence over options included in connection strings._
+ *
* @param {String} uris comma-separated mongodb:// `URI`s
* @param {String} [database] database name if not included in `uris`
* @param {Object} [options] passed to the internal driver
@@ -284,13 +323,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) {
@@ -345,7 +382,7 @@ 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;
@@ -357,6 +394,17 @@ Connection.prototype.openSet = function (uris, database, options, callback) {
this.user = this.pass = undefined;
}
+ // global configuration options
+ if (options && options.config) {
+ if (options.config.autoIndex === false) {
+ this.config.autoIndex = false;
+ }
+ else {
+ this.config.autoIndex = true;
+ }
+
+ }
+
this._open(callback);
return this;
};
@@ -372,10 +420,10 @@ Connection.prototype.openSet = function (uris, database, options, callback) {
* @api private
*/
-Connection.prototype.error = function (err, callback) {
+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.
@@ -384,7 +432,7 @@ Connection.prototype.error = function (err, callback) {
* @api private
*/
-Connection.prototype._open = function (callback) {
+Connection.prototype._open = function(callback) {
this.readyState = STATES.connecting;
this._closeCalled = false;
@@ -395,7 +443,7 @@ Connection.prototype._open = function (callback) {
: 'doOpen';
// open connection
- this[method](function (err) {
+ this[method](function(err) {
if (err) {
self.readyState = STATES.disconnected;
if (self._hasOpened) {
@@ -408,7 +456,7 @@ Connection.prototype._open = function (callback) {
self.onOpen(callback);
});
-}
+};
/**
* Called when the connection is opened
@@ -416,12 +464,12 @@ Connection.prototype._open = function (callback) {
* @api private
*/
-Connection.prototype.onOpen = function (callback) {
+Connection.prototype.onOpen = function(callback) {
var self = this;
- function open (err) {
+ function open(err, isAuth) {
if (err) {
- self.readyState = STATES.disconnected;
+ self.readyState = isAuth ? STATES.unauthorized : STATES.disconnected;
if (self._hasOpened) {
if (callback) callback(err);
} else {
@@ -439,14 +487,16 @@ Connection.prototype.onOpen = function (callback) {
callback && callback();
self.emit('open');
- };
+ }
// re-authenticate
- if (self.user && self.pass) {
- self.db.authenticate(self.user, self.pass, self.options.auth, open);
- }
- else
+ if (this.shouldAuthenticate()) {
+ self.db.authenticate(self.user, self.pass, self.options.auth, function(err) {
+ open(err, true);
+ });
+ } else {
open();
+ }
};
/**
@@ -457,19 +507,20 @@ Connection.prototype.onOpen = function (callback) {
* @api public
*/
-Connection.prototype.close = function (callback) {
+Connection.prototype.close = function(callback) {
var self = 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){
+ this.doClose(function(err) {
+ if (err) {
self.error(err, callback);
} else {
self.onClose();
@@ -479,14 +530,14 @@ Connection.prototype.close = function (callback) {
break;
case 2: // connecting
- this.once('open', function(){
+ this.once('open', function() {
self.close(callback);
});
break;
case 3: // disconnecting
if (!callback) break;
- this.once('close', function () {
+ this.once('close', function() {
callback();
});
break;
@@ -501,7 +552,7 @@ 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
@@ -523,7 +574,7 @@ Connection.prototype.onClose = function () {
* @api public
*/
-Connection.prototype.collection = function (name, options) {
+Connection.prototype.collection = function(name, options) {
if (!(name in this.collections))
this.collections[name] = new Collection(name, this, options);
return this.collections[name];
@@ -561,7 +612,7 @@ 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) {
collection = schema;
@@ -580,12 +631,12 @@ Connection.prototype.model = function (name, schema, collection) {
return this.models[name];
}
- var opts = { cache: false, connection: this }
+ var opts = { cache: false, connection: this };
var model;
if (schema instanceof Schema) {
// 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.
@@ -627,7 +678,7 @@ Connection.prototype.model = function (name, schema, collection) {
}
return this.models[name] = model.__subclass(this, schema, collection);
-}
+};
/**
* Returns an array of model names created on this connection.
@@ -635,68 +686,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
- */
-
-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));
- }
-
- 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);
- });
};
-/*!
- * Noop.
+/**
+ * @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.pass != null) || this.authMechanismDoesNotRequirePassword());
+};
-function noop () {}
+/**
+ * @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;
+ }
+ return true;
+};
+
+/**
+ * @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.
+ */
+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 9a460ae..10f9578 100644
--- a/node_modules/mongoose/lib/document.js
+++ b/node_modules/mongoose/lib/document.js
@@ -1,32 +1,32 @@
+/* eslint no-unused-vars: 1 */
+
/*!
* 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,
+ 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,
+ ValidationError = MongooseError.ValidationError,
+ InternalCache = require('./internal'),
+ deepEqual = utils.deepEqual,
+ hooks = require('hooks-fixed'),
+ PromiseProvider = require('./promise_provider'),
+ DocumentArray,
+ MongooseArray,
+ Embedded;
/**
* 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,8 +34,9 @@ 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;
@@ -49,26 +50,46 @@ 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) {
this.set(obj, undefined, true);
}
- this.$__registerHooks();
+ if (!schema.options.strict && obj) {
+ var self = this,
+ keys = Object.keys(this._doc);
+
+ keys.forEach(function(key) {
+ if (!(key in schema.tree)) {
+ defineKey(key, null, self);
+ }
+ });
+ }
+
+ 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,31 +146,35 @@ 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 self = this;
// determine if this doc is a result of a query with
// excluded fields
- if (fields && 'Object' === fields.constructor.name) {
+ if (fields && 'Object' === utils.getFunctionName(fields.constructor)) {
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];
@@ -159,20 +184,31 @@ Document.prototype.$__buildDoc = function (obj, fields, skipId) {
if (obj && '_id' in obj) continue;
}
- var type = this.schema.paths[p]
- , path = p.split('.')
- , len = path.length
- , last = len-1
- , 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 === 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;
@@ -182,7 +218,7 @@ Document.prototype.$__buildDoc = function (obj, fields, skipId) {
self.$__.activePaths.default(p);
}
- } else if (p in fields) {
+ } else if (included) {
// selected field
def = type.getDefault(self, true);
if ('undefined' !== typeof def) {
@@ -199,9 +235,10 @@ Document.prototype.$__buildDoc = function (obj, fields, skipId) {
}
} else {
doc_ = doc_[piece] || (doc_[piece] = {});
+ curPath += '.';
}
}
- };
+ }
return doc;
};
@@ -216,7 +253,7 @@ Document.prototype.$__buildDoc = function (obj, fields, skipId) {
* @api private
*/
-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
@@ -228,7 +265,8 @@ 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 && 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];
@@ -253,14 +291,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];
@@ -268,7 +306,7 @@ 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 || 'Object' == utils.getFunctionName(obj[i].constructor))) {
// assume nested object
if (!doc[i]) doc[i] = {};
init(self, obj[i], doc[i], path + '.');
@@ -277,18 +315,27 @@ function init (self, obj, doc, prefix) {
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.
@@ -302,27 +349,30 @@ 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 && 'Object' == utils.getFunctionName(key.constructor))) 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 (null != val &&
+ 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
@@ -351,11 +401,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.
@@ -373,10 +423,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
@@ -389,16 +439,16 @@ Document.prototype.update = function update () {
* @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 && 'Object' == utils.getFunctionName(type.constructor)) {
options = type;
type = undefined;
}
- var merge = options && options.merge
- , adhoc = type && true !== type
- , constructing = true === type
- , adhocs
+ var merge = options && options.merge,
+ adhoc = type && true !== type,
+ constructing = true === type,
+ adhocs;
var strict = options && 'strict' in options
? options.strict
@@ -406,7 +456,7 @@ Document.prototype.set = function (path, val, type, options) {
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) {
@@ -422,28 +472,49 @@ Document.prototype.set = function (path, val, type, options) {
? 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),
+ i = keys.length,
+ pathtype,
+ key;
while (i--) {
key = keys[i];
- pathtype = this.schema.pathType(prefix + key);
+ var pathName = prefix + key;
+ pathtype = this.schema.pathType(pathName);
+
if (null != path[key]
- // need to know if plain object - no Buffer, ObjectId, etc
+ // 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)) {
+ && (!path[key].constructor || 'Object' == utils.getFunctionName(path[key].constructor))
+ && 'virtual' !== pathtype
+ && 'real' !== pathtype
+ && !(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) {
+ // 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 (pathtype === 'nested' && path[key] instanceof Document) {
+ this.set(prefix + key,
+ path[key].toObject({ virtuals: false }), constructing);
} else if ('throw' == strict) {
- throw new Error("Field `" + key + "` is not in schema.");
+ throw new Error('Field `' + key + '` is not in schema.');
}
} else if (undefined !== path[key]) {
this.set(prefix + key, path[key], constructing);
@@ -458,10 +529,14 @@ 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 ('nested' == pathType && val) {
+ if (utils.isObject(val) &&
+ (!val.constructor || 'Object' == utils.getFunctionName(val.constructor))) {
+ if (!merge) this.setValue(path, null);
+ this.set(val, path, constructing);
+ return this;
+ }
+ this.invalidate(path, new MongooseError.CastError('Object', val, path));
return this;
}
@@ -473,8 +548,8 @@ Document.prototype.set = function (path, val, type, options) {
// 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
@@ -506,8 +581,8 @@ 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
|| this.get(subpath) === null) {
@@ -522,24 +597,61 @@ Document.prototype.set = function (path, val, type, options) {
// if this doc is being constructed we should not trigger getters
var priorVal = constructing
? undefined
- : this.get(path);
+ : this.getValue(path);
- if (!schema || null === val || 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) {
+ this.populated(path, val._id);
+ didPopulate = true;
+ }
+
+ 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) {
+ this.populated(path, val.map(function(v) { return v._id }));
+ didPopulate = true;
+ }
+ val = schema.applySetters(val, this, false, priorVal);
+
+ if (!didPopulate && this.$__.populated) {
+ delete this.$__.populated[path];
+ }
+
+ this.$markValid(path);
+ } catch (e) {
+ var reason;
+ if (!(e instanceof MongooseError.CastError)) {
+ reason = e;
+ }
+ this.invalidate(path,
+ new MongooseError.CastError(schema.instance, val, path, reason));
+ shouldSet = false;
+ }
if (shouldSet) {
this.$__set(pathToMark, path, constructing, parts, schema, val, priorVal);
}
return this;
-}
+};
/**
* Determine if we should mark this change as modified.
@@ -550,11 +662,10 @@ Document.prototype.set = function (path, val, type, options) {
* @memberOf Document
*/
-Document.prototype.$__shouldModify = function (
+Document.prototype.$__shouldModify = function(
pathToMark, path, constructing, parts, schema, val, priorVal) {
if (this.isNew) return true;
- if (this.isDirectModified(pathToMark)) return false;
if (undefined === val && !this.isSelected(path)) {
// when a path is not selected in a query, its initial
@@ -579,9 +690,8 @@ Document.prototype.$__shouldModify = function (
// 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.
@@ -591,33 +701,45 @@ Document.prototype.$__shouldModify = function (
* @memberOf Document
*/
-Document.prototype.$__set = function (
+Document.prototype.$__set = function(
pathToMark, path, constructing, parts, schema, val, priorVal) {
+ Embedded = Embedded || require('./types/embedded');
var shouldModify = this.$__shouldModify.apply(this, arguments);
+ 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]] && 'Object' === utils.getFunctionName(obj[parts[i]].constructor)) {
+ obj = obj[parts[i]];
+ } else if (obj[parts[i]] && obj[parts[i]] instanceof Embedded) {
obj = obj[parts[i]];
} else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) {
obj = obj[parts[i]];
@@ -626,7 +748,7 @@ Document.prototype.$__set = function (
}
}
}
-}
+};
/**
* Gets a raw value from a path (no getters)
@@ -635,9 +757,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)
@@ -647,10 +769,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.
@@ -668,16 +790,15 @@ Document.prototype.setValue = function (path, val) {
* @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
@@ -685,7 +806,13 @@ Document.prototype.get = function (path, type) {
: 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);
}
@@ -701,9 +828,9 @@ 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;
@@ -727,30 +854,8 @@ 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.
- *
- * @param {Function} fn function to execute
- * @param {Object} scope the scope with which to call fn
- * @api private
- * @method $__try
- * @memberOf Document
- */
-
-Document.prototype.$__try = function (fn, scope) {
- var res;
- try {
- fn.call(scope);
- res = true;
- } catch (e) {
- this.$__error(e);
- res = false;
- }
- return res;
};
/**
@@ -760,12 +865,12 @@ 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('.'));
}, []));
}, []);
@@ -789,12 +894,31 @@ Document.prototype.modifiedPaths = function () {
* @api public
*/
-Document.prototype.isModified = function (path) {
+Document.prototype.isModified = function(path) {
return path
? !!~this.modifiedPaths().indexOf(path)
: 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);
+};
+
/**
* Returns true if `path` was directly set and modified, else false.
*
@@ -809,7 +933,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);
};
@@ -821,7 +945,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);
};
@@ -840,17 +964,17 @@ 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;
}
- 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]) {
// only _id was selected.
@@ -860,7 +984,7 @@ Document.prototype.isSelected = function isSelected (path) {
while (i--) {
cur = paths[i];
if ('_id' == cur) continue;
- inclusive = !! this.$__.selected[cur];
+ inclusive = !!this.$__.selected[cur];
break;
}
@@ -884,11 +1008,11 @@ Document.prototype.isSelected = function isSelected (path) {
}
}
- return ! inclusive;
+ return !inclusive;
}
return true;
-}
+};
/**
* Executes registered validation rules for this document.
@@ -904,15 +1028,63 @@ 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} 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) {
+ var _this = this;
+ var Promise = PromiseProvider.get();
+ if (typeof options === 'function') {
+ callback = options;
+ options = null;
+ }
+
+ if (options && options.__noPromise) {
+ this.$__validate(callback);
+ return;
+ }
+
+ return new Promise.ES6(function(resolve, reject) {
+ _this.$__validate(function(error) {
+ callback && callback(error);
+ if (error) {
+ reject(error);
+ return;
+ }
+ resolve();
+ });
+ });
+};
+
+/*!
+ * ignore
+ */
+
+Document.prototype.$__validate = function(callback) {
+ var self = this;
+ var _complete = function() {
+ var err = self.$__.validationError;
+ self.$__.validationError = undefined;
+ self.emit('validate', self);
+ if (err) {
+ for (var key in err.errors) {
+ // Make sure cast errors persist
+ if (!self.__parent && err.errors[key] instanceof MongooseError.CastError) {
+ self.invalidate(key, err.errors[key]);
+ }
+ }
+
+ return err;
+ } else {
+ return;
+ }
+ };
// only validate required fields when necessary
- var paths = Object.keys(this.$__.activePaths.states.require).filter(function (path) {
+ var paths = Object.keys(this.$__.activePaths.states.require).filter(function(path) {
if (!self.isSelected(path) && !self.isModified(path)) return false;
return true;
});
@@ -922,74 +1094,239 @@ Document.prototype.validate = function (cb) {
paths = paths.concat(Object.keys(this.$__.activePaths.states.default));
if (0 === paths.length) {
- complete();
- return this;
+ 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;
+ // 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 = self.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);
+ }
+ }
+ }
- function validatePath (path) {
+ var complete = function() {
+ var err = _complete();
+ if (err) {
+ callback(err);
+ return;
+ }
+ callback();
+ };
+
+ var validatePath = function(path) {
if (validating[path]) return;
validating[path] = true;
total++;
- process.nextTick(function(){
+ process.nextTick(function() {
var p = self.schema.path(path);
- if (!p) return --total || complete();
+ if (!p) {
+ return --total || complete();
+ }
+
+ // If user marked as invalid or there was a cast error, don't validate
+ if (!self.$isValid(path)) {
+ --total || complete();
+ return;
+ }
var val = self.getValue(path);
- p.doValidate(val, function (err) {
+ p.doValidate(val, function(err) {
if (err) {
- self.invalidate(
- path
- , err
- , undefined
- , true // embedded docs
- );
+ self.invalidate(path, err, undefined, true);
}
--total || complete();
}, self);
});
+ };
+
+ 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 self = 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 = Object.keys(this.$__.activePaths.states.require).filter(function(path) {
+ if (!self.isSelected(path) && !self.isModified(path)) return false;
+ 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));
+
+ 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 = self.schema.path(path);
+ if (!p) return;
+ if (!self.$isValid(path)) {
+ return;
+ }
+
+ var val = self.getValue(path);
+ var err = p.doValidateSync(val, self);
+ if (err) {
+ self.invalidate(path, err, undefined, true);
+ }
+ });
+
+ var err = self.$__.validationError;
+ self.$__.validationError = undefined;
+ self.emit('validate', self);
+
+ if (err) {
+ for (var key in err.errors) {
+ // Make sure cast errors persist
+ if (err.errors[key] instanceof MongooseError.CastError) {
+ self.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
* @api public
*/
-Document.prototype.invalidate = function (path, err, val) {
+Document.prototype.invalidate = function(path, err, val) {
if (!this.$__.validationError) {
this.$__.validationError = new ValidationError(this);
}
+ if (this.$__.validationError.errors[path]) return;
+
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)
+ err = new ValidatorError({
+ path: path,
+ message: err,
+ type: 'user defined',
+ value: val
+ });
}
+ if (this.$__.validationError == err) return;
+
this.$__.validationError.errors[path] = err;
-}
+};
+
+/**
+ * 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.
@@ -1000,18 +1337,18 @@ Document.prototype.invalidate = function (path, err, val) {
* @memberOf Document
*/
-Document.prototype.$__reset = function reset () {
+Document.prototype.$__reset = function reset() {
var self = this;
DocumentArray || (DocumentArray = require('./types/documentarray'));
this.$__.activePaths
- .map('init', 'modify', function (i) {
+ .map('init', 'modify', function(i) {
return self.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];
@@ -1021,24 +1358,25 @@ Document.prototype.$__reset = function 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 = this;
+ this.schema.requiredPaths().forEach(function(path) {
self.$__.activePaths.require(path);
});
return this;
-}
+};
/**
* Returns this documents dirty paths / vals.
@@ -1048,26 +1386,44 @@ Document.prototype.$__reset = function reset () {
* @memberOf Document
*/
-Document.prototype.$__dirty = function () {
+Document.prototype.$__dirty = function() {
var self = 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: self.getValue(path),
+ schema: self.$__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' || !self.getValue(path)) {
+ return;
+ }
+ return {
+ path: path,
+ value: self.getValue(path),
+ schema: self.$__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, i) {
+ if (!item) {
+ return;
+ }
if (item.path.indexOf(lastPath) !== 0) {
lastPath = item.path + '.';
minimal.push(item);
@@ -1086,96 +1442,116 @@ 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
+ defineKey(key
+ , (('Object' === utils.getFunctionName(limb.constructor)
&& Object.keys(limb).length)
- && (!limb.type || limb.type.type)
+ && (!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 = {};
+ enumerable: true,
+ configurable: true,
+ get: function() {
+ var _self = this;
+ if (!this.$__.getters)
+ this.$__.getters = {};
- if (!this.$__.getters[path]) {
- var nested = Object.create(this);
+ if (!this.$__.getters[path]) {
+ var nested = Object.create(Object.getPrototypeOf(this), getOwnPropertyDescriptors(this));
// save scope for nested getters/setters
- if (!prefix) nested.$__.scope = this;
+ 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;
+ var i = 0,
+ len = keys.length;
- for (; i < len; ++i) {
+ 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.
- });
- }
-
- nested.toObject = function () {
- return this.get(path);
- };
-
- compile(subprops, nested, path);
- this.$__.getters[path] = nested;
+ 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];
- }
- , set: function (v) {
- if (v instanceof Document) v = v.toObject();
- return (this.$__.scope || this).set(path, v);
+ nested.toObject = function() {
+ return _self.get(path);
+ };
+
+ nested.toJSON = nested.toObject;
+
+ nested.$__isNested = true;
+
+ compile(subprops, nested, path, options);
+ this.$__.getters[path] = nested;
}
+
+ 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.
@@ -1186,137 +1562,299 @@ 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
- *
- * @api private
- * @method $__registerHooks
- * @memberOf Document
- */
-
-Document.prototype.$__registerHooks = function () {
- if (!this.save) return;
-
- 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();
-
- arrays.forEach(function (array) {
- if (error) return;
-
- // handle sparse arrays by using for loop vs array.forEach
- // which skips the sparse elements
-
- var len = array.length
- subdocs += len;
-
- for (var i = 0; i < len; ++i) {
- if (error) break;
-
- var doc = array[i];
- if (!doc) {
- --subdocs || next();
- continue;
- }
-
- doc.save(handleSave);
- }
- });
-
- function handleSave (err) {
- if (error) return;
-
- if (err) {
- self.$__.validationError = undefined;
- return next(error = err);
- }
-
- --subdocs || next();
- }
-
- }, 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);
- });
-
- // add user defined queues
- this.$__doQueue();
};
+
/**
- * Registers an error
+ * Get active path that were changed and are arrays
*
- * @param {Error} err
* @api private
- * @method $__error
+ * @method $__getArrayPathsToValidate
* @memberOf Document
*/
-Document.prototype.$__error = function (err) {
- this.$__.saveError = err;
- return this;
+Document.prototype.$__getArrayPathsToValidate = function() {
+ DocumentArray || (DocumentArray = require('./types/documentarray'));
+
+ // 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;});
+};
+
+
+/**
+ * Get all subdocs (by bfs)
+ *
+ * @api private
+ * @method $__getAllSubdocs
+ * @memberOf Document
+ */
+
+Document.prototype.$__getAllSubdocs = function() {
+ DocumentArray || (DocumentArray = require('./types/documentarray'));
+ Embedded = Embedded || require('./types/embedded');
+
+ function docReducer(seed, path) {
+ var val = this[path];
+
+ if (val instanceof Embedded) seed.push(val);
+ if (val && val.$isSingleNested) {
+ seed.push(val);
+ }
+ if (val && val.isMongooseDocumentArray) {
+ val.forEach(function _docReduce(doc) {
+ if (!doc || !doc._doc) return;
+ 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);
+ }
+ }
+ return seed;
+ }
+
+ var subDocs = Object.keys(this._doc).reduce(docReducer.bind(this), []);
+
+ return subDocs;
};
/**
* Executes methods queued from the Schema definition
*
* @api private
- * @method $__doQueue
+ * @method $__registerHooksFromSchema
* @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.$__registerHooksFromSchema = function() {
+ Embedded = Embedded || require('./types/embedded');
+ var Promise = PromiseProvider.get();
+
+ var self = this;
+ var q = self.schema && self.schema.callQueue;
+ if (!q.length) return self;
+
+ // 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') {
+ self[pair[0]].apply(self, 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) {
+ self.on.apply(self, args);
+ });
+ delete toWrap.post;
+
+ // 'init' should be synchronous on subdocuments
+ if (toWrap.init && self instanceof Embedded) {
+ if (toWrap.init.pre) {
+ toWrap.init.pre.forEach(function(args) {
+ self.pre.apply(self, args);
+ });
+ }
+ if (toWrap.init.post) {
+ toWrap.init.post.forEach(function(args) {
+ self.post.apply(self, 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) {
+ self.pre.apply(self, args);
+ });
+ }
+ if (toWrap.set.post) {
+ toWrap.set.post.forEach(function(args) {
+ self.post.apply(self, 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 (!self[pointCut]) {
+ return;
+ }
+ self[newName] = self[pointCut];
+ self[pointCut] = function wrappedPointCut() {
+ var args = [].slice.call(arguments);
+ var lastArg = args.pop();
+ var fn;
+
+ return new Promise.ES6(function(resolve, reject) {
+ if (lastArg && typeof lastArg !== 'function') {
+ args.push(lastArg);
+ } else {
+ fn = lastArg;
+ }
+ args.push(function(error, result) {
+ if (error) {
+ self.$__handleReject(error);
+ fn && fn(error);
+ reject(error);
+ return;
+ }
+
+ fn && fn.apply(null, [null].concat(Array.prototype.slice.call(arguments, 1)));
+ resolve(result);
+ });
+
+ self[newName].apply(self, args);
+ });
+ };
+
+ toWrap[pointCut].pre.forEach(function(args) {
+ args[0] = newName;
+ self.pre.apply(self, args);
+ });
+ toWrap[pointCut].post.forEach(function(args) {
+ args[0] = newName;
+ self.post.apply(self, args);
+ });
+ });
+ return self;
+};
+
+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);
+ }
+};
+
+/**
+ * Internal helper for toObject() and toJSON() that doesn't manipulate options
+ *
+ * @api private
+ * @method $toObject
+ * @memberOf Document
+ */
+
+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 && 'Object' == utils.getFunctionName(options.constructor)) ||
+ (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.virtuals || options.getters && false !== options.virtuals) {
+ applyGetters(this, ret, 'virtuals', 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.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 (true === transform ||
+ (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 ('function' == typeof transform) {
+ var xformed = transform(this, ret, options);
+ if ('undefined' != typeof xformed) ret = xformed;
+ }
+
+ return ret;
};
/**
@@ -1330,6 +1868,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
*
@@ -1364,6 +1905,7 @@ Document.prototype.$__doQueue = function () {
* ####Example
*
* // specify the transform schema option
+ * if (!schema.options.toObject) schema.options.toObject = {};
* schema.options.toObject.transform = function (doc, ret, options) {
* // remove the _id of every document before returning the result
* delete ret._id;
@@ -1377,6 +1919,7 @@ Document.prototype.$__doQueue = function () {
*
* With transformations we can do a lot more than remove properties. We can even return completely new customized objects:
*
+ * if (!schema.options.toObject) schema.options.toObject = {};
* schema.options.toObject.transform = function (doc, ret, options) {
* return { movie: ret.name }
* }
@@ -1400,6 +1943,7 @@ Document.prototype.$__doQueue = function () {
*
* _Note: if you call `toObject` and pass any options, the transform declared in your schema options will __not__ be applied. To force its application pass `transform: true`_
*
+ * if (!schema.options.toObject) schema.options.toObject = {};
* schema.options.toObject.hide = '_id';
* schema.options.toObject.transform = function (doc, ret, options) {
* if (options.hide) {
@@ -1414,11 +1958,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`.
*
@@ -1432,49 +1972,45 @@ 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)
- : {};
- }
-
- ;('minimize' in options) || (options.minimize = this.schema.options.minimize);
-
- var ret = clone(this._doc, options);
-
- if (options.virtuals || options.getters && false !== options.virtuals) {
- applyGetters(this, ret, 'virtuals', options);
- }
-
- if (options.getters) {
- applyGetters(this, ret, 'paths', options);
- }
-
- if (true === options.transform) {
- var opts = options.json
- ? this.schema.options.toJSON
- : this.schema.options.toObject;
- if (opts) {
- options.transform = opts.transform;
- }
- }
-
- if ('function' == typeof options.transform) {
- var xformed = options.transform(this, ret, options);
- if ('undefined' != typeof xformed) ret = xformed;
- }
-
- return ret;
+Document.prototype.toObject = function(options) {
+ return this.$toObject(options);
};
+/*!
+ * Minimizes an object, removing undefined values and empty objects
+ *
+ * @param {Object} object to minimize
+ * @return {Object}
+ */
+
+function minimize(obj) {
+ var keys = Object.keys(obj),
+ i = keys.length,
+ hasKeys,
+ key,
+ val;
+
+ while (i--) {
+ key = keys[i];
+ val = obj[key];
+
+ if (utils.isObject(val)) {
+ obj[key] = minimize(val);
+ }
+
+ if (undefined === obj[key]) {
+ delete obj[key];
+ continue;
+ }
+
+ hasKeys = true;
+ }
+
+ return hasKeys
+ ? obj
+ : undefined;
+}
+
/*!
* Applies virtuals properties to `json`.
*
@@ -1484,20 +2020,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];
@@ -1521,24 +2057,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);
};
/**
@@ -1547,11 +2073,10 @@ 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) :
- {};
+Document.prototype.inspect = function(options) {
+ var opts = options && 'Object' == utils.getFunctionName(options.constructor) ? options : {};
opts.minimize = false;
+ opts.retainKeyOrder = true;
return inspect(this.toObject(opts));
};
@@ -1567,20 +2092,25 @@ Document.prototype.toString = Document.prototype.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) {
var tid = this.get('_id');
- var docid = doc.get('_id');
- return tid.equals
+ var docid = doc.get ? doc.get('_id') : doc;
+ if (!tid && !docid) {
+ return deepEqual(this, doc);
+ }
+ return tid && tid.equals
? tid.equals(docid)
: tid === docid;
-}
+};
/**
* Populates document references, executing the `callback` when complete.
@@ -1609,7 +2139,8 @@ Document.prototype.equals = function (doc) {
*
* ####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.
*
@@ -1620,14 +2151,14 @@ Document.prototype.equals = function (doc) {
* @return {Document} this
*/
-Document.prototype.populate = function populate () {
+Document.prototype.populate = function populate() {
if (0 === arguments.length) return this;
var pop = this.$__.populate || (this.$__.populate = {});
var args = utils.args(arguments);
var fn;
- if ('function' == typeof args[args.length-1]) {
+ if ('function' == typeof args[args.length - 1]) {
fn = args.pop();
}
@@ -1647,7 +2178,52 @@ Document.prototype.populate = function populate () {
}
return this;
-}
+};
+
+/**
+ * Explicitly executes population and returns a promise. Useful for ES6
+ * integration.
+ *
+ * ####Example:
+ *
+ * var promise = doc.
+ * populate('company').
+ * populate({
+ * path: 'notes',
+ * match: /airline/,
+ * select: 'text',
+ * model: 'modelName'
+ * options: opts
+ * }).
+ * execPopulate();
+ *
+ * // summary
+ * doc.execPopulate()
+ *
+ *
+ * ####NOTE:
+ *
+ * Population does not occur unless a `callback` is passed.
+ * Passing the same path a second time will overwrite the previous path options.
+ * See [Model.populate()](#model_Model.populate) for explaination of options.
+ *
+ * @see Document.populate #Document_model.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) {
+ if (error) {
+ return reject(error);
+ }
+ resolve(_this);
+ });
+ });
+};
/**
* Gets _id(s) used during population of the given `path`.
@@ -1666,10 +2242,10 @@ 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 (val == null) {
if (!this.$__.populated) return undefined;
var v = this.$__.populated[path];
if (v) return v.value;
@@ -1686,7 +2262,34 @@ Document.prototype.populated = function (path, val, options) {
this.$__.populated || (this.$__.populated = {});
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
+ * @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.
@@ -1698,10 +2301,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.
@@ -1709,4 +2312,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..f7ad8fa
--- /dev/null
+++ b/node_modules/mongoose/lib/document_provider.js
@@ -0,0 +1,20 @@
+'use strict';
+
+/*!
+ * 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;
+ } else {
+ return Document;
+ }
+};
\ No newline at end of file
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..815e8bd 100644
--- a/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
+++ b/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js
@@ -3,10 +3,9 @@
* 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 +16,7 @@ var MongooseCollection = require('../../collection')
* @api private
*/
-function NativeCollection () {
+function NativeCollection() {
this.collection = null;
MongooseCollection.apply(this, arguments);
}
@@ -34,7 +33,7 @@ NativeCollection.prototype.__proto__ = MongooseCollection.prototype;
* @api private
*/
-NativeCollection.prototype.onOpen = function () {
+NativeCollection.prototype.onOpen = function() {
var self = this;
// always get a new collection in case the user changed host:port
@@ -46,21 +45,25 @@ NativeCollection.prototype.onOpen = function () {
}
// capped
- return self.conn.db.collection(self.name, function (err, c) {
+ return self.conn.db.collection(self.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);
+ self.conn.db.listCollections({ name: self.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'
+ 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'
+ + ' http://www.mongodb.org/display/DOCS/Capped+Collections#CappedCollections-Convertingacollectiontocapped';
err = new Error(msg);
callback(err);
}
@@ -73,7 +76,7 @@ NativeCollection.prototype.onOpen = function () {
});
});
- function callback (err, collection) {
+ function callback(err, collection) {
if (err) {
// likely a strict mode error
self.conn.emit('error', err);
@@ -81,7 +84,7 @@ NativeCollection.prototype.onOpen = function () {
self.collection = collection;
MongooseCollection.prototype.onOpen.call(self);
}
- };
+ }
};
/**
@@ -90,7 +93,7 @@ NativeCollection.prototype.onOpen = function () {
* @api private
*/
-NativeCollection.prototype.onClose = function () {
+NativeCollection.prototype.onClose = function() {
MongooseCollection.prototype.onClose.call(this);
};
@@ -99,68 +102,92 @@ NativeCollection.prototype.onClose = function () {
*/
for (var i in Collection.prototype) {
- (function(i){
- NativeCollection.prototype[i] = function () {
+ // 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;
+ }
+
+ (function(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,
+ self = this,
+ debug = self.conn.base.options.debug;
if (debug) {
if ('function' === typeof debug) {
debug.apply(debug
- , [self.name, i].concat(utils.args(args, 0, args.length-1)));
+ , [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]))
+ this.$print(self.name, i, args);
}
}
- collection[i].apply(collection, args);
+ return collection[i].apply(collection, args);
};
})(i);
}
-/*!
+/**
* Debug print helper
+ *
+ * @api public
*/
-function print (arg) {
+NativeCollection.prototype.$print = function(name, i, args) {
+ console.error(
+ '\x1B[0;36mMongoose:\x1B[0m %s.%s(%s) %s %s %s',
+ name,
+ i,
+ this.$format(args[0]),
+ this.$format(args[1]),
+ this.$format(args[2]),
+ this.$format(args[3]));
+};
+
+/**
+ * Formatter for debug print args
+ *
+ * @api public
+ */
+
+NativeCollection.prototype.$format = function(arg) {
var type = typeof arg;
if ('function' === type || 'undefined' === type) return '';
return format(arg);
-}
+};
/*!
* Debug print helper
*/
-function format (obj, sub) {
- var x = utils.clone(obj);
+function format(obj, sub) {
+ var x = utils.clone(obj, { retainKeyOrder: 1 });
+ var representation;
if (x) {
if ('Binary' === x.constructor.name) {
x = '[object Buffer]';
} else if ('ObjectID' === x.constructor.name) {
- var representation = 'ObjectId("' + x.toHexString() + '")';
+ representation = 'ObjectId("' + x.toHexString() + '")';
x = { inspect: function() { return representation; } };
} else if ('Date' === x.constructor.name) {
- var representation = 'new Date("' + x.toUTCString() + '")';
+ 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--) {
+ 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) {
@@ -168,18 +195,18 @@ function format (obj, sub) {
} else if ('Object' === x[key].constructor.name) {
x[key] = format(x[key], true);
} else if ('ObjectID' === x[key].constructor.name) {
- ;(function(x){
+ (function(x) {
var representation = 'ObjectId("' + x[key].toHexString() + '")';
x[key] = { inspect: function() { return representation; } };
- })(x)
+ })(x);
} else if ('Date' === x[key].constructor.name) {
- ;(function(x){
+ (function(x) {
var representation = 'new Date("' + x[key].toUTCString() + '")';
x[key] = { inspect: function() { return representation; } };
- })(x)
+ })(x);
} else if (Array.isArray(x[key])) {
- x[key] = x[key].map(function (o) {
- return format(o, true)
+ x[key] = x[key].map(function(o) {
+ return format(o, true);
});
}
}
@@ -191,7 +218,7 @@ function format (obj, sub) {
return require('util')
.inspect(x, false, 10, true)
.replace(/\n/g, '')
- .replace(/\s{2,}/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 5325419..215146b 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,13 @@
* 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'),
+ mongo = require('mongodb'),
+ Db = mongo.Db,
+ Server = mongo.Server,
+ Mongos = mongo.Mongos,
+ STATES = require('../../connectionstate'),
+ ReplSetServers = mongo.ReplSet;
/**
* A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation.
@@ -20,7 +20,14 @@ var MongooseConnection = require('../../connection')
function NativeConnection() {
MongooseConnection.apply(this, arguments);
this._listening = false;
-};
+}
+
+/**
+ * Expose the possible connection states.
+ * @api public
+ */
+
+NativeConnection.STATES = STATES;
/*!
* Inherits from Connection.
@@ -36,16 +43,18 @@ NativeConnection.prototype.__proto__ = MongooseConnection.prototype;
* @api private
*/
-NativeConnection.prototype.doOpen = function (fn) {
- if (this.db) {
- mute(this);
+NativeConnection.prototype.doOpen = function(fn) {
+ 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);
-
var self = this;
- this.db.open(function (err) {
+ this.db.open(function(err) {
if (err) return fn(err);
listen(self);
fn();
@@ -54,15 +63,75 @@ NativeConnection.prototype.doOpen = function (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 self = this;
+
+ if (this.db && this._readyState === STATES.connected) {
+ wireup();
+ } else {
+ this.once('connected', wireup);
+ }
+
+ function wireup() {
+ newConn.db = self.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) {
+function listen(conn) {
if (conn._listening) return;
conn._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
@@ -75,33 +144,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);
+ });
}
/**
@@ -114,30 +176,26 @@ function mute (conn) {
* @return {Connection} this
*/
-NativeConnection.prototype.doOpenSet = function (fn) {
- if (this.db) {
- mute(this);
- }
+NativeConnection.prototype.doOpenSet = function(fn) {
+ var servers = [],
+ self = this;
- var servers = []
- , self = 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));
- })
+ });
var server = this.options.mongos
? new Mongos(servers, this.options.mongos)
: new ReplSetServers(servers, 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() {
+ self.emit('fullsetup');
});
- this.db.open(function (err) {
+ this.db.open(function(err) {
if (err) return fn(err);
fn();
listen(self);
@@ -154,11 +212,11 @@ NativeConnection.prototype.doOpenSet = function (fn) {
* @api private
*/
-NativeConnection.prototype.doClose = function (fn) {
+NativeConnection.prototype.doClose = function(fn) {
this.db.close();
if (fn) fn();
return this;
-}
+};
/**
* Prepares default connection options for the node-mongodb-native driver.
@@ -170,7 +228,7 @@ 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 = {});
@@ -180,11 +238,12 @@ NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
o.replset.socketOptions || (o.replset.socketOptions = {});
var opts = connStrOpts || {};
- Object.keys(opts).forEach(function (name) {
+ Object.keys(opts).forEach(function(name) {
switch (name) {
+ case 'ssl':
case 'poolSize':
- if ('undefined' == typeof o.server.poolSize) {
- o.server.poolSize = o.replset.poolSize = opts[name];
+ if ('undefined' == typeof o.server[name]) {
+ o.server[name] = o.replset[name] = opts[name];
}
break;
case 'slaveOk':
@@ -197,7 +256,6 @@ NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
o.server.auto_reconnect = opts[name];
}
break;
- case 'ssl':
case 'socketTimeoutMS':
case 'connectTimeoutMS':
if ('undefined' == typeof o.server.socketOptions[name]) {
@@ -256,7 +314,7 @@ NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
}
break;
}
- })
+ });
if (!('auto_reconnect' in o.server)) {
o.server.auto_reconnect = true;
@@ -278,7 +336,7 @@ NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
validate(o);
return o;
-}
+};
/*!
* Validates the driver db options.
@@ -286,7 +344,7 @@ NativeConnection.prototype.parseOptions = function (passed, connStrOpts) {
* @param {Object} o
*/
-function validate (o) {
+function validate(o) {
if (-1 === o.db.w || 0 === o.db.w) {
if (o.db.journal || o.db.fsync || o.db.safe) {
throw new Error(
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..ace4565 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..8a67f1a
--- /dev/null
+++ b/node_modules/mongoose/lib/error/browserMissingSchema.js
@@ -0,0 +1,33 @@
+/* eslint no-unused-vars: 1 */
+/*!
+ * Module dependencies.
+ */
+
+var MongooseError = require('../error.js');
+
+/*!
+ * MissingSchema Error constructor.
+ *
+ * @inherits MongooseError
+ */
+
+function MissingSchemaError(name) {
+ 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..266858c 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/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..75fc235
--- /dev/null
+++ b/node_modules/mongoose/lib/error/messages.js
@@ -0,0 +1,43 @@
+
+/**
+ * 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.
+ *
+ * @property 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/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/validation.js b/node_modules/mongoose/lib/error/validation.js
new file mode 100644
index 0000000..41b1143
--- /dev/null
+++ b/node_modules/mongoose/lib/error/validation.js
@@ -0,0 +1,62 @@
+
+/*!
+ * 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..ca605b8
--- /dev/null
+++ b/node_modules/mongoose/lib/error/validator.js
@@ -0,0 +1,71 @@
+/*!
+ * Module dependencies.
+ */
+
+var MongooseError = require('../error.js');
+var errorMessages = MongooseError.messages;
+
+/**
+ * Schema validator error
+ *
+ * @param {Object} properties
+ * @inherits MongooseError
+ * @api private
+ */
+
+function ValidatorError(properties) {
+ var msg = properties.message;
+ if (!msg) {
+ msg = errorMessages.general.default;
+ }
+
+ this.properties = properties;
+ var message = this.formatMessage(msg, properties);
+ MongooseError.call(this, message);
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this);
+ } else {
+ this.stack = new Error().stack
+ }
+ 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;
+
+/*!
+ * 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/errors/version.js b/node_modules/mongoose/lib/error/version.js
similarity index 53%
rename from node_modules/mongoose/lib/errors/version.js
rename to node_modules/mongoose/lib/error/version.js
index b2388aa..815b1bc 100644
--- a/node_modules/mongoose/lib/errors/version.js
+++ b/node_modules/mongoose/lib/error/version.js
@@ -3,7 +3,7 @@
* Module dependencies.
*/
-var MongooseError = require('../error');
+var MongooseError = require('../error.js');
/**
* Version Error constructor.
@@ -12,17 +12,18 @@ var MongooseError = require('../error');
* @api private
*/
-function VersionError () {
+function VersionError() {
MongooseError.call(this, 'No matching document found.');
- Error.captureStackTrace(this, arguments.callee);
+ Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
this.name = 'VersionError';
-};
+}
/*!
* Inherits from MongooseError.
*/
-VersionError.prototype.__proto__ = MongooseError.prototype;
+VersionError.prototype = Object.create(MongooseError.prototype);
+VersionError.prototype.constructor = MongooseError;
/*!
* exports
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/index.js b/node_modules/mongoose/lib/index.js
index ca4cfa0..1f7a654 100644
--- a/node_modules/mongoose/lib/index.js
+++ b/node_modules/mongoose/lib/index.js
@@ -1,21 +1,25 @@
+'use strict';
/*!
* 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')
+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.
@@ -26,14 +30,24 @@ var Schema = require('./schema')
* @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
@@ -49,9 +63,11 @@ function Mongoose () {
* @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;
};
@@ -76,12 +92,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._
*
@@ -95,11 +144,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)
@@ -120,12 +169,15 @@ var rgxReplSet = /^.+,.+$/;
* @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);
@@ -142,15 +194,34 @@ Mongoose.prototype.createConnection = function () {
*
* _Options passed take precedence over options included in connection strings._
*
+ * ####Example:
+ *
+ * mongoose.connect('mongodb://user:pass@localhost:port/database');
+ *
+ * // replica sets
+ * var uri = 'mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/mydatabase';
+ * mongoose.connect(uri);
+ *
+ * // with options
+ * mongoose.connect(uri, options);
+ *
+ * // connecting to multiple mongos
+ * var uri = 'mongodb://hostA:27501,hostB:27501';
+ * var opts = { mongos: true };
+ * mongoose.connect(uri, opts);
+ *
+ * @param {String} uri(s)
+ * @param {Object} [options]
+ * @param {Function} [callback]
* @see Mongoose#createConnection #index_Mongoose-createConnection
* @api public
* @return {Mongoose} this
*/
-Mongoose.prototype.connect = function () {
+Mongoose.prototype.connect = function() {
var conn = this.connection;
- if (rgxReplSet.test(arguments[0])) {
+ if (rgxReplSet.test(arguments[0]) || checkReplicaSetInUri(arguments[0])) {
conn.openSet.apply(conn, arguments);
} else {
conn.open.apply(conn, arguments);
@@ -167,12 +238,12 @@ Mongoose.prototype.connect = function () {
* @api public
*/
-Mongoose.prototype.disconnect = function (fn) {
- var count = this.connections.length
- , error
+Mongoose.prototype.disconnect = function(fn) {
+ var count = this.connections.length,
+ error;
- this.connections.forEach(function(conn){
- conn.close(function(err){
+ this.connections.forEach(function(conn) {
+ conn.close(function(err) {
if (error) return;
if (err) {
@@ -228,7 +299,7 @@ Mongoose.prototype.disconnect = function (fn) {
* @api public
*/
-Mongoose.prototype.model = function (name, schema, collection, skipInit) {
+Mongoose.prototype.model = function(name, schema, collection, skipInit) {
if ('string' == typeof schema) {
collection = schema;
schema = false;
@@ -252,13 +323,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;
@@ -298,8 +364,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;
@@ -314,7 +384,7 @@ Mongoose.prototype.model = function (name, schema, collection, skipInit) {
}
return this.models[name] = model;
-}
+};
/**
* Returns an array of model names created on this instance of Mongoose.
@@ -327,10 +397,10 @@ 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;
-}
+};
/**
* Applies global plugins to `schema`.
@@ -339,11 +409,11 @@ Mongoose.prototype.modelNames = function () {
* @api private
*/
-Mongoose.prototype._applyPlugins = function (schema) {
+Mongoose.prototype._applyPlugins = function(schema) {
for (var i = 0, l = this.plugins.length; i < l; i++) {
schema.plugin(this.plugins[i][0], this.plugins[i][1]);
}
-}
+};
/**
* Declares a global plugin executed on all Schemas.
@@ -357,7 +427,7 @@ 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;
};
@@ -378,7 +448,7 @@ Mongoose.prototype.plugin = function (fn, opts) {
* @api public
*/
-Mongoose.prototype.__defineGetter__('connection', function(){
+Mongoose.prototype.__defineGetter__('connection', function() {
return this.connections[0];
});
@@ -400,6 +470,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
*
@@ -425,7 +504,7 @@ Mongoose.prototype.Connection = Connection;
* @api public
*/
-Mongoose.prototype.version = require(__dirname + '/../package.json').version;
+Mongoose.prototype.version = pkg.version;
/**
* The Mongoose constructor
@@ -533,7 +612,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.
@@ -553,6 +648,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.
*
@@ -571,6 +675,15 @@ 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');
+
/*!
* 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 7b5419e..4b9019b 100644
--- a/node_modules/mongoose/lib/model.js
+++ b/node_modules/mongoose/lib/model.js
@@ -1,42 +1,49 @@
+/* eslint no-unused-vars: 1 */
+
/*!
* 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 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 Types = require('./schema/index');
+var utils = require('./utils');
+var hasOwnProperty = utils.object.hasOwnProperty;
+var isMongooseObject = utils.isMongooseObject;
+var EventEmitter = require('events').EventEmitter;
+var Promise = require('./promise');
+var util = require('util');
+var tick = utils.tick;
-var VERSION_WHERE = 1
- , VERSION_INC = 2
- , VERSION_ALL = VERSION_WHERE | VERSION_INC;
+var async = require('async');
+var PromiseProvider = require('./promise_provider');
+
+var VERSION_WHERE = 1,
+ VERSION_INC = 2,
+ VERSION_ALL = VERSION_WHERE | VERSION_INC;
/**
* Model constructor
*
- * @param {Object} doc values to with which to create the document
+ * Provides the interface to MongoDB collections as well as creates document instances.
+ *
+ * @param {Object} doc values with which to create the document
* @inherits Document
- * @event `error`: If listening to this Model 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 `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.
@@ -74,61 +81,155 @@ 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);
- }
- promise.error(err);
- promise = self = null;
+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;
}
- self.$__storeShard();
+ this.$__version(true, obj);
+ this.collection.insert(obj, options.safe, function(err, ret) {
+ if (err) {
+ _this.isNew = true;
+ _this.emit('isNew', true);
- var numAffected;
- if (result) {
- // when inserting, the array of created docs is returned
- numAffected = result.length
- ? result.length
- : result;
+ 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;
+ }
+
+ this.collection.update(where, delta[1], options.safe, function(err, ret) {
+ if (err) {
+ callback(err);
+ return;
+ }
+ callback(null, ret);
+ });
} else {
- numAffected = 0;
+ this.$__reset();
+ callback();
+ return;
}
- // 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;
+ this.emit('isNew', false);
+ }
+};
- // 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);
- }
+/*!
+ * ignore
+ */
+
+Model.prototype.$__save = function(options, callback) {
+ var _this = this;
+
+ _this.$__handleSave(options, function(error, result) {
+ if (error) {
+ return callback(error);
+ }
+
+ _this.$__reset();
+ _this.$__storeShard();
+
+ var numAffected = 0;
+ if (result) {
+ 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 {
- // the update failed. pass an error back
- promise.error(new VersionError);
- promise = self = null;
- return;
+ numAffected = result;
}
}
- self.emit('save', self, numAffected);
- promise.complete(self, numAffected);
- promise = self = null;
+ // was this an update that required a version bump?
+ if (_this.$__.version && !_this.$__.inserting) {
+ var doIncrement = VERSION_INC === (VERSION_INC & _this.$__.version);
+ _this.$__.version = undefined;
+
+ if (numAffected <= 0) {
+ // the update failed. pass an error back
+ var err = new VersionError();
+ 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);
+ }
+ }
+
+ _this.emit('save', _this, numAffected);
+ callback(null, _this, numAffected);
});
-}
+};
/**
* Saves this document.
@@ -136,69 +237,84 @@ 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 callback will receive three parameters
*
- * 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.
+ * 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.
*
- * var db = mongoose.createConnection(..);
- * var schema = new Schema(..);
- * var Product = db.model('Product', schema);
+ * As an extra measure of flow control, save will return a Promise.
+ * ####Example:
+ * product.save().then(function(product) {
+ * ...
+ * });
*
- * 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.
- *
- * 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 = {}
-
- if (this.schema.options.safe) {
- options.safe = this.schema.options.safe;
+Model.prototype.save = function(options, fn) {
+ if ('function' == typeof options) {
+ fn = options;
+ options = undefined;
}
- 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;
-
- } 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) return complete(delta);
- var where = this.$__where(delta[0]);
- this.$__reset();
- this.collection.update(where, delta[1], options, complete);
- } else {
- this.$__reset();
- complete(null);
- }
-
- this.emit('isNew', false);
+ if (!options) {
+ options = {};
}
+
+ if (options.__noPromise) {
+ return this.$__save(options, fn);
+ }
+
+ var _this = this;
+
+ return new Promise.ES6(function(resolve, reject) {
+ _this.$__save(options, function(error, doc, numAffected) {
+ if (error) {
+ fn && fn(error);
+ reject(error);
+ return;
+ }
+
+ fn && fn(null, doc, numAffected);
+ resolve(doc, numAffected);
+ });
+ });
};
+/**
+ * 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;
+
+ // Remove any array indexes from the path
+ path = path.replace(/\.\d+\./, '.');
+
+ return skipVersioning[path];
+}
+
/*!
* Apply the operation to the delta (update) clause as
* well as track versioning for our where clause.
@@ -211,7 +327,7 @@ 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] = {};
@@ -220,6 +336,9 @@ function operand (self, where, delta, data, val, op) {
// disabled versioning?
if (false === self.schema.options.versionKey) return;
+ // path excluded from versioning?
+ if (shouldSkipVersioning(self, data.path)) return;
+
// already marked for versioning?
if (VERSION_ALL === (VERSION_ALL & self.$__.version)) return;
@@ -271,28 +390,28 @@ 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) {
+ 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) {
// $set
@@ -311,15 +430,15 @@ function handleAtomics (self, where, delta, data, value) {
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) {
+ val = val.map(function(mem) {
return isMongooseObject(mem)
? mem.toObject({ depopulate: 1 })
: mem;
- })
+ });
} else if (val.valueOf) {
- val = val.valueOf()
+ val = val.valueOf();
}
if ('$addToSet' === op)
@@ -337,22 +456,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;
- var where = {}
- , delta = {}
- , len = dirty.length
- , divergent = []
- , d = 0
- , val
- , obj
+ var where = {},
+ delta = {},
+ len = dirty.length,
+ divergent = [],
+ d = 0;
+
+ 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) {
@@ -392,19 +510,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);
@@ -412,12 +530,17 @@ 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] && doc.$__.selected[top].$elemMatch) ||
+ doc.$__.selected[top + '.$']) {
return top;
}
}
- if (!(pop && array instanceof MongooseArray)) return;
+ if (!(pop && array && array.isMongooseArray)) return;
+
+ if (!pop.options) {
+ return;
+ }
// If the array was populated using options that prevented all
// documents from being returned (match, skip, limit) or they
@@ -433,7 +556,7 @@ function checkDivergentArray (doc, path, array) {
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))
+ /\s?-_id\s?/.test(pop.options.select));
if (check) {
var atomics = array._atomics;
@@ -451,7 +574,7 @@ 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) {
@@ -476,10 +599,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.
@@ -495,10 +620,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.
@@ -508,30 +633,36 @@ 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) {
+ return new Error('No _id found on document!');
+ }
+
return where;
-}
+};
/**
* Removes this document from the db.
*
* ####Example:
- *
* product.remove(function (err, product) {
* if (err) return handleError(err);
* Product.findById(product._id, function (err, product) {
@@ -539,37 +670,70 @@ Model.prototype.$__where = function _where (where) {
* })
* })
*
- * @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) {
+ var Promise = PromiseProvider.get();
+
+ if ('function' == typeof options) {
+ 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 = {}
-
- if (this.schema.options.safe) {
- options.safe = this.schema.options.safe;
- }
-
- this.collection.remove(where, options, tick(function (err) {
- if (err) {
- promise.error(err);
- promise = self = self.$__.removing = where = options = null;
+ var _this = this;
+ var promise = 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;
+ });
+ });
+
+ return promise;
};
/**
@@ -584,10 +748,124 @@ 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) {
+ if (!(schema instanceof Schema)) {
+ 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 + "\"");
+ }
+
+ // merges base schema into new discriminator schema and sets new type field.
+ (function(schema, baseSchema) {
+ utils.merge(schema, baseSchema);
+
+ var obj = {};
+ obj[key] = { type: String, default: name };
+ schema.add(obj);
+ schema.discriminatorMapping = { key: key, value: name, isRoot: false };
+
+ if (baseSchema.options.collection) {
+ schema.options.collection = baseSchema.options.collection;
+ }
+
+ // throws error if options are invalid
+ (function(a, b) {
+ a = utils.clone(a);
+ b = utils.clone(b);
+ delete a.toJSON;
+ delete a.toObject;
+ delete b.toJSON;
+ delete b.toObject;
+ delete a._id;
+ delete b._id;
+
+ if (!utils.deepEqual(a, b)) {
+ throw new Error("Discriminator options are not customizable " +
+ "(except toJSON, toObject, _id)");
+ }
+ })(schema.options, baseSchema.options);
+
+ var toJSON = schema.options.toJSON;
+ var toObject = schema.options.toObject;
+ var _id = schema.options._id;
+
+ 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.callQueue = baseSchema.callQueue.concat(schema.callQueue.slice(schema._defaultMiddleware.length));
+ schema._requiredpaths = undefined; // reset just in case Schema#requiredPaths() was called on either schema
+ })(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");
+ }
+
+ 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
/*!
@@ -603,8 +881,9 @@ for (var i in EventEmitter.prototype)
* @api private
*/
-Model.init = function init () {
- if (this.schema.options.autoIndex) {
+Model.init = function init() {
+ if ((this.schema.options.autoIndex) ||
+ (this.schema.options.autoIndex === null && this.db.config.autoIndex)) {
this.ensureIndexes();
}
@@ -633,42 +912,79 @@ 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 {Function} [cb] optional callback
+ * @return {Promise}
* @api public
*/
-Model.ensureIndexes = function ensureIndexes (cb) {
+Model.ensureIndexes = function ensureIndexes(cb) {
+ var promise = new Promise(cb);
+
var indexes = this.schema.indexes();
if (!indexes.length) {
- return cb && process.nextTick(cb);
+ process.nextTick(promise.fulfill.bind(promise));
+ return promise;
}
// 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 self = this;
- function done (err) {
+ var done = function(err) {
+ if (err && self.schema.options.emitIndexErrors) {
+ self.emit('error', err);
+ }
self.emit('index', err);
- cb && cb(err);
- }
+ promise.resolve(err);
+ };
- function create () {
+ var indexSingleDone = function(err, fields, options, name) {
+ self.emit('index-single-done', err, fields, options, name);
+ };
+ var indexSingleStart = function(fields, options) {
+ self.emit('index-single-start', fields, options);
+ };
+
+ 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);
- create();
+ _handleSafe(options);
+
+ indexSingleStart(indexFields, options);
+
+ self.collection.ensureIndex(indexFields, options, tick(function(err,name) {
+ indexSingleDone(err,indexFields, options, name);
+ if (err) {
+ return done(err);
+ } else {
+ create();
+ }
}));
- }
+ };
create();
+ return promise;
+};
+
+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;
+ }
+ }
}
/**
@@ -711,6 +1027,16 @@ Model.collection;
Model.base;
+/**
+ * Registered discriminators for this model.
+ *
+ * @property discriminators
+ * @receiver Model
+ * @api public
+ */
+
+Model.discriminators;
+
/**
* Removes documents from the collection.
*
@@ -733,23 +1059,20 @@ Model.base;
*
* @param {Object} conditions
* @param {Function} [callback]
- * @return {Query}
+ * @return {Promise} Promise
* @api public
*/
-Model.remove = function remove (conditions, callback) {
+Model.remove = function remove(conditions, callback) {
if ('function' === typeof conditions) {
callback = conditions;
conditions = {};
}
- var query = new Query(conditions).bind(this, 'remove');
+ // get the mongodb collection object
+ var mq = new Query(conditions, {}, this, this.collection);
- if ('undefined' === typeof callback)
- return query;
-
- this._applyNamedScope(query);
- return query.remove(callback);
+ return mq.remove(callback);
};
/**
@@ -784,7 +1107,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}
@@ -793,60 +1116,44 @@ Model.remove = function remove (conditions, callback) {
* @api public
*/
-Model.find = function find (conditions, fields, options, callback) {
+Model.find = function find(conditions, projection, options, callback) {
if ('function' == typeof conditions) {
callback = conditions;
conditions = {};
- fields = null;
+ projection = null;
options = null;
- } else if ('function' == typeof fields) {
- callback = fields;
- fields = null;
+ } else if ('function' == typeof projection) {
+ callback = projection;
+ projection = null;
options = null;
} else if ('function' == typeof options) {
callback = options;
options = null;
}
- var query = new Query(conditions, options);
- query.bind(this, 'find');
- query.select(fields);
+ // get the raw mongodb collection object
+ var mq = new 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;
-
- 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
@@ -870,8 +1177,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}
@@ -880,8 +1187,11 @@ 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;
+ }
+ return this.findOne({ _id: id }, projection, options, callback);
};
/**
@@ -912,8 +1222,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}
@@ -922,28 +1232,30 @@ Model.findById = function findById (id, fields, options, callback) {
* @api public
*/
-Model.findOne = function findOne (conditions, fields, options, callback) {
+Model.findOne = function findOne(conditions, projection, options, callback) {
if ('function' == typeof options) {
callback = options;
options = null;
- } else if ('function' == typeof fields) {
- callback = fields;
- fields = null;
+ } else if ('function' == typeof projection) {
+ callback = projection;
+ projection = null;
options = null;
} else if ('function' == typeof conditions) {
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 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;
-
- this._applyNamedScope(query);
- return query.findOne(callback);
+ return mq.findOne(conditions, callback);
};
/**
@@ -962,20 +1274,20 @@ Model.findOne = function findOne (conditions, fields, options, callback) {
* @api public
*/
-Model.count = function count (conditions, callback) {
+Model.count = function count(conditions, callback) {
if ('function' === typeof conditions)
callback = conditions, conditions = {};
- var query = new Query(conditions).bind(this, 'count');
- if ('undefined' == typeof callback)
- return query;
+ // get the mongodb collection object
+ var mq = new Query({}, {}, this, this.collection);
- 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
*
@@ -986,6 +1298,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]
@@ -993,20 +1308,16 @@ Model.count = function count (conditions, callback) {
* @api public
*/
-Model.distinct = function distinct (field, conditions, callback) {
+Model.distinct = function distinct(field, conditions, callback) {
+ // get the mongodb collection object
+ var mq = new Query({}, {}, this, this.collection);
+
if ('function' == typeof conditions) {
callback = conditions;
conditions = {};
}
- var query = new Query(conditions).bind(this, 'distinct');
- if ('undefined' == typeof callback) {
- query._distinctArg = field;
- return query;
- }
-
- this._applyNamedScope(query);
- return query.distinct(field, callback);
+ return mq.distinct(field, conditions, callback);
};
/**
@@ -1033,9 +1344,10 @@ 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) {
+ // get the mongodb collection object
+ var mq = new Query({}, {}, this, this.collection).find({});
+ return mq.where.apply(mq, arguments);
};
/**
@@ -1053,9 +1365,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 Query({}, {}, this, this.collection).find({});
+ return mq.$where.apply(mq, arguments);
};
/**
@@ -1065,7 +1377,7 @@ 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.
* - `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
@@ -1094,20 +1406,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]
@@ -1118,7 +1434,7 @@ Model.$where = function $where () {
* @api public
*/
-Model.findOneAndUpdate = function (conditions, update, options, callback) {
+Model.findOneAndUpdate = function(conditions, update, options, callback) {
if ('function' == typeof options) {
callback = options;
options = null;
@@ -1131,7 +1447,7 @@ Model.findOneAndUpdate = function (conditions, update, options, callback) {
+ ' ' + this.modelName + '.findOneAndUpdate(conditions, update)\n'
+ ' ' + this.modelName + '.findOneAndUpdate(update)\n'
+ ' ' + this.modelName + '.findOneAndUpdate()\n';
- throw new TypeError(msg)
+ throw new TypeError(msg);
}
update = conditions;
conditions = undefined;
@@ -1143,26 +1459,34 @@ Model.findOneAndUpdate = function (conditions, update, options, callback) {
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 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.
* - `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
@@ -1175,14 +1499,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:
@@ -1198,22 +1514,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]
@@ -1223,24 +1543,30 @@ Model.findOneAndUpdate = function (conditions, update, options, callback) {
* @api public
*/
-Model.findByIdAndUpdate = function (id, update, options, callback) {
+Model.findByIdAndUpdate = function(id, update, options, callback) {
var args;
-
if (1 === arguments.length) {
if ('function' == typeof id) {
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)
+ throw new TypeError(msg);
}
return this.findOneAndUpdate({_id: id }, undefined);
}
args = utils.args(arguments, 1);
- args.unshift({ _id: id });
+
+ // if a model is passed in instead of an id
+ if (id instanceof Document) {
+ id = id._id;
+ }
+ if (id) {
+ args.unshift({ _id: id });
+ }
return this.findOneAndUpdate.apply(this, args);
-}
+};
/**
* Issue a mongodb findAndModify remove command.
@@ -1262,19 +1588,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]
@@ -1284,13 +1615,13 @@ Model.findByIdAndUpdate = function (id, update, options, callback) {
* @api public
*/
-Model.findOneAndRemove = function (conditions, options, callback) {
+Model.findOneAndRemove = function(conditions, options, callback) {
if (1 === arguments.length && 'function' == typeof conditions) {
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)
+ throw new TypeError(msg);
}
if ('function' == typeof options) {
@@ -1304,20 +1635,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 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.
*
@@ -1336,7 +1661,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}
@@ -1344,68 +1669,141 @@ Model.findOneAndRemove = function (conditions, options, callback) {
* @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
*/
-Model.findByIdAndRemove = function (id, options, callback) {
+Model.findByIdAndRemove = function(id, options, callback) {
if (1 === arguments.length && 'function' == typeof id) {
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)
+ throw new TypeError(msg);
}
return this.findOneAndRemove({ _id: id }, options, callback);
-}
+};
/**
* Shortcut for creating a new Document that is automatically saved to the db if valid.
*
* ####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,
+ 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 ('function' == typeof last) {
+ cb = last;
+ args = utils.args(arguments, 0, arguments.length - 1);
+ } else {
+ args = utils.args(arguments);
+ }
}
- if (0 === args.length) return fn(null);
+ var Promise = PromiseProvider.get();
+ var ModelConstructor = this;
- promise = new Promise(fn);
- count = args.length;
+ var promise = new Promise.ES6(function(resolve, reject) {
+ if (args.length === 0) {
+ process.nextTick(function() {
+ cb && cb(null);
+ resolve(null);
+ });
+ return;
+ }
- 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);
+ var toExecute = [];
+ args.forEach(function(doc) {
+ toExecute.push(function(callback) {
+ var toSave = new ModelConstructor(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(ModelConstructor, null, savedDocs);
+ } else {
+ resolve.apply(promise, savedDocs);
+ if (cb) {
+ savedDocs.unshift(null);
+ cb.apply(ModelConstructor, savedDocs);
+ }
+ }
});
});
+
+ return promise;
+};
+
+/**
+ * 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;
};
/**
@@ -1414,9 +1812,8 @@ Model.create = function create (doc, fn) {
* ####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);
* });
*
@@ -1426,13 +1823,13 @@ Model.create = function create (doc, fn) {
* - `upsert` (boolean) whether to create the doc if it doesn't match (false)
* - `multi` (boolean) whether multiple documents should be updated (false)
* - `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:
@@ -1446,6 +1843,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' }`.
*
@@ -1476,43 +1874,34 @@ 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 Query({}, {}, this, this.collection);
+ // 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:
*
@@ -1526,12 +1915,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:
@@ -1550,6 +1941,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) {
@@ -1557,23 +1949,33 @@ 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 promise = new Promise(callback);
var self = this;
if (!Model.mapReduce.schema) {
- var opts = { noId: true, noVirtualId: true, strict: false }
+ var opts = { noId: true, noVirtualId: true, strict: false };
Model.mapReduce.schema = new Schema({}, opts);
}
if (!o.out) o.out = { inline: 1 };
+ if (false !== o.verbose) o.verbose = true;
o.map = String(o.map);
o.reduce = String(o.reduce);
@@ -1585,8 +1987,8 @@ Model.mapReduce = function mapReduce (o, callback) {
q = undefined;
}
- this.collection.mapReduce(null, null, o, function (err, ret, stats) {
- if (err) return callback(err);
+ this.collection.mapReduce(null, null, o, function(err, ret, stats) {
+ if (err) return promise.error(err);
if (ret.findOne && ret.mapReduce) {
// returned a collection, convert to Model
@@ -1599,44 +2001,271 @@ Model.mapReduce = function mapReduce (o, callback) {
model._mapreduce = true;
- return callback(err, model, stats);
+ return promise.fulfill(model, stats);
}
- callback(err, ret, stats);
+ promise.fulfill(ret, stats);
});
-}
+
+ return promise;
+};
/**
- * Executes an aggregate command on this models collection.
+ * geoNear support for Mongoose
+ *
+ * ####Options:
+ * - `lean` {Boolean} return the raw object
+ * - All options supported by the driver are also supported
*
* ####Example:
*
- * // find the max age of all users
+ * // 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
+ */
+
+Model.geoNear = function(near, options, callback) {
+ if ('function' == typeof options) {
+ callback = options;
+ options = {};
+ }
+
+ var self = 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);
+ return;
+ });
+ }
+
+ var x,y;
+ var _this = this;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ var errSeen = false;
+ for (var i = 0; i < res.results.length; i++) {
+ var temp = res.results[i].obj;
+ res.results[i].obj = new self();
+ res.results[i].obj.init(temp, function(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);
+ }
+ });
+ }
+ };
+
+ 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;
+ }
+
+ _this.collection.geoNear(near, options, handler);
+ }
+ });
+};
+
+/**
+ * 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 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 ('function' === typeof args[args.length - 1]) {
+ callback = args.pop();
+ }
+
+ if (1 === args.length && util.isArray(args[0])) {
+ aggregate = new Aggregate(args[0]);
+ } else {
+ aggregate = new Aggregate(args);
+ }
+
+ aggregate.model(this);
+
+ if ('undefined' === typeof callback) {
+ return aggregate;
+ }
+
+ return 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} condition 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 ('function' == typeof options) {
+ callback = options;
+ options = {};
+ }
+
+ var promise = new Promise(callback);
+
+ if (conditions == undefined || !utils.isObject(conditions)) {
+ return promise.error(new Error("Must pass conditions to geoSearch"));
+ }
+
+ if (!options.near) {
+ return promise.error(new Error("Must specify the near option in geoSearch"));
+ }
+
+ if (!Array.isArray(options.near)) {
+ return promise.error(new Error("near option must be an array [x, y]"));
+ }
+
+
+ // send the conditions in the options object
+ options.search = conditions;
+ var self = this;
+
+ 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 || res.errmsg) {
+ if (!err) err = new Error(res.errmsg);
+ if (res && res.code !== undefined) err.code = res.code;
+ return promise.error(err);
+ }
+
+ var count = res.results.length;
+ if (options.lean || (count == 0)) return promise.fulfill(res.results, res.stats);
+
+ var errSeen = false;
+ for (var i = 0; i < res.results.length; i++) {
+ var temp = res.results[i];
+ res.results[i] = new self();
+ res.results[i].init(temp, {}, function(err) {
+ if (err && !errSeen) {
+ errSeen = true;
+ return promise.error(err);
+ }
+
+ --count || (!errSeen && promise.fulfill(res.results, res.stats));
+ });
+ }
+ });
+
+ return promise;
+};
/**
* Populates document references.
@@ -1667,9 +2296,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:
@@ -1696,68 +2324,116 @@ 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} [cb(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, cb) {
+ var promise = new Promise(cb);
- // always callback on nextTick for consistent async behavior
- function callback () {
+ // always resolve on nextTick for consistent async behavior
+ function resolve() {
var args = utils.args(arguments);
- process.nextTick(function () {
- cb.apply(null, args);
+
+ process.nextTick(function() {
+ promise.resolve.apply(promise, args);
});
}
// normalized paths
- var paths = utils.populate(paths);
+ paths = utils.populate(paths);
var pending = paths.length;
if (0 === pending) {
- return callback(null, docs);
+ resolve(null, docs);
+ return promise;
}
// each path has its own query options and must be executed separately
var i = pending;
var path;
+ var model = this;
while (i--) {
path = paths[i];
- populate(this, docs, path, next);
+ if ('function' === typeof path.model) model = path.model;
+ populate(model, docs, path, subPopulate.call(model, docs, path, next));
}
- function next (err) {
- if (next.err) return;
- if (err) return callback(next.err = err);
+ return promise;
+
+ function next(err) {
+ if (err) return resolve(err);
if (--pending) return;
- callback(null, docs);
+ resolve(null, docs);
}
+};
+
+/*!
+ * Populates deeply if `populate` option is present.
+ *
+ * @param {Document|Array} docs
+ * @param {Object} options
+ * @param {Function} cb
+ * @return {Function}
+ * @api private
+ */
+function subPopulate(docs, options, cb) {
+ var model = this;
+ var prefix = options.path + '.';
+ var pop = options.populate;
+
+ if (!pop) {
+ return cb;
+ }
+
+ // normalize as array
+ if (!Array.isArray(pop)) {
+ pop = [pop];
+ }
+
+ return function(err) {
+ var pending = pop.length;
+
+ function next(err) {
+ if (err) return cb(err);
+ if (--pending) return;
+ cb();
+ }
+
+ if (err || !pending) return cb(err);
+
+ pop.forEach(function(subOptions) {
+ // path needs parent's path prefixed to it
+ if (!subOptions._originalPath) {
+ subOptions._originalPath = subOptions.path;
+ subOptions.path = prefix + subOptions.path;
+ }
+
+ var schema = model.schema._getSchema(prefix);
+ if (typeof subOptions.model === 'string') {
+ subOptions.model = model.model(subOptions.model);
+ } else if (schema && schema.caster && schema.caster.options &&
+ schema.caster.options.ref) {
+ var subSchema = model.model(schema.caster.options.ref).schema.
+ _getSchema(subOptions._originalPath);
+ if (subSchema && subSchema.options && subSchema.options.ref) {
+ subOptions.model = model.model(subSchema.options.ref);
+ }
+ }
+ Model.populate.call(subOptions.model || model, docs, subOptions, next);
+ });
+ };
}
/*!
- * 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, cb) {
+ var modelsMap, rawIds;
// normalize single / multiple docs passed
if (!Array.isArray(docs)) {
@@ -1768,108 +2444,18 @@ function populate (model, docs, options, cb) {
return cb();
}
- populateDocs(docs, options, cb);
-}
+ modelsMap = getModelsMapForPopulate(model, docs, options);
+ rawIds = getIdsForAndAddIdsInMapPopulate(modelsMap);
-/*!
- * Populates `docs`
- */
+ var i, len = modelsMap.length,
+ mod, match, select, promise, vals = [];
-function populateDocs (docs, options, cb) {
- var select = options.select;
- var match = options.match;
- var path = options.path;
- var Model = options.model;
-
- var rawIds = [];
- var i, doc, id;
- var len = docs.length;
- var found = 0;
- var isDocument;
- var subpath;
- var ret;
-
- for (i = 0; i < len; i++) {
- ret = undefined;
- doc = docs[i];
- id = String(utils.getValue("_id", doc));
- isDocument = !! doc.$__;
-
- 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 (!ret || Array.isArray(ret) && 0 === ret.length) {
- ret = utils.getValue(path, doc);
- }
-
- if (ret) {
- // previously we always assigned this even if the document had no _id
- options._docs[id] = Array.isArray(ret)
- ? ret.slice()
- : ret;
- }
-
- // always retain original values, even empty values. these are
- // used to map the query results back to the correct position.
- rawIds.push(ret);
-
- if (isDocument) {
- // cache original populated _ids and model used
- doc.populated(path, options._docs[id], options);
- }
- }
-
- var ids = utils.array.flatten(rawIds, function (item) {
- // no need to include undefined values in our query
- return undefined !== item;
- });
-
- if (0 === ids.length || ids.every(utils.isNullOrUndefined)) {
- return cb();
- }
-
- // preserve original match conditions by copying
- if (match) {
- match = utils.object.shallowCopy(match);
- } else {
- match = {};
- }
-
- 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 = null;
- } else {
- // preserve original select conditions by copying
- select = utils.object.shallowCopy(select);
- delete select._id;
- }
- }
-
- Model.find(match, select, options.options, function (err, vals) {
+ promise = new Promise(function(err, vals, options, assignmentOpts) {
if (err) return cb(err);
- var lean = options.options && options.options.lean;
- var len = vals.length;
- var rawOrder = {};
- var rawDocs = {}
- var key;
- var val;
+ var lean = options.options && options.options.lean,
+ len = vals.length,
+ rawOrder = {}, rawDocs = {}, key, val;
// optimization:
// record the document positions as returned by
@@ -1889,12 +2475,222 @@ function populateDocs (docs, options, cb) {
rawDocs: rawDocs,
rawOrder: rawOrder,
docs: docs,
- path: path,
+ path: options.path,
options: assignmentOpts
});
-
cb();
});
+
+ var _remaining = len;
+ for (i = 0; i < len; i++) {
+ mod = modelsMap[i];
+ select = mod.options.select;
+
+ if (mod.options.match) {
+ match = utils.object.shallowCopy(mod.options.match);
+ } else {
+ match = {};
+ }
+
+ var ids = utils.array.flatten(mod.ids, function(item) {
+ // no need to include undefined values in our query
+ return undefined !== item;
+ });
+
+ ids = utils.array.unique(ids);
+
+ if (0 === ids.length || ids.every(utils.isNullOrUndefined)) {
+ return cb();
+ }
+
+ match._id || (match._id = {
+ $in: ids
+ });
+
+ var assignmentOpts = {};
+ assignmentOpts.sort = mod.options.options && mod.options.options.sort || undefined;
+ assignmentOpts.excludeId = excludeIdReg.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(excludeIdRegGlobal, ' ');
+ } else {
+ // preserve original select conditions by copying
+ select = utils.object.shallowCopy(select);
+ delete select._id;
+ }
+ }
+
+ if (mod.options.options && mod.options.options.limit) {
+ assignmentOpts.originalLimit = mod.options.options.limit;
+ mod.options.options.limit = mod.options.options.limit * ids.length;
+ }
+
+ mod.Model.find(match, select, mod.options.options, next.bind(this, mod.options, assignmentOpts));
+ }
+
+ function next(options, assignmentOpts, err, valsFromDb) {
+ if (err) return promise.resolve(err);
+ vals = vals.concat(valsFromDb);
+ if (--_remaining === 0) {
+ promise.resolve(err, vals, options, assignmentOpts);
+ }
+ }
+}
+
+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;
+
+ schema = model._getSchema(options.path);
+
+ if (schema && schema.caster) {
+ schema = schema.caster;
+ }
+
+ if (!schema && model.discriminators) {
+ discriminatorKey = model.schema.discriminatorMapping.key;
+ }
+
+ refPath = schema && schema.options && schema.options.refPath;
+
+ for (i = 0; i < len; i++) {
+ doc = docs[i];
+
+ if (refPath) {
+ modelNames = utils.getValue(refPath, doc);
+ } else {
+ if (!modelNameFromQuery) {
+ var schemaForCurrentDoc;
+
+ if (!schema && discriminatorKey) {
+ modelForFindSchema = utils.getValue(discriminatorKey, doc);
+
+ if (modelForFindSchema) {
+ schemaForCurrentDoc = model.db.model(modelForFindSchema)._getSchema(options.path);
+
+ if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
+ schemaForCurrentDoc = schemaForCurrentDoc.caster;
+ }
+ }
+ } else {
+ schemaForCurrentDoc = schema;
+ }
+
+ modelNames = [
+ schemaForCurrentDoc && schemaForCurrentDoc.options && schemaForCurrentDoc.options.ref // declared in schema
+ || model.modelName // an ad-hoc structure
+ ];
+ } else {
+ modelNames = [modelNameFromQuery]; // query options
+ }
+ }
+
+ if (!modelNames)
+ continue;
+
+ if (!Array.isArray(modelNames)) {
+ modelNames = [modelNames];
+ }
+
+ var k = modelNames.length;
+ while (k--) {
+ modelName = modelNames[k];
+ if (!available[modelName]) {
+ Model = model.db.model(modelName);
+ currentOptions = {
+ model: Model
+ };
+
+ if (schema && !discriminatorKey) {
+ options.model = Model;
+ }
+
+ utils.merge(currentOptions, options);
+
+ available[modelName] = {
+ Model: Model,
+ options: currentOptions,
+ docs: [doc],
+ ids: []
+ };
+ map.push(available[modelName]);
+ } else {
+ available[modelName].docs.push(doc);
+ }
+
+ }
+ }
+
+ return map;
+}
+
+function getIdsForAndAddIdsInMapPopulate(modelsMap) {
+ var rawIds = [], // for the correct position
+ i, j, doc, docs, id, len, len2, ret, isDocument, options, path;
+
+ len2 = modelsMap.length;
+ for (j = 0; j < len2; j++) {
+ docs = modelsMap[j].docs;
+ len = docs.length;
+ options = modelsMap[j].options;
+ path = options.path;
+
+ for (i = 0; i < len; i++) {
+ ret = undefined;
+ doc = docs[i];
+ id = String(utils.getValue("_id", doc));
+ isDocument = !!doc.$__;
+
+ if (!ret || Array.isArray(ret) && 0 === ret.length) {
+ ret = utils.getValue(path, doc);
+ }
+
+ if (ret) {
+ ret = convertTo_id(ret);
+
+ options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
+ }
+
+ rawIds.push(ret);
+ modelsMap[j].ids.push(ret);
+
+ if (isDocument) {
+ // cache original populated _ids and model used
+ doc.populated(path, options._docs[id], options);
+ }
+ }
+ }
+
+ return rawIds;
+}
+
+/*!
+ * 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;
}
/*!
@@ -1902,7 +2698,7 @@ function populateDocs (docs, options, cb) {
* 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
@@ -1917,7 +2713,9 @@ function assignVals (o) {
var options = o.options;
for (var i = 0; i < docs.length; ++i) {
- utils.setValue(path, rawIds[i], docs[i], function (val) {
+ if (utils.getValue(path, docs[i]) == null)
+ continue;
+ utils.setValue(path, rawIds[i], docs[i], function(val) {
return valueFilter(val, options);
});
}
@@ -1941,17 +2739,31 @@ function assignVals (o) {
* that population mapping can occur.
*/
-function valueFilter (val, assignmentOpts) {
+function valueFilter(val, assignmentOpts) {
if (Array.isArray(val)) {
// find logic
var ret = [];
- for (var i = 0; i < val.length; ++i) {
+ 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;
+ }
}
- return ret;
+
+ // 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
@@ -1967,10 +2779,10 @@ function valueFilter (val, assignmentOpts) {
* Remove _id from `subdoc` if user specified "lean" query option
*/
-function maybeRemoveId (subdoc, assignmentOpts) {
+function maybeRemoveId(subdoc, assignmentOpts) {
if (assignmentOpts.excludeId) {
if ('function' == typeof subdoc.setValue) {
- subdoc.setValue('_id', undefined);
+ delete subdoc._doc._id;
} else {
delete subdoc._id;
}
@@ -1982,7 +2794,7 @@ function maybeRemoveId (subdoc, assignmentOpts) {
* by a populate query.
*/
-function isDoc (doc) {
+function isDoc(doc) {
if (null == doc)
return false;
@@ -2023,11 +2835,10 @@ function isDoc (doc) {
* @api private
*/
-function assignRawDocsToIdStructure (rawIds, resultDocs, resultOrder, options, recursed) {
+function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, recursed) {
// honor user specified sort order
var newOrder = [];
var sorting = options.sort && rawIds.length > 1;
- var found;
var doc;
var sid;
var id;
@@ -2050,7 +2861,6 @@ function assignRawDocsToIdStructure (rawIds, resultDocs, resultOrder, options, r
}
sid = String(id);
- found = false;
if (recursed) {
// apply find behavior
@@ -2079,7 +2889,7 @@ 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) {
rawIds[i] = doc;
});
}
@@ -2095,49 +2905,9 @@ function assignRawDocsToIdStructure (rawIds, resultDocs, resultOrder, options, r
* @api private
*/
-Model._getSchema = function _getSchema (path) {
- var schema = this.schema
- , pathschema = schema.path(path);
-
- if (pathschema)
- return pathschema;
-
- // look for arrays
- return (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 Types.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 (p !== parts.length) {
- 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);
- }
- }
- }
- return foundschema;
- }
- }
- })(path.split('.'), schema)
-}
+Model._getSchema = function _getSchema(path) {
+ return this.schema._getSchema(path);
+};
/*!
* Compiler utility.
@@ -2149,7 +2919,7 @@ Model._getSchema = function _getSchema (path) {
* @param {Mongoose} base mongoose instance
*/
-Model.compile = function compile (name, schema, collectionName, connection, base) {
+Model.compile = function compile(name, schema, collectionName, connection, base) {
var versioningEnabled = false !== schema.options.versionKey;
if (versioningEnabled && !schema.paths[schema.options.versionKey]) {
@@ -2160,24 +2930,26 @@ Model.compile = function compile (name, schema, collectionName, connection, base
}
// generate new class
- function model (doc, fields, skipId) {
+ 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(
@@ -2185,24 +2957,54 @@ Model.compile = function compile (name, schema, collectionName, connection, base
, 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;
return model;
};
+/*!
+ * Register methods for this model
+ *
+ * @param {Model} model
+ * @param {Schema} schema
+ */
+var applyMethods = function(model, schema) {
+ for (var i in schema.methods) {
+ if (typeof schema.methods[i] === 'function') {
+ model.prototype[i] = schema.methods[i];
+ } else {
+ (function(_i) {
+ Object.defineProperty(model.prototype, _i, {
+ get: function() {
+ var h = {};
+ for (var k in schema.methods[_i]) {
+ h[k] = schema.methods[_i][k].bind(this);
+ }
+ return h;
+ },
+ configurable: true
+ });
+ })(i);
+ }
+ }
+};
+
+/*!
+ * 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];
+ }
+};
+
/*!
* Subclass this model with `conn`, `schema`, and `collection` settings.
*
@@ -2212,16 +3014,16 @@ 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 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);
- }
+ };
Model.__proto__ = model;
Model.prototype.__proto__ = model.prototype;
@@ -2231,21 +3033,23 @@ Model.__subclass = function subclass (conn, schema, collection) {
? schema
: model.prototype.schema;
+ var options = s.options || {};
+
if (!collection) {
collection = model.prototype.schema.get('collection')
- || utils.toCollectionName(model.modelName);
+ || utils.toCollectionName(model.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;
-}
+};
/*!
* 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..403eaa3 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,29 +13,57 @@ 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
+ }
});
/*!
@@ -84,10 +112,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 +134,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,9 +154,12 @@ 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;
@@ -130,11 +167,26 @@ Promise.prototype.addBack = Promise.prototype.onResolve;
/**
* Fulfills this promise with passed arguments.
*
- * Alias of [mpromise#fulfill](https://github.com/aheckmann/mpromise#fulfill).
- *
- * @method complete
+ * @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 +196,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 +212,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 +253,7 @@ Promise.prototype.addErrback = Promise.prototype.onReject;
* @param {Function} onFulFill
* @param {Function} onReject
* @return {Promise} newPromise
+ * @deprecated
*/
/**
@@ -222,6 +281,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 384876d..96810c1 100644
--- a/node_modules/mongoose/lib/query.js
+++ b/node_modules/mongoose/lib/query.js
@@ -1,545 +1,199 @@
+/* eslint no-unused-vars: 1 */
+
/*!
* 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 mquery = require('mquery');
+var util = require('util');
+var readPref = require('./drivers').ReadPreference;
+var PromiseProvider = require('./promise_provider');
+var updateValidators = require('./services/updateValidators');
+var utils = require('./utils');
+var helpers = require('./queryhelpers');
+var Document = require('./document');
+var QueryStream = require('./querystream');
+var cast = require('./cast');
/**
* 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) *
- * - [safe](http://www.mongodb.org/display/DOCS/getLastError+Command)
- *
- * _* denotes a query helper method is also available_
- *
- * @param {Object} options
- * @api public
- */
-
-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;
- }
-
- 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];
- }
- }
-
- 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}
+ * @param {Object} [options]
+ * @param {Object} [model]
+ * @param {Object} [conditions]
+ * @param {Object} [collection] Mongoose collection
* @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 || {});
+function Query(conditions, options, model, collection) {
+ // this stuff is for dealing with custom queries created by #toConstructor
+ if (!this._mongooseOptions) {
+ this._mongooseOptions = {};
}
- return this;
-};
+ // 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];
+ }
+ }
+
+ if (collection) {
+ this.mongooseCollection = collection;
+ }
+
+ if (model) {
+ this.model = model;
+ this.schema = model.schema;
+ }
+
+ // 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();
+ }
+
+ // inherit mquery
+ mquery.call(this, this.mongooseCollection, options);
+
+ if (conditions) {
+ this.find(conditions);
+ }
+
+ if (this.schema) {
+ this._count = this.model.hooks.createWrapper('count',
+ Query.prototype._count, this);
+ this._execUpdate = this.model.hooks.createWrapper('update',
+ Query.prototype._execUpdate, this);
+ this._find = this.model.hooks.createWrapper('find',
+ Query.prototype._find, this);
+ this._findOne = this.model.hooks.createWrapper('findOne',
+ Query.prototype._findOne, this);
+ this._findOneAndRemove = this.model.hooks.createWrapper('findOneAndRemove',
+ Query.prototype._findOneAndRemove, this);
+ this._findOneAndUpdate = this.model.hooks.createWrapper('findOneAndUpdate',
+ Query.prototype._findOneAndUpdate, this);
+ }
+}
+
+/*!
+ * inherit mquery
+ */
+
+Query.prototype = new mquery;
+Query.prototype.constructor = Query;
+Query.base = mquery.prototype;
/**
- * Executes the query
+ * Flag to opt out of using `$geoWithin`.
*
- * ####Examples
+ * mongoose.Query.use$geoWithin = false;
*
- * query.exec();
- * query.exec(callback);
- * query.exec('update');
- * query.exec('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 {String|Function} [operation]
- * @param {Function} [callback]
- * @return {Promise}
+ * @see http://docs.mongodb.org/manual/reference/operator/geoWithin/
+ * @default true
+ * @property use$geoWithin
+ * @memberOf Query
+ * @receiver Query
* @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 (callback) promise.addBack(callback);
-
- if (!this.op) {
- promise.complete();
- return promise;
- }
-
- if ('update' == this.op) {
- this[this.op](this._updateArg, promise.resolve.bind(promise));
- return promise;
- }
-
- if ('distinct' == this.op) {
- this.distinct(this._distinctArg, promise.resolve.bind(promise));
- return promise;
- }
-
- this[this.op](promise.resolve.bind(promise));
- return promise;
-};
+Query.use$geoWithin = mquery.use$geoWithin;
/**
- * Finds documents.
- *
- * When no `callback` is passed, the query is not executed.
+ * Converts this query to a customized, reusable query constructor with all arguments and options retained.
*
* ####Example
*
- * query.find({ name: 'Los Pollos Hermanos' }).find(callback)
+ * // 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');
*
- * @param {Object} [criteria] mongodb selector
- * @param {Function} [callback]
- * @return {Query} this
+ * // 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)
+ *
+ * New in 3.7.3
+ *
+ * @return {Query} subclass-of-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);
-};
-
-/**
- * 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);
-
- 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 {
-
- 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);
- }
+Query.prototype.toConstructor = function toConstructor() {
+ 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);
+ };
- return obj;
+ util.inherits(CustomQuery, Query);
+
+ // set inherited defaults
+ var p = CustomQuery.prototype;
+
+ p.options = {};
+
+ p.setOptions(this.options);
+
+ 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.model = this.model;
+ p.mongooseCollection = this.mongooseCollection;
+ p._mongooseOptions = this._mongooseOptions;
+
+ return CustomQuery;
};
-/**
- * Returns default options.
- * @param {Model} model
- * @api private
- */
-
-Query.prototype._optionsForExec = function (model) {
- var options = utils.clone(this.options, { retainKeyOrder: true });
- delete options.populate;
-
- if (!('safe' in options))
- options.safe = model.schema.options.safe;
-
- if (!('readPreference' in options) && model.schema.options.read)
- options.readPreference = model.schema.options.read;
-
- return options;
-};
-
-/**
- * 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:
@@ -548,6 +202,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
@@ -555,11 +210,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.
*
@@ -571,36 +221,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()`
*
@@ -612,18 +250,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.
*
@@ -632,18 +265,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.
*
@@ -652,18 +280,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.
*
@@ -671,19 +294,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.
*
@@ -696,11 +314,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
*/
@@ -709,11 +327,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
*/
@@ -722,11 +340,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
*/
@@ -735,10 +353,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
*/
@@ -751,7 +369,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
*/
@@ -764,7 +382,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
*/
@@ -777,7 +395,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
*/
@@ -790,13 +408,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
*
@@ -807,12 +427,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
*/
@@ -825,7 +443,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
*/
@@ -838,133 +456,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
- *
- * @param {String} path
- * @param {Number} 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.
- *
- * @param {String} path
- * @param {Object} 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
*
@@ -980,10 +511,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
@@ -991,309 +524,44 @@ 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()
*
- * @property within
- * @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()`.
+ * 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: [[],[],[],[]] });
*
- * ####Example
+ * query.where('loc').within([], [], []) // polygon
+ * query.where('loc').within([], []) // box
+ * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
*
- * query.intersects.geometry({
- * type: 'LineString'
- * , coordinates: [[180.0, 11.0], [180, 9.0]]
- * })
+ * **MUST** be used after `where()`.
*
- * @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/
- * @memberOf Query
- * @return {Query} this
- * @api public
- */
-
-Object.defineProperty(Query.prototype, 'intersects', {
- get: function () {
- this._geoComparison = '$geoIntersects';
- return this
- }
-});
-
-/**
- * Specifies a $box condition
+ * ####NOTE:
*
- * ####Example
+ * As of Mongoose 3.7, `$geoWithin` is always used for queries. To change this behavior, see [Query.use$geoWithin](#query_Query-use%2524geoWithin).
*
- * var lowerLeft = [40.73083, -73.99756]
- * var upperRight= [40.741404, -73.988135]
- * query.where('loc').within.box({ ll: lowerLeft , ur: upperRight })
+ * ####NOTE:
*
- * @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
- * @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
+ * 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).
*
- * ####Example
- *
- * var area = { center: [50, 50], radius: 10 }
- * query.where('loc').within.center(area)
- *
- * @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/
- * @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
- *
- * ####Example
- *
- * var area = { center: [50, 50], radius: 10 }
- * query.where('loc').within.centerSphere(area)
- *
- * @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 (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
- *
- * ####Example
- *
- * var polyA = [ [ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ] ]
- * query.where('loc').within.polygon(polyA)
- *
- * // or
- * var polyB = { a : { x : 10, y : 20 }, b : { x : 15, y : 25 }, c : { x : 20, y : 20 } }
- * query.where('loc').within.polygon(polyB)
- *
- * @param {String} [path]
- * @param {Array|Object} val
- * @return {Query} this
- * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
+ * @method within
* @see $polygon http://docs.mongodb.org/manual/reference/operator/polygon/
- * @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
- *
- * ####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
- * var polyC = [ 0, 0 ]
- * query.where('loc').intersects.geometry({ type: 'Point', coordinates: polyC })
- *
- * ####NOTE:
- *
- * `geometry()` **must** come after either `intersects` or `within`.
- *
- * The `object` argument must contain `type` and `coordinates` properties.
- * - type {String}
- * - coordinates {Array}
- *
- * When called with one argument, the most recent path passed to `where()` is used.
- *
- * @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.
- * @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 $box http://docs.mongodb.org/manual/reference/operator/box/
* @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
+ * @see $center http://docs.mongodb.org/manual/reference/operator/center/
+ * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
+ * @memberOf Query
* @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.
*
@@ -1305,106 +573,43 @@ Query.prototype.select = function select (arg) {
* query.where('comments').slice(5)
* query.where('comments').slice([-10, 5])
*
- * @param {String} path
- * @param {Number} val number of elements to slice
+ * @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
*/
-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.
- *
- * ####Example
- *
- * // sort by "field" ascending and "test" descending
- * query.sort({ field: 'asc', test: -1 });
- *
- * // equivalent
- * query.sort('field -test');
- *
- * @param {Object|String} arg
- * @return {Query} this
- * @see cursor.sort http://docs.mongodb.org/manual/reference/method/cursor.sort/
- * @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)
+ * query.limit(20)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
*
* @method limit
* @memberOf Query
* @param {Number} val
* @api public
*/
+
/**
* Specifies the number of documents to skip.
*
* ####Example
*
- * Kitten.find().skip(100).limit(20)
+ * query.skip(100).limit(20)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
*
* @method skip
* @memberOf Query
@@ -1412,25 +617,35 @@ function push (arr, field, value) {
* @see cursor.skip http://docs.mongodb.org/manual/reference/method/cursor.skip/
* @api public
*/
+
/**
- * Specifies the maxscan option.
+ * Specifies the maxScan option.
*
* ####Example
*
- * Kitten.find().maxscan(100)
+ * query.maxScan(100)
*
- * @method maxscan
+ * ####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
*/
+
/**
* Specifies the batchSize option.
*
* ####Example
*
- * Kitten.find().batchSize(100)
+ * query.batchSize(100)
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
*
* @method batchSize
* @memberOf Query
@@ -1438,12 +653,17 @@ function push (arr, field, value) {
* @see batchSize http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
* @api public
*/
+
/**
* Specifies the `comment` option.
*
* ####Example
*
- * Kitten.findOne(condition).comment('login query')
+ * query.comment('login query')
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
*
* @method comment
* @memberOf Query
@@ -1452,88 +672,101 @@ function push (arr, field, value) {
* @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()
+ * query.snapshot() // true
+ * query.snapshot(true)
+ * query.snapshot(false)
*
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method snapshot
+ * @memberOf Query
* @see snapshot http://docs.mongodb.org/manual/reference/operator/snapshot/
* @return {Query} this
* @api public
*/
-Query.prototype.snapshot = function () {
- this.options.snapshot = true;
- return this;
-};
-
/**
* Sets query hints.
*
* ####Example
*
- * Model.find().hint({ indexA: 1, indexB: -1})
+ * query.hint({ indexA: 1, indexB: -1})
*
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @method hint
+ * @memberOf Query
* @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).
+ * Specifies which document fields to include or exclude (also known as the query "projection")
*
- * ####Example:
+ * 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).
*
- * new Query().slaveOk() // true
- * new Query().slaveOk(true)
- * new Query().slaveOk(false)
+ * ####Example
*
- * @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/
+ * // include a and b, exclude other fields
+ * query.select('a b');
+ *
+ * // exclude c and d, include other fields
+ * query.select('-c -d');
+ *
+ * // 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 });
+ *
+ * // force inclusion of field excluded at schema level
+ * query.select('+path')
+ *
+ * ####NOTE:
+ *
+ * Cannot be used with `distinct()`.
+ *
+ * _v2 had slightly different syntax such as allowing arrays of field names. This support was removed in v3._
+ *
+ * @method select
+ * @memberOf Query
+ * @param {Object|String} arg
* @return {Query} this
+ * @see SchemaType
* @api public
*/
-Query.prototype.slaveOk = function (v) {
- this.options.slaveOk = arguments.length ? !!v : true;
- return this;
-}
+/**
+ * _DEPRECATED_ Sets the slaveOk option.
+ *
+ * **Deprecated** in MongoDB 2.2 in favor of [read preferences](#query_Query-read).
+ *
+ * ####Example:
+ *
+ * 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
+ */
/**
* Determines the MongoDB nodes from which to read.
@@ -1541,10 +774,10 @@ Query.prototype.slaveOk = function (v) {
* ####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
*
@@ -1572,10 +805,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
@@ -1584,10 +819,167 @@ 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.apply(readPref, arguments);
+ return Query.base.read.call(this, read);
+};
+
+/**
+ * Merges another Query or conditions object into this one.
+ *
+ * When a Query is passed, conditions, field selection and options are merged.
+ *
+ * New in 3.7.0
+ *
+ * @method merge
+ * @memberOf Query
+ * @param {Query|Object} source
+ * @return {Query} this
+ */
+
+/**
+ * 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) *
+ * - [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)
+ *
+ * _* denotes a query helper method is also available_
+ * _** query helper method to set `readPreference` is `read()`_
+ *
+ * @param {Object} options
+ * @api public
+ */
+
+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 && 'Object' == options.constructor.name)) {
+ return this;
+ }
+
+ return Query.base.setOptions.call(this, options);
+};
+
+/**
+ * Returns the current query conditions as a JSON object.
+ *
+ * ####Example:
+ *
+ * var query = new Query();
+ * query.find({ a: 1 }).where('b').gt(2);
+ * query.getQuery(); // { a: 1, b: { $gt: 2 } }
+ *
+ * @return {Object} current query conditions
+ * @api public
+ */
+
+Query.prototype.getQuery = function() {
+ return this._conditions;
+};
+
+/**
+ * Returns the current update operations as a JSON object.
+ *
+ * ####Example:
+ *
+ * var query = new Query();
+ * query.update({}, { $set: { a: 5 } });
+ * query.getUpdate(); // { $set: { a: 5 } }
+ *
+ * @return {Object} current update operations
+ * @api public
+ */
+
+Query.prototype.getUpdate = function() {
+ return this._update;
+};
+
+/**
+ * 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
+ */
+
+/**
+ * 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;
+ } else {
+ 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;
+ }
+
+ return options;
+ }
+};
/**
* Sets the lean option.
@@ -1611,92 +1003,103 @@ Query.prototype.read = function (pref, tags) {
* @api public
*/
-Query.prototype.lean = function (v) {
- this.options.lean = arguments.length ? !!v : true;
- return this;
-}
-
-/**
- * Sets the tailable option (for use with capped collections).
- *
- * ####Example
- *
- * Kitten.find().tailable() // true
- * Kitten.find().tailable(true)
- * Kitten.find().tailable(false)
- *
- * @param {Boolean} bool defaults to true
- * @see tailable http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/
- * @api public
- */
-
-Query.prototype.tailable = function (v) {
- this.options.tailable = arguments.length ? !!v : true;
+Query.prototype.lean = function(v) {
+ this._mongooseOptions.lean = arguments.length ? !!v : true;
return this;
};
/**
- * Executes the query as a find() operation.
+ * Thunk around find()
*
- * @param {Function} callback
+ * @param {Function} [callback]
* @return {Query} this
* @api private
*/
+Query.prototype._find = function(callback) {
+ if (this._castError) {
+ callback(this._castError);
+ return this;
+ }
-Query.prototype.execFind = function (callback) {
- var model = this.model
- , promise = new Promise(callback);
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
+
+ var fields = this._fieldsForExec();
+ var options = this._mongooseOptions;
+ var self = this;
+
+ var cb = function(err, docs) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (docs.length === 0) {
+ return callback(null, docs);
+ }
+
+ if (!options.populate) {
+ return true === options.lean
+ ? callback(null, docs)
+ : completeMany(self.model, docs, fields, self, null, callback);
+ }
+
+ var pop = helpers.preparePopulationOptionsMQ(self, options);
+ self.model.populate(docs, pop, function(err, docs) {
+ if (err) return callback(err);
+ return true === options.lean
+ ? callback(null, docs)
+ : completeMany(self.model, docs, fields, self, 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 ('function' == typeof conditions) {
+ callback = conditions;
+ conditions = {};
+ }
+
+ conditions = utils.toObject(conditions);
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ prepareDiscriminatorCriteria(this);
try {
- this.cast(model);
+ this.cast(this.model);
+ this._castError = null;
} catch (err) {
- promise.error(err);
- return this;
+ this._castError = err;
}
- // apply default schematype path selections
- this._applyPaths();
-
- var self = this
- , castQuery = this._conditions
- , options = this._optionsForExec(model)
- , fields = utils.clone(this._fields)
-
- options.fields = this._castFields(fields);
- if (options.fields instanceof Error) {
- promise.error(options.fields);
- return this;
+ // if we don't have a callback, then just return the query object
+ if (!callback) {
+ return Query.base.find.call(this);
}
- model.collection.find(castQuery, options, function (err, cursor) {
- if (err) return promise.error(err);
- cursor.toArray(tick(cb));
- });
-
- function cb (err, docs) {
- if (err) return promise.error(err);
-
- if (0 === docs.length) {
- return promise.complete(docs);
- }
-
- if (!self.options.populate) {
- return true === options.lean
- ? promise.complete(docs)
- : completeMany(model, docs, fields, self, null, promise);
- }
-
- 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);
- });
- }
+ this._find(callback);
return this;
-}
+};
/*!
* hydrates many documents
@@ -1706,190 +1109,1063 @@ 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);
+ for (var i = 0; i < len; ++i) {
+ arr[i] = helpers.createModel(model, docs[i], fields);
+ arr[i].init(docs[i], opts, function(err) {
+ if (err) return callback(err);
+ --count || callback(null, arr);
});
}
}
/**
- * Executes the query as a findOne() operation, passing the first found document to the callback.
+ * Thunk around findOne()
+ *
+ * @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 self = this;
+
+ // don't pass in the conditions because we already merged them in
+ Query.base.findOne.call(self, {}, function(err, doc) {
+ if (err) {
+ return callback(err);
+ }
+ if (!doc) {
+ return callback(null, null);
+ }
+
+ if (!options.populate) {
+ return true === options.lean
+ ? callback(null, doc)
+ : completeOne(self.model, doc, null, projection, self, null, callback);
+ }
+
+ var pop = helpers.preparePopulationOptionsMQ(self, options);
+ self.model.populate(doc, pop, function(err, doc) {
+ if (err) {
+ return callback(err);
+ }
+
+ return true === options.lean
+ ? callback(null, doc)
+ : completeOne(self.model, doc, null, projection, self, pop, callback);
+ });
+ });
+};
+
+/**
+ * Declares the query a findOne operation. When executed, the first found document is passed to the callback.
+ *
+ * Passing a `callback` executes the query. The result of the query is a single document.
*
* ####Example
*
- * var query = Kitten.find({ color: 'white'});
- *
+ * 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 (http://bit.ly/1HotzBo)
+ * @param {Function} [callback]
* @return {Query} this
* @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
* @api public
*/
-Query.prototype.findOne = function (callback) {
+Query.prototype.findOne = function(conditions, projection, options, callback) {
+ if ('function' == typeof conditions) {
+ callback = conditions;
+ conditions = null;
+ projection = null;
+ options = null;
+ } else if ('function' == typeof projection) {
+ callback = projection;
+ options = null;
+ projection = null;
+ } else if ('function' == typeof options) {
+ callback = options;
+ options = null;
+ }
+
+ // make sure we don't send in the whole Document to merge()
+ conditions = utils.toObject(conditions);
+
this.op = 'findOne';
- if (!callback) return this;
+ if (options) {
+ this.setOptions(options);
+ }
- var model = this.model;
- var promise = new Promise(callback);
+ if (projection) {
+ this.select(projection);
+ }
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ prepareDiscriminatorCriteria(this);
try {
- this.cast(model);
+ this.cast(this.model);
+ this._castError = null;
} catch (err) {
- promise.error(err);
- return this;
+ this._castError = err;
}
- // apply default schematype path selections
- this._applyPaths();
-
- var self = this
- , castQuery = this._conditions
- , options = this._optionsForExec(model)
- , fields = utils.clone(this._fields)
-
- options.fields = this._castFields(fields);
- if (options.fields instanceof Error) {
- promise.error(options.fields);
- return this;
+ if (!callback) {
+ // already merged in the conditions, don't need to send them in.
+ return Query.base.findOne.call(this);
}
- model.collection.findOne(castQuery, options, tick(function (err, doc) {
- if (err) return promise.error(err);
- if (!doc) return promise.complete(null);
-
- if (!self.options.populate) {
- return true === options.lean
- ? promise.complete(doc)
- : completeOne(model, doc, fields, self, null, promise);
- }
-
- 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;
-}
+};
+
+/**
+ * Thunk around count()
+ *
+ * @param {Function} [callback]
+ * @see count http://docs.mongodb.org/manual/reference/method/db.collection.count/
+ * @api private
+ */
+
+Query.prototype._count = function(callback) {
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ process.nextTick(function() {
+ callback(err);
+ });
+ return this;
+ }
+
+ var conds = this._conditions;
+ var options = this._optionsForExec();
+
+ this._collection.count(conds, options, utils.tick(callback));
+};
+
+/**
+ * Specifying this query as a `count` query.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * ####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 kittens', count);
+ * })
+ *
+ * @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(conditions, callback) {
+ if ('function' == typeof conditions) {
+ callback = conditions;
+ conditions = undefined;
+ }
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ this.op = 'count';
+ if (!callback) {
+ return this;
+ }
+
+ this._count(callback);
+
+ return this;
+};
+
+/**
+ * Declares or executes a distict() operation.
+ *
+ * Passing a `callback` executes the query.
+ *
+ * ####Example
+ *
+ * distinct(field, conditions, callback)
+ * distinct(field, conditions)
+ * distinct(field, callback)
+ * distinct(field)
+ * distinct(callback)
+ * distinct()
+ *
+ * @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, conditions, callback) {
+ if (!callback) {
+ if ('function' === typeof conditions) {
+ callback = conditions;
+ conditions = undefined;
+ } else if ('function' === typeof field) {
+ callback = field;
+ field = undefined;
+ conditions = undefined;
+ }
+ }
+
+ conditions = utils.toObject(conditions);
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ if (!callback) {
+ throw err;
+ }
+ callback(err);
+ return this;
+ }
+
+ return Query.base.distinct.call(this, {}, field, callback);
+};
+
+/**
+ * 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.
+ *
+ * ####Example
+ *
+ * // sort by "field" ascending and "test" descending
+ * query.sort({ field: 'asc', test: -1 });
+ *
+ * // equivalent
+ * query.sort('field -test');
+ *
+ * ####Note
+ *
+ * Cannot be used with `distinct()`
+ *
+ * @param {Object|String} arg
+ * @return {Query} this
+ * @see cursor.sort http://docs.mongodb.org/manual/reference/method/cursor.sort/
+ * @api public
+ */
+
+Query.prototype.sort = function(arg) {
+ var nArg = {};
+
+ if (arguments.length > 1) {
+ throw new Error("sort() only takes 1 Argument");
+ }
+
+ 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];
+ }
+
+ } else {
+ nArg = arg;
+ }
+
+ // workaround for gh-2374 when sort is called after count
+ // if previous operation is count, we ignore
+ if (this.op == 'count') {
+ delete this.op;
+ }
+ return Query.base.sort.call(this, nArg);
+};
+
+/**
+ * Declare and/or execute this query as a remove() operation.
+ *
+ * ####Example
+ *
+ * Model.remove({ artist: 'Anne Murray' }, callback)
+ *
+ * ####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 (unsafe write)
+ * 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.remove = function(cond, callback) {
+ if ('function' == typeof cond) {
+ callback = cond;
+ cond = null;
+ }
+
+ var cb = 'function' == typeof callback;
+
+ try {
+ this.cast(this.model);
+ } catch (err) {
+ if (cb) return process.nextTick(callback.bind(null, err));
+ return this;
+ }
+
+ return Query.base.remove.call(this, cond, callback);
+};
/*!
* hydrates a document
*
* @param {Model} model
* @param {Document} doc
+ * @param {Object} res 3rd parameter to callback
* @param {Object} fields
* @param {Query} self
* @param {Array} [pop] array of paths used in population
- * @param {Promise} promise
+ * @param {Function} callback
*/
-function completeOne (model, doc, fields, self, pop, promise) {
+function completeOne(model, doc, res, fields, self, pop, callback) {
var opts = pop ?
{ populated: pop }
: undefined;
- var casted = new model(undefined, fields, true);
- casted.init(doc, opts, function (err) {
- if (err) return promise.error(err);
- promise.complete(casted);
+ var casted = helpers.createModel(model, doc, fields);
+ casted.init(doc, opts, function(err) {
+ if (err) {
+ return callback(err);
+ }
+ if (res) {
+ return callback(null, casted, res);
+ }
+ callback(null, casted);
});
}
+/*!
+ * If the model is a discriminator type and not root, then add the key & value to the criteria.
+ */
+
+function prepareDiscriminatorCriteria(query) {
+ if (!query || !query.model || !query.model.schema) {
+ return;
+ }
+
+ var schema = query.model.schema;
+
+ if (schema && schema.discriminatorMapping && !schema.discriminatorMapping.isRoot) {
+ query._conditions[schema.discriminatorMapping.key] = schema.discriminatorMapping.value;
+ }
+}
+
/**
- * Exectues the query as a count() operation.
+ * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) update command.
*
- * ####Example
+ * 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.
*
- * Kitten.where('color', 'black').count(function (err, count) {
- * if (err) return handleError(err);
- * console.log('there are %d black kittens', count);
- * })
+ * ####Available options
*
- * @param {Function} callback
+ * - `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.
+ * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
+ * - `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) {
+ * // error: any errors that occurred
+ * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
+ * }
+ *
+ * ####Examples
+ *
+ * query.findOneAndUpdate(conditions, update, options, callback) // executes
+ * 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
+ *
+ * @method findOneAndUpdate
+ * @memberOf Query
+ * @param {Object|Query} [query]
+ * @param {Object} [doc]
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
* @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.findOneAndUpdate = function(criteria, doc, options, callback) {
+ this.op = 'findOneAndUpdate';
+ this._validate();
- var model = this.model;
-
- try {
- this.cast(model);
- } catch (err) {
- return callback(err);
+ switch (arguments.length) {
+ case 3:
+ if ('function' == typeof options) {
+ callback = options;
+ options = {};
+ }
+ break;
+ case 2:
+ if ('function' == typeof doc) {
+ callback = doc;
+ doc = criteria;
+ criteria = undefined;
+ }
+ options = undefined;
+ break;
+ case 1:
+ if ('function' == typeof criteria) {
+ callback = criteria;
+ criteria = options = doc = undefined;
+ } else {
+ doc = criteria;
+ criteria = options = undefined;
+ }
}
- var castQuery = this._conditions;
- model.collection.count(castQuery, tick(callback));
+ if (mquery.canMerge(criteria)) {
+ this.merge(criteria);
+ }
+
+ // apply doc
+ if (doc) {
+ this._mergeUpdate(doc);
+ }
+
+ options && this.setOptions(options);
+
+ if (!callback) {
+ return this;
+ }
+
+ return this._findOneAndUpdate(callback);
+};
+
+/**
+ * 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
+ * - `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
+ * A.where().findOneAndRemove(conditions, callback) // executes
+ * A.where().findOneAndRemove(conditions) // returns Query
+ * A.where().findOneAndRemove(callback) // executes
+ * A.where().findOneAndRemove() // returns Query
+ *
+ * @method findOneAndRemove
+ * @memberOf Query
+ * @param {Object} [conditions]
+ * @param {Object} [options]
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
+ * @api public
+ */
+
+Query.prototype.findOneAndRemove = function(conditions, options, callback) {
+ this.op = 'findOneAndRemove';
+ this._validate();
+
+ switch (arguments.length) {
+ case 2:
+ if ('function' == typeof options) {
+ callback = options;
+ options = {};
+ }
+ break;
+ case 1:
+ if ('function' == typeof conditions) {
+ callback = conditions;
+ conditions = undefined;
+ options = undefined;
+ }
+ break;
+ }
+
+ if (mquery.canMerge(conditions)) {
+ this.merge(conditions);
+ }
+
+ options && this.setOptions(options);
+
+ if (!callback) {
+ return this;
+ }
+
+ this._findOneAndRemove(callback);
return this;
};
/**
- * Executes this query as a distict() operation.
+ * Thunk around findOneAndRemove()
+ *
+ * @param {Function} [callback]
+ * @return {Query} this
+ * @api private
+ */
+Query.prototype._findOneAndRemove = function(callback) {
+ Query.base.findOneAndRemove.call(this, callback);
+};
+
+/**
+ * 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) {
+ if ('function' != typeof callback) {
+ throw new Error("Expected callback in _findAndModify");
+ }
+
+ var model = this.model;
+ var schema = model.schema;
+ var self = this;
+ var castedQuery;
+ var castedDoc;
+ var fields;
+ var opts;
+ var doValidate;
+
+ castedQuery = castQuery(this);
+ if (castedQuery instanceof Error) {
+ return callback(castedQuery);
+ }
+
+ opts = this._optionsForExec(model);
+
+ if ('strict' in opts) {
+ this._mongooseOptions.strict = opts.strict;
+ }
+
+ if ('remove' == type) {
+ opts.remove = true;
+ } else {
+ 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, opts.overwrite);
+ if (!castedDoc) {
+ if (opts.upsert) {
+ // still need to do the upsert to empty doc
+ var doc = utils.clone(castedQuery);
+ delete doc._id;
+ castedDoc = { $set: doc };
+ } else {
+ return this.findOne(callback);
+ }
+ } else if (castedDoc instanceof Error) {
+ 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();
+
+ self = this;
+ var options = this._mongooseOptions;
+
+ if (this._fields) {
+ fields = utils.clone(this._fields);
+ opts.fields = this._castFields(fields);
+ if (opts.fields instanceof Error) {
+ return callback(opts.fields);
+ }
+ }
+
+ if (opts.sort) convertSortToArray(opts);
+
+ var cb = function(err, doc, res) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (!doc || (utils.isObject(doc) && Object.keys(doc).length === 0)) {
+ return callback(null, null);
+ }
+
+ if (!opts.passRawResult) {
+ res = null;
+ }
+
+ if (!options.populate) {
+ return true === options.lean
+ ? callback(null, doc)
+ : completeOne(self.model, doc, res, fields, self, null, callback);
+ }
+
+ var pop = helpers.preparePopulationOptionsMQ(self, options);
+ self.model.populate(doc, pop, function(err, doc) {
+ if (err) {
+ return callback(err);
+ }
+
+ return true === options.lean
+ ? callback(null, doc)
+ : completeOne(self.model, doc, res, fields, self, pop, callback);
+ });
+ };
+
+ if ((opts.runValidators || opts.setDefaultsOnInsert) && doValidate) {
+ doValidate(function(error) {
+ if (error) {
+ return callback(error);
+ }
+ self._collection.findAndModify(castedQuery, castedDoc, opts, utils.tick(function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+ });
+ } else {
+ this._collection.findAndModify(castedQuery, castedDoc, opts, utils.tick(function(error, res) {
+ return cb(error, res ? res.value : res, res);
+ }));
+ }
+
+ return this;
+};
+
+/**
+ * Override mquery.prototype._mergeUpdate to handle mongoose objects in
+ * updates.
+ *
+ * @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 {Function} callback
+ * @see Model.update #model_Model.update
+ * @api private
+ */
+Query.prototype._execUpdate = function(callback) {
+ var schema = this.model.schema;
+ var doValidate;
+ var _this;
+
+ var castedQuery = this._conditions;
+ var castedDoc = this._update;
+ var options = this.options;
+
+ if (this._castError) {
+ callback(this._castError);
+ return this;
+ }
+
+ if (this.options.runValidators || this.options.setDefaultsOnInsert) {
+ _this = this;
+ doValidate = updateValidators(this, schema, castedDoc, options);
+ doValidate(function(err) {
+ if (err) {
+ return callback(err);
+ }
+
+ Query.base.update.call(_this, castedQuery, castedDoc, options, callback);
+ });
+ return this;
+ }
+
+ Query.base.update.call(this, castedQuery, castedDoc, options, callback);
+ return this;
+};
+
+/**
+ * Declare and/or execute this query as an update() operation.
+ *
+ * _All paths passed that are not $atomic operations will become $set ops._
*
* ####Example
*
- * Link.find({ clicks: { $gt: 100 }}).distinct('url', function (err, result) {
- * if (err) return handleError(err);
+ * Model.where({ _id: id }).update({ title: 'words' })
*
- * assert(Array.isArray(result));
- * console.log('unique urls with more than 100 clicks', result);
- * })
+ * // becomes
*
- * @param {String} field
- * @param {Function} callback
+ * Model.where({ _id: id }).update({ $set: { title: 'words' }})
+ *
+ * ####Note
+ *
+ * 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.
+ *
+ * ####Note
+ *
+ * The operation is only executed when a callback is passed. To force execution without a callback (which would be an unsafe write), 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 as unsafe
+ *
+ * // 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 (unsafe write)
+ * update()
+ *
+ * @param {Object} [criteria]
+ * @param {Object} [doc] the update command
+ * @param {Object} [options]
+ * @param {Function} [callback]
* @return {Query} this
- * @see distinct http://docs.mongodb.org/manual/reference/method/db.collection.distinct/
+ * @see Model.update #model_Model.update
+ * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
* @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.update = function(conditions, doc, options, callback) {
+ if ('function' === typeof options) {
+ // .update(conditions, doc, callback)
+ callback = options;
+ options = null;
+ } else if ('function' === typeof doc) {
+ // .update(doc, callback);
+ callback = doc;
+ doc = conditions;
+ conditions = {};
+ options = null;
+ } else if ('function' === typeof conditions) {
+ // .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;
}
- var castQuery = this._conditions;
- model.collection.distinct(field, castQuery, tick(callback));
+ // make sure we don't send in the whole Document to merge()
+ conditions = utils.toObject(conditions);
- return this;
+ var oldCb = callback;
+ if (oldCb) {
+ callback = function(error, result) {
+ oldCb(error, result ? result.result : { ok: 0, n: 0, nModified: 0 });
+ };
+ }
+
+ // 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();
+ }
+
+ 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 {
+ var $options = { retainKeyOrder: true };
+ if (options && options.minimize) {
+ $options.minimize = true;
+ }
+ castedDoc = this._castUpdate(utils.clone(doc, $options),
+ options && options.overwrite);
+ } catch (err) {
+ this._castError = castedQuery;
+ if (callback) {
+ callback(err);
+ return this;
+ } else if (!options || !options.dontThrowCastError) {
+ throw err;
+ }
+ }
+
+ 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
+ */
+
+Query.prototype.exec = function exec(op, callback) {
+ var Promise = PromiseProvider.get();
+ var _this = this;
+
+ if ('function' == typeof op) {
+ callback = op;
+ op = null;
+ } else if ('string' == typeof op) {
+ 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);
+}
+
+/**
+ * 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);
};
/*!
@@ -1898,10 +2174,10 @@ Query.prototype.distinct = function (field, callback) {
*/
var castOps = {
- $push: 1
- , $pushAll: 1
- , $addToSet: 1
- , $set: 1
+ $push: 1,
+ $pushAll: 1,
+ $addToSet: 1,
+ $set: 1
};
/*!
@@ -1910,75 +2186,9 @@ var castOps = {
*/
var numberOps = {
- $pop: 1
- , $unset: 1
- , $inc: 1
-}
-
-/**
- * Executes this query as an update() operation.
- *
- * _All paths passed that are not $atomic operations will become $set ops so we retain backwards compatibility._
- *
- * ####Example
- *
- * Model.update({..}, { title: 'remove words' }, ...)
- *
- * // becomes
- *
- * Model.update({..}, { $set: { title: 'remove words' }}, ...)
- *
- * ####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.
- *
- * @param {Object} doc the update conditions
- * @param {Function} callback
- * @return {Query} this
- * @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;
-
- 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 (castedDoc instanceof Error) {
- if (fn) {
- process.nextTick(callback.bind(null, castedDoc));
- return this;
- }
- throw castedDoc;
- }
-
- if (!fn) {
- options.safe = { w: 0 };
- }
-
- model.collection.update(castedQuery, castedDoc, options, tick(callback));
- return this;
+ $pop: 1,
+ $unset: 1,
+ $inc: 1
};
/**
@@ -1989,16 +2199,22 @@ Query.prototype.update = function update (doc, callback) {
* @api private
*/
-Query.prototype._castUpdate = function _castUpdate (obj) {
- var ops = Object.keys(obj)
- , i = ops.length
- , ret = {}
- , hasKeys
- , val
+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 ('$' !== op[0]) {
+ // 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) {
@@ -2022,11 +2238,28 @@ Query.prototype._castUpdate = function _castUpdate (obj) {
// 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];
- if ('Object' === val.constructor.name) {
+ hasDollarKey = hasDollarKey || op.charAt(0) === '$';
+ if (val &&
+ val.constructor.name === 'Object' &&
+ (!overwrite || hasDollarKey)) {
hasKeys |= this._walkUpdatePath(val, op);
+ } else if (overwrite && 'Object' === ret.constructor.name) {
+ // 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;
@@ -2035,7 +2268,7 @@ Query.prototype._castUpdate = function _castUpdate (obj) {
}
return hasKeys && ret;
-}
+};
/**
* Walk each path of obj and cast its values
@@ -2048,17 +2281,17 @@ Query.prototype._castUpdate = function _castUpdate (obj) {
* @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
+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
+ var strict = 'strict' in this._mongooseOptions
+ ? this._mongooseOptions.strict
: this.model.schema.options.strict;
while (i--) {
@@ -2070,40 +2303,59 @@ Query.prototype._walkUpdatePath = function _walkUpdatePath (obj, op, pref) {
schema = this._getSchema(prefix + key);
if (schema && schema.caster && op in castOps) {
// embedded doc schema
+ hasKeys = true;
- 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];
+ 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 {
- 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);
+ 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).replace(/\.\$$/, '');
+ pathToCheck = pathToCheck.replace('.$.', '.0.');
+ var positionalPath = (prefix + '0.' + key).replace(/\.\$$/, '');
+ positionalPath = positionalPath.replace('.$.', '.0.');
+ if (this.model.schema.pathType(pathToCheck) === 'adhocOrUndefined' &&
+ !this.model.schema.hasMixedParent(pathToCheck) &&
+ this.model.schema.pathType(positionalPath) === 'adhocOrUndefined' &&
+ !this.model.schema.hasMixedParent(positionalPath)) {
+ if (strict === 'throw') {
+ throw new Error('Field `' + pathToCheck + '` is not in schema.');
+ } else if (strict) {
+ delete obj[key];
+ continue;
}
}
- } else {
- hasKeys |= this._walkUpdatePath(val, op, prefix + key);
+
+ // 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 {
- schema = '$each' === key
+ schema = ('$each' === key || '$or' === key || '$and' === key)
? this._getSchema(pref)
: this._getSchema(prefix + key);
@@ -2118,13 +2370,20 @@ Query.prototype._walkUpdatePath = function _walkUpdatePath (obj, op, pref) {
delete obj[key];
}
} else {
+ // gh-1845 temporary fix: ignore $rename. See gh-3027 for tracking
+ // improving this.
+ if (op === '$rename') {
+ hasKeys = true;
+ return;
+ }
+
hasKeys = true;
obj[key] = this._castUpdateVal(schema, val, op, key);
}
}
}
return hasKeys;
-}
+};
/**
* Casts `val` according to `schema` and atomic `op`.
@@ -2136,353 +2395,69 @@ Query.prototype._walkUpdatePath = function _walkUpdatePath (obj, op, pref) {
* @api private
*/
-Query.prototype._castUpdateVal = function _castUpdateVal (schema, val, op, $conditional) {
+Query.prototype._castUpdateVal = function _castUpdateVal(schema, val, op, $conditional) {
if (!schema) {
// non-existing schema path
return op in numberOps
? Number(val)
- : val
+ : val;
}
- if (schema.caster && op in castOps &&
- ('Object' === val.constructor.name || Array.isArray(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) 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);
-}
-
-/**
- * Casts selected field arguments for field selection with mongo 2.2
- *
- * query.select({ ids: { $elemMatch: { $in: [hexString] }})
- *
- * @param {Object} fields
- * @see https://github.com/LearnBoost/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 (op in numberOps) {
+ 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);
}
- 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;
-}
-
-/**
- * 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/
- */
-
-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;
- }
-
- if (!cb) {
- options.safe = { w: 0 };
- }
-
- var castQuery = this._conditions;
- model.collection.remove(castQuery, options, tick(callback));
- return this;
+ return schema.castForQuery(val);
};
-/**
- * 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.
- *
- * ####Available 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
- *
- * ####Examples
- *
- * query.findOneAndUpdate(conditions, update, options, callback) // executes
- * query.findOneAndUpdate(conditions, update, options) // returns Query
- * query.findOneAndUpdate(conditions, update, callback) // executes
- * query.findOneAndUpdate(conditions, update) // returns Query
- * query.findOneAndUpdate(callback) // executes
- * query.findOneAndUpdate() // returns Query
- *
- * @param {Object} [query]
- * @param {Object} [doc]
- * @param {Object} [options]
- * @param {Function} [callback]
- * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
- * @return {Query} this
- * @api public
- */
-
-Query.prototype.findOneAndUpdate = function (query, doc, options, callback) {
- this.op = 'findOneAndUpdate';
-
- switch (arguments.length) {
- case 3:
- if ('function' == typeof options)
- callback = options, options = {};
- break;
- case 2:
- if ('function' == typeof doc) {
- callback = doc;
- doc = query;
- query = undefined;
- }
- options = undefined;
- break;
- case 1:
- if ('function' == typeof query) {
- callback = query;
- query = options = doc = undefined;
- } else {
- doc = query;
- query = 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());
- }
- }
-
- // apply doc
- if (doc) {
- merge(this._updateArg, doc);
- }
-
- // apply options
- options && this.setOptions(options);
-
- if (!callback) return this;
-
- return this._findAndModify('update', 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
- *
- * A.where().findOneAndRemove(conditions, options, callback) // executes
- * A.where().findOneAndRemove(conditions, options) // return Query
- * A.where().findOneAndRemove(conditions, callback) // executes
- * A.where().findOneAndRemove(conditions) // returns Query
- * A.where().findOneAndRemove(callback) // executes
- * A.where().findOneAndRemove() // returns Query
- *
- * @param {Object} [conditions]
- * @param {Object} [options]
- * @param {Function} [callback]
- * @return {Query} this
- * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
- * @api public
- */
-
-Query.prototype.findOneAndRemove = function (conditions, options, callback) {
- this.op = 'findOneAndRemove';
-
- if ('function' == typeof options) {
- callback = options;
- options = undefined;
- } else if ('function' == typeof conditions) {
- callback = conditions;
- conditions = undefined;
- }
-
- // 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());
- }
- }
-
- // apply options
- options && this.setOptions(options);
-
- if (!callback) return this;
-
- return this._findAndModify('remove', callback);
-}
-
-/**
- * _findAndModify
- *
- * @param {String} type - either "remove" or "update"
- * @param {Function} callback
+/*!
+ * castQuery
* @api private
*/
-Query.prototype._findAndModify = function (type, callback) {
- var model = this.model
- , promise = new Promise(callback)
- , self = this
- , castedQuery
- , castedDoc
- , fields
- , sort
- , opts
-
- castedQuery = castQuery(this);
- if (castedQuery instanceof Error) {
- process.nextTick(promise.error.bind(promise, castedQuery));
- return promise;
+function castQuery(query) {
+ try {
+ return query.cast(query.model);
+ } catch (err) {
+ return err;
}
+}
- opts = this._optionsForExec(model);
+/*!
+ * castDoc
+ * @api private
+ */
- if ('remove' == type) {
- opts.remove = true;
- } else {
- if (!('new' in opts)) opts.new = true;
- if (!('upsert' in opts)) opts.upsert = false;
-
- castedDoc = castDoc(this);
- if (!castedDoc) {
- if (opts.upsert) {
- // still need to do the upsert to empty doc
- castedDoc = { $set: {} };
- } else {
- return this.findOne(callback);
- }
- } else if (castedDoc instanceof Error) {
- process.nextTick(promise.error.bind(promise, castedDoc));
- return promise;
- }
+function castDoc(query, overwrite) {
+ try {
+ return query._castUpdate(query._update, overwrite);
+ } catch (err) {
+ return err;
}
-
- this._applyPaths();
-
- if (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;
- }
- }
-
- // the driver needs a default
- sort = opts.sort || [];
-
- 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 (!self.options.populate) {
- return true === opts.lean
- ? promise.complete(doc)
- : completeOne(model, doc, fields, self, null, promise);
- }
-
- 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);
- })
- }));
-
- return promise;
}
/**
@@ -2522,9 +2497,9 @@ Query.prototype._findAndModify = function (type, callback) {
* @api public
*/
-Query.prototype.populate = function populate () {
+Query.prototype.populate = function() {
var res = utils.populate.apply(null, arguments);
- var opts = this.options;
+ var opts = this._mongooseOptions;
if (!utils.isObject(opts.populate)) {
opts.populate = {};
@@ -2535,7 +2510,178 @@ Query.prototype.populate = function populate () {
}
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 = 0 === fields[keys[ki]];
+ 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 ('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);
+ };
+
+ 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;
+ }
+
+ return 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.
@@ -2572,41 +2718,425 @@ Query.prototype.populate = function populate () {
* @api public
*/
-Query.prototype.stream = function stream (opts) {
+Query.prototype.stream = function stream(opts) {
+ this._applyPaths();
+ this._fields = this._castFields(this._fields);
return new QueryStream(this, opts);
-}
+};
-// helpers
+// the rest of these are basically to support older Mongoose syntax with mquery
-/*!
- * castDoc
- * @api private
+/**
+ * _DEPRECATED_ Alias of `maxScan`
+ *
+ * @deprecated
+ * @see maxScan #query_Query-maxScan
+ * @method maxscan
+ * @memberOf Query
*/
-function castDoc (query) {
- try {
- return query._castUpdate(query._updateArg);
- } catch (err) {
- return err;
- }
-}
+Query.prototype.maxscan = Query.base.maxScan;
-/*!
- * castQuery
- * @api private
+/**
+ * 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
+ * @see tailable http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/
+ * @api public
*/
-function castQuery (query) {
- try {
- return query.cast(query.model);
- } catch (err) {
- return err;
+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
+ */
/*!
- * Exports.
+ * 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 ('string' == typeof arguments[0]) {
+ // just passing a path
+ params.push(arguments[0]);
+ } else if (utils.isObject(arguments[0])) {
+ if ('boolean' != typeof arguments[0].spherical) {
+ arguments[0].spherical = sphere;
+ }
+ params.push(arguments[0]);
+ } else {
+ throw new TypeError('invalid argument');
+ }
+ } else if (arguments.length === 2) {
+ if ('number' == typeof arguments[0] && 'number' == typeof arguments[1]) {
+ params.push({ center: [arguments[0], arguments[1]], spherical: sphere});
+ } else if ('string' == typeof arguments[0] && Array.isArray(arguments[1])) {
+ params.push(arguments[0]);
+ params.push({ center: arguments[1], spherical: sphere });
+ } else if ('string' == typeof arguments[0] && utils.isObject(arguments[1])) {
+ params.push(arguments[0]);
+ if ('boolean' != typeof arguments[1].spherical) {
+ arguments[1].spherical = sphere;
+ }
+ params.push(arguments[1]);
+ } else {
+ throw new TypeError('invalid argument');
+ }
+ } else if (arguments.length === 3) {
+ if ('string' == typeof arguments[0] && 'number' == typeof arguments[1]
+ && 'number' == typeof arguments[2]) {
+ 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/within/
+ * @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
+ */
+
+/**
+ * Executes this query and returns a promise
+ *
+ * @method then
+ * @memberOf Query
+ * @return {Promise}
+ * @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
+ */
+
+/*!
+ * Export
*/
module.exports = Query;
-module.exports.QueryStream = QueryStream;
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..086a203 100644
--- a/node_modules/mongoose/lib/querystream.js
+++ b/node_modules/mongoose/lib/querystream.js
@@ -1,12 +1,13 @@
+/* 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 +50,7 @@ var K = function(k){ return k }
* @api public
*/
-function QueryStream (query, options) {
+function QueryStream(query, options) {
Stream.call(this);
this.query = query;
@@ -67,7 +68,7 @@ function QueryStream (query, options) {
// give time to hook up events
var self = this;
- process.nextTick(function () {
+ process.nextTick(function() {
self._init();
});
}
@@ -107,13 +108,13 @@ var T_CONT = 2;
* @api private
*/
-QueryStream.prototype._init = function () {
+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),
+ self = this;
try {
query.cast(model);
@@ -124,12 +125,12 @@ QueryStream.prototype._init = function () {
self._fields = utils.clone(query._fields);
options.fields = query._castFields(self._fields);
- model.collection.find(query._conditions, options, function (err, cursor) {
+ model.collection.find(query._conditions, options, function(err, cursor) {
if (err) return self.destroy(err);
self._cursor = cursor;
self._next();
});
-}
+};
/**
* Trampoline for pulling the next doc from cursor.
@@ -138,7 +139,7 @@ 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;
}
@@ -155,7 +156,7 @@ QueryStream.prototype._next = function _next () {
// avoid stack overflows with large result sets.
// trampoline instead of recursion.
while (this.__next()) {}
-}
+};
/**
* Pulls the next doc from the cursor.
@@ -164,14 +165,14 @@ QueryStream.prototype._next = function _next () {
* @api private
*/
-QueryStream.prototype.__next = function () {
+QueryStream.prototype.__next = function() {
if (this.paused || this._destroyed)
return this._running = false;
var self = this;
self._inline = T_INIT;
- self._cursor.nextObject(function cursorcb (err, doc) {
+ self._cursor.nextObject(function cursorcb(err, doc) {
self._onNextObject(err, doc);
});
@@ -185,7 +186,7 @@ QueryStream.prototype.__next = function () {
// the trampoline anymore.
this._inline = T_IDLE;
}
-}
+};
/**
* Transforms raw `doc`s returned from the cursor into a model instance.
@@ -195,7 +196,7 @@ QueryStream.prototype.__next = function () {
* @api private
*/
-QueryStream.prototype._onNextObject = function _onNextObject (err, doc) {
+QueryStream.prototype._onNextObject = function _onNextObject(err, doc) {
if (this._destroyed) return;
if (this.paused) {
@@ -212,28 +213,37 @@ 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 true === opts.lean ?
+ emit(this, doc) :
+ createAndEmit(this, null, doc);
}
var self = this;
- var pop = helpers.preparePopulationOptions(self.query, self.query.options);
+ var pop = helpers.preparePopulationOptionsMQ(self.query, self.query._mongooseOptions);
- self.query.model.populate(doc, pop, function (err, doc) {
+ // Hack to work around gh-3108
+ pop.forEach(function(option) {
+ delete option.model;
+ });
+
+ 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);
- })
-}
+ return true === opts.lean ?
+ emit(self, doc) :
+ createAndEmit(self, pop, doc);
+ });
+};
-function createAndEmit (self, doc) {
- var instance = new self.query.model(undefined, self._fields, true);
- instance.init(doc, function (err) {
+function createAndEmit(self, populatedIds, doc) {
+ var instance = helpers.createModel(self.query.model, doc, self._fields);
+ var opts = populatedIds ?
+ { populated: populatedIds } :
+ undefined;
+
+ instance.init(doc, opts, function(err) {
if (err) return self.destroy(err);
emit(self, instance);
});
@@ -243,7 +253,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 +273,9 @@ function emit (self, doc) {
* @api public
*/
-QueryStream.prototype.pause = function () {
+QueryStream.prototype.pause = function() {
this.paused = true;
-}
+};
/**
* Resumes this stream.
@@ -273,7 +283,7 @@ QueryStream.prototype.pause = function () {
* @api public
*/
-QueryStream.prototype.resume = function () {
+QueryStream.prototype.resume = function() {
this.paused = false;
if (!this._cursor) {
@@ -290,7 +300,7 @@ 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.
@@ -299,7 +309,7 @@ QueryStream.prototype.resume = function () {
* @api public
*/
-QueryStream.prototype.destroy = function (err) {
+QueryStream.prototype.destroy = function(err) {
if (this._destroyed) return;
this._destroyed = true;
this._running = false;
@@ -314,7 +324,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..fb7ede2 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 PromiseProvider = require('./promise_provider');
+
+var IS_QUERY_HOOK = {
+ count: true,
+ find: true,
+ findOne: true,
+ findOneAndUpdate: true,
+ findOneAndRemove: 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,11 +48,13 @@ 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
+ * - [typeKey](/docs/guide.html#typeKey) - string - defaults to 'type'
+ * - [validateBeforeSave](/docs/guide.html#validateBeforeSave) - bool - defaults to `true`
* - [versionKey](/docs/guide.html#versionKey): bool - 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
* @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
@@ -48,7 +62,7 @@ var EventEmitter = require('events').EventEmitter
* @api public
*/
-function Schema (obj, options) {
+function Schema(obj, options) {
if (!(this instanceof Schema))
return new Schema(obj, options);
@@ -63,6 +77,13 @@ function Schema (obj, options) {
this.statics = {};
this.tree = {};
this._requiredpaths = undefined;
+ this.discriminatorMapping = undefined;
+ this._indexedpaths = undefined;
+
+ this.s = {
+ hooks: new Kareem(),
+ queryHooks: IS_QUERY_HOOK
+ };
this.options = this.defaultOptions(options);
@@ -71,24 +92,85 @@ 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);
+ }
+
+ // adds updatedAt and createdAt timestamps to documents if enabled
+ var timestamps = this.options.timestamps;
+ if (timestamps) {
+ var createdAt = timestamps.createdAt || 'createdAt',
+ updatedAt = timestamps.updatedAt || 'updatedAt',
+ schemaAdditions = {};
+
+ schemaAdditions[updatedAt] = Date;
+
+ if (!this.paths[createdAt]) {
+ schemaAdditions[createdAt] = Date;
+ }
+
+ this.add(schemaAdditions);
+
+ this.pre('save', function(next) {
+ var defaultTimestamp = new Date();
+
+ if (!this[createdAt]) {
+ this[createdAt] = auto_id ? this._id.getTimestamp() : defaultTimestamp;
+ }
+
+ 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.pre('findOneAndUpdate', function(next) {
+ this.findOneAndUpdate({}, genUpdates());
+ next();
+ });
+
+ this.pre('update', function(next) {
+ this.update({}, genUpdates());
+ next();
+ });
+ }
+
}
/*!
* Returns this documents _id cast to a string.
*/
-function idGetter () {
+function idGetter() {
if (this.$__._id) {
return this.$__._id;
}
@@ -101,8 +183,97 @@ function idGetter () {
/*!
* Inherit from EventEmitter.
*/
+Schema.prototype = Object.create( EventEmitter.prototype );
+Schema.prototype.constructor = Schema;
-Schema.prototype.__proto__ = EventEmitter.prototype;
+/**
+ * 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) {
+ // Nested docs have their own presave
+ if (this.ownerDocument) {
+ return next();
+ }
+
+ var hasValidateBeforeSaveOption = options &&
+ (typeof options === 'object') &&
+ ('validateBeforeSave' in options);
+
+ 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) {
+ next(error);
+ });
+ } else {
+ this.validate({ __noPromise: true }, function(error) {
+ next(error);
+ });
+ }
+ } else {
+ next();
+ }
+ }
+ }, {
+ kind: 'pre',
+ hook: 'save',
+ isAsync: true,
+ fn: function(next, done) {
+ var Promise = PromiseProvider.get(),
+ subdocs = this.$__getAllSubdocs();
+
+ if (!subdocs.length || this.$__preSavingFromParent) {
+ done();
+ next();
+ return;
+ }
+
+ new Promise.ES6(function(resolve, reject) {
+ 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) {
+ reject(error);
+ return;
+ }
+ resolve();
+ });
+ }).then(function() {
+ next();
+ done();
+ }, done);
+ }
+ }]
+});
/**
* Schema as flat paths
@@ -144,32 +315,41 @@ Schema.prototype.tree;
* @api private
*/
-Schema.prototype.defaultOptions = function (options) {
+Schema.prototype.defaultOptions = function(options) {
if (options && false === options.safe) {
options.safe = { w: 0 };
}
+ if (options && options.safe && 0 === options.safe.w) {
+ // 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,7 +364,7 @@ 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);
@@ -192,10 +372,16 @@ Schema.prototype.add = function add (obj, prefix) {
var key = keys[i];
if (null == obj[key]) {
- throw new TypeError('Invalid value for schema path `'+ prefix + key +'`');
+ 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 && null == obj[key][0]) {
+ throw new TypeError('Invalid value for schema Array path `' + prefix + key + '`');
+ }
+
+ if (utils.isObject(obj[key]) &&
+ (!obj[key].constructor || 'Object' == utils.getFunctionName(obj[key].constructor)) &&
+ (!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;
@@ -214,7 +400,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 +410,34 @@ Schema.prototype.add = function add (obj, prefix) {
Schema.reserved = Object.create(null);
var reserved = Schema.reserved;
+// EventEmitter
+reserved.emit =
reserved.on =
-reserved.db =
-reserved.init =
-reserved.isNew =
-reserved.errors =
-reserved.schema =
-reserved.options =
-reserved.modelName =
+reserved.once =
+// document properties and functions
reserved.collection =
+reserved.db =
+reserved.errors =
+reserved.init =
+reserved.isModified =
+reserved.isNew =
+reserved.get =
+reserved.modelName =
+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,7 +455,7 @@ reserved._pres = reserved._posts = 1 // hooks.js
* @api public
*/
-Schema.prototype.path = function (path, obj) {
+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];
@@ -270,10 +471,14 @@ Schema.prototype.path = function (path, obj) {
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] = {};
@@ -290,7 +495,7 @@ 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);
return this;
};
@@ -302,19 +507,25 @@ 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 ('Object' == utils.getFunctionName(type.constructor) || 'mixed' == type) {
+ return new MongooseTypes.Mixed(path, obj);
}
if (Array.isArray(type) || Array == type || 'array' == type) {
@@ -324,35 +535,55 @@ Schema.interpretAsType = function (path, obj) {
: type[0];
if (cast instanceof Schema) {
- return new Types.DocumentArray(path, cast, obj);
+ 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
+ cast = MongooseTypes[cast.charAt(0).toUpperCase() + cast.substring(1)];
+ } else if (cast && (!cast[options.typeKey] || (options.typeKey === 'type' && cast.type.type))
+ && 'Object' == utils.getFunctionName(cast.constructor)
&& Object.keys(cast).length) {
- return new Types.DocumentArray(path, new Schema(cast), obj);
+
+ // 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 Types.Array(path, cast || Types.Mixed, obj);
+ return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj);
}
- var name = 'string' == typeof type
- ? type
- : type.name;
+ if (type instanceof Schema) {
+ return new MongooseTypes.Embedded(type, path, obj);
+ }
+
+ var name;
+ if (Buffer.isBuffer(type)) {
+ name = 'Buffer';
+ } else {
+ name = 'string' == typeof type
+ ? 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 +596,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,15 +611,16 @@ 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];
@@ -396,7 +628,20 @@ Schema.prototype.requiredPaths = function 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;
+
+ return this._indexedpaths = this.indexes();
+};
/**
* Returns the pathType of `path` for this schema.
@@ -408,41 +653,65 @@ Schema.prototype.requiredPaths = function requiredPaths () {
* @api public
*/
-Schema.prototype.pathType = function (path) {
+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)) {
- return 'real';
+ if (/\.\d+\.|\.\d+$/.test(path)) {
+ return getPositionalPathType(this, path);
} else {
- return 'adhocOrUndefined'
+ 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;
+};
+
/*!
* ignore
*/
-function getPositionalPath (self, path) {
+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]);
+ 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 {
@@ -459,10 +728,29 @@ function getPositionalPath (self, path) {
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 +758,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;
};
@@ -501,12 +789,17 @@ Schema.prototype.queue = function(name, args){
* @api public
*/
-Schema.prototype.pre = function(){
+Schema.prototype.pre = function() {
+ var name = arguments[0];
+ if (IS_QUERY_HOOK[name]) {
+ this.s.hooks.pre.apply(this.s.hooks, arguments);
+ return this;
+ }
return this.queue('pre', arguments);
};
/**
- * Defines a post for the document
+ * Defines a post hook for the document
*
* Post hooks fire `on` the event emitted from document instances of Models compiled from this schema.
*
@@ -528,20 +821,39 @@ Schema.prototype.pre = function(){
* @api public
*/
-Schema.prototype.post = function(method, fn){
- return this.queue('on', arguments);
+Schema.prototype.post = function(method, fn) {
+ if (IS_QUERY_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);
+ }]);
+ }
+
+ 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 self = this;
+ var args = Array.prototype.slice.call(arguments, 1);
+ fn.call(this, this, function(err) {
+ return next.apply(self, [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,7 +890,7 @@ Schema.prototype.plugin = function (fn, opts) {
* @api public
*/
-Schema.prototype.method = function (name, fn) {
+Schema.prototype.method = function(name, fn) {
if ('string' != typeof name)
for (var i in name)
this.methods[i] = name[i];
@@ -626,11 +938,12 @@ 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)
@@ -643,31 +956,38 @@ 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) {
+Schema.prototype.set = function(key, value, _tags) {
if (1 === arguments.length) {
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
+ : value;
break;
default:
this.options[key] = value;
}
return this;
-}
+};
/**
* Gets a schema option.
@@ -676,9 +996,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 +1011,9 @@ 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 +1021,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,7 +1041,7 @@ Schema.prototype.indexes = function () {
key = keys[i];
path = schema.paths[key];
- if (path instanceof Types.DocumentArray) {
+ if (path instanceof MongooseTypes.DocumentArray) {
collectIndexes(path.schema, key + '.');
} else {
index = path._index;
@@ -754,12 +1073,16 @@ Schema.prototype.indexes = function () {
if (prefix) {
fixSubIndexPaths(schema, prefix);
} else {
- schema._indexes.forEach(function (index) {
+ 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 +1091,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 +1117,7 @@ Schema.prototype.indexes = function () {
indexes.push([newindex, subindexes[i][1]]);
}
}
-}
+};
/**
* Creates a virtual type with the given name.
@@ -804,11 +1127,11 @@ Schema.prototype.indexes = function () {
* @return {VirtualType}
*/
-Schema.prototype.virtual = function (name, options) {
+Schema.prototype.virtual = function(name, options) {
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)
+ return virtuals[name] = parts.reduce(function(mem, part, i) {
+ mem[part] || (mem[part] = (i === parts.length - 1)
? new VirtualType(options, name)
: {});
return mem[part];
@@ -822,44 +1145,78 @@ 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];
+ }
+ }, this);
+ }
+};
+
+/*!
+ * ignore
*/
-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;
- };
+Schema.prototype._getSchema = function(path) {
+ var schema = this;
+ var pathschema = schema.path(path);
+
+ if (pathschema) {
+ return pathschema;
+ }
+
+ // look for arrays
+ return (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);
+ } else {
+ // this is the last path of the selector
+ return search(parts.slice(p), foundschema.schema);
+ }
+ }
+ }
+ return foundschema;
+ }
}
- });
- return newScope;
+ })(path.split('.'), schema);
};
/*!
@@ -897,14 +1254,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..945fb2d 100644
--- a/node_modules/mongoose/lib/schema/array.js
+++ b/node_modules/mongoose/lib/schema/array.js
@@ -2,23 +2,22 @@
* 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'),
+ CastError = SchemaType.CastError,
+ 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'),
+ cast = require('../cast'),
+ utils = require('../utils'),
+ isMongooseObject = utils.isMongooseObject;
/**
* Array SchemaType constructor
@@ -30,11 +29,11 @@ var SchemaType = require('../schematype')
* @api private
*/
-function SchemaArray (key, cast, options) {
+function SchemaArray(key, cast, options) {
if (cast) {
var castOptions = {};
- if ('Object' === cast.constructor.name) {
+ if ('Object' === utils.getFunctionName(cast.constructor)) {
if (cast.type) {
// support { type: Woot }
castOptions = utils.clone(cast); // do not alter user arguments
@@ -48,7 +47,7 @@ function SchemaArray (key, cast, options) {
// support { type: 'String' }
var name = 'string' == typeof cast
? cast
- : cast.name;
+ : utils.getFunctionName(cast);
var caster = name in Types
? Types[name]
@@ -61,28 +60,36 @@ function SchemaArray (key, cast, options) {
}
}
- SchemaType.call(this, key, options);
+ SchemaType.call(this, key, options, 'Array');
- var self = this
- , defaultArr
- , fn;
+ var self = this,
+ defaultArr,
+ fn;
if (this.defaultValue) {
defaultArr = this.defaultValue;
fn = 'function' == typeof defaultArr;
}
- this.default(function(){
+ this.default(function() {
var arr = fn ? defaultArr() : defaultArr || [];
return new MongooseArray(arr, self.path, this);
});
-};
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api private
+ */
+SchemaArray.schemaName = 'Array';
/*!
* Inherits from SchemaType.
*/
-
-SchemaArray.prototype.__proto__ = SchemaType.prototype;
+SchemaArray.prototype = Object.create( SchemaType.prototype );
+SchemaArray.prototype.constructor = SchemaArray;
/**
* Check required
@@ -91,7 +98,7 @@ SchemaArray.prototype.__proto__ = SchemaType.prototype;
* @api private
*/
-SchemaArray.prototype.checkRequired = function (value) {
+SchemaArray.prototype.checkRequired = function(value) {
return !!(value && value.length);
};
@@ -103,7 +110,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 +120,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,15 +128,27 @@ 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 ('2dsphere' === pathIndex || '2d' === pathIndex) {
+ 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) {
@@ -140,175 +159,234 @@ SchemaArray.prototype.cast = function (value, doc, init) {
return value;
} else {
+ // 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)
+
+ 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;
+
+ return val && isMongooseObject(val) ?
+ val.toObject({ virtuals: false }) :
+ val;
};
/*!
* @ignore
+ *
+ * $atomic cast helpers
*/
-function castToNumber (val) {
+function castToNumber(val) {
return Types.Number.prototype.cast.call(this, val);
}
-function castArray (arr, self) {
+function castArraysOfNumbers(arr, self) {
self || (self = this);
- arr.forEach(function (v, i) {
+ arr.forEach(function(v, i) {
if (Array.isArray(v)) {
- castArray(v, self);
+ castArraysOfNumbers(v, self);
} else {
arr[i] = castToNumber.call(self, v);
}
});
}
-SchemaArray.prototype.$conditionalHandlers = {
- '$all': function handle$all (val) {
- if (!Array.isArray(val)) {
- val = [val];
+function cast$near(val) {
+ if (Array.isArray(val)) {
+ castArraysOfNumbers(val, this);
+ return 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;
+ }
+
+ if (val.$maxDistance) {
+ val.$maxDistance = castToNumber.call(self, val.$maxDistance);
+ }
+
+ return val;
+}
+
+function cast$within(val) {
+ var self = this;
+
+ if (val.$maxDistance) {
+ val.$maxDistance = castToNumber.call(self, val.$maxDistance);
+ }
+
+ 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);
+ }
- 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 val;
+}
- return this.castForQuery(val);
+function cast$all(val) {
+ if (!Array.isArray(val)) {
+ val = [val];
+ }
+
+ val = val.map(function(v) {
+ if (utils.isObject(v)) {
+ var o = {};
+ o[this.path] = v;
+ return cast(this.casterConstructor.schema, o)[this.path];
}
- , '$elemMatch': function (val) {
- if (val.$in) {
- val.$in = this.castForQuery('$in', val.$in);
- return val;
- }
+ return v;
+ }, this);
- var query = new Query(val);
- query.cast(this.casterConstructor);
- return query._conditions;
+ 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);
}
- , '$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;
- }
- }
+function cast$geoIntersects(val) {
+ var geo = val.$geometry;
+ if (!geo) return;
- return val;
- }
- , '$geoIntersects': function (val) {
- var geo = val.$geometry;
- if (!geo) return;
+ cast$geometry(val, this);
+ return val;
+}
- 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;
- }
- , '$maxDistance': castToNumber
+handle.$all = cast$all;
+handle.$options = String;
+handle.$elemMatch = cast$elemMatch;
+handle.$geoIntersects = cast$geoIntersects;
+handle.$or = handle.$and = function(val) {
+ if (!Array.isArray(val)) {
+ throw new TypeError('conditional $or/$and require array');
+ }
+
+ var ret = [];
+ for (var i = 0; i < val.length; ++i) {
+ ret.push(cast(this.casterConstructor.schema, val[i]));
+ }
+
+ return ret;
};
+handle.$near =
+handle.$nearSphere = cast$near;
+
+handle.$within =
+handle.$geoWithin = cast$within;
+
+handle.$size =
+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..971ab15 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');
/**
@@ -13,14 +15,23 @@ var SchemaType = require('../schematype');
* @api private
*/
-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 private
+ */
+SchemaBoolean.schemaName = 'Boolean';
/*!
* Inherits from SchemaType.
*/
-SchemaBoolean.prototype.__proto__ = SchemaType.prototype;
+SchemaBoolean.prototype = Object.create( SchemaType.prototype );
+SchemaBoolean.prototype.constructor = SchemaBoolean;
/**
* Required validator
@@ -28,7 +39,7 @@ SchemaBoolean.prototype.__proto__ = SchemaType.prototype;
* @api private
*/
-SchemaBoolean.prototype.checkRequired = function (value) {
+SchemaBoolean.prototype.checkRequired = function(value) {
return value === true || value === false;
};
@@ -39,28 +50,16 @@ SchemaBoolean.prototype.checkRequired = function (value) {
* @api private
*/
-SchemaBoolean.prototype.cast = function (value) {
+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;
-}
+ return !!value;
+};
-/*!
- * ignore
- */
-
-function handleArray (val) {
- var self = this;
- return val.map(function (m) {
- return self.cast(m);
- });
-}
-
-SchemaBoolean.$conditionalHandlers = {
- '$in': handleArray
-}
+SchemaBoolean.$conditionalHandlers =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {});
/**
* Casts contents for queries.
@@ -70,7 +69,7 @@ SchemaBoolean.$conditionalHandlers = {
* @api private
*/
-SchemaBoolean.prototype.castForQuery = function ($conditional, val) {
+SchemaBoolean.prototype.castForQuery = function($conditional, val) {
var handler;
if (2 === arguments.length) {
handler = SchemaBoolean.$conditionalHandlers[$conditional];
diff --git a/node_modules/mongoose/lib/schema/buffer.js b/node_modules/mongoose/lib/schema/buffer.js
index e699c10..159a28a 100644
--- a/node_modules/mongoose/lib/schema/buffer.js
+++ b/node_modules/mongoose/lib/schema/buffer.js
@@ -2,13 +2,14 @@
* Module dependencies.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , MongooseBuffer = require('../types').Buffer
- , Binary = MongooseBuffer.Binary
- , Query = require('../query')
- , utils = require('../utils')
- , Document
+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
@@ -19,15 +20,23 @@ var SchemaType = require('../schematype')
* @api private
*/
-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 private
+ */
+SchemaBuffer.schemaName = 'Buffer';
/*!
* Inherits from SchemaType.
*/
-
-SchemaBuffer.prototype.__proto__ = SchemaType.prototype;
+SchemaBuffer.prototype = Object.create( SchemaType.prototype );
+SchemaBuffer.prototype.constructor = SchemaBuffer;
/**
* Check required
@@ -35,7 +44,7 @@ SchemaBuffer.prototype.__proto__ = SchemaType.prototype;
* @api private
*/
-SchemaBuffer.prototype.checkRequired = function (value, doc) {
+SchemaBuffer.prototype.checkRequired = function(value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
return null != value;
} else {
@@ -52,7 +61,8 @@ 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
@@ -81,7 +91,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,17 +101,22 @@ 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;
}
@@ -109,7 +124,7 @@ SchemaBuffer.prototype.cast = function (value, doc, init) {
var type = typeof value;
if ('string' == type || 'number' == type || Array.isArray(value)) {
- var ret = new MongooseBuffer(value, [this.path, doc]);
+ ret = new MongooseBuffer(value, [this.path, doc]);
return ret;
}
@@ -119,26 +134,17 @@ 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 =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ '$gt' : handleSingle,
+ '$gte': handleSingle,
+ '$lt' : handleSingle,
+ '$lte': handleSingle
});
-}
-
-SchemaBuffer.prototype.$conditionalHandlers = {
- '$ne' : handleSingle
- , '$in' : handleArray
- , '$nin': handleArray
- , '$gt' : handleSingle
- , '$lt' : handleSingle
- , '$gte': handleSingle
- , '$lte': handleSingle
-};
/**
* Casts contents for queries.
@@ -148,7 +154,7 @@ 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];
diff --git a/node_modules/mongoose/lib/schema/date.js b/node_modules/mongoose/lib/schema/date.js
index 86e4062..57d36e9 100644
--- a/node_modules/mongoose/lib/schema/date.js
+++ b/node_modules/mongoose/lib/schema/date.js
@@ -2,10 +2,13 @@
* Module requirements.
*/
-var SchemaType = require('../schematype');
-var CastError = SchemaType.CastError;
+var errorMessages = require('../error').messages;
var utils = require('../utils');
+var SchemaType = require('../schematype');
+
+var CastError = SchemaType.CastError;
+
/**
* Date SchemaType constructor.
*
@@ -15,15 +18,23 @@ var utils = require('../utils');
* @api private
*/
-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 private
+ */
+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.
@@ -56,7 +67,7 @@ SchemaDate.prototype.__proto__ = SchemaType.prototype;
* @api public
*/
-SchemaDate.prototype.expires = function (when) {
+SchemaDate.prototype.expires = function(when) {
if (!this._index || 'Object' !== this._index.constructor.name) {
this._index = {};
}
@@ -72,10 +83,122 @@ SchemaDate.prototype.expires = function (when) {
* @api private
*/
-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 || errorMessages.Date.min;
+ msg = msg.replace(/{MIN}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString()));
+ var self = this;
+ this.validators.push({
+ validator: this.minValidator = function(val) {
+ var min = (value === Date.now ? value() : self.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 || errorMessages.Date.max;
+ msg = msg.replace(/{MAX}/, (value === Date.now ? 'Date.now()' : this.cast(value).toString()));
+ var self = this;
+ this.validators.push({
+ validator: this.maxValidator = function(val) {
+ var max = (value === Date.now ? value() : self.cast(value));
+ return val === null || val.valueOf() <= max.valueOf();
+ },
+ message: msg,
+ type: 'max',
+ max: value
+ });
+ }
+
+ return this;
+};
+
/**
* Casts to date
*
@@ -83,8 +206,9 @@ 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 === '')
return null;
if (value instanceof Date)
@@ -93,16 +217,19 @@ SchemaDate.prototype.cast = function (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 !== 'undefined') {
+ if (value instanceof Number || 'number' == typeof value
+ || String(value) == Number(value)) {
+ date = new Date(Number(value));
+ } else if (value.toString) {
+ // support for date strings
+ date = new Date(value.toString());
+ }
- // support for date strings
- else if (value.toString)
- date = new Date(value.toString());
-
- if (date.toString() != 'Invalid Date')
- return date;
+ if (date.toString() != 'Invalid Date') {
+ return date;
+ }
+ }
throw new CastError('date', value, this.path);
};
@@ -113,27 +240,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 =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ '$gt': handleSingle,
+ '$gte': handleSingle,
+ '$lt': handleSingle,
+ '$lte': handleSingle
});
-}
-
-SchemaDate.prototype.$conditionalHandlers = {
- '$lt': handleSingle
- , '$lte': handleSingle
- , '$gt': handleSingle
- , '$gte': handleSingle
- , '$ne': handleSingle
- , '$in': handleArray
- , '$nin': handleArray
- , '$all': handleArray
-};
/**
@@ -144,7 +261,7 @@ SchemaDate.prototype.$conditionalHandlers = {
* @api private
*/
-SchemaDate.prototype.castForQuery = function ($conditional, val) {
+SchemaDate.prototype.castForQuery = function($conditional, val) {
var handler;
if (2 !== arguments.length) {
diff --git a/node_modules/mongoose/lib/schema/documentarray.js b/node_modules/mongoose/lib/schema/documentarray.js
index 3b02887..d51b6de 100644
--- a/node_modules/mongoose/lib/schema/documentarray.js
+++ b/node_modules/mongoose/lib/schema/documentarray.js
@@ -1,13 +1,14 @@
+/* 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');
/**
* SubdocsArray SchemaType constructor
@@ -19,24 +20,22 @@ var SchemaType = require('../schematype')
* @api private
*/
-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;
// apply methods
- for (var i in schema.methods) {
+ for (var i in schema.methods)
EmbeddedDocument.prototype[i] = schema.methods[i];
- }
// apply statics
- for (var i in schema.statics)
+ for (i in schema.statics)
EmbeddedDocument[i] = schema.statics[i];
EmbeddedDocument.options = options;
@@ -48,18 +47,26 @@ function DocumentArray (key, schema, options) {
var path = this.path;
var fn = this.defaultValue;
- this.default(function(){
+ 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 private
+ */
+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,14 +74,14 @@ DocumentArray.prototype.__proto__ = ArrayType.prototype;
* @api private
*/
-DocumentArray.prototype.doValidate = function (array, fn, scope) {
- var self = this;
+DocumentArray.prototype.doValidate = function(array, fn, scope) {
+ SchemaType.prototype.doValidate.call(this, array, function(err) {
+ if (err) {
+ return fn(err);
+ }
- 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();
@@ -86,24 +93,61 @@ DocumentArray.prototype.doValidate = function (array, fn, scope) {
// 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);
+ doc.validate(function(err) {
+ if (err) {
+ error = err;
+ }
+ --count || fn(error);
+ });
}
}, 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,17 +156,27 @@ 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) {
+ 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)) {
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;
@@ -131,26 +185,37 @@ DocumentArray.prototype.cast = function (value, doc, init, prev) {
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) {
+ throw new CastError('embedded', value[i], value._path);
+ }
}
-
- // 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,15 +226,15 @@ 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) {
+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];
diff --git a/node_modules/mongoose/lib/schema/embedded.js b/node_modules/mongoose/lib/schema/embedded.js
new file mode 100644
index 0000000..699b81a
--- /dev/null
+++ b/node_modules/mongoose/lib/schema/embedded.js
@@ -0,0 +1,118 @@
+var SchemaType = require('../schematype');
+var Subdocument = require('../types/subdocument');
+
+module.exports = Embedded;
+
+/**
+ * Sub-schema schematype constructor
+ *
+ * @param {Schema} schema
+ * @param {String} key
+ * @param {Object} options
+ * @inherits SchemaType
+ * @api private
+ */
+
+function Embedded(schema, path, options) {
+ var _embedded = function() {
+ Subdocument.apply(this, arguments);
+ };
+ _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);
+
+/**
+ * Casts contents
+ *
+ * @param {Object} value
+ * @api private
+ */
+
+Embedded.prototype.cast = function(val, doc) {
+ var subdoc = new this.caster();
+ subdoc = subdoc.init(val);
+ subdoc.$parent = doc;
+ 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);
+ } else {
+ val = $conditional;
+ 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);
+ }
+ 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;
+ }
+ return value.validateSync();
+};
+
+/**
+ * Required validator for single nested docs
+ *
+ * @api private
+ */
+
+Embedded.prototype.checkRequired = function(value) {
+ return !!value && value.$isSingleNested;
+};
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..ff2173a 100644
--- a/node_modules/mongoose/lib/schema/mixed.js
+++ b/node_modules/mongoose/lib/schema/mixed.js
@@ -15,7 +15,7 @@ var utils = require('../utils');
* @api private
*/
-function Mixed (path, options) {
+function Mixed(path, options) {
if (options && options.default) {
var def = options.default;
if (Array.isArray(def) && 0 === def.length) {
@@ -25,20 +25,28 @@ function Mixed (path, options) {
utils.isObject(def) &&
0 === Object.keys(def).length) {
// prevent odd "shared" objects between documents
- options.default = function () {
- return {}
- }
+ options.default = function() {
+ return {};
+ };
}
}
- SchemaType.call(this, path, options);
-};
+ SchemaType.call(this, path, options, 'Mixed');
+}
+
+/**
+ * This schema type's name, to defend against minifiers that mangle
+ * function names.
+ *
+ * @api private
+ */
+Mixed.schemaName = 'Mixed';
/*!
* Inherits from SchemaType.
*/
-
-Mixed.prototype.__proto__ = SchemaType.prototype;
+Mixed.prototype = Object.create( SchemaType.prototype );
+Mixed.prototype.constructor = Mixed;
/**
* Required validator
@@ -46,8 +54,8 @@ Mixed.prototype.__proto__ = SchemaType.prototype;
* @api private
*/
-Mixed.prototype.checkRequired = function (val) {
- return true;
+Mixed.prototype.checkRequired = function(val) {
+ return (val !== undefined) && (val !== null);
};
/**
@@ -59,7 +67,7 @@ Mixed.prototype.checkRequired = function (val) {
* @api private
*/
-Mixed.prototype.cast = function (val) {
+Mixed.prototype.cast = function(val) {
return val;
};
@@ -71,7 +79,7 @@ Mixed.prototype.cast = function (val) {
* @api private
*/
-Mixed.prototype.castForQuery = function ($cond, 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..7c427cf 100644
--- a/node_modules/mongoose/lib/schema/number.js
+++ b/node_modules/mongoose/lib/schema/number.js
@@ -2,10 +2,11 @@
* Module requirements.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , utils = require('../utils')
- , Document
+var SchemaType = require('../schematype'),
+ CastError = SchemaType.CastError,
+ errorMessages = require('../error').messages,
+ utils = require('../utils'),
+ Document;
/**
* Number SchemaType constructor.
@@ -16,15 +17,23 @@ var SchemaType = require('../schematype')
* @api private
*/
-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 private
+ */
+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
@@ -32,7 +41,7 @@ SchemaNumber.prototype.__proto__ = SchemaType.prototype;
* @api private
*/
-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 {
@@ -54,22 +63,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 (null != value) {
+ var msg = message || errorMessages.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 +117,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 (null != value) {
+ var msg = message || errorMessages.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,7 +166,7 @@ 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
@@ -157,15 +204,17 @@ SchemaNumber.prototype.cast = function (value, doc, init) {
? value._id // documents
: value;
- if (!isNaN(val)){
+ 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 (typeof val === 'string' || typeof val === 'boolean') {
+ 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)
+ return new Number(val);
}
}
@@ -176,28 +225,28 @@ 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) {
+function handleArray(val) {
var self = this;
- return val.map(function (m) {
- return self.cast(m)
+ if (!Array.isArray(val)) {
+ return [this.cast(val)];
+ }
+ return val.map(function(m) {
+ return self.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, {
+ '$gt' : handleSingle,
+ '$gte': handleSingle,
+ '$lt' : handleSingle,
+ '$lte': handleSingle,
+ '$mod': handleArray
+ });
/**
* Casts contents for queries.
@@ -207,7 +256,7 @@ 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];
@@ -216,7 +265,7 @@ SchemaNumber.prototype.castForQuery = function ($conditional, val) {
return handler.call(this, val);
} else {
val = this.cast($conditional);
- return val == null ? val : val
+ return val == null ? val : val;
}
};
diff --git a/node_modules/mongoose/lib/schema/objectid.js b/node_modules/mongoose/lib/schema/objectid.js
index 99f9623..26f6621 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.
@@ -18,15 +19,23 @@ var SchemaType = require('../schematype')
* @api private
*/
-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 private
+ */
+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,10 +44,10 @@ 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;
@@ -50,7 +59,7 @@ ObjectId.prototype.auto = function (turnOn) {
* @api private
*/
-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 {
@@ -67,7 +76,7 @@ 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
@@ -101,17 +110,26 @@ ObjectId.prototype.cast = function (value, doc, init) {
return ret;
}
- if (value === null) return value;
+ // If null or undefined
+ if (value == null) return value;
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 oid.createFromHexString(value._id.toString());
+ } catch (e) {}
+ }
+ }
- if (value.toString) {
+ if (value.toString instanceof Function) {
try {
- return oid.fromString(value.toString());
+ return oid.createFromHexString(value.toString());
} catch (err) {
throw new CastError('ObjectId', value, this.path);
}
@@ -124,27 +142,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 =
+ utils.options(SchemaType.prototype.$conditionalHandlers, {
+ '$gt': handleSingle,
+ '$gte': handleSingle,
+ '$lt': handleSingle,
+ '$lte': handleSingle
});
-}
-
-ObjectId.prototype.$conditionalHandlers = {
- '$ne': handleSingle
- , '$in': handleArray
- , '$nin': handleArray
- , '$gt': handleSingle
- , '$lt': handleSingle
- , '$gte': handleSingle
- , '$lte': handleSingle
- , '$all': handleArray
-};
/**
* Casts contents for queries.
@@ -154,7 +162,7 @@ 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];
@@ -170,11 +178,11 @@ ObjectId.prototype.castForQuery = function ($conditional, val) {
* 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/string.js b/node_modules/mongoose/lib/schema/string.js
index 235a12f..799fcc5 100644
--- a/node_modules/mongoose/lib/schema/string.js
+++ b/node_modules/mongoose/lib/schema/string.js
@@ -3,10 +3,11 @@
* Module dependencies.
*/
-var SchemaType = require('../schematype')
- , CastError = SchemaType.CastError
- , utils = require('../utils')
- , Document
+var SchemaType = require('../schematype'),
+ CastError = SchemaType.CastError,
+ errorMessages = require('../error').messages,
+ utils = require('../utils'),
+ Document;
/**
* String SchemaType constructor.
@@ -17,64 +18,100 @@ var SchemaType = require('../schematype')
* @api private
*/
-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 private
+ */
+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 (undefined === arguments[0] || false === arguments[0]) {
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 = errorMessages.String.enum;
+ }
+
+ for (var i = 0; i < values.length; i++) {
+ if (undefined !== values[i]) {
+ this.enumValues.push(this.cast(values[i]));
}
}
- if (!this.enumValidator) {
- var values = this.enumValues;
- this.enumValidator = function(v){
- return undefined === v || ~values.indexOf(v);
- };
- this.validators.push([this.enumValidator, 'enum']);
- }
+ 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,9 +130,9 @@ 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)
+SchemaString.prototype.lowercase = function() {
+ return this.set(function(v, self) {
+ if ('string' != typeof v) v = self.cast(v);
if (v) return v.toLowerCase();
return v;
});
@@ -115,9 +152,9 @@ 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)
+SchemaString.prototype.uppercase = function() {
+ return this.set(function(v, self) {
+ if ('string' != typeof v) v = self.cast(v);
if (v) return v.toUpperCase();
return v;
});
@@ -141,14 +178,122 @@ 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)
+SchemaString.prototype.trim = function() {
+ return this.set(function(v, self) {
+ if ('string' != typeof v) v = self.cast(v);
if (v) 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 (null != value) {
+ var msg = message || errorMessages.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 (null != value) {
+ var msg = message || errorMessages.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,27 +303,57 @@ 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 || errorMessages.String.match;
+
+ var matchValidator = function(v) {
+ if (!regExp) {
+ return false;
+ }
+
+ var ret = ((null != v && '' !== v)
+ ? regExp.test(v)
+ : true);
+ return ret;
+ };
+
+ this.validators.push({
+ validator: matchValidator,
+ message: msg,
+ type: 'regexp',
+ regexp: regExp
+ });
return this;
};
@@ -189,7 +364,7 @@ SchemaString.prototype.match = function match (regExp) {
* @api private
*/
-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 {
@@ -203,7 +378,7 @@ 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
@@ -237,7 +412,8 @@ SchemaString.prototype.cast = function (value, doc, init) {
return ret;
}
- if (value === null) {
+ // If null or undefined
+ if (value == null) {
return value;
}
@@ -246,12 +422,15 @@ SchemaString.prototype.cast = function (value, doc, init) {
if (value._id && 'string' == typeof value._id) {
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 +438,30 @@ SchemaString.prototype.cast = function (value, doc, init) {
* ignore
*/
-function handleSingle (val) {
+function handleSingle(val) {
return this.castForQuery(val);
}
-function handleArray (val) {
+function handleArray(val) {
var self = this;
- return val.map(function (m) {
+ if (!Array.isArray(val)) {
+ return [this.castForQuery(val)];
+ }
+ return val.map(function(m) {
return self.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,7 +471,7 @@ 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];
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..0cfe08b 100644
--- a/node_modules/mongoose/lib/schematype.js
+++ b/node_modules/mongoose/lib/schematype.js
@@ -3,8 +3,10 @@
*/
var utils = require('./utils');
-var CastError = require('./error').CastError;
-var ValidatorError = require('./error').ValidatorError;
+var error = require('./error');
+var errorMessages = error.messages;
+var CastError = error.CastError;
+var ValidatorError = error.ValidatorError;
/**
* SchemaType constructor
@@ -15,7 +17,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 +27,19 @@ 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] && 'function' == typeof this[i]) {
+ // { unique: true, index: true }
+ if ('index' == i && 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,17 +56,35 @@ 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) {
+SchemaType.prototype.default = function(val) {
if (1 === arguments.length) {
this.defaultValue = typeof val === 'function'
? val
@@ -99,7 +121,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 +132,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,7 +142,7 @@ SchemaType.prototype.index = function (options) {
* @api public
*/
-SchemaType.prototype.unique = function (bool) {
+SchemaType.prototype.unique = function(bool) {
if (null == this._index || 'boolean' == typeof this._index) {
this._index = {};
} else if ('string' == typeof this._index) {
@@ -131,6 +153,29 @@ SchemaType.prototype.unique = function (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 bool
+ * @return {SchemaType} this
+ * @api public
+ */
+
+SchemaType.prototype.text = function(bool) {
+ if (null == this._index || 'boolean' == typeof this._index) {
+ this._index = {};
+ } else if ('string' == typeof this._index) {
+ this._index = { type: this._index };
+ }
+
+ this._index.text = bool;
+ return this;
+};
+
/**
* Declares a sparse index.
*
@@ -144,7 +189,7 @@ SchemaType.prototype.unique = function (bool) {
* @api public
*/
-SchemaType.prototype.sparse = function (bool) {
+SchemaType.prototype.sparse = function(bool) {
if (null == this._index || 'boolean' == typeof this._index) {
this._index = {};
} else if ('string' == typeof this._index) {
@@ -228,7 +273,7 @@ SchemaType.prototype.sparse = function (bool) {
* @api public
*/
-SchemaType.prototype.set = function (fn) {
+SchemaType.prototype.set = function(fn) {
if ('function' != typeof fn)
throw new TypeError('A setter must be a function.');
this.setters.push(fn);
@@ -297,7 +342,7 @@ SchemaType.prototype.set = function (fn) {
* @api public
*/
-SchemaType.prototype.get = function (fn) {
+SchemaType.prototype.get = function(fn) {
if ('function' != typeof fn)
throw new TypeError('A getter must be a function.');
this.getters.push(fn);
@@ -307,43 +352,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 baseic 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,71 +425,113 @@ 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 ('function' == typeof obj || obj && 'RegExp' === utils.getFunctionName(obj.constructor)) {
+ 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 = errorMessages.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 && 'Object' === utils.getFunctionName(arg.constructor))) {
var msg = 'Invalid validator. Received (' + typeof arg + ') '
+ 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 required validator is added
+ * to the front of the 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 :( ');
+ *
*
* @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
* @api public
*/
-SchemaType.prototype.required = function (required) {
- var self = this;
+SchemaType.prototype.required = function(required, message) {
+ if (false === required) {
+ this.validators = this.validators.filter(function(v) {
+ return v.validator != this.requiredValidator;
+ }, this);
- function __checkRequired (v) {
+ this.isRequired = false;
+ return this;
+ }
+
+ var self = this;
+ this.isRequired = true;
+
+ 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);
+
+ return (('function' === typeof required) && !required.apply(this)) ||
+ self.checkRequired(v, this);
+ };
+
+ if ('string' == typeof required) {
+ message = required;
+ required = undefined;
}
- 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']);
- }
+ var msg = message || errorMessages.general.required;
+ this.validators.unshift({
+ validator: this.requiredValidator,
+ message: msg,
+ type: 'required'
+ });
return this;
};
@@ -440,7 +544,7 @@ SchemaType.prototype.required = function (required) {
* @api private
*/
-SchemaType.prototype.getDefault = function (scope, init) {
+SchemaType.prototype.getDefault = function(scope, init) {
var ret = 'function' === typeof this.defaultValue
? this.defaultValue.call(scope)
: this.defaultValue;
@@ -461,26 +565,24 @@ 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) {
+ var v = value,
+ setters = this.setters,
+ len = setters.length,
+ caster = this.caster;
while (len--) {
v = setters[len].call(scope, v, this);
}
+ 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 (null === v || undefined === v) return v;
// do not cast until all setters are applied #665
@@ -497,12 +599,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 +632,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 +646,111 @@ 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);
- function validate (ok, msg, val) {
+ 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);
}
- }
+ };
- this.validators.forEach(function (v) {
- var validator = v[0]
- , message = v[1];
+ var self = this;
+ 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), message, value);
+ validate(validator.test(value), validatorProperties);
} else if ('function' === typeof validator) {
+ if (value === undefined && !self.isRequired) {
+ validate(true, validatorProperties);
+ return;
+ }
if (2 === validator.length) {
- validator.call(scope, value, function (ok) {
- validate(ok, message, value);
+ validator.call(scope, value, function(ok, customMsg) {
+ if (customMsg) {
+ validatorProperties.message = customMsg;
+ }
+ validate(ok, 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 self = this;
+ if (value === undefined && !self.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 ('function' === typeof validator) {
+ // if not async validators
+ if (2 !== validator.length) {
+ validate(validator.call(scope, value), validatorProperties);
+ }
+ }
+ });
+
+ return err;
+};
+
/**
* Determines if value is a valid Reference.
*
@@ -591,7 +762,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;
@@ -615,8 +786,64 @@ SchemaType._isRef = function (self, value, doc, init) {
}
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);
+ } else {
+ val = $conditional;
+ return this.cast(val);
+ }
+};
+
/*!
* Module exports.
*/
diff --git a/node_modules/mongoose/lib/services/updateValidators.js b/node_modules/mongoose/lib/services/updateValidators.js
new file mode 100644
index 0000000..d921190
--- /dev/null
+++ b/node_modules/mongoose/lib/services/updateValidators.js
@@ -0,0 +1,199 @@
+/*!
+ * Module dependencies.
+ */
+
+var async = require('async');
+var ValidationError = require('../error/validation.js');
+var ObjectId = require('../types/objectid');
+
+/**
+ * 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);
+ }
+
+ 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.$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.$setOnInsert = castedDoc.$setOnInsert || {};
+ castedDoc.$setOnInsert[path] = def;
+ updatedValues[path] = def;
+ }
+ }
+ });
+ }
+ }
+
+ var updates = Object.keys(updatedValues);
+ var numUpdates = updates.length;
+ var validatorsToExecute = [];
+ var validationErrors = [];
+ for (i = 0; i < numUpdates; ++i) {
+ (function(i) {
+ var schemaPath = schema._getSchema(updates[i]);
+ if (schemaPath) {
+ 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);
+ });
+ }
+ })(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);
+ });
+ };
+};
+
+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;
+}
+
+function flatten(update, path) {
+ 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 (shouldFlatten(val)) {
+ var flat = flatten(val, path + key);
+ for (var k in flat) {
+ result[k] = flat[k];
+ }
+ } else {
+ result[path + key] = val;
+ }
+ }
+
+ return result;
+}
+
+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/statemachine.js b/node_modules/mongoose/lib/statemachine.js
index 76005d8..98a37ed 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 () {
+StateMachine.prototype.some = function some() {
var self = this;
var what = arguments.length ? arguments : this.stateNames;
- return Array.prototype.some.call(what, function (state) {
+ return Array.prototype.some.call(what, function(state) {
return Object.keys(self.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 paths = states.reduce(function (paths, state) {
+ var paths = states.reduce(function(paths, state) {
return paths.concat(Object.keys(self.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..542d69b 100644
--- a/node_modules/mongoose/lib/types/array.js
+++ b/node_modules/mongoose/lib/types/array.js
@@ -1,4 +1,3 @@
-
/*!
* Module dependencies.
*/
@@ -24,450 +23,724 @@ 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);
+
+ utils.decorate( arr, MongooseArray.mixin );
+ arr.isMongooseArray = true;
+
+ var _options = { enumerable: false, configurable: true, writable: true };
+ var keys = Object.keys(MongooseArray.mixin).
+ concat(['isMongooseArray', 'validators', '_path']);
+ for (var i = 0; i < keys.length; ++i) {
+ Object.defineProperty(arr, keys[i], _options);
+ }
arr._atomics = {};
arr.validators = [];
arr._path = path;
- 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.prototype._parent;
-
-/**
- * Casts a member based on this arrays schema.
- *
- * @param {any} value
- * @return value the casted value
- * @api private
- */
-
-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;
- }
-
- populated = owner.populated(this._path, true);
- }
-
- if (populated && null != value) {
- // cast to the populated Models schema
- var Model = populated.options.model;
-
- // 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 };
- }
-
- value = new Model(value);
- return this._schema.caster.cast(value, this._parent, true)
- }
-
- 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
- * @api private
- */
+MongooseArray.mixin = {
-MongooseArray.prototype._markModified = function (elem, embeddedPath) {
- var parent = this._parent
- , dirtyPath;
+ /**
+ * Stores a queue of atomic operations to perform
+ *
+ * @property _atomics
+ * @api private
+ */
- if (parent) {
- dirtyPath = this._path;
+ _atomics: undefined,
- if (arguments.length) {
- if (null != embeddedPath) {
- // an embedded doc bubbled up the change
- dirtyPath = dirtyPath + '.' + this.indexOf(elem) + '.' + embeddedPath;
- } else {
- // directly set an index
- dirtyPath = dirtyPath + '.' + elem;
+ /**
+ * 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 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;
}
+
+ populated = owner.populated(this._path, true);
}
- parent.markModified(dirtyPath);
- }
- return this;
-};
+ if (populated && null != value) {
+ // cast to the populated Models schema
+ Model = populated.options.model;
-/**
- * Register an atomic operation with the parent.
- *
- * @param {Array} op operation
- * @param {any} val
- * @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 };
+ }
+
+ // 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);
+ }
+
+ 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 (null != embeddedPath) {
+ // an embedded doc bubbled up the change
+ dirtyPath = dirtyPath + '.' + this.indexOf(elem) + '.' + embeddedPath;
+ } else {
+ // directly set an index
+ dirtyPath = dirtyPath + '.' + elem;
+ }
+ }
+
+ parent.markModified(dirtyPath);
+ }
-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;
+ /**
+ * Register an atomic operation with the parent.
+ *
+ * @param {Array} op operation
+ * @param {any} val
+ * @method _registerAtomic
+ * @api private
+ * @receiver MongooseArray
+ */
- // reset pop/shift after save
- if ('$pop' == op && !('$pop' in atomics)) {
- var self = this;
- this._parent.once('save', function () {
- self._popped = self._shifted = null;
- });
- }
+ _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;
+ }
+
+ 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 {
+ selector = pullOp['_id'] || (pullOp['_id'] = {'$in' : [] });
+ selector['$in'] = selector['$in'].concat(val);
+ }
+ } else {
+ atomics[op] = val;
+ }
- // 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 (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;
- }
+ /**
+ * 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
+ */
- return this;
-};
+ $__getAtomics: function() {
+ var ret = [];
+ var keys = Object.keys(this._atomics);
+ var i = keys.length;
-/**
- * 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
- */
+ if (0 === i) {
+ ret[0] = ['$set', this.toObject({ depopulate: 1, transform: false })];
+ return ret;
+ }
-MongooseArray.prototype.$__getAtomics = function () {
- var ret = [];
- var keys = Object.keys(this._atomics);
- var i = keys.length;
+ 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();
+ }
+
+ if ('$addToSet' == op) {
+ val = { $each: val };
+ }
+
+ ret.push([op, val]);
+ }
- if (0 === i) {
- ret[0] = ['$set', this.toObject({ depopulate: 1 })];
return ret;
- }
+ },
- while (i--) {
- var op = keys[i];
- var val = this._atomics[op];
+ /**
+ * Returns the number of pending atomic operations to send to the db for this array.
+ *
+ * @api private
+ * @return {Number}
+ * @method hasAtomics
+ * @receiver MongooseArray
+ */
- // 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();
+ hasAtomics: function hasAtomics() {
+ if (!(this._atomics && 'Object' === this._atomics.constructor.name)) {
+ return 0;
}
- if ('$addToSet' == op) {
- val = { $each: val }
- }
+ return Object.keys(this._atomics).length;
+ },
- ret.push([op, val]);
- }
+ /**
+ * Internal helper for .map()
+ *
+ * @api private
+ * @return {Number}
+ * @method _mapCast
+ * @receiver MongooseArray
+ */
+ _mapCast: function(val, index) {
+ return this._cast(val, this.length + index);
+ },
- return ret;
-}
+ /**
+ * 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
+ */
-/**
- * Returns the number of pending atomic operations to send to the db for this array.
- *
- * @api private
- * @return {Number}
- */
+ push: function() {
+ var values = [].map.call(arguments, this._mapCast, this);
+ values = this._schema.applySetters(values, this._parent);
+ var ret = [].push.apply(this, values);
-MongooseArray.prototype.hasAtomics = function hasAtomics () {
- if (!(this._atomics && 'Object' === this._atomics.constructor.name)) {
- return 0;
- }
+ // $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;
+ },
- return Object.keys(this._atomics).length;
-}
+ /**
+ * 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
+ */
-/**
- * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
- *
- * @param {Object} [args...]
- * @api public
- */
+ nonAtomicPush: function() {
+ var values = [].map.call(arguments, this._mapCast, this);
+ var ret = [].push.apply(this, values);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
-MongooseArray.prototype.push = function () {
- var values = [].map.call(arguments, this._cast, this)
- , ret = [].push.apply(this, values);
+ /**
+ * 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
+ */
- // $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;
-};
+ $pop: function() {
+ this._registerAtomic('$pop', 1);
+ this._markModified();
-/**
- * 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
- */
+ // only allow popping once
+ if (this._popped) return;
+ this._popped = true;
-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;
-};
+ return [].pop.call(this);
+ },
-/**
- * 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
- */
+ /**
+ * 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
+ */
-MongooseArray.prototype.$pop = function () {
- this._registerAtomic('$pop', 1);
- this._markModified();
+ pop: function() {
+ var ret = [].pop.call(this);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
- // only allow popping once
- if (this._popped) return;
- this._popped = true;
+ /**
+ * 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
+ */
- return [].pop.call(this);
-};
+ $shift: function $shift() {
+ this._registerAtomic('$pop', -1);
+ this._markModified();
-/**
- * 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
- */
+ // only allow shifting once
+ if (this._shifted) return;
+ this._shifted = true;
-MongooseArray.prototype.pop = function () {
- var ret = [].pop.call(this);
- this._registerAtomic('$set', this);
- this._markModified();
- return ret;
-};
+ return [].shift.call(this);
+ },
-/**
- * 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
- */
+ /**
+ * 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
+ */
-MongooseArray.prototype.$shift = function $shift () {
- this._registerAtomic('$pop', -1);
- this._markModified();
+ shift: function() {
+ var ret = [].shift.call(this);
+ this._registerAtomic('$set', this);
+ this._markModified();
+ return ret;
+ },
- // only allow shifting once
- if (this._shifted) return;
- this._shifted = true;
+ /**
+ * 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
+ *
+ * @param {any} [args...]
+ * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
+ * @api public
+ * @method pull
+ * @receiver MongooseArray
+ */
- return [].shift.call(this);
-};
+ pull: function() {
+ var values = [].map.call(arguments, this._cast, this),
+ cur = this._parent.get(this._path),
+ i = cur.length,
+ mem;
-/**
- * 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); } )) {
+ while (i--) {
+ mem = cur[i];
+ if (mem instanceof EmbeddedDocument) {
+ if (values.some(function(v) { return v.equals(mem); } )) {
+ [].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);
- }
+ if (values[0] instanceof EmbeddedDocument) {
+ this._registerAtomic('$pullDocs', values.map(function(v) {
+ return v._id || v;
+ }));
+ } else {
+ this._registerAtomic('$pullAll', values);
+ }
- this._markModified();
- return this;
+ 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], arguments[0] + (i - 2));
+ }
+ ret = [].splice.apply(this, vals);
+ 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
+ * @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 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 = 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;
+ });
+ }
+
+ 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();
+ for (var i = 0, len = this.length; i < len; ++i) {
+ if (obj == this[i])
+ return i;
+ }
+ return -1;
+ }
};
/**
@@ -480,197 +753,7 @@ MongooseArray.prototype.pull = function () {
* @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
- */
-
-MongooseArray.prototype.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]);
- }
- ret = [].splice.apply(this, vals);
- 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;
-}
-
-/**
- * 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
- */
-
-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
- */
-
-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
- });
- }
-
- 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.
- *
- * @param {Object} obj the item to look for
- * @return {Number}
- * @api public
- */
-
-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..0660d70 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,7 +18,7 @@ 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;
@@ -47,95 +41,118 @@ 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) {
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);
+//MongooseBuffer.prototype = new Buffer(0);
-/**
- * Parent owner document
- *
- * @api private
- * @property _parent
- */
+MongooseBuffer.mixin = {
-MongooseBuffer.prototype._parent;
+ /**
+ * Parent owner document
+ *
+ * @api private
+ * @property _parent
+ * @receiver MongooseBuffer
+ */
-/**
- * Marks this buffer as modified.
- *
- * @api private
- */
+ _parent: undefined,
-MongooseBuffer.prototype._markModified = function () {
- var parent = this._parent;
+ /**
+ * Default subtype for the Binary representing this Buffer
+ *
+ * @api private
+ * @property _subtype
+ * @receiver MongooseBuffer
+ */
- if (parent) {
- parent.markModified(this._path);
+ _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 {MongooseBuffer}
+ * @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 ' +
@@ -145,13 +162,13 @@ MongooseBuffer.prototype.copy = function (target) {
'writeUInt16LE writeUInt16BE writeUInt32LE writeUInt32BE ' +
'writeInt16LE writeInt16BE writeInt32LE writeInt32BE ' +
'writeFloatLE writeFloatBE writeDoubleLE writeDoubleBE'
-).split(' ').forEach(function (method) {
+).split(' ').forEach(function(method) {
if (!Buffer.prototype[method]) return;
- MongooseBuffer.prototype[method] = new Function(
- 'var ret = Buffer.prototype.'+method+'.apply(this, arguments);' +
+ MongooseBuffer.mixin[method] = new Function(
+ 'var ret = Buffer.prototype.' + method + '.apply(this, arguments);' +
'this._markModified();' +
'return ret;'
- )
+ );
});
/**
@@ -173,12 +190,14 @@ MongooseBuffer.prototype.copy = function (target) {
* @param {Hex} [subtype]
* @return {Binary}
* @api public
+ * @method toObject
+ * @receiver MongooseBuffer
*/
-MongooseBuffer.prototype.toObject = function (options) {
+MongooseBuffer.mixin.toObject = function(options) {
var subtype = 'number' == typeof options
? options
- : (this._subtype || 0x00);
+ : (this._subtype || 0);
return new Binary(this, subtype);
};
@@ -187,9 +206,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;
}
@@ -203,7 +224,41 @@ MongooseBuffer.prototype.equals = function (other) {
}
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 ('number' != typeof subtype) {
+ 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..d3055ef 100644
--- a/node_modules/mongoose/lib/types/documentarray.js
+++ b/node_modules/mongoose/lib/types/documentarray.js
@@ -1,15 +1,13 @@
-
/*!
* 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'),
+ util = require('util'),
+ Document = require('../document');
/**
* DocumentArray constructor
@@ -23,47 +21,59 @@ 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);
// 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;
+ utils.decorate( arr, MongooseDocumentArray.mixin );
+ arr.isMongooseArray = true;
+ arr.isMongooseDocumentArray = true;
arr._atomics = {};
arr.validators = [];
arr._path = path;
- 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;
+MongooseDocumentArray.mixin = Object.create( MongooseArray.mixin );
/**
* Overrides MongooseArray#cast
*
+ * @method _cast
* @api private
+ * @receiver MongooseDocumentArray
*/
-MongooseDocumentArray.prototype._cast = function (value) {
+MongooseDocumentArray.mixin._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;
}
@@ -74,8 +84,7 @@ MongooseDocumentArray.prototype._cast = function (value) {
value instanceof ObjectId || !utils.isObject(value)) {
value = { _id: value };
}
-
- return new this._schema.casterConstructor(value, this);
+ return new this._schema.casterConstructor(value, this, undefined, undefined, index);
};
/**
@@ -85,18 +94,22 @@ MongooseDocumentArray.prototype._cast = function (value) {
*
* var embeddedDoc = m.array.id(some_id);
*
- * @return {EmbeddedDocument|null} the subdocuent or null if not found.
+ * @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
*/
-MongooseDocumentArray.prototype.id = function (id) {
- var casted
- , sid
- , _id
+MongooseDocumentArray.mixin.id = function(id) {
+ var casted,
+ sid,
+ _id;
try {
- casted = ObjectId.toString(ObjectIdSchema.prototype.cast.call({}, id));
+ var casted_ = ObjectIdSchema.prototype.cast.call({}, id);
+ if (casted_) casted = String(casted_);
} catch (e) {
casted = null;
}
@@ -104,12 +117,13 @@ MongooseDocumentArray.prototype.id = function (id) {
for (var i = 0, l = this.length; i < l; i++) {
_id = this[i].get('_id');
- if (_id instanceof Document) {
+ 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)) {
- sid || (sid = String(id));
- if (sid == _id) return this[i];
+ if (utils.deepEqual(id, _id)) return this[i];
} else if (casted == _id) {
return this[i];
}
@@ -127,11 +141,13 @@ MongooseDocumentArray.prototype.id = function (id) {
*
* @param {Object} [options] optional options to pass to each documents `toObject` method call during conversion
* @return {Array}
+ * @method toObject
* @api public
+ * @receiver MongooseDocumentArray
*/
-MongooseDocumentArray.prototype.toObject = function (options) {
- return this.map(function (doc) {
+MongooseDocumentArray.mixin.toObject = function(options) {
+ return this.map(function(doc) {
return doc && doc.toObject(options) || null;
});
};
@@ -139,17 +155,19 @@ MongooseDocumentArray.prototype.toObject = function (options) {
/**
* Helper for console.log
*
+ * @method inspect
* @api public
+ * @receiver MongooseDocumentArray
*/
-MongooseDocumentArray.prototype.inspect = function () {
- return '[' + this.map(function (doc) {
+MongooseDocumentArray.mixin.inspect = function() {
+ return '[' + Array.prototype.map.call(this, function(doc) {
if (doc) {
return doc.inspect
? doc.inspect()
- : util.inspect(doc)
+ : util.inspect(doc);
}
- return 'null'
+ return 'null';
}).join('\n') + ']';
};
@@ -159,31 +177,44 @@ MongooseDocumentArray.prototype.inspect = function () {
* 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
*/
-MongooseDocumentArray.prototype.create = function (obj) {
+MongooseDocumentArray.mixin.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
*/
-MongooseDocumentArray.prototype.notify = function notify (event) {
+MongooseDocumentArray.mixin.notify = function notify(event) {
var self = this;
- return function notify (val) {
+ return function notify(val) {
var i = self.length;
while (i--) {
if (!self[i]) continue;
+ switch (event) {
+ // only swap for save event for now, we may change this to all event types later
+ case 'save':
+ val = self[i];
+ break;
+ default:
+ // NO-OP
+ break;
+ }
self[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..df694cf 100644
--- a/node_modules/mongoose/lib/types/embedded.js
+++ b/node_modules/mongoose/lib/types/embedded.js
@@ -1,9 +1,12 @@
+/* eslint no-func-assign: 1 */
+
/*!
* Module dependencies.
*/
-var Document = require('../document')
- , inspect = require('util').inspect;
+var Document = require('../document_provider')();
+var inspect = require('util').inspect;
+var PromiseProvider = require('../promise_provider');
/**
* EmbeddedDocument constructor.
@@ -15,7 +18,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 +26,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) {
+ this.on('isNew', function(val) {
self.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 +53,21 @@ 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,14 +78,16 @@ 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();
+ });
};
/**
@@ -90,7 +97,7 @@ EmbeddedDocument.prototype.save = function(fn) {
* @api public
*/
-EmbeddedDocument.prototype.remove = function (fn) {
+EmbeddedDocument.prototype.remove = function(fn) {
if (!this.__parentArray) return this;
var _id;
@@ -102,6 +109,7 @@ EmbeddedDocument.prototype.remove = function (fn) {
}
this.__parentArray.pull({ _id: _id });
this.willRemove = true;
+ registerRemoveListener(this);
}
if (fn)
@@ -110,14 +118,36 @@ EmbeddedDocument.prototype.remove = function (fn) {
return this;
};
+/*!
+ * Registers remove event listeners for triggering
+ * on subdocuments.
+ *
+ * @param {EmbeddedDocument} sub
+ * @api private
+ */
+
+function registerRemoveListener(sub) {
+ var owner = sub.ownerDocument();
+
+ owner.on('save', emitRemove);
+ owner.on('remove', emitRemove);
+
+ function emitRemove() {
+ owner.removeListener('save', emitRemove);
+ owner.removeListener('remove', emitRemove);
+ sub.emit('remove', sub);
+ owner = sub = emitRemove = null;
+ }
+}
+
/**
* Override #update method of parent documents.
* @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,7 +155,7 @@ EmbeddedDocument.prototype.update = function () {
* @api public
*/
-EmbeddedDocument.prototype.inspect = function () {
+EmbeddedDocument.prototype.inspect = function() {
return inspect(this.toObject());
};
@@ -138,29 +168,67 @@ 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.'
+ var msg = 'Unable to invalidate a subdocument that has not been added to an array.';
throw new Error(msg);
}
- 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[path];
+ }
+
+ return true;
+};
/**
* Returns the top level document of this sub-document.
@@ -168,7 +236,7 @@ EmbeddedDocument.prototype.invalidate = function (path, err, val, first) {
* @return {Document}
*/
-EmbeddedDocument.prototype.ownerDocument = function () {
+EmbeddedDocument.prototype.ownerDocument = function() {
if (this.$__.ownerDocument) {
return this.$__.ownerDocument;
}
@@ -181,7 +249,7 @@ EmbeddedDocument.prototype.ownerDocument = function () {
}
return this.$__.ownerDocument = parent;
-}
+};
/**
* Returns the full path to this document. If optional `path` is passed, it is appended to the full path.
@@ -193,7 +261,7 @@ 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;
@@ -215,7 +283,7 @@ EmbeddedDocument.prototype.$__fullPath = function (path) {
return path
? this.$__.fullPath + '.' + path
: this.$__.fullPath;
-}
+};
/**
* Returns this sub-documents parent document.
@@ -223,9 +291,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 +301,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');
+var ObjectId = require('../drivers').ObjectId;
+
module.exports = 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;
diff --git a/node_modules/mongoose/lib/types/subdocument.js b/node_modules/mongoose/lib/types/subdocument.js
new file mode 100644
index 0000000..1a345b9
--- /dev/null
+++ b/node_modules/mongoose/lib/types/subdocument.js
@@ -0,0 +1,62 @@
+var Document = require('../document');
+var PromiseProvider = require('../promise_provider');
+
+module.exports = Subdocument;
+
+/**
+ * Subdocument constructor.
+ *
+ * @inherits Document
+ * @api private
+ */
+
+function Subdocument() {
+ Document.apply(this, arguments);
+ this.$isSingleNested = true;
+}
+
+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) {
+ if (this.$parent) {
+ 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);
+ }
+};
diff --git a/node_modules/mongoose/lib/utils.js b/node_modules/mongoose/lib/utils.js
index 678a7a8..3c279e9 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,11 @@ var ReadPref = require('mongodb').ReadPreference
* @api private
*/
-exports.toCollectionName = function (name) {
+exports.toCollectionName = function(name, options) {
+ options = options || {};
if ('system.profile' === name) return name;
if ('system.indexes' === name) return name;
+ if (options.pluralization === false) return name;
return pluralize(name.toLowerCase());
};
@@ -52,8 +53,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 +107,16 @@ 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]);
}
return str;
-};
+}
/*!
* Determines if `a` and `b` are deep equal.
@@ -126,7 +129,7 @@ function pluralize (str) {
* @api private
*/
-exports.deepEqual = function deepEqual (a, b) {
+exports.deepEqual = function deepEqual(a, b) {
if (a === b) return true;
if (a instanceof Date && b instanceof Date)
@@ -147,7 +150,7 @@ exports.deepEqual = function deepEqual (a, b) {
return a == b;
if (a === null || b === null || a === undefined || b === undefined)
- return false
+ return false;
if (a.prototype !== b.prototype) return false;
@@ -157,12 +160,7 @@ 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();
@@ -214,7 +212,7 @@ exports.deepEqual = function deepEqual (a, b) {
* @api private
*/
-exports.clone = function clone (obj, options) {
+exports.clone = function clone(obj, options) {
if (obj === undefined || obj === null)
return obj;
@@ -230,7 +228,7 @@ exports.clone = function clone (obj, options) {
}
if (obj.constructor) {
- switch (obj.constructor.name) {
+ switch (exports.getFunctionName(obj.constructor)) {
case 'Object':
return cloneObject(obj, options);
case 'Date':
@@ -260,15 +258,15 @@ 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) {
@@ -299,14 +297,14 @@ function cloneObject (obj, options) {
return minimize
? hasKeys && ret
: ret;
-};
+}
-function cloneArray (arr, options) {
+function cloneArray(arr, options) {
var ret = [];
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 +315,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 +338,7 @@ exports.options = function (defaults, options) {
* @api private
*/
-exports.random = function () {
+exports.random = function() {
return Math.random().toString().substr(3);
};
@@ -352,21 +350,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]) {
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 +371,49 @@ 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) {
+ 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 +422,12 @@ var toString = Object.prototype.toString;
* @return {Boolean}
*/
-exports.isObject = function (arg) {
+exports.isObject = function(arg) {
+ if (Buffer.isBuffer(arg)) {
+ return true;
+ }
return '[object Object]' == toString.call(arg);
-}
+};
/*!
* A faster Array.prototype.slice.call(arguments) alternative
@@ -407,20 +447,20 @@ exports.args = sliced;
* @api private
*/
-exports.tick = function tick (callback) {
+exports.tick = function tick(callback) {
if ('function' !== typeof callback) return;
- return function () {
+ 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 +471,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,7 +489,7 @@ var isMongooseObject = exports.isMongooseObject;
* @api private
*/
-exports.expires = function expires (object) {
+exports.expires = function expires(object) {
if (!(object && 'Object' == object.constructor.name)) return;
if (!('expires' in object)) return;
@@ -461,54 +501,21 @@ exports.expires = function expires (object) {
}
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,7 +529,7 @@ 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).
@@ -534,7 +541,7 @@ exports.populate = function populate (path, select, model, match, options) {
}
if (Array.isArray(path)) {
- return path.map(function(o){
+ return path.map(function(o) {
return exports.populate(o)[0];
});
}
@@ -544,9 +551,10 @@ 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 ('string' !== typeof model && 'function' !== typeof model) {
options = match;
match = model;
model = undefined;
@@ -556,14 +564,18 @@ exports.populate = function populate (path, select, model, match, options) {
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(' ');
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 +584,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 +596,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 +609,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 +634,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 +645,9 @@ exports.object.hasOwnProperty = function (obj, prop) {
* @return {Boolean}
*/
-exports.isNullOrUndefined = function (val) {
- return null == val
-}
+exports.isNullOrUndefined = function(val) {
+ return null == val;
+};
/*!
* ignore
@@ -653,10 +666,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 +680,118 @@ 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} from
+ * @api private
+ */
+
+exports.mergeClone = function(to, from) {
+ var keys = Object.keys(from),
+ i = keys.length,
+ key;
+
+ while (i--) {
+ key = keys[i];
+ if ('undefined' === typeof to[key]) {
+ // make sure to retain key order here because of a bug handling the $each
+ // operator in mongodb 2.4.4
+ to[key] = exports.clone(from[key], { retainKeyOrder : 1});
+ } else {
+ if (exports.isObject(from[key])) {
+ exports.mergeClone(to[key], from[key]);
+ } 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(from[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/mpromise/node_modules/sliced/.travis.yml b/node_modules/mongoose/node_modules/async/.travis.yml
similarity index 62%
rename from node_modules/mongoose/node_modules/mpromise/node_modules/sliced/.travis.yml
rename to node_modules/mongoose/node_modules/async/.travis.yml
index 895dbd3..6e5919d 100644
--- a/node_modules/mongoose/node_modules/mpromise/node_modules/sliced/.travis.yml
+++ b/node_modules/mongoose/node_modules/async/.travis.yml
@@ -1,4 +1,3 @@
language: node_js
node_js:
- - 0.6
- - 0.8
+ - "0.10"
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..0bea531
--- /dev/null
+++ b/node_modules/mongoose/node_modules/async/README.md
@@ -0,0 +1,1646 @@
+# Async.js
+
+[](https://travis-ci.org/caolan/async)
+
+
+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), it can also be used directly in the
+browser. Also supports [component](https://github.com/component/component).
+
+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
+
+### 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](http://github.com/caolan/async).
+Alternatively, you can install using Node Package Manager (`npm`):
+
+ npm 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
+
+### Collections
+
+* [`each`](#each)
+* [`eachSeries`](#eachSeries)
+* [`eachLimit`](#eachLimit)
+* [`map`](#map)
+* [`mapSeries`](#mapSeries)
+* [`mapLimit`](#mapLimit)
+* [`filter`](#filter)
+* [`filterSeries`](#filterSeries)
+* [`reject`](#reject)
+* [`rejectSeries`](#rejectSeries)
+* [`reduce`](#reduce)
+* [`reduceRight`](#reduceRight)
+* [`detect`](#detect)
+* [`detectSeries`](#detectSeries)
+* [`sortBy`](#sortBy)
+* [`some`](#some)
+* [`every`](#every)
+* [`concat`](#concat)
+* [`concatSeries`](#concatSeries)
+
+### Control Flow
+
+* [`series`](#seriestasks-callback)
+* [`parallel`](#parallel)
+* [`parallelLimit`](#parallellimittasks-limit-callback)
+* [`whilst`](#whilst)
+* [`doWhilst`](#doWhilst)
+* [`until`](#until)
+* [`doUntil`](#doUntil)
+* [`forever`](#forever)
+* [`waterfall`](#waterfall)
+* [`compose`](#compose)
+* [`seq`](#seq)
+* [`applyEach`](#applyEach)
+* [`applyEachSeries`](#applyEachSeries)
+* [`queue`](#queue)
+* [`priorityQueue`](#priorityQueue)
+* [`cargo`](#cargo)
+* [`auto`](#auto)
+* [`retry`](#retry)
+* [`iterator`](#iterator)
+* [`apply`](#apply)
+* [`nextTick`](#nextTick)
+* [`times`](#times)
+* [`timesSeries`](#timesSeries)
+
+### Utils
+
+* [`memoize`](#memoize)
+* [`unmemoize`](#unmemoize)
+* [`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 occured, the `callback` should be run without
+ arguments or with an explicit `null` argument.
+* `callback(err)` - 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');
+ }
+});
+```
+
+---------------------------------------
+
+
+
+### eachSeries(arr, iterator, callback)
+
+The same as [`each`](#each), only `iterator` is applied to each item in `arr` in
+series. The next `iterator` is only called once the current one has completed.
+This means the `iterator` functions will complete in order.
+
+
+---------------------------------------
+
+
+
+### eachLimit(arr, limit, iterator, callback)
+
+The same as [`each`](#each), only no more than `limit` `iterator`s will be simultaneously
+running at any time.
+
+Note that the items in `arr` are not processed in batches, so there is no guarantee that
+the first `limit` `iterator` functions will complete before any others are started.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `limit` - The maximum number of `iterator`s to run at any time.
+* `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 occured, the callback should be run without
+ arguments or with an explicit `null` argument.
+* `callback(err)` - A callback which is called when all `iterator` functions
+ have finished, or an error occurs.
+
+__Example__
+
+```js
+// Assume documents is an array of JSON objects and requestApi is a
+// function that interacts with a rate-limited REST api.
+
+async.eachLimit(documents, 20, requestApi, function(err){
+ // if any of the saves produced an error, err would equal that error
+});
+```
+
+---------------------------------------
+
+
+### 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 this
+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)` - 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
+});
+```
+
+---------------------------------------
+
+
+### mapSeries(arr, iterator, callback)
+
+The same as [`map`](#map), only the `iterator` is applied to each item in `arr` in
+series. The next `iterator` is only called once the current one has completed.
+The results array will be in the same order as the original.
+
+
+---------------------------------------
+
+
+### mapLimit(arr, limit, iterator, callback)
+
+The same as [`map`](#map), only no more than `limit` `iterator`s will be simultaneously
+running at any time.
+
+Note that the items are not processed in batches, so there is no guarantee that
+the first `limit` `iterator` functions will complete before any others are started.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `limit` - The maximum number of `iterator`s to run at any time.
+* `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)` - A callback which is called when all `iterator`
+ calls have finished, or an error occurs. The result is an array of the
+ transformed items from the original `arr`.
+
+__Example__
+
+```js
+async.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+```
+
+---------------------------------------
+
+
+
+### 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)` - 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
+});
+```
+
+---------------------------------------
+
+
+
+### filterSeries(arr, iterator, callback)
+
+__Alias:__ `selectSeries`
+
+The same as [`filter`](#filter) only the `iterator` is applied to each item in `arr` in
+series. The next `iterator` is only called once the current one has completed.
+The results array will be in the same order as the original.
+
+---------------------------------------
+
+
+### reject(arr, iterator, callback)
+
+The opposite of [`filter`](#filter). Removes values that pass an `async` truth test.
+
+---------------------------------------
+
+
+### rejectSeries(arr, iterator, callback)
+
+The same as [`reject`](#reject), only the `iterator` is applied to each item in `arr`
+in series.
+
+
+---------------------------------------
+
+
+### 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)` - 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.
+* `callback(result)` - 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.
+
+__Example__
+
+```js
+async.detect(['file1','file2','file3'], fs.exists, function(result){
+ // result now equals the first file in the list that exists
+});
+```
+
+---------------------------------------
+
+
+### detectSeries(arr, iterator, callback)
+
+The same as [`detect`](#detect), only the `iterator` is applied to each item in `arr`
+in series. This means the result is always the first in the original `arr` (in
+terms of array order) that passes the truth test.
+
+
+---------------------------------------
+
+
+### 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)` - 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(err, x);
+}, function(err,result){
+ //result callback
+} );
+
+//descending order
+async.sortBy([1,9,3,5], function(x, callback){
+ callback(err, 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)` - 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.
+
+__Example__
+
+```js
+async.some(['file1','file2','file3'], fs.exists, function(result){
+ // if result is true then at least one of the files exists
+});
+```
+
+---------------------------------------
+
+
+### 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)` - A callback which is called after all the `iterator`
+ functions have finished. Result will be either `true` or `false` depending on
+ the values of the async tests.
+
+__Example__
+
+```js
+async.every(['file1','file2','file3'], fs.exists, function(result){
+ // if result is true then every file exists
+});
+```
+
+---------------------------------------
+
+
+### 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)` - 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
+});
+```
+
+---------------------------------------
+
+
+### concatSeries(arr, iterator, callback)
+
+Same as [`concat`](#concat), but executes in series instead of parallel.
+
+
+## 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 Specifcation](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.
+
+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. 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}
+});
+```
+
+---------------------------------------
+
+
+### parallelLimit(tasks, limit, [callback])
+
+The same as [`parallel`](#parallel), only `tasks` are executed in parallel
+with a maximum of `limit` tasks executing at any time.
+
+Note that the `tasks` are not executed in batches, so there is no guarantee that
+the first `limit` tasks will complete before any others are started.
+
+__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.
+* `limit` - The maximum number of `tasks` to run at any time.
+* `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.
+
+---------------------------------------
+
+
+### 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)` - A callback which is called after the test fails and repeated
+ execution of `fn` has stopped.
+
+__Example__
+
+```js
+var count = 0;
+
+async.whilst(
+ function () { return count < 5; },
+ function (callback) {
+ count++;
+ setTimeout(callback, 1000);
+ },
+ function (err) {
+ // 5 seconds have passed
+ }
+);
+```
+
+---------------------------------------
+
+
+### 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.
+
+The inverse of [`whilst`](#whilst).
+
+---------------------------------------
+
+
+### doUntil(fn, test, callback)
+
+Like [`doWhilst`](#doWhilst), except the `test` is inverted. Note the argument ordering differs from `until`.
+
+---------------------------------------
+
+
+### 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'
+});
+```
+
+---------------------------------------
+
+### 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 following function consumes the return value of the latter function.
+
+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) {
+ function handleError(err, data, callback) {
+ if (err) {
+ console.error(err);
+ response.json({ status: 'error', message: err.message });
+ }
+ else {
+ callback(data);
+ }
+ }
+ var User = request.models.User;
+ async.seq(
+ _.bind(User.get, User), // 'User.get' has signature (id, callback(err, data))
+ handleError,
+ function(user, fn) {
+ user.getCats(fn); // 'getCats' has signature (callback(err, data))
+ },
+ handleError,
+ function(cats) {
+ response.json({ status: 'ok', message: 'Cats found', data: cats });
+ }
+ )(req.session.user_id);
+ }
+});
+```
+
+---------------------------------------
+
+### 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
+);
+```
+
+---------------------------------------
+
+
+### applyEachSeries(arr, iterator, callback)
+
+The same as [`applyEach`](#applyEach) only the functions are applied in series.
+
+---------------------------------------
+
+
+### 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.
+* `concurrency` - An `integer` for determining how many `worker` functions should be
+ run in parallel.
+
+__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.
+* `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 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 bar');
+});
+
+// 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 [this animation](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) 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`.
+
+__Example__
+
+```js
+// create a cargo object with payload 2
+
+var cargo = async.cargo(function (tasks, callback) {
+ for(var i=0; i