index.js 15 KB

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