PageRenderTime 62ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/node_modules/mongoose/node_modules/mongodb/lib/db.js

https://bitbucket.org/coleman333/smartsite
JavaScript | 1809 lines | 1008 code | 217 blank | 584 comment | 296 complexity | 335236f3088dbb5debebe08971a07a1a MD5 | raw file
Possible License(s): Apache-2.0, JSON, BSD-3-Clause, 0BSD, MIT, CC-BY-SA-3.0
  1. 'use strict';
  2. var EventEmitter = require('events').EventEmitter,
  3. inherits = require('util').inherits,
  4. getSingleProperty = require('./utils').getSingleProperty,
  5. shallowClone = require('./utils').shallowClone,
  6. parseIndexOptions = require('./utils').parseIndexOptions,
  7. debugOptions = require('./utils').debugOptions,
  8. CommandCursor = require('./command_cursor'),
  9. handleCallback = require('./utils').handleCallback,
  10. filterOptions = require('./utils').filterOptions,
  11. toError = require('./utils').toError,
  12. ReadPreference = require('mongodb-core').ReadPreference,
  13. f = require('util').format,
  14. Admin = require('./admin'),
  15. Code = require('mongodb-core').BSON.Code,
  16. MongoError = require('mongodb-core').MongoError,
  17. ObjectID = require('mongodb-core').ObjectID,
  18. Define = require('./metadata'),
  19. Logger = require('mongodb-core').Logger,
  20. Collection = require('./collection'),
  21. crypto = require('crypto'),
  22. mergeOptionsAndWriteConcern = require('./utils').mergeOptionsAndWriteConcern,
  23. executeOperation = require('./utils').executeOperation;
  24. var debugFields = [
  25. 'authSource',
  26. 'w',
  27. 'wtimeout',
  28. 'j',
  29. 'native_parser',
  30. 'forceServerObjectId',
  31. 'serializeFunctions',
  32. 'raw',
  33. 'promoteLongs',
  34. 'promoteValues',
  35. 'promoteBuffers',
  36. 'bufferMaxEntries',
  37. 'numberOfRetries',
  38. 'retryMiliSeconds',
  39. 'readPreference',
  40. 'pkFactory',
  41. 'parentDb',
  42. 'promiseLibrary',
  43. 'noListener'
  44. ];
  45. // Filter out any write concern options
  46. var illegalCommandFields = [
  47. 'w',
  48. 'wtimeout',
  49. 'j',
  50. 'fsync',
  51. 'autoIndexId',
  52. 'strict',
  53. 'serializeFunctions',
  54. 'pkFactory',
  55. 'raw',
  56. 'readPreference',
  57. 'session'
  58. ];
  59. /**
  60. * @fileOverview The **Db** class is a class that represents a MongoDB Database.
  61. *
  62. * @example
  63. * const MongoClient = require('mongodb').MongoClient;
  64. * const test = require('assert');
  65. * // Connection url
  66. * const url = 'mongodb://localhost:27017';
  67. * // Database Name
  68. * const dbName = 'test';
  69. * // Connect using MongoClient
  70. * MongoClient.connect(url, function(err, client) {
  71. * // Get an additional db
  72. * const testDb = client.db('test');
  73. * client.close();
  74. * });
  75. */
  76. // Allowed parameters
  77. var legalOptionNames = [
  78. 'w',
  79. 'wtimeout',
  80. 'fsync',
  81. 'j',
  82. 'readPreference',
  83. 'readPreferenceTags',
  84. 'native_parser',
  85. 'forceServerObjectId',
  86. 'pkFactory',
  87. 'serializeFunctions',
  88. 'raw',
  89. 'bufferMaxEntries',
  90. 'authSource',
  91. 'ignoreUndefined',
  92. 'promoteLongs',
  93. 'promiseLibrary',
  94. 'readConcern',
  95. 'retryMiliSeconds',
  96. 'numberOfRetries',
  97. 'parentDb',
  98. 'noListener',
  99. 'loggerLevel',
  100. 'logger',
  101. 'promoteBuffers',
  102. 'promoteLongs',
  103. 'promoteValues',
  104. 'compression',
  105. 'retryWrites'
  106. ];
  107. /**
  108. * Creates a new Db instance
  109. * @class
  110. * @param {string} databaseName The name of the database this instance represents.
  111. * @param {(Server|ReplSet|Mongos)} topology The server topology for the database.
  112. * @param {object} [options=null] Optional settings.
  113. * @param {string} [options.authSource=null] If the database authentication is dependent on another databaseName.
  114. * @param {(number|string)} [options.w=null] The write concern.
  115. * @param {number} [options.wtimeout=null] The write concern timeout.
  116. * @param {boolean} [options.j=false] Specify a journal write concern.
  117. * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver.
  118. * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
  119. * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
  120. * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
  121. * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
  122. * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
  123. * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
  124. * @param {number} [options.bufferMaxEntries=-1] Sets a cap on how many operations the driver will buffer up before giving up on getting a working connection, default is -1 which is unlimited.
  125. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  126. * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys.
  127. * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
  128. * @param {object} [options.readConcern=null] Specify a read concern for the collection. (only MongoDB 3.2 or higher supported)
  129. * @param {object} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
  130. * @property {(Server|ReplSet|Mongos)} serverConfig Get the current db topology.
  131. * @property {number} bufferMaxEntries Current bufferMaxEntries value for the database
  132. * @property {string} databaseName The name of the database this instance represents.
  133. * @property {object} options The options associated with the db instance.
  134. * @property {boolean} native_parser The current value of the parameter native_parser.
  135. * @property {boolean} slaveOk The current slaveOk value for the db instance.
  136. * @property {object} writeConcern The current write concern values.
  137. * @property {object} topology Access the topology object (single server, replicaset or mongos).
  138. * @fires Db#close
  139. * @fires Db#reconnect
  140. * @fires Db#error
  141. * @fires Db#timeout
  142. * @fires Db#parseError
  143. * @fires Db#fullsetup
  144. * @return {Db} a Db instance.
  145. */
  146. var Db = function(databaseName, topology, options) {
  147. options = options || {};
  148. if (!(this instanceof Db)) return new Db(databaseName, topology, options);
  149. EventEmitter.call(this);
  150. var self = this;
  151. // Get the promiseLibrary
  152. var promiseLibrary = options.promiseLibrary || Promise;
  153. // Filter the options
  154. options = filterOptions(options, legalOptionNames);
  155. // Ensure we put the promiseLib in the options
  156. options.promiseLibrary = promiseLibrary;
  157. // var self = this; // Internal state of the db object
  158. this.s = {
  159. // Database name
  160. databaseName: databaseName,
  161. // DbCache
  162. dbCache: {},
  163. // Children db's
  164. children: [],
  165. // Topology
  166. topology: topology,
  167. // Options
  168. options: options,
  169. // Logger instance
  170. logger: Logger('Db', options),
  171. // Get the bson parser
  172. bson: topology ? topology.bson : null,
  173. // Unpack read preference
  174. readPreference: options.readPreference,
  175. // Set buffermaxEntries
  176. bufferMaxEntries: typeof options.bufferMaxEntries === 'number' ? options.bufferMaxEntries : -1,
  177. // Parent db (if chained)
  178. parentDb: options.parentDb || null,
  179. // Set up the primary key factory or fallback to ObjectID
  180. pkFactory: options.pkFactory || ObjectID,
  181. // Get native parser
  182. nativeParser: options.nativeParser || options.native_parser,
  183. // Promise library
  184. promiseLibrary: promiseLibrary,
  185. // No listener
  186. noListener: typeof options.noListener === 'boolean' ? options.noListener : false,
  187. // ReadConcern
  188. readConcern: options.readConcern
  189. };
  190. // Ensure we have a valid db name
  191. validateDatabaseName(self.s.databaseName);
  192. // Add a read Only property
  193. getSingleProperty(this, 'serverConfig', self.s.topology);
  194. getSingleProperty(this, 'bufferMaxEntries', self.s.bufferMaxEntries);
  195. getSingleProperty(this, 'databaseName', self.s.databaseName);
  196. // This is a child db, do not register any listeners
  197. if (options.parentDb) return;
  198. if (this.s.noListener) return;
  199. // Add listeners
  200. topology.on('error', createListener(self, 'error', self));
  201. topology.on('timeout', createListener(self, 'timeout', self));
  202. topology.on('close', createListener(self, 'close', self));
  203. topology.on('parseError', createListener(self, 'parseError', self));
  204. topology.once('open', createListener(self, 'open', self));
  205. topology.once('fullsetup', createListener(self, 'fullsetup', self));
  206. topology.once('all', createListener(self, 'all', self));
  207. topology.on('reconnect', createListener(self, 'reconnect', self));
  208. };
  209. inherits(Db, EventEmitter);
  210. var define = (Db.define = new Define('Db', Db, false));
  211. // Topology
  212. Object.defineProperty(Db.prototype, 'topology', {
  213. enumerable: true,
  214. get: function() {
  215. return this.s.topology;
  216. }
  217. });
  218. // Options
  219. Object.defineProperty(Db.prototype, 'options', {
  220. enumerable: true,
  221. get: function() {
  222. return this.s.options;
  223. }
  224. });
  225. // slaveOk specified
  226. Object.defineProperty(Db.prototype, 'slaveOk', {
  227. enumerable: true,
  228. get: function() {
  229. if (
  230. this.s.options.readPreference != null &&
  231. (this.s.options.readPreference !== 'primary' ||
  232. this.s.options.readPreference.mode !== 'primary')
  233. ) {
  234. return true;
  235. }
  236. return false;
  237. }
  238. });
  239. // get the write Concern
  240. Object.defineProperty(Db.prototype, 'writeConcern', {
  241. enumerable: true,
  242. get: function() {
  243. var ops = {};
  244. if (this.s.options.w != null) ops.w = this.s.options.w;
  245. if (this.s.options.j != null) ops.j = this.s.options.j;
  246. if (this.s.options.fsync != null) ops.fsync = this.s.options.fsync;
  247. if (this.s.options.wtimeout != null) ops.wtimeout = this.s.options.wtimeout;
  248. return ops;
  249. }
  250. });
  251. /**
  252. * Ensures provided read preference is properly converted into an object
  253. * @param {(ReadPreference|string|object)} readPreference the user provided read preference
  254. * @return {ReadPreference}
  255. */
  256. const convertReadPreference = function(readPreference) {
  257. if (readPreference) {
  258. if (typeof readPreference === 'string') {
  259. return new ReadPreference(readPreference);
  260. } else if (
  261. readPreference &&
  262. !(readPreference instanceof ReadPreference) &&
  263. typeof readPreference === 'object'
  264. ) {
  265. const mode = readPreference.mode || readPreference.preference;
  266. if (mode && typeof mode === 'string') {
  267. return new ReadPreference(mode, readPreference.tags, {
  268. maxStalenessSeconds: readPreference.maxStalenessSeconds
  269. });
  270. }
  271. } else if (!(readPreference instanceof ReadPreference)) {
  272. throw new TypeError('Invalid read preference: ' + readPreference);
  273. }
  274. }
  275. return readPreference;
  276. };
  277. /**
  278. * The callback format for results
  279. * @callback Db~resultCallback
  280. * @param {MongoError} error An error instance representing the error during the execution.
  281. * @param {object} result The result object if the command was executed successfully.
  282. */
  283. var executeCommand = function(self, command, options, callback) {
  284. // Did the user destroy the topology
  285. if (self.serverConfig && self.serverConfig.isDestroyed())
  286. return callback(new MongoError('topology was destroyed'));
  287. // Get the db name we are executing against
  288. var dbName = options.dbName || options.authdb || self.s.databaseName;
  289. // If we have a readPreference set
  290. if (options.readPreference == null && self.s.readPreference) {
  291. options.readPreference = self.s.readPreference;
  292. }
  293. // Convert the readPreference if its not a write
  294. if (options.readPreference) {
  295. options.readPreference = convertReadPreference(options.readPreference);
  296. } else {
  297. options.readPreference = ReadPreference.primary;
  298. }
  299. // Debug information
  300. if (self.s.logger.isDebug())
  301. self.s.logger.debug(
  302. f(
  303. 'executing command %s against %s with options [%s]',
  304. JSON.stringify(command),
  305. f('%s.$cmd', dbName),
  306. JSON.stringify(debugOptions(debugFields, options))
  307. )
  308. );
  309. // Execute command
  310. self.s.topology.command(f('%s.$cmd', dbName), command, options, function(err, result) {
  311. if (err) return handleCallback(callback, err);
  312. if (options.full) return handleCallback(callback, null, result);
  313. handleCallback(callback, null, result.result);
  314. });
  315. };
  316. /**
  317. * Execute a command
  318. * @method
  319. * @param {object} command The command hash
  320. * @param {object} [options=null] Optional settings.
  321. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  322. * @param {ClientSession} [options.session] optional session to use for this operation
  323. * @param {Db~resultCallback} [callback] The command result callback
  324. * @return {Promise} returns Promise if no callback passed
  325. */
  326. Db.prototype.command = function(command, options, callback) {
  327. // Change the callback
  328. if (typeof options === 'function') (callback = options), (options = {});
  329. // Clone the options
  330. options = shallowClone(options);
  331. return executeOperation(this.s.topology, executeCommand, [this, command, options, callback]);
  332. };
  333. define.classMethod('command', { callback: true, promise: true });
  334. /**
  335. * Return the Admin db instance
  336. * @method
  337. * @return {Admin} return the new Admin db instance
  338. */
  339. Db.prototype.admin = function() {
  340. return new Admin(this, this.s.topology, this.s.promiseLibrary);
  341. };
  342. define.classMethod('admin', { callback: false, promise: false, returns: [Admin] });
  343. /**
  344. * The callback format for the collection method, must be used if strict is specified
  345. * @callback Db~collectionResultCallback
  346. * @param {MongoError} error An error instance representing the error during the execution.
  347. * @param {Collection} collection The collection instance.
  348. */
  349. var collectionKeys = [
  350. 'pkFactory',
  351. 'readPreference',
  352. 'serializeFunctions',
  353. 'strict',
  354. 'readConcern',
  355. 'ignoreUndefined',
  356. 'promoteValues',
  357. 'promoteBuffers',
  358. 'promoteLongs'
  359. ];
  360. /**
  361. * Fetch a specific collection (containing the actual collection information). If the application does not use strict mode you
  362. * can use it without a callback in the following way: `var collection = db.collection('mycollection');`
  363. *
  364. * @method
  365. * @param {string} name the collection name we wish to access.
  366. * @param {object} [options=null] Optional settings.
  367. * @param {(number|string)} [options.w=null] The write concern.
  368. * @param {number} [options.wtimeout=null] The write concern timeout.
  369. * @param {boolean} [options.j=false] Specify a journal write concern.
  370. * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
  371. * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys.
  372. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  373. * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
  374. * @param {boolean} [options.strict=false] Returns an error if the collection does not exist
  375. * @param {object} [options.readConcern=null] Specify a read concern for the collection. (only MongoDB 3.2 or higher supported)
  376. * @param {object} [options.readConcern.level='local'] Specify a read concern level for the collection operations, one of [local|majority]. (only MongoDB 3.2 or higher supported)
  377. * @param {Db~collectionResultCallback} [callback] The collection result callback
  378. * @return {Collection} return the new Collection instance if not in strict mode
  379. */
  380. Db.prototype.collection = function(name, options, callback) {
  381. var self = this;
  382. if (typeof options === 'function') (callback = options), (options = {});
  383. options = options || {};
  384. options = shallowClone(options);
  385. // Set the promise library
  386. options.promiseLibrary = this.s.promiseLibrary;
  387. // If we have not set a collection level readConcern set the db level one
  388. options.readConcern = options.readConcern || this.s.readConcern;
  389. // Do we have ignoreUndefined set
  390. if (this.s.options.ignoreUndefined) {
  391. options.ignoreUndefined = this.s.options.ignoreUndefined;
  392. }
  393. // Merge in all needed options and ensure correct writeConcern merging from db level
  394. options = mergeOptionsAndWriteConcern(options, this.s.options, collectionKeys, true);
  395. // Execute
  396. if (options == null || !options.strict) {
  397. try {
  398. var collection = new Collection(
  399. this,
  400. this.s.topology,
  401. this.s.databaseName,
  402. name,
  403. this.s.pkFactory,
  404. options
  405. );
  406. if (callback) callback(null, collection);
  407. return collection;
  408. } catch (err) {
  409. if (err instanceof MongoError && callback) return callback(err);
  410. throw err;
  411. }
  412. }
  413. // Strict mode
  414. if (typeof callback !== 'function') {
  415. throw toError(f('A callback is required in strict mode. While getting collection %s.', name));
  416. }
  417. // Did the user destroy the topology
  418. if (self.serverConfig && self.serverConfig.isDestroyed()) {
  419. return callback(new MongoError('topology was destroyed'));
  420. }
  421. // Strict mode
  422. this.listCollections({ name: name }, options).toArray(function(err, collections) {
  423. if (err != null) return handleCallback(callback, err, null);
  424. if (collections.length === 0)
  425. return handleCallback(
  426. callback,
  427. toError(f('Collection %s does not exist. Currently in strict mode.', name)),
  428. null
  429. );
  430. try {
  431. return handleCallback(
  432. callback,
  433. null,
  434. new Collection(self, self.s.topology, self.s.databaseName, name, self.s.pkFactory, options)
  435. );
  436. } catch (err) {
  437. return handleCallback(callback, err, null);
  438. }
  439. });
  440. };
  441. define.classMethod('collection', { callback: true, promise: false, returns: [Collection] });
  442. function decorateWithWriteConcern(command, self, options) {
  443. // Do we support write concerns 3.4 and higher
  444. if (self.s.topology.capabilities().commandsTakeWriteConcern) {
  445. // Get the write concern settings
  446. var finalOptions = writeConcern(shallowClone(options), self, options);
  447. // Add the write concern to the command
  448. if (finalOptions.writeConcern) {
  449. command.writeConcern = finalOptions.writeConcern;
  450. }
  451. }
  452. }
  453. var createCollection = function(self, name, options, callback) {
  454. // Get the write concern options
  455. var finalOptions = writeConcern(shallowClone(options), self, options);
  456. // Did the user destroy the topology
  457. if (self.serverConfig && self.serverConfig.isDestroyed()) {
  458. return callback(new MongoError('topology was destroyed'));
  459. }
  460. // Check if we have the name
  461. self
  462. .listCollections({ name: name }, finalOptions)
  463. .setReadPreference(ReadPreference.PRIMARY)
  464. .toArray(function(err, collections) {
  465. if (err != null) return handleCallback(callback, err, null);
  466. if (collections.length > 0 && finalOptions.strict) {
  467. return handleCallback(
  468. callback,
  469. MongoError.create({
  470. message: f('Collection %s already exists. Currently in strict mode.', name),
  471. driver: true
  472. }),
  473. null
  474. );
  475. } else if (collections.length > 0) {
  476. try {
  477. return handleCallback(
  478. callback,
  479. null,
  480. new Collection(
  481. self,
  482. self.s.topology,
  483. self.s.databaseName,
  484. name,
  485. self.s.pkFactory,
  486. options
  487. )
  488. );
  489. } catch (err) {
  490. return handleCallback(callback, err);
  491. }
  492. }
  493. // Create collection command
  494. var cmd = { create: name };
  495. // Decorate command with writeConcern if supported
  496. decorateWithWriteConcern(cmd, self, options);
  497. // Add all optional parameters
  498. for (var n in options) {
  499. if (
  500. options[n] != null &&
  501. typeof options[n] !== 'function' &&
  502. illegalCommandFields.indexOf(n) === -1
  503. ) {
  504. cmd[n] = options[n];
  505. }
  506. }
  507. // Force a primary read Preference
  508. finalOptions.readPreference = ReadPreference.PRIMARY;
  509. // Execute command
  510. self.command(cmd, finalOptions, function(err) {
  511. if (err) return handleCallback(callback, err);
  512. handleCallback(
  513. callback,
  514. null,
  515. new Collection(
  516. self,
  517. self.s.topology,
  518. self.s.databaseName,
  519. name,
  520. self.s.pkFactory,
  521. options
  522. )
  523. );
  524. });
  525. });
  526. };
  527. /**
  528. * Create a new collection on a server with the specified options. Use this to create capped collections.
  529. * More information about command options available at https://docs.mongodb.com/manual/reference/command/create/
  530. *
  531. * @method
  532. * @param {string} name the collection name we wish to access.
  533. * @param {object} [options=null] Optional settings.
  534. * @param {(number|string)} [options.w=null] The write concern.
  535. * @param {number} [options.wtimeout=null] The write concern timeout.
  536. * @param {boolean} [options.j=false] Specify a journal write concern.
  537. * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
  538. * @param {object} [options.pkFactory=null] A primary key factory object for generation of custom _id keys.
  539. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  540. * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
  541. * @param {boolean} [options.strict=false] Returns an error if the collection does not exist
  542. * @param {boolean} [options.capped=false] Create a capped collection.
  543. * @param {boolean} [options.autoIndexId=true] Create an index on the _id field of the document, True by default on MongoDB 2.2 or higher off for version < 2.2.
  544. * @param {number} [options.size=null] The size of the capped collection in bytes.
  545. * @param {number} [options.max=null] The maximum number of documents in the capped collection.
  546. * @param {number} [options.flags=null] Optional. Available for the MMAPv1 storage engine only to set the usePowerOf2Sizes and the noPadding flag.
  547. * @param {object} [options.storageEngine=null] Allows users to specify configuration to the storage engine on a per-collection basis when creating a collection on MongoDB 3.0 or higher.
  548. * @param {object} [options.validator=null] Allows users to specify validation rules or expressions for the collection. For more information, see Document Validation on MongoDB 3.2 or higher.
  549. * @param {string} [options.validationLevel=null] Determines how strictly MongoDB applies the validation rules to existing documents during an update on MongoDB 3.2 or higher.
  550. * @param {string} [options.validationAction=null] Determines whether to error on invalid documents or just warn about the violations but allow invalid documents to be inserted on MongoDB 3.2 or higher.
  551. * @param {object} [options.indexOptionDefaults=null] Allows users to specify a default configuration for indexes when creating a collection on MongoDB 3.2 or higher.
  552. * @param {string} [options.viewOn=null] The name of the source collection or view from which to create the view. The name is not the full namespace of the collection or view; i.e. does not include the database name and implies the same database as the view to create on MongoDB 3.4 or higher.
  553. * @param {array} [options.pipeline=null] An array that consists of the aggregation pipeline stage. create creates the view by applying the specified pipeline to the viewOn collection or view on MongoDB 3.4 or higher.
  554. * @param {object} [options.collation=null] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
  555. * @param {ClientSession} [options.session] optional session to use for this operation
  556. * @param {Db~collectionResultCallback} [callback] The results callback
  557. * @return {Promise} returns Promise if no callback passed
  558. */
  559. Db.prototype.createCollection = function(name, options, callback) {
  560. if (typeof options === 'function') (callback = options), (options = {});
  561. options = options || {};
  562. options.promiseLibrary = options.promiseLibrary || this.s.promiseLibrary;
  563. return executeOperation(this.s.topology, createCollection, [this, name, options, callback]);
  564. };
  565. define.classMethod('createCollection', { callback: true, promise: true });
  566. /**
  567. * Get all the db statistics.
  568. *
  569. * @method
  570. * @param {object} [options=null] Optional settings.
  571. * @param {number} [options.scale=null] Divide the returned sizes by scale value.
  572. * @param {ClientSession} [options.session] optional session to use for this operation
  573. * @param {Db~resultCallback} [callback] The collection result callback
  574. * @return {Promise} returns Promise if no callback passed
  575. */
  576. Db.prototype.stats = function(options, callback) {
  577. if (typeof options === 'function') (callback = options), (options = {});
  578. options = options || {};
  579. // Build command object
  580. var commandObject = { dbStats: true };
  581. // Check if we have the scale value
  582. if (options['scale'] != null) commandObject['scale'] = options['scale'];
  583. // If we have a readPreference set
  584. if (options.readPreference == null && this.s.readPreference) {
  585. options.readPreference = this.s.readPreference;
  586. }
  587. // Execute the command
  588. return this.command(commandObject, options, callback);
  589. };
  590. define.classMethod('stats', { callback: true, promise: true });
  591. // Transformation methods for cursor results
  592. var listCollectionsTranforms = function(databaseName) {
  593. var matching = f('%s.', databaseName);
  594. return {
  595. doc: function(doc) {
  596. var index = doc.name.indexOf(matching);
  597. // Remove database name if available
  598. if (doc.name && index === 0) {
  599. doc.name = doc.name.substr(index + matching.length);
  600. }
  601. return doc;
  602. }
  603. };
  604. };
  605. /**
  606. * Get the list of all collection information for the specified db.
  607. *
  608. * @method
  609. * @param {object} [filter={}] Query to filter collections by
  610. * @param {object} [options=null] Optional settings.
  611. * @param {number} [options.batchSize=null] The batchSize for the returned command cursor or if pre 2.8 the systems batch collection
  612. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  613. * @param {ClientSession} [options.session] optional session to use for this operation
  614. * @return {CommandCursor}
  615. */
  616. Db.prototype.listCollections = function(filter, options) {
  617. filter = filter || {};
  618. options = options || {};
  619. // Shallow clone the object
  620. options = shallowClone(options);
  621. // Set the promise library
  622. options.promiseLibrary = this.s.promiseLibrary;
  623. // Ensure valid readPreference
  624. if (options.readPreference) {
  625. options.readPreference = convertReadPreference(options.readPreference);
  626. } else {
  627. options.readPreference = this.s.readPreference || ReadPreference.primary;
  628. }
  629. // We have a list collections command
  630. if (this.serverConfig.capabilities().hasListCollectionsCommand) {
  631. // Cursor options
  632. var cursor = options.batchSize ? { batchSize: options.batchSize } : {};
  633. // Build the command
  634. var command = { listCollections: true, filter: filter, cursor: cursor };
  635. // Set the AggregationCursor constructor
  636. options.cursorFactory = CommandCursor;
  637. // Create the cursor
  638. cursor = this.s.topology.cursor(f('%s.$cmd', this.s.databaseName), command, options);
  639. // Do we have a readPreference, apply it
  640. if (options.readPreference) {
  641. cursor.setReadPreference(options.readPreference);
  642. }
  643. // Return the cursor
  644. return cursor;
  645. }
  646. // We cannot use the listCollectionsCommand
  647. if (!this.serverConfig.capabilities().hasListCollectionsCommand) {
  648. // If we have legacy mode and have not provided a full db name filter it
  649. if (
  650. typeof filter.name === 'string' &&
  651. !new RegExp('^' + this.databaseName + '\\.').test(filter.name)
  652. ) {
  653. filter = shallowClone(filter);
  654. filter.name = f('%s.%s', this.s.databaseName, filter.name);
  655. }
  656. }
  657. // No filter, filter by current database
  658. if (filter == null) {
  659. filter.name = f('/%s/', this.s.databaseName);
  660. }
  661. // Rewrite the filter to use $and to filter out indexes
  662. if (filter.name) {
  663. filter = { $and: [{ name: filter.name }, { name: /^((?!\$).)*$/ }] };
  664. } else {
  665. filter = { name: /^((?!\$).)*$/ };
  666. }
  667. // Return options
  668. var _options = { transforms: listCollectionsTranforms(this.s.databaseName) };
  669. // Get the cursor
  670. cursor = this.collection(Db.SYSTEM_NAMESPACE_COLLECTION).find(filter, _options);
  671. // Do we have a readPreference, apply it
  672. if (options.readPreference) cursor.setReadPreference(options.readPreference);
  673. // Set the passed in batch size if one was provided
  674. if (options.batchSize) cursor = cursor.batchSize(options.batchSize);
  675. // We have a fallback mode using legacy systems collections
  676. return cursor;
  677. };
  678. define.classMethod('listCollections', {
  679. callback: false,
  680. promise: false,
  681. returns: [CommandCursor]
  682. });
  683. var evaluate = function(self, code, parameters, options, callback) {
  684. var finalCode = code;
  685. var finalParameters = [];
  686. // Did the user destroy the topology
  687. if (self.serverConfig && self.serverConfig.isDestroyed())
  688. return callback(new MongoError('topology was destroyed'));
  689. // If not a code object translate to one
  690. if (!(finalCode && finalCode._bsontype === 'Code')) finalCode = new Code(finalCode);
  691. // Ensure the parameters are correct
  692. if (parameters != null && !Array.isArray(parameters) && typeof parameters !== 'function') {
  693. finalParameters = [parameters];
  694. } else if (parameters != null && Array.isArray(parameters) && typeof parameters !== 'function') {
  695. finalParameters = parameters;
  696. }
  697. // Create execution selector
  698. var cmd = { $eval: finalCode, args: finalParameters };
  699. // Check if the nolock parameter is passed in
  700. if (options['nolock']) {
  701. cmd['nolock'] = options['nolock'];
  702. }
  703. // Set primary read preference
  704. options.readPreference = new ReadPreference(ReadPreference.PRIMARY);
  705. // Execute the command
  706. self.command(cmd, options, function(err, result) {
  707. if (err) return handleCallback(callback, err, null);
  708. if (result && result.ok === 1) return handleCallback(callback, null, result.retval);
  709. if (result)
  710. return handleCallback(
  711. callback,
  712. MongoError.create({ message: f('eval failed: %s', result.errmsg), driver: true }),
  713. null
  714. );
  715. handleCallback(callback, err, result);
  716. });
  717. };
  718. /**
  719. * Evaluate JavaScript on the server
  720. *
  721. * @method
  722. * @param {Code} code JavaScript to execute on server.
  723. * @param {(object|array)} parameters The parameters for the call.
  724. * @param {object} [options=null] Optional settings.
  725. * @param {boolean} [options.nolock=false] Tell MongoDB not to block on the evaulation of the javascript.
  726. * @param {ClientSession} [options.session] optional session to use for this operation
  727. * @param {Db~resultCallback} [callback] The results callback
  728. * @deprecated Eval is deprecated on MongoDB 3.2 and forward
  729. * @return {Promise} returns Promise if no callback passed
  730. */
  731. Db.prototype.eval = function(code, parameters, options, callback) {
  732. var args = Array.prototype.slice.call(arguments, 1);
  733. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  734. parameters = args.length ? args.shift() : parameters;
  735. options = args.length ? args.shift() || {} : {};
  736. return executeOperation(this.s.topology, evaluate, [this, code, parameters, options, callback]);
  737. };
  738. define.classMethod('eval', { callback: true, promise: true });
  739. /**
  740. * Rename a collection.
  741. *
  742. * @method
  743. * @param {string} fromCollection Name of current collection to rename.
  744. * @param {string} toCollection New name of of the collection.
  745. * @param {object} [options=null] Optional settings.
  746. * @param {boolean} [options.dropTarget=false] Drop the target name collection if it previously exists.
  747. * @param {ClientSession} [options.session] optional session to use for this operation
  748. * @param {Db~collectionResultCallback} [callback] The results callback
  749. * @return {Promise} returns Promise if no callback passed
  750. */
  751. Db.prototype.renameCollection = function(fromCollection, toCollection, options, callback) {
  752. if (typeof options === 'function') (callback = options), (options = {});
  753. options = options || {};
  754. // Add return new collection
  755. options.new_collection = true;
  756. const collection = this.collection(fromCollection);
  757. return executeOperation(this.s.topology, collection.rename.bind(collection), [
  758. toCollection,
  759. options,
  760. callback
  761. ]);
  762. };
  763. define.classMethod('renameCollection', { callback: true, promise: true });
  764. /**
  765. * Drop a collection from the database, removing it permanently. New accesses will create a new collection.
  766. *
  767. * @method
  768. * @param {string} name Name of collection to drop
  769. * @param {Object} [options] Optional settings
  770. * @param {ClientSession} [options.session] optional session to use for this operation
  771. * @param {Db~resultCallback} [callback] The results callback
  772. * @return {Promise} returns Promise if no callback passed
  773. */
  774. Db.prototype.dropCollection = function(name, options, callback) {
  775. if (typeof options === 'function') (callback = options), (options = {});
  776. options = options || {};
  777. // Command to execute
  778. var cmd = { drop: name };
  779. // Decorate with write concern
  780. decorateWithWriteConcern(cmd, this, options);
  781. // options
  782. const opts = Object.assign({}, this.s.options, { readPreference: ReadPreference.PRIMARY });
  783. if (options.session) opts.session = options.session;
  784. return executeOperation(this.s.topology, dropCollection, [this, cmd, opts, callback]);
  785. };
  786. const dropCollection = (self, cmd, options, callback) => {
  787. return self.command(cmd, options, function(err, result) {
  788. // Did the user destroy the topology
  789. if (self.serverConfig && self.serverConfig.isDestroyed()) {
  790. return callback(new MongoError('topology was destroyed'));
  791. }
  792. if (err) return handleCallback(callback, err);
  793. if (result.ok) return handleCallback(callback, null, true);
  794. handleCallback(callback, null, false);
  795. });
  796. };
  797. define.classMethod('dropCollection', { callback: true, promise: true });
  798. /**
  799. * Drop a database, removing it permanently from the server.
  800. *
  801. * @method
  802. * @param {Object} [options] Optional settings
  803. * @param {ClientSession} [options.session] optional session to use for this operation
  804. * @param {Db~resultCallback} [callback] The results callback
  805. * @return {Promise} returns Promise if no callback passed
  806. */
  807. Db.prototype.dropDatabase = function(options, callback) {
  808. if (typeof options === 'function') (callback = options), (options = {});
  809. options = options || {};
  810. // Drop database command
  811. var cmd = { dropDatabase: 1 };
  812. // Decorate with write concern
  813. decorateWithWriteConcern(cmd, this, options);
  814. // Ensure primary only
  815. const finalOptions = Object.assign(
  816. {},
  817. { readPreference: ReadPreference.PRIMARY },
  818. this.s.options
  819. );
  820. if (options.session) {
  821. finalOptions.session = options.session;
  822. }
  823. return executeOperation(this.s.topology, dropDatabase, [this, cmd, finalOptions, callback]);
  824. };
  825. const dropDatabase = (self, cmd, options, callback) => {
  826. self.command(cmd, options, function(err, result) {
  827. // Did the user destroy the topology
  828. if (self.serverConfig && self.serverConfig.isDestroyed()) {
  829. return callback(new MongoError('topology was destroyed'));
  830. }
  831. if (callback == null) return;
  832. if (err) return handleCallback(callback, err, null);
  833. handleCallback(callback, null, result.ok ? true : false);
  834. });
  835. };
  836. define.classMethod('dropDatabase', { callback: true, promise: true });
  837. /**
  838. * Fetch all collections for the current db.
  839. *
  840. * @method
  841. * @param {Object} [options] Optional settings
  842. * @param {ClientSession} [options.session] optional session to use for this operation
  843. * @param {Db~collectionsResultCallback} [callback] The results callback
  844. * @return {Promise} returns Promise if no callback passed
  845. */
  846. Db.prototype.collections = function(options, callback) {
  847. if (typeof options === 'function') (callback = options), (options = {});
  848. options = options || {};
  849. return executeOperation(this.s.topology, collections, [this, options, callback]);
  850. };
  851. var collections = function(self, options, callback) {
  852. // Let's get the collection names
  853. self.listCollections({}, options).toArray(function(err, documents) {
  854. if (err != null) return handleCallback(callback, err, null);
  855. // Filter collections removing any illegal ones
  856. documents = documents.filter(function(doc) {
  857. return doc.name.indexOf('$') === -1;
  858. });
  859. // Return the collection objects
  860. handleCallback(
  861. callback,
  862. null,
  863. documents.map(function(d) {
  864. return new Collection(
  865. self,
  866. self.s.topology,
  867. self.s.databaseName,
  868. d.name,
  869. self.s.pkFactory,
  870. self.s.options
  871. );
  872. })
  873. );
  874. });
  875. };
  876. define.classMethod('collections', { callback: true, promise: true });
  877. /**
  878. * Runs a command on the database as admin.
  879. * @method
  880. * @param {object} command The command hash
  881. * @param {object} [options=null] Optional settings.
  882. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  883. * @param {ClientSession} [options.session] optional session to use for this operation
  884. * @param {Db~resultCallback} [callback] The command result callback
  885. * @return {Promise} returns Promise if no callback passed
  886. */
  887. Db.prototype.executeDbAdminCommand = function(selector, options, callback) {
  888. if (typeof options === 'function') (callback = options), (options = {});
  889. options = options || {};
  890. // Convert read preference
  891. if (options.readPreference) {
  892. options.readPreference = convertReadPreference(options.readPreference);
  893. }
  894. return executeOperation(this.s.topology, executeDbAdminCommand, [
  895. this,
  896. selector,
  897. options,
  898. callback
  899. ]);
  900. };
  901. const executeDbAdminCommand = (self, selector, options, callback) => {
  902. self.s.topology.command('admin.$cmd', selector, options, function(err, result) {
  903. // Did the user destroy the topology
  904. if (self.serverConfig && self.serverConfig.isDestroyed()) {
  905. return callback(new MongoError('topology was destroyed'));
  906. }
  907. if (err) return handleCallback(callback, err);
  908. handleCallback(callback, null, result.result);
  909. });
  910. };
  911. define.classMethod('executeDbAdminCommand', { callback: true, promise: true });
  912. /**
  913. * Creates an index on the db and collection collection.
  914. * @method
  915. * @param {string} name Name of the collection to create the index on.
  916. * @param {(string|object)} fieldOrSpec Defines the index.
  917. * @param {object} [options=null] Optional settings.
  918. * @param {(number|string)} [options.w=null] The write concern.
  919. * @param {number} [options.wtimeout=null] The write concern timeout.
  920. * @param {boolean} [options.j=false] Specify a journal write concern.
  921. * @param {boolean} [options.unique=false] Creates an unique index.
  922. * @param {boolean} [options.sparse=false] Creates a sparse index.
  923. * @param {boolean} [options.background=false] Creates the index in the background, yielding whenever possible.
  924. * @param {boolean} [options.dropDups=false] A unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
  925. * @param {number} [options.min=null] For geospatial indexes set the lower bound for the co-ordinates.
  926. * @param {number} [options.max=null] For geospatial indexes set the high bound for the co-ordinates.
  927. * @param {number} [options.v=null] Specify the format version of the indexes.
  928. * @param {number} [options.expireAfterSeconds=null] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
  929. * @param {number} [options.name=null] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
  930. * @param {object} [options.partialFilterExpression=null] Creates a partial index based on the given filter object (MongoDB 3.2 or higher)
  931. * @param {ClientSession} [options.session] optional session to use for this operation
  932. * @param {Db~resultCallback} [callback] The command result callback
  933. * @return {Promise} returns Promise if no callback passed
  934. */
  935. Db.prototype.createIndex = function(name, fieldOrSpec, options, callback) {
  936. if (typeof options === 'function') (callback = options), (options = {});
  937. options = options ? shallowClone(options) : {};
  938. return executeOperation(this.s.topology, createIndex, [
  939. this,
  940. name,
  941. fieldOrSpec,
  942. options,
  943. callback
  944. ]);
  945. };
  946. var createIndex = function(self, name, fieldOrSpec, options, callback) {
  947. // Get the write concern options
  948. var finalOptions = Object.assign({}, { readPreference: ReadPreference.PRIMARY }, options);
  949. finalOptions = writeConcern(finalOptions, self, options);
  950. // Ensure we have a callback
  951. if (finalOptions.writeConcern && typeof callback !== 'function') {
  952. throw MongoError.create({
  953. message: 'Cannot use a writeConcern without a provided callback',
  954. driver: true
  955. });
  956. }
  957. // Did the user destroy the topology
  958. if (self.serverConfig && self.serverConfig.isDestroyed())
  959. return callback(new MongoError('topology was destroyed'));
  960. // Attempt to run using createIndexes command
  961. createIndexUsingCreateIndexes(self, name, fieldOrSpec, options, function(err, result) {
  962. if (err == null) return handleCallback(callback, err, result);
  963. // 67 = 'CannotCreateIndex' (malformed index options)
  964. // 85 = 'IndexOptionsConflict' (index already exists with different options)
  965. // 11000 = 'DuplicateKey' (couldn't build unique index because of dupes)
  966. // 11600 = 'InterruptedAtShutdown' (interrupted at shutdown)
  967. // These errors mean that the server recognized `createIndex` as a command
  968. // and so we don't need to fallback to an insert.
  969. if (err.code === 67 || err.code === 11000 || err.code === 85 || err.code === 11600) {
  970. return handleCallback(callback, err, result);
  971. }
  972. // Create command
  973. var doc = createCreateIndexCommand(self, name, fieldOrSpec, options);
  974. // Set no key checking
  975. finalOptions.checkKeys = false;
  976. // Insert document
  977. self.s.topology.insert(
  978. f('%s.%s', self.s.databaseName, Db.SYSTEM_INDEX_COLLECTION),
  979. doc,
  980. finalOptions,
  981. function(err, result) {
  982. if (callback == null) return;
  983. if (err) return handleCallback(callback, err);
  984. if (result == null) return handleCallback(callback, null, null);
  985. if (result.result.writeErrors)
  986. return handleCallback(callback, MongoError.create(result.result.writeErrors[0]), null);
  987. handleCallback(callback, null, doc.name);
  988. }
  989. );
  990. });
  991. };
  992. define.classMethod('createIndex', { callback: true, promise: true });
  993. /**
  994. * Ensures that an index exists, if it does not it creates it
  995. * @method
  996. * @deprecated since version 2.0
  997. * @param {string} name The index name
  998. * @param {(string|object)} fieldOrSpec Defines the index.
  999. * @param {object} [options=null] Optional settings.
  1000. * @param {(number|string)} [options.w=null] The write concern.
  1001. * @param {number} [options.wtimeout=null] The write concern timeout.
  1002. * @param {boolean} [options.j=false] Specify a journal write concern.
  1003. * @param {boolean} [options.unique=false] Creates an unique index.
  1004. * @param {boolean} [options.sparse=false] Creates a sparse index.
  1005. * @param {boolean} [options.background=false] Creates the index in the background, yielding whenever possible.
  1006. * @param {boolean} [options.dropDups=false] A unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
  1007. * @param {number} [options.min=null] For geospatial indexes set the lower bound for the co-ordinates.
  1008. * @param {number} [options.max=null] For geospatial indexes set the high bound for the co-ordinates.
  1009. * @param {number} [options.v=null] Specify the format version of the indexes.
  1010. * @param {number} [options.expireAfterSeconds=null] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
  1011. * @param {number} [options.name=null] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
  1012. * @param {ClientSession} [options.session] optional session to use for this operation
  1013. * @param {Db~resultCallback} [callback] The command result callback
  1014. * @return {Promise} returns Promise if no callback passed
  1015. */
  1016. Db.prototype.ensureIndex = function(name, fieldOrSpec, options, callback) {
  1017. if (typeof options === 'function') (callback = options), (options = {});
  1018. options = options || {};
  1019. return executeOperation(this.s.topology, ensureIndex, [
  1020. this,
  1021. name,
  1022. fieldOrSpec,
  1023. options,
  1024. callback
  1025. ]);
  1026. };
  1027. var ensureIndex = function(self, name, fieldOrSpec, options, callback) {
  1028. // Get the write concern options
  1029. var finalOptions = writeConcern({}, self, options);
  1030. // Create command
  1031. var selector = createCreateIndexCommand(self, name, fieldOrSpec, options);
  1032. var index_name = selector.name;
  1033. // Did the user destroy the topology
  1034. if (self.serverConfig && self.serverConfig.isDestroyed())
  1035. return callback(new MongoError('topology was destroyed'));
  1036. // Merge primary readPreference
  1037. finalOptions.readPreference = ReadPreference.PRIMARY;
  1038. // Check if the index allready exists
  1039. self.indexInformation(name, finalOptions, function(err, indexInformation) {
  1040. if (err != null && err.code !== 26) return handleCallback(callback, err, null);
  1041. // If the index does not exist, create it
  1042. if (indexInformation == null || !indexInformation[index_name]) {
  1043. self.createIndex(name, fieldOrSpec, options, callback);
  1044. } else {
  1045. if (typeof callback === 'function') return handleCallback(callback, null, index_name);
  1046. }
  1047. });
  1048. };
  1049. define.classMethod('ensureIndex', { callback: true, promise: true });
  1050. Db.prototype.addChild = function(db) {
  1051. if (this.s.parentDb) return this.s.parentDb.addChild(db);
  1052. this.s.children.push(db);
  1053. };
  1054. var _executeAuthCreateUserCommand = function(self, username, password, options, callback) {
  1055. // Special case where there is no password ($external users)
  1056. if (typeof username === 'string' && password != null && typeof password === 'object') {
  1057. options = password;
  1058. password = null;
  1059. }
  1060. // Unpack all options
  1061. if (typeof options === 'function') {
  1062. callback = options;
  1063. options = {};
  1064. }
  1065. // Error out if we digestPassword set
  1066. if (options.digestPassword != null) {
  1067. throw toError(
  1068. "The digestPassword option is not supported via add_user. Please use db.command('createUser', ...) instead for this option."
  1069. );
  1070. }
  1071. // Get additional values
  1072. var customData = options.customData != null ? options.customData : {};
  1073. var roles = Array.isArray(options.roles) ? options.roles : [];
  1074. var maxTimeMS = typeof options.maxTimeMS === 'number' ? options.maxTimeMS : null;
  1075. // If not roles defined print deprecated message
  1076. if (roles.length === 0) {
  1077. console.log('Creating a user without roles is deprecated in MongoDB >= 2.6');
  1078. }
  1079. // Get the error options
  1080. var commandOptions = { writeCommand: true };
  1081. if (options['dbName']) commandOptions.dbName = options['dbName'];
  1082. // Add maxTimeMS to options if set
  1083. if (maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
  1084. // Check the db name and add roles if needed
  1085. if (
  1086. (self.databaseName.toLowerCase() === 'admin' || options.dbName === 'admin') &&
  1087. !Array.isArray(options.roles)
  1088. ) {
  1089. roles = ['root'];
  1090. } else if (!Array.isArray(options.roles)) {
  1091. roles = ['dbOwner'];
  1092. }
  1093. // Build the command to execute
  1094. var command = {
  1095. createUser: username,
  1096. customData: customData,
  1097. roles: roles,
  1098. digestPassword: false
  1099. };
  1100. // Apply write concern to command
  1101. command = writeConcern(command, self, options);
  1102. // Use node md5 generator
  1103. var md5 = crypto.createHash('md5');
  1104. // Generate keys used for authentication
  1105. md5.update(username + ':mongo:' + password);
  1106. var userPassword = md5.digest('hex');
  1107. // No password
  1108. if (typeof password === 'string') {
  1109. command.pwd = userPassword;
  1110. }
  1111. // Force write using primary
  1112. commandOptions.readPreference = ReadPreference.primary;
  1113. // Execute the command
  1114. self.command(command, commandOptions, function(err, result) {
  1115. if (err && err.ok === 0 && err.code === undefined)
  1116. return handleCallback(callback, { code: -5000 }, null);
  1117. if (err) return handleCallback(callback, err, null);
  1118. handleCallback(
  1119. callback,
  1120. !result.ok ? toError(result) : null,
  1121. result.ok ? [{ user: username, pwd: '' }] : null
  1122. );
  1123. });
  1124. };
  1125. var addUser = function(self, username, password, options, callback) {
  1126. // Did the user destroy the topology
  1127. if (self.serverConfig && self.serverConfig.isDestroyed())
  1128. return callback(new MongoError('topology was destroyed'));
  1129. // Attempt to execute auth command
  1130. _executeAuthCreateUserCommand(self, username, password, options, function(err, r) {
  1131. // We need to perform the backward compatible insert operation
  1132. if (err && err.code === -5000) {
  1133. var finalOptions = writeConcern(shallowClone(options), self, options);
  1134. // Use node md5 generator
  1135. var md5 = crypto.createHash('md5');
  1136. // Generate keys used for authentication
  1137. md5.update(username + ':mongo:' + password);
  1138. var userPassword = md5.digest('hex');
  1139. // If we have another db set
  1140. var db = options.dbName ? new Db(options.dbName, self.s.topology, self.s.options) : self;
  1141. // Fetch a user collection
  1142. var collection = db.collection(Db.SYSTEM_USER_COLLECTION);
  1143. // Check if we are inserting the first user
  1144. collection.count({}, finalOptions, function(err, count) {
  1145. // We got an error (f.ex not authorized)
  1146. if (err != null) return handleCallback(callback, err, null);
  1147. // Check if the user exists and update i
  1148. collection
  1149. .find({ user: username }, { dbName: options['dbName'] }, finalOptions)
  1150. .toArray(function(err) {
  1151. // We got an error (f.ex not authorized)
  1152. if (err != null) return handleCallback(callback, err, null);
  1153. // Add command keys
  1154. finalOptions.upsert = true;
  1155. // We have a user, let's update the password or upsert if not
  1156. collection.update(
  1157. { user: username },
  1158. { $set: { user: username, pwd: userPassword } },
  1159. finalOptions,
  1160. function(err) {
  1161. if (count === 0 && err)
  1162. return handleCallback(callback, null, [{ user: username, pwd: userPassword }]);
  1163. if (err) return handleCallback(callback, err, null);
  1164. handleCallback(callback, null, [{ user: username, pwd: userPassword }]);
  1165. }
  1166. );
  1167. });
  1168. });
  1169. return;
  1170. }
  1171. if (err) return handleCallback(callback, err);
  1172. handleCallback(callback, err, r);
  1173. });
  1174. };
  1175. /**
  1176. * Add a user to the database.
  1177. * @method
  1178. * @param {string} username The username.
  1179. * @param {string} password The password.
  1180. * @param {object} [options=null] Optional settings.
  1181. * @param {(number|string)} [options.w=null] The write concern.
  1182. * @param {number} [options.wtimeout=null] The write concern timeout.
  1183. * @param {boolean} [options.j=false] Specify a journal write concern.
  1184. * @param {object} [options.customData=null] Custom data associated with the user (only Mongodb 2.6 or higher)
  1185. * @param {object[]} [options.roles=null] Roles associated with the created user (only Mongodb 2.6 or higher)
  1186. * @param {ClientSession} [options.session] optional session to use for this operation
  1187. * @param {Db~resultCallback} [callback] The command result callback
  1188. * @return {Promise} returns Promise if no callback passed
  1189. */
  1190. Db.prototype.addUser = function(username, password, options, callback) {
  1191. // Unpack the parameters
  1192. var args = Array.prototype.slice.call(arguments, 2);
  1193. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  1194. options = args.length ? args.shift() || {} : {};
  1195. return executeOperation(this.s.topology, addUser, [this, username, password, options, callback]);
  1196. };
  1197. define.classMethod('addUser', { callback: true, promise: true });
  1198. var _executeAuthRemoveUserCommand = function(self, username, options, callback) {
  1199. if (typeof options === 'function') (callback = options), (options = {});
  1200. options = options || {};
  1201. // Did the user destroy the topology
  1202. if (self.serverConfig && self.serverConfig.isDestroyed())
  1203. return callback(new MongoError('topology was destroyed'));
  1204. // Get the error options
  1205. var commandOptions = { writeCommand: true };
  1206. if (options['dbName']) commandOptions.dbName = options['dbName'];
  1207. // Get additional values
  1208. var maxTimeMS = typeof options.maxTimeMS === 'number' ? options.maxTimeMS : null;
  1209. // Add maxTimeMS to options if set
  1210. if (maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
  1211. // Build the command to execute
  1212. var command = {
  1213. dropUser: username
  1214. };
  1215. // Apply write concern to command
  1216. command = writeConcern(command, self, options);
  1217. // Force write using primary
  1218. commandOptions.readPreference = ReadPreference.primary;
  1219. // Execute the command
  1220. self.command(command, commandOptions, function(err, result) {
  1221. if (err && !err.ok && err.code === undefined) return handleCallback(callback, { code: -5000 });
  1222. if (err) return handleCallback(callback, err, null);
  1223. handleCallback(callback, null, result.ok ? true : false);
  1224. });
  1225. };
  1226. var removeUser = function(self, username, options, callback) {
  1227. // Attempt to execute command
  1228. _executeAuthRemoveUserCommand(self, username, options, function(err, result) {
  1229. if (err && err.code === -5000) {
  1230. var finalOptions = writeConcern(shallowClone(options), self, options);
  1231. // If we have another db set
  1232. var db = options.dbName ? new Db(options.dbName, self.s.topology, self.s.options) : self;
  1233. // Fetch a user collection
  1234. var collection = db.collection(Db.SYSTEM_USER_COLLECTION);
  1235. // Locate the user
  1236. collection.findOne({ user: username }, finalOptions, function(err, user) {
  1237. if (user == null) return handleCallback(callback, err, false);
  1238. collection.remove({ user: username }, finalOptions, function(err) {
  1239. handleCallback(callback, err, true);
  1240. });
  1241. });
  1242. return;
  1243. }
  1244. if (err) return handleCallback(callback, err);
  1245. handleCallback(callback, err, result);
  1246. });
  1247. };
  1248. define.classMethod('removeUser', { callback: true, promise: true });
  1249. /**
  1250. * Remove a user from a database
  1251. * @method
  1252. * @param {string} username The username.
  1253. * @param {object} [options=null] Optional settings.
  1254. * @param {(number|string)} [options.w=null] The write concern.
  1255. * @param {number} [options.wtimeout=null] The write concern timeout.
  1256. * @param {boolean} [options.j=false] Specify a journal write concern.
  1257. * @param {ClientSession} [options.session] optional session to use for this operation
  1258. * @param {Db~resultCallback} [callback] The command result callback
  1259. * @return {Promise} returns Promise if no callback passed
  1260. */
  1261. Db.prototype.removeUser = function(username, options, callback) {
  1262. var args = Array.prototype.slice.call(arguments, 1);
  1263. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  1264. options = args.length ? args.shift() || {} : {};
  1265. return executeOperation(this.s.topology, removeUser, [this, username, options, callback]);
  1266. };
  1267. /**
  1268. * Set the current profiling level of MongoDB
  1269. *
  1270. * @param {string} level The new profiling level (off, slow_only, all).
  1271. * @param {Object} [options] Optional settings
  1272. * @param {ClientSession} [options.session] optional session to use for this operation
  1273. * @param {Db~resultCallback} [callback] The command result callback.
  1274. * @return {Promise} returns Promise if no callback passed
  1275. */
  1276. Db.prototype.setProfilingLevel = function(level, options, callback) {
  1277. if (typeof options === 'function') (callback = options), (options = {});
  1278. options = options || {};
  1279. return executeOperation(this.s.topology, setProfilingLevel, [this, level, options, callback]);
  1280. };
  1281. var setProfilingLevel = function(self, level, options, callback) {
  1282. var command = {};
  1283. var profile = 0;
  1284. if (level === 'off') {
  1285. profile = 0;
  1286. } else if (level === 'slow_only') {
  1287. profile = 1;
  1288. } else if (level === 'all') {
  1289. profile = 2;
  1290. } else {
  1291. return callback(new Error('Error: illegal profiling level value ' + level));
  1292. }
  1293. // Set up the profile number
  1294. command['profile'] = profile;
  1295. self.command(command, options, function(err, doc) {
  1296. if (err == null && doc.ok === 1) return callback(null, level);
  1297. return err != null
  1298. ? callback(err, null)
  1299. : callback(new Error('Error with profile command'), null);
  1300. });
  1301. };
  1302. define.classMethod('setProfilingLevel', { callback: true, promise: true });
  1303. /**
  1304. * Retrive the current profiling information for MongoDB
  1305. *
  1306. * @param {Object} [options] Optional settings
  1307. * @param {ClientSession} [options.session] optional session to use for this operation
  1308. * @param {Db~resultCallback} [callback] The command result callback.
  1309. * @return {Promise} returns Promise if no callback passed
  1310. * @deprecated Query the system.profile collection directly.
  1311. */
  1312. Db.prototype.profilingInfo = function(options, callback) {
  1313. if (typeof options === 'function') (callback = options), (options = {});
  1314. options = options || {};
  1315. return executeOperation(this.s.topology, profilingInfo, [this, options, callback]);
  1316. };
  1317. var profilingInfo = function(self, options, callback) {
  1318. try {
  1319. self
  1320. .collection('system.profile')
  1321. .find({}, null, options)
  1322. .toArray(callback);
  1323. } catch (err) {
  1324. return callback(err, null);
  1325. }
  1326. };
  1327. define.classMethod('profilingInfo', { callback: true, promise: true });
  1328. /**
  1329. * Retrieve the current profiling Level for MongoDB
  1330. *
  1331. * @param {Object} [options] Optional settings
  1332. * @param {ClientSession} [options.session] optional session to use for this operation
  1333. * @param {Db~resultCallback} [callback] The command result callback
  1334. * @return {Promise} returns Promise if no callback passed
  1335. */
  1336. Db.prototype.profilingLevel = function(options, callback) {
  1337. if (typeof options === 'function') (callback = options), (options = {});
  1338. options = options || {};
  1339. return executeOperation(this.s.topology, profilingLevel, [this, options, callback]);
  1340. };
  1341. var profilingLevel = function(self, options, callback) {
  1342. self.command({ profile: -1 }, options, function(err, doc) {
  1343. if (err == null && doc.ok === 1) {
  1344. var was = doc.was;
  1345. if (was === 0) return callback(null, 'off');
  1346. if (was === 1) return callback(null, 'slow_only');
  1347. if (was === 2) return callback(null, 'all');
  1348. return callback(new Error('Error: illegal profiling level value ' + was), null);
  1349. } else {
  1350. err != null ? callback(err, null) : callback(new Error('Error with profile command'), null);
  1351. }
  1352. });
  1353. };
  1354. define.classMethod('profilingLevel', { callback: true, promise: true });
  1355. /**
  1356. * Retrieves this collections index info.
  1357. * @method
  1358. * @param {string} name The name of the collection.
  1359. * @param {object} [options=null] Optional settings.
  1360. * @param {boolean} [options.full=false] Returns the full raw index information.
  1361. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  1362. * @param {ClientSession} [options.session] optional session to use for this operation
  1363. * @param {Db~resultCallback} [callback] The command result callback
  1364. * @return {Promise} returns Promise if no callback passed
  1365. */
  1366. Db.prototype.indexInformation = function(name, options, callback) {
  1367. if (typeof options === 'function') (callback = options), (options = {});
  1368. options = options || {};
  1369. return executeOperation(this.s.topology, indexInformation, [this, name, options, callback]);
  1370. };
  1371. var indexInformation = function(self, name, options, callback) {
  1372. // If we specified full information
  1373. var full = options['full'] == null ? false : options['full'];
  1374. // Did the user destroy the topology
  1375. if (self.serverConfig && self.serverConfig.isDestroyed())
  1376. return callback(new MongoError('topology was destroyed'));
  1377. // Process all the results from the index command and collection
  1378. var processResults = function(indexes) {
  1379. // Contains all the information
  1380. var info = {};
  1381. // Process all the indexes
  1382. for (var i = 0; i < indexes.length; i++) {
  1383. var index = indexes[i];
  1384. // Let's unpack the object
  1385. info[index.name] = [];
  1386. for (var name in index.key) {
  1387. info[index.name].push([name, index.key[name]]);
  1388. }
  1389. }
  1390. return info;
  1391. };
  1392. // Get the list of indexes of the specified collection
  1393. self
  1394. .collection(name)
  1395. .listIndexes(options)
  1396. .toArray(function(err, indexes) {
  1397. if (err) return callback(toError(err));
  1398. if (!Array.isArray(indexes)) return handleCallback(callback, null, []);
  1399. if (full) return handleCallback(callback, null, indexes);
  1400. handleCallback(callback, null, processResults(indexes));
  1401. });
  1402. };
  1403. define.classMethod('indexInformation', { callback: true, promise: true });
  1404. var createCreateIndexCommand = function(db, name, fieldOrSpec, options) {
  1405. var indexParameters = parseIndexOptions(fieldOrSpec);
  1406. var fieldHash = indexParameters.fieldHash;
  1407. // Generate the index name
  1408. var indexName = typeof options.name === 'string' ? options.name : indexParameters.name;
  1409. var selector = {
  1410. ns: db.databaseName + '.' + name,
  1411. key: fieldHash,
  1412. name: indexName
  1413. };
  1414. // Ensure we have a correct finalUnique
  1415. var finalUnique = options == null || 'object' === typeof options ? false : options;
  1416. // Set up options
  1417. options = options == null || typeof options === 'boolean' ? {} : options;
  1418. // Add all the options
  1419. var keysToOmit = Object.keys(selector);
  1420. for (var optionName in options) {
  1421. if (keysToOmit.indexOf(optionName) === -1) {
  1422. selector[optionName] = options[optionName];
  1423. }
  1424. }
  1425. if (selector['unique'] == null) selector['unique'] = finalUnique;
  1426. // Remove any write concern operations
  1427. var removeKeys = ['w', 'wtimeout', 'j', 'fsync', 'readPreference'];
  1428. for (var i = 0; i < removeKeys.length; i++) {
  1429. delete selector[removeKeys[i]];
  1430. }
  1431. // Return the command creation selector
  1432. return selector;
  1433. };
  1434. var createIndexUsingCreateIndexes = function(self, name, fieldOrSpec, options, callback) {
  1435. // Build the index
  1436. var indexParameters = parseIndexOptions(fieldOrSpec);
  1437. // Generate the index name
  1438. var indexName = typeof options.name === 'string' ? options.name : indexParameters.name;
  1439. // Set up the index
  1440. var indexes = [{ name: indexName, key: indexParameters.fieldHash }];
  1441. // merge all the options
  1442. var keysToOmit = Object.keys(indexes[0]).concat([
  1443. 'w',
  1444. 'wtimeout',
  1445. 'j',
  1446. 'fsync',
  1447. 'readPreference',
  1448. 'session'
  1449. ]);
  1450. for (var optionName in options) {
  1451. if (keysToOmit.indexOf(optionName) === -1) {
  1452. indexes[0][optionName] = options[optionName];
  1453. }
  1454. }
  1455. // Get capabilities
  1456. var capabilities = self.s.topology.capabilities();
  1457. // Did the user pass in a collation, check if our write server supports it
  1458. if (indexes[0].collation && capabilities && !capabilities.commandsTakeCollation) {
  1459. // Create a new error
  1460. var error = new MongoError(f('server/primary/mongos does not support collation'));
  1461. error.code = 67;
  1462. // Return the error
  1463. return callback(error);
  1464. }
  1465. // Create command, apply write concern to command
  1466. var cmd = writeConcern({ createIndexes: name, indexes: indexes }, self, options);
  1467. // Decorate command with writeConcern if supported
  1468. decorateWithWriteConcern(cmd, self, options);
  1469. // ReadPreference primary
  1470. options.readPreference = ReadPreference.PRIMARY;
  1471. // Build the command
  1472. self.command(cmd, options, function(err, result) {
  1473. if (err) return handleCallback(callback, err, null);
  1474. if (result.ok === 0) return handleCallback(callback, toError(result), null);
  1475. // Return the indexName for backward compatibility
  1476. handleCallback(callback, null, indexName);
  1477. });
  1478. };
  1479. // Validate the database name
  1480. var validateDatabaseName = function(databaseName) {
  1481. if (typeof databaseName !== 'string')
  1482. throw MongoError.create({ message: 'database name must be a string', driver: true });
  1483. if (databaseName.length === 0)
  1484. throw MongoError.create({ message: 'database name cannot be the empty string', driver: true });
  1485. if (databaseName === '$external') return;
  1486. var invalidChars = [' ', '.', '$', '/', '\\'];
  1487. for (var i = 0; i < invalidChars.length; i++) {
  1488. if (databaseName.indexOf(invalidChars[i]) !== -1)
  1489. throw MongoError.create({
  1490. message: "database names cannot contain the character '" + invalidChars[i] + "'",
  1491. driver: true
  1492. });
  1493. }
  1494. };
  1495. // Get write concern
  1496. var writeConcern = function(target, db, options) {
  1497. if (options.w != null || options.j != null || options.fsync != null) {
  1498. var opts = {};
  1499. if (options.w) opts.w = options.w;
  1500. if (options.wtimeout) opts.wtimeout = options.wtimeout;
  1501. if (options.j) opts.j = options.j;
  1502. if (options.fsync) opts.fsync = options.fsync;
  1503. target.writeConcern = opts;
  1504. } else if (
  1505. db.writeConcern.w != null ||
  1506. db.writeConcern.j != null ||
  1507. db.writeConcern.fsync != null
  1508. ) {
  1509. target.writeConcern = db.writeConcern;
  1510. }
  1511. return target;
  1512. };
  1513. // Add listeners to topology
  1514. var createListener = function(self, e, object) {
  1515. var listener = function(err) {
  1516. if (object.listeners(e).length > 0) {
  1517. object.emit(e, err, self);
  1518. // Emit on all associated db's if available
  1519. for (var i = 0; i < self.s.children.length; i++) {
  1520. self.s.children[i].emit(e, err, self.s.children[i]);
  1521. }
  1522. }
  1523. };
  1524. return listener;
  1525. };
  1526. /**
  1527. * Unref all sockets
  1528. * @method
  1529. */
  1530. Db.prototype.unref = function() {
  1531. this.s.topology.unref();
  1532. };
  1533. /**
  1534. * Db close event
  1535. *
  1536. * Emitted after a socket closed against a single server or mongos proxy.
  1537. *
  1538. * @event Db#close
  1539. * @type {MongoError}
  1540. */
  1541. /**
  1542. * Db reconnect event
  1543. *
  1544. * * Server: Emitted when the driver has reconnected and re-authenticated.
  1545. * * ReplicaSet: N/A
  1546. * * Mongos: Emitted when the driver reconnects and re-authenticates successfully against a Mongos.
  1547. *
  1548. * @event Db#reconnect
  1549. * @type {object}
  1550. */
  1551. /**
  1552. * Db error event
  1553. *
  1554. * Emitted after an error occurred against a single server or mongos proxy.
  1555. *
  1556. * @event Db#error
  1557. * @type {MongoError}
  1558. */
  1559. /**
  1560. * Db timeout event
  1561. *
  1562. * Emitted after a socket timeout occurred against a single server or mongos proxy.
  1563. *
  1564. * @event Db#timeout
  1565. * @type {MongoError}
  1566. */
  1567. /**
  1568. * Db parseError event
  1569. *
  1570. * The parseError event is emitted if the driver detects illegal or corrupt BSON being received from the server.
  1571. *
  1572. * @event Db#parseError
  1573. * @type {MongoError}
  1574. */
  1575. /**
  1576. * Db fullsetup event, emitted when all servers in the topology have been connected to at start up time.
  1577. *
  1578. * * Server: Emitted when the driver has connected to the single server and has authenticated.
  1579. * * ReplSet: Emitted after the driver has attempted to connect to all replicaset members.
  1580. * * Mongos: Emitted after the driver has attempted to connect to all mongos proxies.
  1581. *
  1582. * @event Db#fullsetup
  1583. * @type {Db}
  1584. */
  1585. // Constants
  1586. Db.SYSTEM_NAMESPACE_COLLECTION = 'system.namespaces';
  1587. Db.SYSTEM_INDEX_COLLECTION = 'system.indexes';
  1588. Db.SYSTEM_PROFILE_COLLECTION = 'system.profile';
  1589. Db.SYSTEM_USER_COLLECTION = 'system.users';
  1590. Db.SYSTEM_COMMAND_COLLECTION = '$cmd';
  1591. Db.SYSTEM_JS_COLLECTION = 'system.js';
  1592. module.exports = Db;