You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

624 lines
15 KiB

  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. var Schema = require('./schema')
  6. , SchemaType = require('./schematype')
  7. , VirtualType = require('./virtualtype')
  8. , SchemaTypes = Schema.Types
  9. , SchemaDefaults = require('./schemadefault')
  10. , Types = require('./types')
  11. , Query = require('./query')
  12. , Promise = require('./promise')
  13. , Model = require('./model')
  14. , Document = require('./document')
  15. , utils = require('./utils')
  16. , format = utils.toCollectionName
  17. , mongodb = require('mongodb')
  18. , pkg = require('../package.json')
  19. /*!
  20. * Warn users if they are running an unstable release.
  21. *
  22. * Disable the warning by setting the MONGOOSE_DISABLE_STABILITY_WARNING
  23. * environment variable.
  24. */
  25. if (pkg.publishConfig && 'unstable' == pkg.publishConfig.tag) {
  26. if (!process.env.MONGOOSE_DISABLE_STABILITY_WARNING) {
  27. console.log('\u001b[33m');
  28. console.log('##############################################################');
  29. console.log('#');
  30. console.log('# !!! MONGOOSE WARNING !!!');
  31. console.log('#');
  32. console.log('# This is an UNSTABLE release of Mongoose.');
  33. console.log('# Unstable releases are available for preview/testing only.');
  34. console.log('# DO NOT run this in production.');
  35. console.log('#');
  36. console.log('##############################################################');
  37. console.log('\u001b[0m');
  38. }
  39. }
  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. * @api public
  47. */
  48. function Mongoose () {
  49. this.connections = [];
  50. this.plugins = [];
  51. this.models = {};
  52. this.modelSchemas = {};
  53. this.options = {};
  54. this.createConnection(); // default connection
  55. };
  56. /**
  57. * Sets mongoose options
  58. *
  59. * ####Example:
  60. *
  61. * mongoose.set('test', value) // sets the 'test' option to `value`
  62. *
  63. * mongoose.set('debug', true) // enable logging collection methods + arguments to the console
  64. *
  65. * @param {String} key
  66. * @param {String} value
  67. * @api public
  68. */
  69. Mongoose.prototype.set = function (key, value) {
  70. if (arguments.length == 1)
  71. return this.options[key];
  72. this.options[key] = value;
  73. return this;
  74. };
  75. /**
  76. * Gets mongoose options
  77. *
  78. * ####Example:
  79. *
  80. * mongoose.get('test') // returns the 'test' value
  81. *
  82. * @param {String} key
  83. * @method get
  84. * @api public
  85. */
  86. Mongoose.prototype.get = Mongoose.prototype.set;
  87. /*!
  88. * ReplSet connection string check.
  89. */
  90. var rgxReplSet = /^.+,.+$/;
  91. /**
  92. * Creates a Connection instance.
  93. *
  94. * Each `connection` instance maps to a single database. This method is helpful when mangaging multiple db connections.
  95. *
  96. * 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.
  97. *
  98. * _Options passed take precedence over options included in connection strings._
  99. *
  100. * ####Example:
  101. *
  102. * // with mongodb:// URI
  103. * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database');
  104. *
  105. * // and options
  106. * var opts = { db: { native_parser: true }}
  107. * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database', opts);
  108. *
  109. * // replica sets
  110. * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database,mongodb://anotherhost:port,mongodb://yetanother:port');
  111. *
  112. * // and options
  113. * var opts = { replset: { strategy: 'ping', rs_name: 'testSet' }}
  114. * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database,mongodb://anotherhost:port,mongodb://yetanother:port', opts);
  115. *
  116. * // with [host, database_name[, port] signature
  117. * db = mongoose.createConnection('localhost', 'database', port)
  118. *
  119. * // and options
  120. * var opts = { server: { auto_reconnect: false }, user: 'username', pass: 'mypassword' }
  121. * db = mongoose.createConnection('localhost', 'database', port, opts)
  122. *
  123. * // initialize now, connect later
  124. * db = mongoose.createConnection();
  125. * db.open('localhost', 'database', port, [opts]);
  126. *
  127. * @param {String} [uri] a mongodb:// URI
  128. * @param {Object} [options] options to pass to the driver
  129. * @see Connection#open #connection_Connection-open
  130. * @see Connection#openSet #connection_Connection-openSet
  131. * @return {Connection} the created Connection object
  132. * @api public
  133. */
  134. Mongoose.prototype.createConnection = function () {
  135. var conn = new Connection(this);
  136. this.connections.push(conn);
  137. if (arguments.length) {
  138. if (rgxReplSet.test(arguments[0])) {
  139. conn.openSet.apply(conn, arguments);
  140. } else {
  141. conn.open.apply(conn, arguments);
  142. }
  143. }
  144. return conn;
  145. };
  146. /**
  147. * Opens the default mongoose connection.
  148. *
  149. * If arguments are passed, they are proxied to either [Connection#open](#connection_Connection-open) or [Connection#openSet](#connection_Connection-openSet) appropriately.
  150. *
  151. * _Options passed take precedence over options included in connection strings._
  152. *
  153. * ####Example:
  154. *
  155. * mongoose.connect('mongodb://user:pass@localhost:port/database');
  156. *
  157. * // replica sets
  158. * var uri = 'mongodb://user:pass@localhost:port/database,mongodb://anotherhost:port,mongodb://yetanother:port';
  159. * mongoose.connect(uri);
  160. *
  161. * // with options
  162. * mongoose.connect(uri, options);
  163. *
  164. * // connecting to multiple mongos
  165. * var uri = 'mongodb://hostA:27501,hostB:27501';
  166. * var opts = { mongos: true };
  167. * mongoose.connect(uri, opts);
  168. *
  169. * @param {String} uri(s)
  170. * @param {Object} [options]
  171. * @param {Function} [callback]
  172. * @see Mongoose#createConnection #index_Mongoose-createConnection
  173. * @api public
  174. * @return {Mongoose} this
  175. */
  176. Mongoose.prototype.connect = function () {
  177. var conn = this.connection;
  178. if (rgxReplSet.test(arguments[0])) {
  179. conn.openSet.apply(conn, arguments);
  180. } else {
  181. conn.open.apply(conn, arguments);
  182. }
  183. return this;
  184. };
  185. /**
  186. * Disconnects all connections.
  187. *
  188. * @param {Function} [fn] called after all connection close.
  189. * @return {Mongoose} this
  190. * @api public
  191. */
  192. Mongoose.prototype.disconnect = function (fn) {
  193. var count = this.connections.length
  194. , error
  195. this.connections.forEach(function(conn){
  196. conn.close(function(err){
  197. if (error) return;
  198. if (err) {
  199. error = err;
  200. if (fn) return fn(err);
  201. throw err;
  202. }
  203. if (fn)
  204. --count || fn();
  205. });
  206. });
  207. return this;
  208. };
  209. /**
  210. * Defines a model or retrieves it.
  211. *
  212. * Models defined on the `mongoose` instance are available to all connection created by the same `mongoose` instance.
  213. *
  214. * ####Example:
  215. *
  216. * var mongoose = require('mongoose');
  217. *
  218. * // define an Actor model with this mongoose instance
  219. * mongoose.model('Actor', new Schema({ name: String }));
  220. *
  221. * // create a new connection
  222. * var conn = mongoose.createConnection(..);
  223. *
  224. * // retrieve the Actor model
  225. * var Actor = conn.model('Actor');
  226. *
  227. * _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._
  228. *
  229. * ####Example:
  230. *
  231. * var schema = new Schema({ name: String }, { collection: 'actor' });
  232. *
  233. * // or
  234. *
  235. * schema.set('collection', 'actor');
  236. *
  237. * // or
  238. *
  239. * var collectionName = 'actor'
  240. * var M = mongoose.model('Actor', schema, collectionName)
  241. *
  242. * @param {String} name model name
  243. * @param {Schema} [schema]
  244. * @param {String} [collection] name (optional, induced from model name)
  245. * @param {Boolean} [skipInit] whether to skip initialization (defaults to false)
  246. * @api public
  247. */
  248. Mongoose.prototype.model = function (name, schema, collection, skipInit) {
  249. if ('string' == typeof schema) {
  250. collection = schema;
  251. schema = false;
  252. }
  253. if (utils.isObject(schema) && !(schema instanceof Schema)) {
  254. schema = new Schema(schema);
  255. }
  256. if ('boolean' === typeof collection) {
  257. skipInit = collection;
  258. collection = null;
  259. }
  260. // handle internal options from connection.model()
  261. var options;
  262. if (skipInit && utils.isObject(skipInit)) {
  263. options = skipInit;
  264. skipInit = true;
  265. } else {
  266. options = {};
  267. }
  268. // look up schema for the collection. this might be a
  269. // default schema like system.indexes stored in SchemaDefaults.
  270. if (!this.modelSchemas[name]) {
  271. if (!schema && name in SchemaDefaults) {
  272. schema = SchemaDefaults[name];
  273. }
  274. if (schema) {
  275. // cache it so we only apply plugins once
  276. this.modelSchemas[name] = schema;
  277. this._applyPlugins(schema);
  278. } else {
  279. throw new mongoose.Error.MissingSchemaError(name);
  280. }
  281. }
  282. var model;
  283. var sub;
  284. // connection.model() may be passing a different schema for
  285. // an existing model name. in this case don't read from cache.
  286. if (this.models[name] && false !== options.cache) {
  287. if (schema instanceof Schema && schema != this.models[name].schema) {
  288. throw new mongoose.Error.OverwriteModelError(name);
  289. }
  290. if (collection) {
  291. // subclass current model with alternate collection
  292. model = this.models[name];
  293. schema = model.prototype.schema;
  294. sub = model.__subclass(this.connection, schema, collection);
  295. // do not cache the sub model
  296. return sub;
  297. }
  298. return this.models[name];
  299. }
  300. // ensure a schema exists
  301. if (!schema) {
  302. schema = this.modelSchemas[name];
  303. if (!schema) {
  304. throw new mongoose.Error.MissingSchemaError(name);
  305. }
  306. }
  307. if (!collection) {
  308. collection = schema.get('collection') || format(name);
  309. }
  310. var connection = options.connection || this.connection;
  311. model = Model.compile(name, schema, collection, connection, this);
  312. if (!skipInit) {
  313. model.init();
  314. }
  315. if (false === options.cache) {
  316. return model;
  317. }
  318. return this.models[name] = model;
  319. }
  320. /**
  321. * Returns an array of model names created on this instance of Mongoose.
  322. *
  323. * ####Note:
  324. *
  325. * _Does not include names of models created using `connection.model()`._
  326. *
  327. * @api public
  328. * @return {Array}
  329. */
  330. Mongoose.prototype.modelNames = function () {
  331. var names = Object.keys(this.models);
  332. return names;
  333. }
  334. /**
  335. * Applies global plugins to `schema`.
  336. *
  337. * @param {Schema} schema
  338. * @api private
  339. */
  340. Mongoose.prototype._applyPlugins = function (schema) {
  341. for (var i = 0, l = this.plugins.length; i < l; i++) {
  342. schema.plugin(this.plugins[i][0], this.plugins[i][1]);
  343. }
  344. }
  345. /**
  346. * Declares a global plugin executed on all Schemas.
  347. *
  348. * Equivalent to calling `.plugin(fn)` on each Schema you create.
  349. *
  350. * @param {Function} fn plugin callback
  351. * @param {Object} [opts] optional options
  352. * @return {Mongoose} this
  353. * @see plugins ./plugins.html
  354. * @api public
  355. */
  356. Mongoose.prototype.plugin = function (fn, opts) {
  357. this.plugins.push([fn, opts]);
  358. return this;
  359. };
  360. /**
  361. * The default connection of the mongoose module.
  362. *
  363. * ####Example:
  364. *
  365. * var mongoose = require('mongoose');
  366. * mongoose.connect(...);
  367. * mongoose.connection.on('error', cb);
  368. *
  369. * This is the connection used by default for every model created using [mongoose.model](#index_Mongoose-model).
  370. *
  371. * @property connection
  372. * @return {Connection}
  373. * @api public
  374. */
  375. Mongoose.prototype.__defineGetter__('connection', function(){
  376. return this.connections[0];
  377. });
  378. /*!
  379. * Driver depentend APIs
  380. */
  381. var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native';
  382. /*!
  383. * Connection
  384. */
  385. var Connection = require(driver + '/connection');
  386. /*!
  387. * Collection
  388. */
  389. var Collection = require(driver + '/collection');
  390. /**
  391. * The Mongoose Collection constructor
  392. *
  393. * @method Collection
  394. * @api public
  395. */
  396. Mongoose.prototype.Collection = Collection;
  397. /**
  398. * The Mongoose [Connection](#connection_Connection) constructor
  399. *
  400. * @method Connection
  401. * @api public
  402. */
  403. Mongoose.prototype.Connection = Connection;
  404. /**
  405. * The Mongoose version
  406. *
  407. * @property version
  408. * @api public
  409. */
  410. Mongoose.prototype.version = pkg.version;
  411. /**
  412. * The Mongoose constructor
  413. *
  414. * The exports of the mongoose module is an instance of this class.
  415. *
  416. * ####Example:
  417. *
  418. * var mongoose = require('mongoose');
  419. * var mongoose2 = new mongoose.Mongoose();
  420. *
  421. * @method Mongoose
  422. * @api public
  423. */
  424. Mongoose.prototype.Mongoose = Mongoose;
  425. /**
  426. * The Mongoose [Schema](#schema_Schema) constructor
  427. *
  428. * ####Example:
  429. *
  430. * var mongoose = require('mongoose');
  431. * var Schema = mongoose.Schema;
  432. * var CatSchema = new Schema(..);
  433. *
  434. * @method Schema
  435. * @api public
  436. */
  437. Mongoose.prototype.Schema = Schema;
  438. /**
  439. * The Mongoose [SchemaType](#schematype_SchemaType) constructor
  440. *
  441. * @method SchemaType
  442. * @api public
  443. */
  444. Mongoose.prototype.SchemaType = SchemaType;
  445. /**
  446. * The various Mongoose SchemaTypes.
  447. *
  448. * ####Note:
  449. *
  450. * _Alias of mongoose.Schema.Types for backwards compatibility._
  451. *
  452. * @property SchemaTypes
  453. * @see Schema.SchemaTypes #schema_Schema.Types
  454. * @api public
  455. */
  456. Mongoose.prototype.SchemaTypes = Schema.Types;
  457. /**
  458. * The Mongoose [VirtualType](#virtualtype_VirtualType) constructor
  459. *
  460. * @method VirtualType
  461. * @api public
  462. */
  463. Mongoose.prototype.VirtualType = VirtualType;
  464. /**
  465. * The various Mongoose Types.
  466. *
  467. * ####Example:
  468. *
  469. * var mongoose = require('mongoose');
  470. * var array = mongoose.Types.Array;
  471. *
  472. * ####Types:
  473. *
  474. * - [ObjectId](#types-objectid-js)
  475. * - [Buffer](#types-buffer-js)
  476. * - [SubDocument](#types-embedded-js)
  477. * - [Array](#types-array-js)
  478. * - [DocumentArray](#types-documentarray-js)
  479. *
  480. * Using this exposed access to the `ObjectId` type, we can construct ids on demand.
  481. *
  482. * var ObjectId = mongoose.Types.ObjectId;
  483. * var id1 = new ObjectId;
  484. *
  485. * @property Types
  486. * @api public
  487. */
  488. Mongoose.prototype.Types = Types;
  489. /**
  490. * The Mongoose [Query](#query_Query) constructor.
  491. *
  492. * @method Query
  493. * @api public
  494. */
  495. Mongoose.prototype.Query = Query;
  496. /**
  497. * The Mongoose [Promise](#promise_Promise) constructor.
  498. *
  499. * @method Promise
  500. * @api public
  501. */
  502. Mongoose.prototype.Promise = Promise;
  503. /**
  504. * The Mongoose [Model](#model_Model) constructor.
  505. *
  506. * @method Model
  507. * @api public
  508. */
  509. Mongoose.prototype.Model = Model;
  510. /**
  511. * The Mongoose [Document](#document-js) constructor.
  512. *
  513. * @method Document
  514. * @api public
  515. */
  516. Mongoose.prototype.Document = Document;
  517. /**
  518. * The [MongooseError](#error_MongooseError) constructor.
  519. *
  520. * @method Error
  521. * @api public
  522. */
  523. Mongoose.prototype.Error = require('./error');
  524. /**
  525. * The [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver Mongoose uses.
  526. *
  527. * @property mongo
  528. * @api public
  529. */
  530. Mongoose.prototype.mongo = require('mongodb');
  531. /*!
  532. * The exports object is an instance of Mongoose.
  533. *
  534. * @api public
  535. */
  536. var mongoose = module.exports = exports = new Mongoose;