db.js 72 KB

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