123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818 |
- 'use strict';
- /*!
- * Module dependencies.
- */
- 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.
- *
- * The exports object of the `mongoose` module is an instance of this class.
- * Most apps will only use this one instance.
- *
- * @api public
- */
- function Mongoose() {
- this.connections = [];
- this.plugins = [];
- this.models = {};
- this.modelSchemas = {};
- // 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
- *
- * ####Example:
- *
- * mongoose.set('test', value) // sets the 'test' option to `value`
- *
- * mongoose.set('debug', true) // enable logging collection methods + arguments to the console
- *
- * mongoose.set('debug', function(collectionName, methodName, arg1, arg2...) {}); // use custom function to log collection methods + arguments
- *
- * @param {String} key
- * @param {String|Function} value
- * @api public
- */
- Mongoose.prototype.set = function(key, value) {
- if (arguments.length === 1) {
- return this.options[key];
- }
- this.options[key] = value;
- return this;
- };
- Mongoose.prototype.set.$hasSideEffects = true;
- /**
- * Gets mongoose options
- *
- * ####Example:
- *
- * mongoose.get('test') // returns the 'test' value
- *
- * @param {String} key
- * @method get
- * @api public
- */
- Mongoose.prototype.get = Mongoose.prototype.set;
- /*!
- * ReplSet connection string check.
- */
- 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. _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._
- *
- * ####Example:
- *
- * // with mongodb:// URI
- * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database');
- *
- * // and options
- * var opts = { db: { native_parser: true }}
- * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database', opts);
- *
- * // replica sets
- * 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,anotherhost:port,yetanother:port/database', opts);
- *
- * // with [host, database_name[, port] signature
- * db = mongoose.createConnection('localhost', 'database', port)
- *
- * // and options
- * var opts = { server: { auto_reconnect: false }, user: 'username', pass: 'mypassword' }
- * db = mongoose.createConnection('localhost', 'database', port, opts)
- *
- * // initialize now, connect later
- * db = mongoose.createConnection();
- * db.open('localhost', 'database', port, [opts]);
- *
- * @param {String} [uri] a mongodb:// URI
- * @param {Object} [options] options to pass to the driver
- * @param {Object} [options.config] mongoose-specific options
- * @param {Boolean} [options.config.autoIndex] set to false to disable automatic index creation for all models associated with this connection.
- * @see Connection#open #connection_Connection-open
- * @see Connection#openSet #connection_Connection-openSet
- * @return {Connection} the created Connection object
- * @api public
- */
- Mongoose.prototype.createConnection = function(uri, options) {
- var conn = new Connection(this);
- this.connections.push(conn);
- var rsOption = options && (options.replset || options.replSet);
- if (arguments.length) {
- if (rgxReplSet.test(arguments[0]) || checkReplicaSetInUri(arguments[0])) {
- conn.openSet.apply(conn, arguments).catch(function() {});
- } else if (rsOption &&
- (rsOption.replicaSet || rsOption.rs_name)) {
- conn.openSet.apply(conn, arguments).catch(function() {});
- } else {
- conn.open.apply(conn, arguments).catch(function() {});
- }
- }
- return conn;
- };
- Mongoose.prototype.createConnection.$hasSideEffects = true;
- /**
- * Opens the default mongoose connection.
- *
- * If arguments are passed, they are proxied to either
- * [Connection#open](#connection_Connection-open) or
- * [Connection#openSet](#connection_Connection-openSet) appropriately.
- *
- * _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);
- *
- * // optional callback that gets fired when initial connection completed
- * var uri = 'mongodb://nonexistent.domain:27000';
- * mongoose.connect(uri, function(error) {
- * // if error is truthy, the initial connection failed.
- * })
- *
- * @param {String} uri(s)
- * @param {Object} [options]
- * @param {Function} [callback]
- * @see Mongoose#createConnection #index_Mongoose-createConnection
- * @api public
- * @return {MongooseThenable} pseudo-promise wrapper around this
- */
- Mongoose.prototype.connect = function() {
- var conn = this.connection;
- if (rgxReplSet.test(arguments[0]) || checkReplicaSetInUri(arguments[0])) {
- return new MongooseThenable(this, conn.openSet.apply(conn, arguments));
- }
- return new MongooseThenable(this, conn.open.apply(conn, arguments));
- };
- Mongoose.prototype.connect.$hasSideEffects = true;
- /**
- * Disconnects all connections.
- *
- * @param {Function} [fn] called after all connection close.
- * @return {MongooseThenable} pseudo-promise wrapper around this
- * @api public
- */
- Mongoose.prototype.disconnect = function(fn) {
- var error;
- this.connections.forEach(function(conn) {
- conn.close(function(err) {
- if (error) {
- return;
- }
- if (err) {
- error = err;
- }
- });
- });
- var Promise = PromiseProvider.get();
- return new MongooseThenable(this, new Promise.ES6(function(resolve, reject) {
- fn && fn(error);
- if (error) {
- reject(error);
- return;
- }
- resolve();
- }));
- };
- Mongoose.prototype.disconnect.$hasSideEffects = true;
- /**
- * Defines a model or retrieves it.
- *
- * Models defined on the `mongoose` instance are available to all connection created by the same `mongoose` instance.
- *
- * ####Example:
- *
- * var mongoose = require('mongoose');
- *
- * // define an Actor model with this mongoose instance
- * mongoose.model('Actor', new Schema({ name: String }));
- *
- * // create a new connection
- * var conn = mongoose.createConnection(..);
- *
- * // retrieve the Actor model
- * var Actor = conn.model('Actor');
- *
- * _When no `collection` argument is passed, Mongoose produces a collection name by passing the model `name` to the [utils.toCollectionName](#utils_exports.toCollectionName) method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option._
- *
- * ####Example:
- *
- * var schema = new Schema({ name: String }, { collection: 'actor' });
- *
- * // or
- *
- * schema.set('collection', 'actor');
- *
- * // or
- *
- * var collectionName = 'actor'
- * var M = mongoose.model('Actor', schema, collectionName)
- *
- * @param {String|Function} name model name or class extending Model
- * @param {Schema} [schema]
- * @param {String} [collection] name (optional, inferred from model name)
- * @param {Boolean} [skipInit] whether to skip initialization (defaults to false)
- * @api public
- */
- Mongoose.prototype.model = function(name, schema, collection, skipInit) {
- var model;
- if (typeof name === 'function') {
- model = name;
- name = model.name;
- if (!(model.prototype instanceof Model)) {
- throw new mongoose.Error('The provided class ' + name + ' must extend Model');
- }
- }
- if (typeof schema === 'string') {
- collection = schema;
- schema = false;
- }
- if (utils.isObject(schema) && !(schema.instanceOfSchema)) {
- schema = new Schema(schema);
- }
- if (schema && !schema.instanceOfSchema) {
- throw new Error('The 2nd parameter to `mongoose.model()` should be a ' +
- 'schema or a POJO');
- }
- if (typeof collection === 'boolean') {
- skipInit = collection;
- collection = null;
- }
- // handle internal options from connection.model()
- var options;
- if (skipInit && utils.isObject(skipInit)) {
- options = skipInit;
- skipInit = true;
- } else {
- options = {};
- }
- // look up schema for the collection.
- if (!this.modelSchemas[name]) {
- if (schema) {
- // cache it so we only apply plugins once
- this.modelSchemas[name] = schema;
- } else {
- throw new mongoose.Error.MissingSchemaError(name);
- }
- }
- if (schema) {
- this._applyPlugins(schema);
- }
- var sub;
- // connection.model() may be passing a different schema for
- // an existing model name. in this case don't read from cache.
- if (this.models[name] && options.cache !== false) {
- if (schema && schema.instanceOfSchema && schema !== this.models[name].schema) {
- throw new mongoose.Error.OverwriteModelError(name);
- }
- if (collection) {
- // subclass current model with alternate collection
- model = this.models[name];
- schema = model.prototype.schema;
- sub = model.__subclass(this.connection, schema, collection);
- // do not cache the sub model
- return sub;
- }
- return this.models[name];
- }
- // ensure a schema exists
- if (!schema) {
- schema = this.modelSchemas[name];
- if (!schema) {
- throw new mongoose.Error.MissingSchemaError(name);
- }
- }
- // 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, schema.options);
- }
- var connection = options.connection || this.connection;
- model = this.Model.compile(model || name, schema, collection, connection, this);
- if (!skipInit) {
- model.init();
- }
- if (options.cache === false) {
- return model;
- }
- this.models[name] = model;
- return this.models[name];
- };
- Mongoose.prototype.model.$hasSideEffects = true;
- /**
- * Returns an array of model names created on this instance of Mongoose.
- *
- * ####Note:
- *
- * _Does not include names of models created using `connection.model()`._
- *
- * @api public
- * @return {Array}
- */
- Mongoose.prototype.modelNames = function() {
- var names = Object.keys(this.models);
- return names;
- };
- Mongoose.prototype.modelNames.$hasSideEffects = true;
- /**
- * Applies global plugins to `schema`.
- *
- * @param {Schema} schema
- * @api private
- */
- Mongoose.prototype._applyPlugins = function(schema) {
- if (schema.$globalPluginsApplied) {
- return;
- }
- var i;
- var len;
- for (i = 0, len = this.plugins.length; i < len; ++i) {
- schema.plugin(this.plugins[i][0], this.plugins[i][1]);
- }
- schema.$globalPluginsApplied = true;
- for (i = 0, len = schema.childSchemas.length; i < len; ++i) {
- this._applyPlugins(schema.childSchemas[i]);
- }
- };
- Mongoose.prototype._applyPlugins.$hasSideEffects = true;
- /**
- * Declares a global plugin executed on all Schemas.
- *
- * Equivalent to calling `.plugin(fn)` on each Schema you create.
- *
- * @param {Function} fn plugin callback
- * @param {Object} [opts] optional options
- * @return {Mongoose} this
- * @see plugins ./plugins.html
- * @api public
- */
- Mongoose.prototype.plugin = function(fn, opts) {
- this.plugins.push([fn, opts]);
- return this;
- };
- Mongoose.prototype.plugin.$hasSideEffects = true;
- /**
- * The default connection of the mongoose module.
- *
- * ####Example:
- *
- * var mongoose = require('mongoose');
- * mongoose.connect(...);
- * mongoose.connection.on('error', cb);
- *
- * This is the connection used by default for every model created using [mongoose.model](#index_Mongoose-model).
- *
- * @property connection
- * @return {Connection}
- * @api public
- */
- Mongoose.prototype.__defineGetter__('connection', function() {
- return this.connections[0];
- });
- Mongoose.prototype.__defineSetter__('connection', function(v) {
- this.connections[0] = v;
- });
- /*!
- * Driver depentend APIs
- */
- var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
- /*!
- * Connection
- */
- var Connection = require(driver + '/connection');
- /*!
- * Collection
- */
- var Collection = require(driver + '/collection');
- /**
- * The Mongoose Aggregate constructor
- *
- * @method Aggregate
- * @api public
- */
- Mongoose.prototype.Aggregate = Aggregate;
- /**
- * The Mongoose Collection constructor
- *
- * @method Collection
- * @api public
- */
- Mongoose.prototype.Collection = Collection;
- /**
- * The Mongoose [Connection](#connection_Connection) constructor
- *
- * @method Connection
- * @api public
- */
- Mongoose.prototype.Connection = Connection;
- /**
- * The Mongoose version
- *
- * @property version
- * @api public
- */
- Mongoose.prototype.version = pkg.version;
- /**
- * The Mongoose constructor
- *
- * The exports of the mongoose module is an instance of this class.
- *
- * ####Example:
- *
- * var mongoose = require('mongoose');
- * var mongoose2 = new mongoose.Mongoose();
- *
- * @method Mongoose
- * @api public
- */
- Mongoose.prototype.Mongoose = Mongoose;
- /**
- * The Mongoose [Schema](#schema_Schema) constructor
- *
- * ####Example:
- *
- * var mongoose = require('mongoose');
- * var Schema = mongoose.Schema;
- * var CatSchema = new Schema(..);
- *
- * @method Schema
- * @api public
- */
- Mongoose.prototype.Schema = Schema;
- /**
- * The Mongoose [SchemaType](#schematype_SchemaType) constructor
- *
- * @method SchemaType
- * @api public
- */
- Mongoose.prototype.SchemaType = SchemaType;
- /**
- * The various Mongoose SchemaTypes.
- *
- * ####Note:
- *
- * _Alias of mongoose.Schema.Types for backwards compatibility._
- *
- * @property SchemaTypes
- * @see Schema.SchemaTypes #schema_Schema.Types
- * @api public
- */
- Mongoose.prototype.SchemaTypes = Schema.Types;
- /**
- * The Mongoose [VirtualType](#virtualtype_VirtualType) constructor
- *
- * @method VirtualType
- * @api public
- */
- Mongoose.prototype.VirtualType = VirtualType;
- /**
- * 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
- */
- Mongoose.prototype.Types = Types;
- /**
- * The Mongoose [Query](#query_Query) constructor.
- *
- * @method Query
- * @api public
- */
- Mongoose.prototype.Query = Query;
- /**
- * The Mongoose [Promise](#promise_Promise) constructor.
- *
- * @method Promise
- * @api public
- */
- 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.
- *
- * @method Model
- * @api public
- */
- Mongoose.prototype.Model = Model;
- /**
- * The Mongoose [Document](#document-js) constructor.
- *
- * @method Document
- * @api public
- */
- Mongoose.prototype.Document = Document;
- /**
- * The Mongoose DocumentProvider constructor.
- *
- * @method DocumentProvider
- * @api public
- */
- Mongoose.prototype.DocumentProvider = require('./document_provider');
- /**
- * The [MongooseError](#error_MongooseError) constructor.
- *
- * @method Error
- * @api public
- */
- Mongoose.prototype.Error = require('./error');
- /**
- * The Mongoose CastError constructor
- *
- * @method CastError
- * @param {String} type The name of the type
- * @param {Any} value The value that failed to cast
- * @param {String} path The path `a.b.c` in the doc where this cast error occurred
- * @param {Error} [reason] The original error that was thrown
- * @api public
- */
- Mongoose.prototype.CastError = require('./error/cast');
- /**
- * The [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver Mongoose uses.
- *
- * @property mongo
- * @api public
- */
- Mongoose.prototype.mongo = require('mongodb');
- /**
- * The [mquery](https://github.com/aheckmann/mquery) query builder Mongoose uses.
- *
- * @property mquery
- * @api public
- */
- Mongoose.prototype.mquery = require('mquery');
- /**
- * Wraps the given Mongoose instance into a thenable (pseudo-promise). This
- * is so `connect()` and `disconnect()` can return a thenable while maintaining
- * backwards compatibility.
- *
- * @api private
- */
- function MongooseThenable(mongoose, promise) {
- var _this = this;
- for (var key in mongoose) {
- if (typeof mongoose[key] === 'function' && mongoose[key].$hasSideEffects) {
- (function(key) {
- _this[key] = function() {
- return mongoose[key].apply(mongoose, arguments);
- };
- })(key);
- } else if (['connection', 'connections'].indexOf(key) !== -1) {
- _this[key] = mongoose[key];
- }
- }
- this.$opPromise = promise;
- }
- MongooseThenable.prototype = new Mongoose;
- /**
- * Ability to use mongoose object as a pseudo-promise so `.connect().then()`
- * and `.disconnect().then()` are viable.
- *
- * @param {Function} onFulfilled
- * @param {Function} onRejected
- * @return {Promise}
- * @api private
- */
- MongooseThenable.prototype.then = function(onFulfilled, onRejected) {
- var Promise = PromiseProvider.get();
- if (!this.$opPromise) {
- return new Promise.ES6(function(resolve, reject) {
- reject(new Error('Can only call `.then()` if connect() or disconnect() ' +
- 'has been called'));
- }).then(onFulfilled, onRejected);
- }
- this.$opPromise.$hasHandler = true;
- return this.$opPromise.then(onFulfilled, onRejected);
- };
- /**
- * Ability to use mongoose object as a pseudo-promise so `.connect().then()`
- * and `.disconnect().then()` are viable.
- *
- * @param {Function} onFulfilled
- * @param {Function} onRejected
- * @return {Promise}
- * @api private
- */
- MongooseThenable.prototype.catch = function(onRejected) {
- return this.then(null, onRejected);
- };
- /*!
- * The exports object is an instance of Mongoose.
- *
- * @api public
- */
- var mongoose = module.exports = exports = new Mongoose;
|