index.js 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. if (global.MONGOOSE_DRIVER_PATH) {
  6. const deprecationWarning = 'The `MONGOOSE_DRIVER_PATH` global property is ' +
  7. 'deprecated. Use `mongoose.driver.set()` instead.';
  8. const setDriver = require('util').deprecate(function() {
  9. require('./driver').set(require(global.MONGOOSE_DRIVER_PATH));
  10. }, deprecationWarning);
  11. setDriver();
  12. } else {
  13. require('./driver').set(require('./drivers/node-mongodb-native'));
  14. }
  15. const Document = require('./document');
  16. const Schema = require('./schema');
  17. const SchemaType = require('./schematype');
  18. const SchemaTypes = require('./schema/index');
  19. const VirtualType = require('./virtualtype');
  20. const STATES = require('./connectionstate');
  21. const VALID_OPTIONS = require('./validoptions');
  22. const Types = require('./types');
  23. const Query = require('./query');
  24. const Model = require('./model');
  25. const applyPlugins = require('./helpers/schema/applyPlugins');
  26. const get = require('./helpers/get');
  27. const promiseOrCallback = require('./helpers/promiseOrCallback');
  28. const legacyPluralize = require('mongoose-legacy-pluralize');
  29. const utils = require('./utils');
  30. const pkg = require('../package.json');
  31. const cast = require('./cast');
  32. const removeSubdocs = require('./plugins/removeSubdocs');
  33. const saveSubdocs = require('./plugins/saveSubdocs');
  34. const validateBeforeSave = require('./plugins/validateBeforeSave');
  35. const Aggregate = require('./aggregate');
  36. const PromiseProvider = require('./promise_provider');
  37. const shardingPlugin = require('./plugins/sharding');
  38. const defaultMongooseSymbol = Symbol.for('mongoose:default');
  39. require('./helpers/printJestWarning');
  40. /**
  41. * Mongoose constructor.
  42. *
  43. * The exports object of the `mongoose` module is an instance of this class.
  44. * Most apps will only use this one instance.
  45. *
  46. * ####Example:
  47. * const mongoose = require('mongoose');
  48. * mongoose instanceof mongoose.Mongoose; // true
  49. *
  50. * // Create a new Mongoose instance with its own `connect()`, `set()`, `model()`, etc.
  51. * const m = new mongoose.Mongoose();
  52. *
  53. * @api public
  54. * @param {Object} options see [`Mongoose#set()` docs](/docs/api/mongoose.html#mongoose_Mongoose-set)
  55. */
  56. function Mongoose(options) {
  57. this.connections = [];
  58. this.models = {};
  59. this.modelSchemas = {};
  60. // default global options
  61. this.options = Object.assign({
  62. pluralization: true
  63. }, options);
  64. const conn = this.createConnection(); // default connection
  65. conn.models = this.models;
  66. if (this.options.pluralization) {
  67. this._pluralize = legacyPluralize;
  68. }
  69. // If a user creates their own Mongoose instance, give them a separate copy
  70. // of the `Schema` constructor so they get separate custom types. (gh-6933)
  71. if (!options || !options[defaultMongooseSymbol]) {
  72. const _this = this;
  73. this.Schema = function() {
  74. this.base = _this;
  75. return Schema.apply(this, arguments);
  76. };
  77. this.Schema.prototype = Object.create(Schema.prototype);
  78. Object.assign(this.Schema, Schema);
  79. this.Schema.base = this;
  80. this.Schema.Types = Object.assign({}, Schema.Types);
  81. } else {
  82. // Hack to work around babel's strange behavior with
  83. // `import mongoose, { Schema } from 'mongoose'`. Because `Schema` is not
  84. // an own property of a Mongoose global, Schema will be undefined. See gh-5648
  85. for (const key of ['Schema', 'model']) {
  86. this[key] = Mongoose.prototype[key];
  87. }
  88. }
  89. this.Schema.prototype.base = this;
  90. Object.defineProperty(this, 'plugins', {
  91. configurable: false,
  92. enumerable: true,
  93. writable: false,
  94. value: [
  95. [saveSubdocs, { deduplicate: true }],
  96. [validateBeforeSave, { deduplicate: true }],
  97. [shardingPlugin, { deduplicate: true }],
  98. [removeSubdocs, { deduplicate: true }]
  99. ]
  100. });
  101. }
  102. Mongoose.prototype.cast = cast;
  103. /**
  104. * Expose connection states for user-land
  105. *
  106. * @memberOf Mongoose
  107. * @property STATES
  108. * @api public
  109. */
  110. Mongoose.prototype.STATES = STATES;
  111. /**
  112. * The underlying driver this Mongoose instance uses to communicate with
  113. * the database. A driver is a Mongoose-specific interface that defines functions
  114. * like `find()`.
  115. *
  116. * @memberOf Mongoose
  117. * @property driver
  118. * @api public
  119. */
  120. Mongoose.prototype.driver = require('./driver');
  121. /**
  122. * Sets mongoose options
  123. *
  124. * ####Example:
  125. *
  126. * mongoose.set('test', value) // sets the 'test' option to `value`
  127. *
  128. * mongoose.set('debug', true) // enable logging collection methods + arguments to the console
  129. *
  130. * mongoose.set('debug', function(collectionName, methodName, arg1, arg2...) {}); // use custom function to log collection methods + arguments
  131. *
  132. * Currently supported options are:
  133. * - 'debug': prints the operations mongoose sends to MongoDB to the console
  134. * - 'bufferCommands': enable/disable mongoose's buffering mechanism for all connections and models
  135. * - 'useCreateIndex': false by default. Set to `true` to make Mongoose's default index build use `createIndex()` instead of `ensureIndex()` to avoid deprecation warnings from the MongoDB driver.
  136. * - 'useFindAndModify': true by default. Set to `false` to make `findOneAndUpdate()` and `findOneAndRemove()` use native `findOneAndUpdate()` rather than `findAndModify()`.
  137. * - 'useNewUrlParser': false by default. Set to `true` to make all connections set the `useNewUrlParser` option by default
  138. * - 'useUnifiedTopology': false by default. Set to `true` to make all connections set the `useUnifiedTopology` option by default
  139. * - 'cloneSchemas': false by default. Set to `true` to `clone()` all schemas before compiling into a model.
  140. * - 'applyPluginsToDiscriminators': false by default. Set to true to apply global plugins to discriminator schemas. This typically isn't necessary because plugins are applied to the base schema and discriminators copy all middleware, methods, statics, and properties from the base schema.
  141. * - 'applyPluginsToChildSchemas': true by default. Set to false to skip applying global plugins to child schemas
  142. * - 'objectIdGetter': true by default. Mongoose adds a getter to MongoDB ObjectId's called `_id` that returns `this` for convenience with populate. Set this to false to remove the getter.
  143. * - 'runValidators': false by default. Set to true to enable [update validators](/docs/validation.html#update-validators) for all validators by default.
  144. * - 'toObject': `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toObject()`](/docs/api.html#document_Document-toObject)
  145. * - 'toJSON': `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toJSON()`](/docs/api.html#document_Document-toJSON), for determining how Mongoose documents get serialized by `JSON.stringify()`
  146. * - 'strict': true by default, may be `false`, `true`, or `'throw'`. Sets the default strict mode for schemas.
  147. * - 'selectPopulatedPaths': true by default. Set to false to opt out of Mongoose adding all fields that you `populate()` to your `select()`. The schema-level option `selectPopulatedPaths` overwrites this one.
  148. * - 'typePojoToMixed': true by default, may be `false` or `true`. Sets the default typePojoToMixed for schemas.
  149. * - 'maxTimeMS': If set, attaches [maxTimeMS](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/) to every query
  150. * - 'autoIndex': true by default. Set to false to disable automatic index creation for all models associated with this Mongoose instance.
  151. *
  152. * @param {String} key
  153. * @param {String|Function|Boolean} value
  154. * @api public
  155. */
  156. Mongoose.prototype.set = function(key, value) {
  157. const _mongoose = this instanceof Mongoose ? this : mongoose;
  158. if (VALID_OPTIONS.indexOf(key) === -1) throw new Error(`\`${key}\` is an invalid option.`);
  159. if (arguments.length === 1) {
  160. return _mongoose.options[key];
  161. }
  162. _mongoose.options[key] = value;
  163. if (key === 'objectIdGetter') {
  164. if (value) {
  165. Object.defineProperty(mongoose.Types.ObjectId.prototype, '_id', {
  166. enumerable: false,
  167. configurable: true,
  168. get: function() {
  169. return this;
  170. }
  171. });
  172. } else {
  173. delete mongoose.Types.ObjectId.prototype._id;
  174. }
  175. }
  176. return _mongoose;
  177. };
  178. /**
  179. * Gets mongoose options
  180. *
  181. * ####Example:
  182. *
  183. * mongoose.get('test') // returns the 'test' value
  184. *
  185. * @param {String} key
  186. * @method get
  187. * @api public
  188. */
  189. Mongoose.prototype.get = Mongoose.prototype.set;
  190. /**
  191. * Creates a Connection instance.
  192. *
  193. * Each `connection` instance maps to a single database. This method is helpful when mangaging multiple db connections.
  194. *
  195. *
  196. * _Options passed take precedence over options included in connection strings._
  197. *
  198. * ####Example:
  199. *
  200. * // with mongodb:// URI
  201. * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database');
  202. *
  203. * // and options
  204. * var opts = { db: { native_parser: true }}
  205. * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database', opts);
  206. *
  207. * // replica sets
  208. * db = mongoose.createConnection('mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database');
  209. *
  210. * // and options
  211. * var opts = { replset: { strategy: 'ping', rs_name: 'testSet' }}
  212. * db = mongoose.createConnection('mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/database', opts);
  213. *
  214. * // and options
  215. * var opts = { server: { auto_reconnect: false }, user: 'username', pass: 'mypassword' }
  216. * db = mongoose.createConnection('localhost', 'database', port, opts)
  217. *
  218. * // initialize now, connect later
  219. * db = mongoose.createConnection();
  220. * db.openUri('localhost', 'database', port, [opts]);
  221. *
  222. * @param {String} [uri] a mongodb:// URI
  223. * @param {Object} [options] passed down to the [MongoDB driver's `connect()` function](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html), except for 4 mongoose-specific options explained below.
  224. * @param {Boolean} [options.bufferCommands=true] Mongoose specific option. Set to false to [disable buffering](http://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection.
  225. * @param {String} [options.dbName] The name of the database we want to use. If not provided, use database name from connection string.
  226. * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility.
  227. * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility.
  228. * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
  229. * @param {Boolean} [options.useNewUrlParser=false] False by default. Set to `true` to make all connections set the `useNewUrlParser` option by default.
  230. * @param {Boolean} [options.useUnifiedTopology=false] False by default. Set to `true` to make all connections set the `useUnifiedTopology` option by default.
  231. * @param {Boolean} [options.useCreateIndex=true] Mongoose-specific option. If `true`, this connection will use [`createIndex()` instead of `ensureIndex()`](/docs/deprecations.html#ensureindex) for automatic index builds via [`Model.init()`](/docs/api.html#model_Model.init).
  232. * @param {Boolean} [options.useFindAndModify=true] True by default. Set to `false` to make `findOneAndUpdate()` and `findOneAndRemove()` use native `findOneAndUpdate()` rather than `findAndModify()`.
  233. * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections.
  234. * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above.
  235. * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html).
  236. * @param {Number} [options.poolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. By default, `poolSize` is 5. Keep in mind that, as of MongoDB 3.4, MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
  237. * @param {Number} [options.bufferMaxEntries] This option does nothing if `useUnifiedTopology` is set. The MongoDB driver also has its own buffering mechanism that kicks in when the driver is disconnected. Set this option to 0 and set `bufferCommands` to `false` on your schemas if you want your database operations to fail immediately when the driver is not connected, as opposed to waiting for reconnection.
  238. * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
  239. * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
  240. * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
  241. * @return {Connection} the created Connection object. Connections are thenable, so you can do `await mongoose.createConnection()`
  242. * @api public
  243. */
  244. Mongoose.prototype.createConnection = function(uri, options, callback) {
  245. const _mongoose = this instanceof Mongoose ? this : mongoose;
  246. const conn = new Connection(_mongoose);
  247. if (typeof options === 'function') {
  248. callback = options;
  249. options = null;
  250. }
  251. _mongoose.connections.push(conn);
  252. if (arguments.length > 0) {
  253. return conn.openUri(uri, options, callback);
  254. }
  255. return conn;
  256. };
  257. /**
  258. * Opens the default mongoose connection.
  259. *
  260. * ####Example:
  261. *
  262. * mongoose.connect('mongodb://user:pass@localhost:port/database');
  263. *
  264. * // replica sets
  265. * var uri = 'mongodb://user:pass@localhost:port,anotherhost:port,yetanother:port/mydatabase';
  266. * mongoose.connect(uri);
  267. *
  268. * // with options
  269. * mongoose.connect(uri, options);
  270. *
  271. * // optional callback that gets fired when initial connection completed
  272. * var uri = 'mongodb://nonexistent.domain:27000';
  273. * mongoose.connect(uri, function(error) {
  274. * // if error is truthy, the initial connection failed.
  275. * })
  276. *
  277. * @param {String} uri(s)
  278. * @param {Object} [options] passed down to the [MongoDB driver's `connect()` function](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html), except for 4 mongoose-specific options explained below.
  279. * @param {Boolean} [options.bufferCommands=true] Mongoose specific option. Set to false to [disable buffering](http://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection.
  280. * @param {String} [options.dbName] The name of the database we want to use. If not provided, use database name from connection string.
  281. * @param {String} [options.user] username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility.
  282. * @param {String} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility.
  283. * @param {Boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
  284. * @param {Boolean} [options.useNewUrlParser=false] False by default. Set to `true` to opt in to the MongoDB driver's new URL parser logic.
  285. * @param {Boolean} [options.useUnifiedTopology=false] False by default. Set to `true` to opt in to the MongoDB driver's replica set and sharded cluster monitoring engine.
  286. * @param {Boolean} [options.useCreateIndex=true] Mongoose-specific option. If `true`, this connection will use [`createIndex()` instead of `ensureIndex()`](/docs/deprecations.html#ensureindex) for automatic index builds via [`Model.init()`](/docs/api.html#model_Model.init).
  287. * @param {Boolean} [options.useFindAndModify=true] True by default. Set to `false` to make `findOneAndUpdate()` and `findOneAndRemove()` use native `findOneAndUpdate()` rather than `findAndModify()`.
  288. * @param {Number} [options.reconnectTries=30] If you're connected to a single server or mongos proxy (as opposed to a replica set), the MongoDB driver will try to reconnect every `reconnectInterval` milliseconds for `reconnectTries` times, and give up afterward. When the driver gives up, the mongoose connection emits a `reconnectFailed` event. This option does nothing for replica set connections.
  289. * @param {Number} [options.reconnectInterval=1000] See `reconnectTries` option above.
  290. * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html).
  291. * @param {Number} [options.poolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. By default, `poolSize` is 5. Keep in mind that, as of MongoDB 3.4, MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](http://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
  292. * @param {Number} [options.bufferMaxEntries] This option does nothing if `useUnifiedTopology` is set. The MongoDB driver also has its own buffering mechanism that kicks in when the driver is disconnected. Set this option to 0 and set `bufferCommands` to `false` on your schemas if you want your database operations to fail immediately when the driver is not connected, as opposed to waiting for reconnection.
  293. * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
  294. * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
  295. * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
  296. * @param {Function} [callback]
  297. * @see Mongoose#createConnection #index_Mongoose-createConnection
  298. * @api public
  299. * @return {Promise} resolves to `this` if connection succeeded
  300. */
  301. Mongoose.prototype.connect = function(uri, options, callback) {
  302. const _mongoose = this instanceof Mongoose ? this : mongoose;
  303. const conn = _mongoose.connection;
  304. return conn.openUri(uri, options, callback).then(() => _mongoose);
  305. };
  306. /**
  307. * Runs `.close()` on all connections in parallel.
  308. *
  309. * @param {Function} [callback] called after all connection close, or when first error occurred.
  310. * @return {Promise} resolves when all connections are closed, or rejects with the first error that occurred.
  311. * @api public
  312. */
  313. Mongoose.prototype.disconnect = function(callback) {
  314. const _mongoose = this instanceof Mongoose ? this : mongoose;
  315. return promiseOrCallback(callback, cb => {
  316. let remaining = _mongoose.connections.length;
  317. if (remaining <= 0) {
  318. return cb(null);
  319. }
  320. _mongoose.connections.forEach(conn => {
  321. conn.close(function(error) {
  322. if (error) {
  323. return cb(error);
  324. }
  325. if (!--remaining) {
  326. cb(null);
  327. }
  328. });
  329. });
  330. });
  331. };
  332. /**
  333. * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://docs.mongodb.com/manual/release-notes/3.6/#client-sessions)
  334. * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/),
  335. * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html).
  336. *
  337. * Calling `mongoose.startSession()` is equivalent to calling `mongoose.connection.startSession()`.
  338. * Sessions are scoped to a connection, so calling `mongoose.startSession()`
  339. * starts a session on the [default mongoose connection](/docs/api.html#mongoose_Mongoose-connection).
  340. *
  341. * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#startSession)
  342. * @param {Boolean} [options.causalConsistency=true] set to false to disable causal consistency
  343. * @param {Function} [callback]
  344. * @return {Promise<ClientSession>} promise that resolves to a MongoDB driver `ClientSession`
  345. * @api public
  346. */
  347. Mongoose.prototype.startSession = function() {
  348. const _mongoose = this instanceof Mongoose ? this : mongoose;
  349. return _mongoose.connection.startSession.apply(_mongoose.connection, arguments);
  350. };
  351. /**
  352. * Getter/setter around function for pluralizing collection names.
  353. *
  354. * @param {Function|null} [fn] overwrites the function used to pluralize collection names
  355. * @return {Function|null} the current function used to pluralize collection names, defaults to the legacy function from `mongoose-legacy-pluralize`.
  356. * @api public
  357. */
  358. Mongoose.prototype.pluralize = function(fn) {
  359. const _mongoose = this instanceof Mongoose ? this : mongoose;
  360. if (arguments.length > 0) {
  361. _mongoose._pluralize = fn;
  362. }
  363. return _mongoose._pluralize;
  364. };
  365. /**
  366. * Defines a model or retrieves it.
  367. *
  368. * Models defined on the `mongoose` instance are available to all connection
  369. * created by the same `mongoose` instance.
  370. *
  371. * If you call `mongoose.model()` with twice the same name but a different schema,
  372. * you will get an `OverwriteModelError`. If you call `mongoose.model()` with
  373. * the same name and same schema, you'll get the same schema back.
  374. *
  375. * ####Example:
  376. *
  377. * var mongoose = require('mongoose');
  378. *
  379. * // define an Actor model with this mongoose instance
  380. * const Schema = new Schema({ name: String });
  381. * mongoose.model('Actor', schema);
  382. *
  383. * // create a new connection
  384. * var conn = mongoose.createConnection(..);
  385. *
  386. * // create Actor model
  387. * var Actor = conn.model('Actor', schema);
  388. * conn.model('Actor') === Actor; // true
  389. * conn.model('Actor', schema) === Actor; // true, same schema
  390. * conn.model('Actor', schema, 'actors') === Actor; // true, same schema and collection name
  391. *
  392. * // This throws an `OverwriteModelError` because the schema is different.
  393. * conn.model('Actor', new Schema({ name: String }));
  394. *
  395. * _When no `collection` argument is passed, Mongoose uses the model name. If you don't like this behavior, either pass a collection name, use `mongoose.pluralize()`, or set your schemas collection name option._
  396. *
  397. * ####Example:
  398. *
  399. * var schema = new Schema({ name: String }, { collection: 'actor' });
  400. *
  401. * // or
  402. *
  403. * schema.set('collection', 'actor');
  404. *
  405. * // or
  406. *
  407. * var collectionName = 'actor'
  408. * var M = mongoose.model('Actor', schema, collectionName)
  409. *
  410. * @param {String|Function} name model name or class extending Model
  411. * @param {Schema} [schema] the schema to use.
  412. * @param {String} [collection] name (optional, inferred from model name)
  413. * @param {Boolean} [skipInit] whether to skip initialization (defaults to false)
  414. * @return {Model} The model associated with `name`. Mongoose will create the model if it doesn't already exist.
  415. * @api public
  416. */
  417. Mongoose.prototype.model = function(name, schema, collection, skipInit) {
  418. const _mongoose = this instanceof Mongoose ? this : mongoose;
  419. let model;
  420. if (typeof name === 'function') {
  421. model = name;
  422. name = model.name;
  423. if (!(model.prototype instanceof Model)) {
  424. throw new _mongoose.Error('The provided class ' + name + ' must extend Model');
  425. }
  426. }
  427. if (typeof schema === 'string') {
  428. collection = schema;
  429. schema = false;
  430. }
  431. if (utils.isObject(schema) && !(schema.instanceOfSchema)) {
  432. schema = new Schema(schema);
  433. }
  434. if (schema && !schema.instanceOfSchema) {
  435. throw new Error('The 2nd parameter to `mongoose.model()` should be a ' +
  436. 'schema or a POJO');
  437. }
  438. if (typeof collection === 'boolean') {
  439. skipInit = collection;
  440. collection = null;
  441. }
  442. // handle internal options from connection.model()
  443. let options;
  444. if (skipInit && utils.isObject(skipInit)) {
  445. options = skipInit;
  446. skipInit = true;
  447. } else {
  448. options = {};
  449. }
  450. // look up schema for the collection.
  451. if (!_mongoose.modelSchemas[name]) {
  452. if (schema) {
  453. // cache it so we only apply plugins once
  454. _mongoose.modelSchemas[name] = schema;
  455. } else {
  456. throw new mongoose.Error.MissingSchemaError(name);
  457. }
  458. }
  459. const originalSchema = schema;
  460. if (schema) {
  461. if (_mongoose.get('cloneSchemas')) {
  462. schema = schema.clone();
  463. }
  464. _mongoose._applyPlugins(schema);
  465. }
  466. let sub;
  467. // connection.model() may be passing a different schema for
  468. // an existing model name. in this case don't read from cache.
  469. if (_mongoose.models[name] && options.cache !== false) {
  470. if (originalSchema &&
  471. originalSchema.instanceOfSchema &&
  472. originalSchema !== _mongoose.models[name].schema) {
  473. throw new _mongoose.Error.OverwriteModelError(name);
  474. }
  475. if (collection && collection !== _mongoose.models[name].collection.name) {
  476. // subclass current model with alternate collection
  477. model = _mongoose.models[name];
  478. schema = model.prototype.schema;
  479. sub = model.__subclass(_mongoose.connection, schema, collection);
  480. // do not cache the sub model
  481. return sub;
  482. }
  483. return _mongoose.models[name];
  484. }
  485. // ensure a schema exists
  486. if (!schema) {
  487. schema = this.modelSchemas[name];
  488. if (!schema) {
  489. throw new mongoose.Error.MissingSchemaError(name);
  490. }
  491. }
  492. // Apply relevant "global" options to the schema
  493. if (!('pluralization' in schema.options)) {
  494. schema.options.pluralization = _mongoose.options.pluralization;
  495. }
  496. if (!collection) {
  497. collection = schema.get('collection') ||
  498. utils.toCollectionName(name, _mongoose.pluralize());
  499. }
  500. const connection = options.connection || _mongoose.connection;
  501. model = _mongoose.Model.compile(model || name, schema, collection, connection, _mongoose);
  502. if (!skipInit) {
  503. // Errors handled internally, so safe to ignore error
  504. model.init(function $modelInitNoop() {});
  505. }
  506. if (options.cache === false) {
  507. return model;
  508. }
  509. _mongoose.models[name] = model;
  510. return _mongoose.models[name];
  511. };
  512. /**
  513. * Removes the model named `name` from the default connection, if it exists.
  514. * You can use this function to clean up any models you created in your tests to
  515. * prevent OverwriteModelErrors.
  516. *
  517. * Equivalent to `mongoose.connection.deleteModel(name)`.
  518. *
  519. * ####Example:
  520. *
  521. * mongoose.model('User', new Schema({ name: String }));
  522. * console.log(mongoose.model('User')); // Model object
  523. * mongoose.deleteModel('User');
  524. * console.log(mongoose.model('User')); // undefined
  525. *
  526. * // Usually useful in a Mocha `afterEach()` hook
  527. * afterEach(function() {
  528. * mongoose.deleteModel(/.+/); // Delete every model
  529. * });
  530. *
  531. * @api public
  532. * @param {String|RegExp} name if string, the name of the model to remove. If regexp, removes all models whose name matches the regexp.
  533. * @return {Mongoose} this
  534. */
  535. Mongoose.prototype.deleteModel = function(name) {
  536. const _mongoose = this instanceof Mongoose ? this : mongoose;
  537. _mongoose.connection.deleteModel(name);
  538. return _mongoose;
  539. };
  540. /**
  541. * Returns an array of model names created on this instance of Mongoose.
  542. *
  543. * ####Note:
  544. *
  545. * _Does not include names of models created using `connection.model()`._
  546. *
  547. * @api public
  548. * @return {Array}
  549. */
  550. Mongoose.prototype.modelNames = function() {
  551. const _mongoose = this instanceof Mongoose ? this : mongoose;
  552. const names = Object.keys(_mongoose.models);
  553. return names;
  554. };
  555. /**
  556. * Applies global plugins to `schema`.
  557. *
  558. * @param {Schema} schema
  559. * @api private
  560. */
  561. Mongoose.prototype._applyPlugins = function(schema, options) {
  562. const _mongoose = this instanceof Mongoose ? this : mongoose;
  563. options = options || {};
  564. options.applyPluginsToDiscriminators = get(_mongoose,
  565. 'options.applyPluginsToDiscriminators', false);
  566. options.applyPluginsToChildSchemas = get(_mongoose,
  567. 'options.applyPluginsToChildSchemas', true);
  568. applyPlugins(schema, _mongoose.plugins, options, '$globalPluginsApplied');
  569. };
  570. /**
  571. * Declares a global plugin executed on all Schemas.
  572. *
  573. * Equivalent to calling `.plugin(fn)` on each Schema you create.
  574. *
  575. * @param {Function} fn plugin callback
  576. * @param {Object} [opts] optional options
  577. * @return {Mongoose} this
  578. * @see plugins ./plugins.html
  579. * @api public
  580. */
  581. Mongoose.prototype.plugin = function(fn, opts) {
  582. const _mongoose = this instanceof Mongoose ? this : mongoose;
  583. _mongoose.plugins.push([fn, opts]);
  584. return _mongoose;
  585. };
  586. /**
  587. * The Mongoose module's default connection. Equivalent to `mongoose.connections[0]`, see [`connections`](#mongoose_Mongoose-connections).
  588. *
  589. * ####Example:
  590. *
  591. * var mongoose = require('mongoose');
  592. * mongoose.connect(...);
  593. * mongoose.connection.on('error', cb);
  594. *
  595. * This is the connection used by default for every model created using [mongoose.model](#index_Mongoose-model).
  596. *
  597. * To create a new connection, use [`createConnection()`](#mongoose_Mongoose-createConnection).
  598. *
  599. * @memberOf Mongoose
  600. * @instance
  601. * @property {Connection} connection
  602. * @api public
  603. */
  604. Mongoose.prototype.__defineGetter__('connection', function() {
  605. return this.connections[0];
  606. });
  607. Mongoose.prototype.__defineSetter__('connection', function(v) {
  608. if (v instanceof Connection) {
  609. this.connections[0] = v;
  610. this.models = v.models;
  611. }
  612. });
  613. /**
  614. * An array containing all [connections](connections.html) associated with this
  615. * Mongoose instance. By default, there is 1 connection. Calling
  616. * [`createConnection()`](#mongoose_Mongoose-createConnection) adds a connection
  617. * to this array.
  618. *
  619. * ####Example:
  620. *
  621. * const mongoose = require('mongoose');
  622. * mongoose.connections.length; // 1, just the default connection
  623. * mongoose.connections[0] === mongoose.connection; // true
  624. *
  625. * mongoose.createConnection('mongodb://localhost:27017/test');
  626. * mongoose.connections.length; // 2
  627. *
  628. * @memberOf Mongoose
  629. * @instance
  630. * @property {Array} connections
  631. * @api public
  632. */
  633. Mongoose.prototype.connections;
  634. /*!
  635. * Driver dependent APIs
  636. */
  637. const driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
  638. /*!
  639. * Connection
  640. */
  641. const Connection = require(driver + '/connection');
  642. /*!
  643. * Collection
  644. */
  645. const Collection = require(driver + '/collection');
  646. /**
  647. * The Mongoose Aggregate constructor
  648. *
  649. * @method Aggregate
  650. * @api public
  651. */
  652. Mongoose.prototype.Aggregate = Aggregate;
  653. /**
  654. * The Mongoose Collection constructor
  655. *
  656. * @method Collection
  657. * @api public
  658. */
  659. Mongoose.prototype.Collection = Collection;
  660. /**
  661. * The Mongoose [Connection](#connection_Connection) constructor
  662. *
  663. * @memberOf Mongoose
  664. * @instance
  665. * @method Connection
  666. * @api public
  667. */
  668. Mongoose.prototype.Connection = Connection;
  669. /**
  670. * The Mongoose version
  671. *
  672. * #### Example
  673. *
  674. * console.log(mongoose.version); // '5.x.x'
  675. *
  676. * @property version
  677. * @api public
  678. */
  679. Mongoose.prototype.version = pkg.version;
  680. /**
  681. * The Mongoose constructor
  682. *
  683. * The exports of the mongoose module is an instance of this class.
  684. *
  685. * ####Example:
  686. *
  687. * var mongoose = require('mongoose');
  688. * var mongoose2 = new mongoose.Mongoose();
  689. *
  690. * @method Mongoose
  691. * @api public
  692. */
  693. Mongoose.prototype.Mongoose = Mongoose;
  694. /**
  695. * The Mongoose [Schema](#schema_Schema) constructor
  696. *
  697. * ####Example:
  698. *
  699. * var mongoose = require('mongoose');
  700. * var Schema = mongoose.Schema;
  701. * var CatSchema = new Schema(..);
  702. *
  703. * @method Schema
  704. * @api public
  705. */
  706. Mongoose.prototype.Schema = Schema;
  707. /**
  708. * The Mongoose [SchemaType](#schematype_SchemaType) constructor
  709. *
  710. * @method SchemaType
  711. * @api public
  712. */
  713. Mongoose.prototype.SchemaType = SchemaType;
  714. /**
  715. * The various Mongoose SchemaTypes.
  716. *
  717. * ####Note:
  718. *
  719. * _Alias of mongoose.Schema.Types for backwards compatibility._
  720. *
  721. * @property SchemaTypes
  722. * @see Schema.SchemaTypes #schema_Schema.Types
  723. * @api public
  724. */
  725. Mongoose.prototype.SchemaTypes = Schema.Types;
  726. /**
  727. * The Mongoose [VirtualType](#virtualtype_VirtualType) constructor
  728. *
  729. * @method VirtualType
  730. * @api public
  731. */
  732. Mongoose.prototype.VirtualType = VirtualType;
  733. /**
  734. * The various Mongoose Types.
  735. *
  736. * ####Example:
  737. *
  738. * var mongoose = require('mongoose');
  739. * var array = mongoose.Types.Array;
  740. *
  741. * ####Types:
  742. *
  743. * - [ObjectId](#types-objectid-js)
  744. * - [Buffer](#types-buffer-js)
  745. * - [SubDocument](#types-embedded-js)
  746. * - [Array](#types-array-js)
  747. * - [DocumentArray](#types-documentarray-js)
  748. *
  749. * Using this exposed access to the `ObjectId` type, we can construct ids on demand.
  750. *
  751. * var ObjectId = mongoose.Types.ObjectId;
  752. * var id1 = new ObjectId;
  753. *
  754. * @property Types
  755. * @api public
  756. */
  757. Mongoose.prototype.Types = Types;
  758. /**
  759. * The Mongoose [Query](#query_Query) constructor.
  760. *
  761. * @method Query
  762. * @api public
  763. */
  764. Mongoose.prototype.Query = Query;
  765. /**
  766. * The Mongoose [Promise](#promise_Promise) constructor.
  767. *
  768. * @memberOf Mongoose
  769. * @instance
  770. * @property Promise
  771. * @api public
  772. */
  773. Object.defineProperty(Mongoose.prototype, 'Promise', {
  774. get: function() {
  775. return PromiseProvider.get();
  776. },
  777. set: function(lib) {
  778. PromiseProvider.set(lib);
  779. }
  780. });
  781. /**
  782. * Storage layer for mongoose promises
  783. *
  784. * @method PromiseProvider
  785. * @api public
  786. */
  787. Mongoose.prototype.PromiseProvider = PromiseProvider;
  788. /**
  789. * The Mongoose [Model](#model_Model) constructor.
  790. *
  791. * @method Model
  792. * @api public
  793. */
  794. Mongoose.prototype.Model = Model;
  795. /**
  796. * The Mongoose [Document](/api/document.html) constructor.
  797. *
  798. * @method Document
  799. * @api public
  800. */
  801. Mongoose.prototype.Document = Document;
  802. /**
  803. * The Mongoose DocumentProvider constructor. Mongoose users should not have to
  804. * use this directly
  805. *
  806. * @method DocumentProvider
  807. * @api public
  808. */
  809. Mongoose.prototype.DocumentProvider = require('./document_provider');
  810. /**
  811. * The Mongoose ObjectId [SchemaType](/docs/schematypes.html). Used for
  812. * declaring paths in your schema that should be
  813. * [MongoDB ObjectIds](https://docs.mongodb.com/manual/reference/method/ObjectId/).
  814. * Do not use this to create a new ObjectId instance, use `mongoose.Types.ObjectId`
  815. * instead.
  816. *
  817. * ####Example:
  818. *
  819. * const childSchema = new Schema({ parentId: mongoose.ObjectId });
  820. *
  821. * @property ObjectId
  822. * @api public
  823. */
  824. Mongoose.prototype.ObjectId = SchemaTypes.ObjectId;
  825. /**
  826. * Returns true if Mongoose can cast the given value to an ObjectId, or
  827. * false otherwise.
  828. *
  829. * ####Example:
  830. *
  831. * mongoose.isValidObjectId(new mongoose.Types.ObjectId()); // true
  832. * mongoose.isValidObjectId('0123456789ab'); // true
  833. * mongoose.isValidObjectId(6); // false
  834. *
  835. * @method isValidObjectId
  836. * @api public
  837. */
  838. Mongoose.prototype.isValidObjectId = function(v) {
  839. if (v == null) {
  840. return true;
  841. }
  842. const base = this || mongoose;
  843. const ObjectId = base.driver.get().ObjectId;
  844. if (v instanceof ObjectId) {
  845. return true;
  846. }
  847. if (v._id != null) {
  848. if (v._id instanceof ObjectId) {
  849. return true;
  850. }
  851. if (v._id.toString instanceof Function) {
  852. v = v._id.toString();
  853. return typeof v === 'string' && (v.length === 12 || v.length === 24);
  854. }
  855. }
  856. if (v.toString instanceof Function) {
  857. v = v.toString();
  858. }
  859. if (typeof v === 'string' && (v.length === 12 || v.length === 24)) {
  860. return true;
  861. }
  862. return false;
  863. };
  864. /**
  865. * The Mongoose Decimal128 [SchemaType](/docs/schematypes.html). Used for
  866. * declaring paths in your schema that should be
  867. * [128-bit decimal floating points](http://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-decimal.html).
  868. * Do not use this to create a new Decimal128 instance, use `mongoose.Types.Decimal128`
  869. * instead.
  870. *
  871. * ####Example:
  872. *
  873. * const vehicleSchema = new Schema({ fuelLevel: mongoose.Decimal128 });
  874. *
  875. * @property Decimal128
  876. * @api public
  877. */
  878. Mongoose.prototype.Decimal128 = SchemaTypes.Decimal128;
  879. /**
  880. * The Mongoose Mixed [SchemaType](/docs/schematypes.html). Used for
  881. * declaring paths in your schema that Mongoose's change tracking, casting,
  882. * and validation should ignore.
  883. *
  884. * ####Example:
  885. *
  886. * const schema = new Schema({ arbitrary: mongoose.Mixed });
  887. *
  888. * @property Mixed
  889. * @api public
  890. */
  891. Mongoose.prototype.Mixed = SchemaTypes.Mixed;
  892. /**
  893. * The Mongoose Date [SchemaType](/docs/schematypes.html).
  894. *
  895. * ####Example:
  896. *
  897. * const schema = new Schema({ test: Date });
  898. * schema.path('test') instanceof mongoose.Date; // true
  899. *
  900. * @property Date
  901. * @api public
  902. */
  903. Mongoose.prototype.Date = SchemaTypes.Date;
  904. /**
  905. * The Mongoose Number [SchemaType](/docs/schematypes.html). Used for
  906. * declaring paths in your schema that Mongoose should cast to numbers.
  907. *
  908. * ####Example:
  909. *
  910. * const schema = new Schema({ num: mongoose.Number });
  911. * // Equivalent to:
  912. * const schema = new Schema({ num: 'number' });
  913. *
  914. * @property Number
  915. * @api public
  916. */
  917. Mongoose.prototype.Number = SchemaTypes.Number;
  918. /**
  919. * The [MongooseError](#error_MongooseError) constructor.
  920. *
  921. * @method Error
  922. * @api public
  923. */
  924. Mongoose.prototype.Error = require('./error/index');
  925. /**
  926. * Mongoose uses this function to get the current time when setting
  927. * [timestamps](/docs/guide.html#timestamps). You may stub out this function
  928. * using a tool like [Sinon](https://www.npmjs.com/package/sinon) for testing.
  929. *
  930. * @method now
  931. * @returns Date the current time
  932. * @api public
  933. */
  934. Mongoose.prototype.now = function now() { return new Date(); };
  935. /**
  936. * The Mongoose CastError constructor
  937. *
  938. * @method CastError
  939. * @param {String} type The name of the type
  940. * @param {Any} value The value that failed to cast
  941. * @param {String} path The path `a.b.c` in the doc where this cast error occurred
  942. * @param {Error} [reason] The original error that was thrown
  943. * @api public
  944. */
  945. Mongoose.prototype.CastError = require('./error/cast');
  946. /**
  947. * The constructor used for schematype options
  948. *
  949. * @method SchemaTypeOptions
  950. * @api public
  951. */
  952. Mongoose.prototype.SchemaTypeOptions = require('./options/SchemaTypeOptions');
  953. /**
  954. * The [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver Mongoose uses.
  955. *
  956. * @property mongo
  957. * @api public
  958. */
  959. Mongoose.prototype.mongo = require('mongodb');
  960. /**
  961. * The [mquery](https://github.com/aheckmann/mquery) query builder Mongoose uses.
  962. *
  963. * @property mquery
  964. * @api public
  965. */
  966. Mongoose.prototype.mquery = require('mquery');
  967. /*!
  968. * The exports object is an instance of Mongoose.
  969. *
  970. * @api public
  971. */
  972. const mongoose = module.exports = exports = new Mongoose({
  973. [defaultMongooseSymbol]: true
  974. });