UNPKG

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