123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881 |
- "use strict";
- var EventEmitter = require('events').EventEmitter
- , inherits = require('util').inherits
- , getSingleProperty = require('./utils').getSingleProperty
- , shallowClone = require('./utils').shallowClone
- , parseIndexOptions = require('./utils').parseIndexOptions
- , debugOptions = require('./utils').debugOptions
- , CommandCursor = require('./command_cursor')
- , handleCallback = require('./utils').handleCallback
- , filterOptions = require('./utils').filterOptions
- , toError = require('./utils').toError
- , ReadPreference = require('./read_preference')
- , f = require('util').format
- , Admin = require('./admin')
- , Code = require('mongodb-core').BSON.Code
- , CoreReadPreference = require('mongodb-core').ReadPreference
- , MongoError = require('mongodb-core').MongoError
- , ObjectID = require('mongodb-core').ObjectID
- , Define = require('./metadata')
- , Logger = require('mongodb-core').Logger
- , Collection = require('./collection')
- , crypto = require('crypto')
- , assign = require('./utils').assign;
- var debugFields = ['authSource', 'w', 'wtimeout', 'j', 'native_parser', 'forceServerObjectId'
- , 'serializeFunctions', 'raw', 'promoteLongs', 'promoteValues', 'promoteBuffers', 'bufferMaxEntries', 'numberOfRetries', 'retryMiliSeconds'
- , 'readPreference', 'pkFactory', 'parentDb', 'promiseLibrary', 'noListener'];
- var illegalCommandFields = ['w', 'wtimeout', 'j', 'fsync', 'autoIndexId'
- , 'strict', 'serializeFunctions', 'pkFactory', 'raw', 'readPreference'];
- var legalOptionNames = ['w', 'wtimeout', 'fsync', 'j', 'readPreference', 'readPreferenceTags', 'native_parser'
- , 'forceServerObjectId', 'pkFactory', 'serializeFunctions', 'raw', 'bufferMaxEntries', 'authSource'
- , 'ignoreUndefined', 'promoteLongs', 'promiseLibrary', 'readConcern', 'retryMiliSeconds', 'numberOfRetries'
- , 'parentDb', 'noListener', 'loggerLevel', 'logger', 'promoteBuffers', 'promoteLongs', 'promoteValues'];
- var Db = function(databaseName, topology, options) {
- options = options || {};
- if(!(this instanceof Db)) return new Db(databaseName, topology, options);
- EventEmitter.call(this);
- var self = this;
-
- var promiseLibrary = options.promiseLibrary;
-
- if(!promiseLibrary) {
- promiseLibrary = typeof global.Promise == 'function' ?
- global.Promise : require('es6-promise').Promise;
- }
-
- options = filterOptions(options, legalOptionNames);
-
- options.promiseLibrary = promiseLibrary;
-
- this.s = {
-
- databaseName: databaseName
-
- , dbCache: {}
-
- , children: []
-
- , topology: topology
-
- , options: options
-
- , logger: Logger('Db', options)
-
- , bson: topology ? topology.bson : null
-
- , authSource: options.authSource
-
- , readPreference: options.readPreference
-
- , bufferMaxEntries: typeof options.bufferMaxEntries == 'number' ? options.bufferMaxEntries : -1
-
- , parentDb: options.parentDb || null
-
- , pkFactory: options.pkFactory || ObjectID
-
- , nativeParser: options.nativeParser || options.native_parser
-
- , promiseLibrary: promiseLibrary
-
- , noListener: typeof options.noListener == 'boolean' ? options.noListener : false
-
- , readConcern: options.readConcern
- }
-
- validateDatabaseName(self.s.databaseName);
-
- getSingleProperty(this, 'serverConfig', self.s.topology);
- getSingleProperty(this, 'bufferMaxEntries', self.s.bufferMaxEntries);
- getSingleProperty(this, 'databaseName', self.s.databaseName);
-
- if(options.parentDb) return;
- if(this.s.noListener) return;
-
- topology.on('error', createListener(self, 'error', self));
- topology.on('timeout', createListener(self, 'timeout', self));
- topology.on('close', createListener(self, 'close', self));
- topology.on('parseError', createListener(self, 'parseError', self));
- topology.once('open', createListener(self, 'open', self));
- topology.once('fullsetup', createListener(self, 'fullsetup', self));
- topology.once('all', createListener(self, 'all', self));
- topology.on('reconnect', createListener(self, 'reconnect', self));
- }
- inherits(Db, EventEmitter);
- var define = Db.define = new Define('Db', Db, false);
- Object.defineProperty(Db.prototype, 'topology', {
- enumerable:true,
- get: function() { return this.s.topology; }
- });
- Object.defineProperty(Db.prototype, 'options', {
- enumerable:true,
- get: function() { return this.s.options; }
- });
- Object.defineProperty(Db.prototype, 'slaveOk', {
- enumerable:true,
- get: function() {
- if(this.s.options.readPreference != null
- && (this.s.options.readPreference != 'primary' || this.s.options.readPreference.mode != 'primary')) {
- return true;
- }
- return false;
- }
- });
- Object.defineProperty(Db.prototype, 'writeConcern', {
- enumerable:true,
- get: function() {
- var ops = {};
- if(this.s.options.w != null) ops.w = this.s.options.w;
- if(this.s.options.j != null) ops.j = this.s.options.j;
- if(this.s.options.fsync != null) ops.fsync = this.s.options.fsync;
- if(this.s.options.wtimeout != null) ops.wtimeout = this.s.options.wtimeout;
- return ops;
- }
- });
- var open = function(self, callback) {
- self.s.topology.connect(self, self.s.options, function(err) {
- if(callback == null) return;
- var internalCallback = callback;
- callback == null;
- if(err) {
- self.close();
- return internalCallback(err);
- }
- internalCallback(null, self);
- });
- }
- Db.prototype.open = function(callback) {
- var self = this;
-
- if(typeof callback == 'function') return open(self, callback);
-
- return new self.s.promiseLibrary(function(resolve, reject) {
- open(self, function(err, db) {
- if(err) return reject(err);
- resolve(db);
- })
- });
- }
- define.classMethod('open', {callback: true, promise:true});
- var convertReadPreference = function(readPreference) {
- if(readPreference && typeof readPreference == 'string') {
- return new CoreReadPreference(readPreference);
- } else if(readPreference instanceof ReadPreference) {
- return new CoreReadPreference(readPreference.mode, readPreference.tags, {maxStalenessSeconds: readPreference.maxStalenessSeconds});
- } else if(readPreference && typeof readPreference == 'object') {
- var mode = readPreference.mode || readPreference.preference;
- if (mode && typeof mode == 'string') {
- readPreference = new CoreReadPreference(mode, readPreference.tags, {maxStalenessSeconds: readPreference.maxStalenessSeconds});
- }
- }
- return readPreference;
- }
- var executeCommand = function(self, command, options, callback) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
- var dbName = options.dbName || options.authdb || self.s.databaseName;
-
- if(options.readPreference == null && self.s.readPreference) {
- options.readPreference = self.s.readPreference;
- }
-
- if(options.readPreference) {
- options.readPreference = convertReadPreference(options.readPreference);
- } else {
- options.readPreference = CoreReadPreference.primary;
- }
-
- if(self.s.logger.isDebug()) self.s.logger.debug(f('executing command %s against %s with options [%s]'
- , JSON.stringify(command), f('%s.$cmd', dbName), JSON.stringify(debugOptions(debugFields, options))));
-
- self.s.topology.command(f('%s.$cmd', dbName), command, options, function(err, result) {
- if(err) return handleCallback(callback, err);
- if(options.full) return handleCallback(callback, null, result);
- handleCallback(callback, null, result.result);
- });
- }
- Db.prototype.command = function(command, options, callback) {
- var self = this;
-
- if(typeof options == 'function') callback = options, options = {};
-
- options = shallowClone(options);
-
- if(typeof callback == 'function') return executeCommand(self, command, options, callback);
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- executeCommand(self, command, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- }
- define.classMethod('command', {callback: true, promise:true});
- Db.prototype.close = function(force, callback) {
- if(typeof force == 'function') callback = force, force = false;
- this.s.topology.close(force);
- var self = this;
-
- if(this.listeners('close').length > 0) {
- this.emit('close');
-
- if(this.parentDb == null) {
-
- for(var i = 0; i < this.s.children.length; i++) {
- this.s.children[i].emit('close');
- }
- }
-
- self.removeAllListeners('close');
- }
-
- if(this.s.parentDb) this.s.parentDb.close();
-
- if(typeof callback == 'function') return process.nextTick(function() {
- handleCallback(callback, null);
- })
-
- return new this.s.promiseLibrary(function(resolve) {
- resolve();
- });
- }
- define.classMethod('close', {callback: true, promise:true});
- Db.prototype.admin = function() {
- return new Admin(this, this.s.topology, this.s.promiseLibrary);
- };
- define.classMethod('admin', {callback: false, promise:false, returns: [Admin]});
- Db.prototype.collection = function(name, options, callback) {
- var self = this;
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
- options = shallowClone(options);
-
- options.promiseLibrary = this.s.promiseLibrary;
-
- options.readConcern = options.readConcern || this.s.readConcern;
-
- if(this.s.options.ignoreUndefined) {
- options.ignoreUndefined = this.s.options.ignoreUndefined;
- }
-
- if(options == null || !options.strict) {
- try {
- var collection = new Collection(this, this.s.topology, this.s.databaseName, name, this.s.pkFactory, options);
- if(callback) callback(null, collection);
- return collection;
- } catch(err) {
- if(callback) return callback(err);
- throw err;
- }
- }
-
- if(typeof callback != 'function') {
- throw toError(f("A callback is required in strict mode. While getting collection %s.", name));
- }
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) {
- return callback(new MongoError('topology was destroyed'));
- }
-
- this.listCollections({name:name}).toArray(function(err, collections) {
- if(err != null) return handleCallback(callback, err, null);
- if(collections.length == 0) return handleCallback(callback, toError(f("Collection %s does not exist. Currently in strict mode.", name)), null);
- try {
- return handleCallback(callback, null, new Collection(self, self.s.topology, self.s.databaseName, name, self.s.pkFactory, options));
- } catch(err) {
- return handleCallback(callback, err, null);
- }
- });
- }
- define.classMethod('collection', {callback: true, promise:false, returns: [Collection]});
- function decorateWithWriteConcern(command, self, options) {
-
- if(self.s.topology.capabilities().commandsTakeWriteConcern) {
-
- var finalOptions = writeConcern(shallowClone(options), self, options);
-
- if(finalOptions.writeConcern) {
- command.writeConcern = finalOptions.writeConcern;
- }
- }
- }
- var createCollection = function(self, name, options, callback) {
-
- var finalOptions = writeConcern(shallowClone(options), self, options);
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
- self.listCollections({name: name})
- .setReadPreference(ReadPreference.PRIMARY)
- .toArray(function(err, collections) {
- if(err != null) return handleCallback(callback, err, null);
- if(collections.length > 0 && finalOptions.strict) {
- return handleCallback(callback, MongoError.create({message: f("Collection %s already exists. Currently in strict mode.", name), driver:true}), null);
- } else if (collections.length > 0) {
- try { return handleCallback(callback, null, new Collection(self, self.s.topology, self.s.databaseName, name, self.s.pkFactory, options)); }
- catch(err) { return handleCallback(callback, err); }
- }
-
- var cmd = {'create':name};
-
- decorateWithWriteConcern(cmd, self, options);
-
- for(var n in options) {
- if(options[n] != null
- && typeof options[n] != 'function' && illegalCommandFields.indexOf(n) == -1) {
- cmd[n] = options[n];
- }
- }
-
- finalOptions.readPreference = ReadPreference.PRIMARY;
-
- self.command(cmd, finalOptions, function(err) {
- if(err) return handleCallback(callback, err);
- handleCallback(callback, null, new Collection(self, self.s.topology, self.s.databaseName, name, self.s.pkFactory, options));
- });
- });
- }
- Db.prototype.createCollection = function(name, options, callback) {
- var self = this;
- var args = Array.prototype.slice.call(arguments, 0);
- callback = args.pop();
- if(typeof callback != 'function') args.push(callback);
- name = args.length ? args.shift() : null;
- options = args.length ? args.shift() || {} : {};
-
- options.promiseLibrary = options.promiseLibrary || this.s.promiseLibrary;
-
- if(typeof callback == 'string') name = callback;
-
- if(typeof callback == 'function') return createCollection(self, name, options, callback);
- return new this.s.promiseLibrary(function(resolve, reject) {
- createCollection(self, name, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- }
- define.classMethod('createCollection', {callback: true, promise:true});
- Db.prototype.stats = function(options, callback) {
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
-
- var commandObject = { dbStats:true };
-
- if(options['scale'] != null) commandObject['scale'] = options['scale'];
-
- if(options.readPreference == null && this.s.readPreference) {
- options.readPreference = this.s.readPreference;
- }
-
- return this.command(commandObject, options, callback);
- }
- define.classMethod('stats', {callback: true, promise:true});
- var listCollectionsTranforms = function(databaseName) {
- var matching = f('%s.', databaseName);
- return {
- doc: function(doc) {
- var index = doc.name.indexOf(matching);
-
- if(doc.name && index == 0) {
- doc.name = doc.name.substr(index + matching.length);
- }
- return doc;
- }
- }
- }
- Db.prototype.listCollections = function(filter, options) {
- filter = filter || {};
- options = options || {};
-
- options = shallowClone(options);
-
- options.promiseLibrary = this.s.promiseLibrary;
-
- if(options.readPreference) {
- options.readPreference = convertReadPreference(options.readPreference);
- }
-
- if(this.serverConfig.capabilities().hasListCollectionsCommand) {
-
- var cursor = options.batchSize ? {batchSize: options.batchSize} : {}
-
- var command = { listCollections : true, filter: filter, cursor: cursor };
-
- options.cursorFactory = CommandCursor;
-
- cursor = this.s.topology.cursor(f('%s.$cmd', this.s.databaseName), command, options);
-
- if(options.readPreference) {
- cursor.setReadPreference(options.readPreference);
- }
-
- return cursor;
- }
-
- if(!this.serverConfig.capabilities().hasListCollectionsCommand) {
-
- if(typeof filter.name == 'string' && !(new RegExp('^' + this.databaseName + '\\.').test(filter.name))) {
- filter = shallowClone(filter);
- filter.name = f('%s.%s', this.s.databaseName, filter.name);
- }
- }
-
- if(filter == null) {
- filter.name = f('/%s/', this.s.databaseName);
- }
-
- if(filter.name) {
- filter = {$and: [{name: filter.name}, {name:/^((?!\$).)*$/}]};
- } else {
- filter = {name:/^((?!\$).)*$/};
- }
-
- var _options = {transforms: listCollectionsTranforms(this.s.databaseName)}
-
- cursor = this.collection(Db.SYSTEM_NAMESPACE_COLLECTION).find(filter, _options);
-
- if(options.readPreference) cursor.setReadPreference(options.readPreference);
-
- if(options.batchSize) cursor = cursor.batchSize(options.batchSize);
-
- return cursor;
- };
- define.classMethod('listCollections', {callback: false, promise:false, returns: [CommandCursor]});
- var evaluate = function(self, code, parameters, options, callback) {
- var finalCode = code;
- var finalParameters = [];
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
- if(!(finalCode && finalCode._bsontype == 'Code')) finalCode = new Code(finalCode);
-
- if(parameters != null && !Array.isArray(parameters) && typeof parameters !== 'function') {
- finalParameters = [parameters];
- } else if(parameters != null && Array.isArray(parameters) && typeof parameters !== 'function') {
- finalParameters = parameters;
- }
-
- var cmd = {'$eval':finalCode, 'args':finalParameters};
-
- if(options['nolock']) {
- cmd['nolock'] = options['nolock'];
- }
-
- options.readPreference = new CoreReadPreference(ReadPreference.PRIMARY);
-
- self.command(cmd, options, function(err, result) {
- if(err) return handleCallback(callback, err, null);
- if(result && result.ok == 1) return handleCallback(callback, null, result.retval);
- if(result) return handleCallback(callback, MongoError.create({message: f("eval failed: %s", result.errmsg), driver:true}), null);
- handleCallback(callback, err, result);
- });
- }
- Db.prototype.eval = function(code, parameters, options, callback) {
- var self = this;
- var args = Array.prototype.slice.call(arguments, 1);
- callback = args.pop();
- if(typeof callback != 'function') args.push(callback);
- parameters = args.length ? args.shift() : parameters;
- options = args.length ? args.shift() || {} : {};
-
- if(typeof callback == 'function') return evaluate(self, code, parameters, options, callback);
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- evaluate(self, code, parameters, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- define.classMethod('eval', {callback: true, promise:true});
- Db.prototype.renameCollection = function(fromCollection, toCollection, options, callback) {
- var self = this;
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
-
- options.new_collection = true;
-
- if(typeof callback == 'function') {
- return this.collection(fromCollection).rename(toCollection, options, callback);
- }
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- self.collection(fromCollection).rename(toCollection, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- define.classMethod('renameCollection', {callback: true, promise:true});
- Db.prototype.dropCollection = function(name, options, callback) {
- var self = this;
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
-
- var cmd = {'drop':name}
-
- decorateWithWriteConcern(cmd, self, options);
-
- options = assign({}, this.s.options, {readPreference: ReadPreference.PRIMARY});
-
- if(typeof callback == 'function') return this.command(cmd, options, function(err, result) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
- if(err) return handleCallback(callback, err);
- if(result.ok) return handleCallback(callback, null, true);
- handleCallback(callback, null, false);
- });
-
- options = shallowClone(self.s.options);
-
- options.readPreference = ReadPreference.PRIMARY;
-
- return new this.s.promiseLibrary(function(resolve, reject) {
-
- self.command(cmd, options, function(err, result) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return reject(new MongoError('topology was destroyed'));
- if(err) return reject(err);
- if(result.ok) return resolve(true);
- resolve(false);
- });
- });
- };
- define.classMethod('dropCollection', {callback: true, promise:true});
- Db.prototype.dropDatabase = function(options, callback) {
- var self = this;
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
-
- var cmd = {'dropDatabase':1};
-
- decorateWithWriteConcern(cmd, self, options);
-
- options = assign({}, this.s.options, {readPreference: ReadPreference.PRIMARY});
-
- if(typeof callback == 'function') return this.command(cmd, options, function(err, result) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
- if(callback == null) return;
- if(err) return handleCallback(callback, err, null);
- handleCallback(callback, null, result.ok ? true : false);
- });
-
- return new this.s.promiseLibrary(function(resolve, reject) {
-
- self.command(cmd, options, function(err, result) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return reject(new MongoError('topology was destroyed'));
- if(err) return reject(err);
- if(result.ok) return resolve(true);
- resolve(false);
- });
- });
- }
- define.classMethod('dropDatabase', {callback: true, promise:true});
- var collections = function(self, callback) {
-
- self.listCollections().toArray(function(err, documents) {
- if(err != null) return handleCallback(callback, err, null);
-
- documents = documents.filter(function(doc) {
- return doc.name.indexOf('$') == -1;
- });
-
- handleCallback(callback, null, documents.map(function(d) {
- return new Collection(self, self.s.topology, self.s.databaseName, d.name.replace(self.s.databaseName + ".", ''), self.s.pkFactory, self.s.options);
- }));
- });
- }
- Db.prototype.collections = function(callback) {
- var self = this;
-
- if(typeof callback == 'function') return collections(self, callback);
-
- return new self.s.promiseLibrary(function(resolve, reject) {
- collections(self, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- define.classMethod('collections', {callback: true, promise:true});
- Db.prototype.executeDbAdminCommand = function(selector, options, callback) {
- var self = this;
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
-
- if(typeof callback == 'function') {
-
- if(options.readPreference) {
- options.readPreference = convertReadPreference(options.readPreference)
- }
- return self.s.topology.command('admin.$cmd', selector, options, function(err, result) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
- if(err) return handleCallback(callback, err);
- handleCallback(callback, null, result.result);
- });
- }
-
- return new self.s.promiseLibrary(function(resolve, reject) {
- self.s.topology.command('admin.$cmd', selector, options, function(err, result) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return reject(new MongoError('topology was destroyed'));
- if(err) return reject(err);
- resolve(result.result);
- });
- });
- };
- define.classMethod('executeDbAdminCommand', {callback: true, promise:true});
- Db.prototype.createIndex = function(name, fieldOrSpec, options, callback) {
- var self = this;
- var args = Array.prototype.slice.call(arguments, 2);
- callback = args.pop();
- if(typeof callback != 'function') args.push(callback);
- options = args.length ? args.shift() || {} : {};
- options = typeof callback === 'function' ? options : callback;
- options = options == null ? {} : options;
-
- options = shallowClone(options);
-
- options.readPreference = ReadPreference.PRIMARY;
-
- if(typeof callback == 'function') return createIndex(self, name, fieldOrSpec, options, callback);
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- createIndex(self, name, fieldOrSpec, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- var createIndex = function(self, name, fieldOrSpec, options, callback) {
-
- var finalOptions = writeConcern({}, self, options);
-
- if(finalOptions.writeConcern && typeof callback != 'function') {
- throw MongoError.create({message: "Cannot use a writeConcern without a provided callback", driver:true});
- }
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
- createIndexUsingCreateIndexes(self, name, fieldOrSpec, options, function(err, result) {
- if(err == null) return handleCallback(callback, err, result);
-
-
-
-
-
- if(err.code === 67 || err.code == 11000 || err.code === 85) {
- return handleCallback(callback, err, result);
- }
-
- var doc = createCreateIndexCommand(self, name, fieldOrSpec, options);
-
- finalOptions.checkKeys = false;
-
- self.s.topology.insert(f("%s.%s", self.s.databaseName, Db.SYSTEM_INDEX_COLLECTION), doc, finalOptions, function(err, result) {
- if(callback == null) return;
- if(err) return handleCallback(callback, err);
- if(result == null) return handleCallback(callback, null, null);
- if(result.result.writeErrors) return handleCallback(callback, MongoError.create(result.result.writeErrors[0]), null);
- handleCallback(callback, null, doc.name);
- });
- });
- }
- define.classMethod('createIndex', {callback: true, promise:true});
- Db.prototype.ensureIndex = function(name, fieldOrSpec, options, callback) {
- var self = this;
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
-
- if(typeof callback == 'function') return ensureIndex(self, name, fieldOrSpec, options, callback);
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- ensureIndex(self, name, fieldOrSpec, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- var ensureIndex = function(self, name, fieldOrSpec, options, callback) {
-
- var finalOptions = writeConcern({}, self, options);
-
- var selector = createCreateIndexCommand(self, name, fieldOrSpec, options);
- var index_name = selector.name;
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
- self.indexInformation(name, finalOptions, function(err, indexInformation) {
- if(err != null && err.code != 26) return handleCallback(callback, err, null);
-
- if(indexInformation == null || !indexInformation[index_name]) {
- self.createIndex(name, fieldOrSpec, options, callback);
- } else {
- if(typeof callback === 'function') return handleCallback(callback, null, index_name);
- }
- });
- }
- define.classMethod('ensureIndex', {callback: true, promise:true});
- Db.prototype.addChild = function(db) {
- if(this.s.parentDb) return this.s.parentDb.addChild(db);
- this.s.children.push(db);
- }
- Db.prototype.db = function(dbName, options) {
- options = options || {};
-
- var finalOptions = assign({}, this.options, options);
-
- if(this.s.dbCache[dbName] && finalOptions.returnNonCachedInstance !== true) {
- return this.s.dbCache[dbName];
- }
-
- if(finalOptions.noListener == null || finalOptions.noListener == false) {
- finalOptions.parentDb = this;
- }
-
- finalOptions.promiseLibrary = this.s.promiseLibrary;
-
- var db = new Db(dbName, this.s.topology, finalOptions)
-
- if(finalOptions.noListener == null || finalOptions.noListener == false) {
- this.addChild(db);
- }
-
- this.s.dbCache[dbName] = db;
-
- return db;
- };
- define.classMethod('db', {callback: false, promise:false, returns: [Db]});
- var _executeAuthCreateUserCommand = function(self, username, password, options, callback) {
-
- if(typeof username == 'string'
- && password != null && typeof password == 'object') {
- options = password;
- password = null;
- }
-
- if(typeof options == 'function') {
- callback = options;
- options = {};
- }
-
- if(options.digestPassword != null) {
- throw toError("The digestPassword option is not supported via add_user. Please use db.command('createUser', ...) instead for this option.");
- }
-
- var customData = options.customData != null ? options.customData : {};
- var roles = Array.isArray(options.roles) ? options.roles : [];
- var maxTimeMS = typeof options.maxTimeMS == 'number' ? options.maxTimeMS : null;
-
- if(roles.length == 0) {
- console.log("Creating a user without roles is deprecated in MongoDB >= 2.6");
- }
-
- var commandOptions = {writeCommand:true};
- if(options['dbName']) commandOptions.dbName = options['dbName'];
-
- if(maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
-
- if((self.databaseName.toLowerCase() == 'admin' || options.dbName == 'admin') && !Array.isArray(options.roles)) {
- roles = ['root']
- } else if(!Array.isArray(options.roles)) {
- roles = ['dbOwner']
- }
-
- var command = {
- createUser: username
- , customData: customData
- , roles: roles
- , digestPassword:false
- }
-
- command = writeConcern(command, self, options);
-
- var md5 = crypto.createHash('md5');
-
- md5.update(username + ":mongo:" + password);
- var userPassword = md5.digest('hex');
-
- if(typeof password == 'string') {
- command.pwd = userPassword;
- }
-
- commandOptions.readPreference = ReadPreference.primary;
-
- self.command(command, commandOptions, function(err, result) {
- if(err && err.ok == 0 && err.code == undefined) return handleCallback(callback, {code: -5000}, null);
- if(err) return handleCallback(callback, err, null);
- handleCallback(callback, !result.ok ? toError(result) : null
- , result.ok ? [{user: username, pwd: ''}] : null);
- })
- }
- var addUser = function(self, username, password, options, callback) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
- _executeAuthCreateUserCommand(self, username, password, options, function(err, r) {
-
- if(err && err.code == -5000) {
- var finalOptions = writeConcern(shallowClone(options), self, options);
-
- var md5 = crypto.createHash('md5');
-
- md5.update(username + ":mongo:" + password);
- var userPassword = md5.digest('hex');
-
- var db = options.dbName ? self.db(options.dbName) : self;
-
- var collection = db.collection(Db.SYSTEM_USER_COLLECTION);
-
- collection.count({}, function(err, count) {
-
- if(err != null) return handleCallback(callback, err, null);
-
- collection.find({user: username}, {dbName: options['dbName']}).toArray(function(err) {
-
- if(err != null) return handleCallback(callback, err, null);
-
- finalOptions.upsert = true;
-
- collection.update({user: username},{$set: {user: username, pwd: userPassword}}, finalOptions, function(err) {
- if(count == 0 && err) return handleCallback(callback, null, [{user:username, pwd:userPassword}]);
- if(err) return handleCallback(callback, err, null)
- handleCallback(callback, null, [{user:username, pwd:userPassword}]);
- });
- });
- });
- return;
- }
- if(err) return handleCallback(callback, err);
- handleCallback(callback, err, r);
- });
- }
- Db.prototype.addUser = function(username, password, options, callback) {
-
- var self = this;
- var args = Array.prototype.slice.call(arguments, 2);
- callback = args.pop();
- if(typeof callback != 'function') args.push(callback);
- options = args.length ? args.shift() || {} : {};
-
- if(typeof callback == 'function') return addUser(self, username, password, options, callback);
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- addUser(self, username, password, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- define.classMethod('addUser', {callback: true, promise:true});
- var _executeAuthRemoveUserCommand = function(self, username, options, callback) {
- if(typeof options == 'function') callback = options, options = {};
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
- var commandOptions = {writeCommand:true};
- if(options['dbName']) commandOptions.dbName = options['dbName'];
-
- var maxTimeMS = typeof options.maxTimeMS == 'number' ? options.maxTimeMS : null;
-
- if(maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
-
- var command = {
- dropUser: username
- }
-
- command = writeConcern(command, self, options);
-
- commandOptions.readPreference = ReadPreference.primary;
-
- self.command(command, commandOptions, function(err, result) {
- if(err && !err.ok && err.code == undefined) return handleCallback(callback, {code: -5000});
- if(err) return handleCallback(callback, err, null);
- handleCallback(callback, null, result.ok ? true : false);
- })
- }
- var removeUser = function(self, username, options, callback) {
-
- _executeAuthRemoveUserCommand(self, username, options, function(err, result) {
- if(err && err.code == -5000) {
- var finalOptions = writeConcern(shallowClone(options), self, options);
-
- var db = options.dbName ? self.db(options.dbName) : self;
-
- var collection = db.collection(Db.SYSTEM_USER_COLLECTION);
-
- collection.findOne({user: username}, {}, function(err, user) {
- if(user == null) return handleCallback(callback, err, false);
- collection.remove({user: username}, finalOptions, function(err) {
- handleCallback(callback, err, true);
- });
- });
- return;
- }
- if(err) return handleCallback(callback, err);
- handleCallback(callback, err, result);
- });
- }
- define.classMethod('removeUser', {callback: true, promise:true});
- Db.prototype.removeUser = function(username, options, callback) {
-
- var self = this;
- var args = Array.prototype.slice.call(arguments, 1);
- callback = args.pop();
- if(typeof callback != 'function') args.push(callback);
- options = args.length ? args.shift() || {} : {};
-
- if(typeof callback == 'function') return removeUser(self, username, options, callback);
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- removeUser(self, username, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- var authenticate = function(self, username, password, options, callback) {
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
-
- var authdb = options.dbName ? options.dbName : self.databaseName;
- authdb = self.authSource ? self.authSource : authdb;
- authdb = options.authdb ? options.authdb : authdb;
- authdb = options.authSource ? options.authSource : authdb;
-
- var _callback = function(err, result) {
- if(self.listeners('authenticated').length > 0) {
- self.emit('authenticated', err, result);
- }
-
- handleCallback(callback, err, result);
- }
-
- var authMechanism = options.authMechanism || '';
- authMechanism = authMechanism.toUpperCase();
-
- if(authMechanism == 'MONGODB-CR') {
- self.s.topology.auth('mongocr', authdb, username, password, function(err) {
- if(err) return handleCallback(callback, err, false);
- _callback(null, true);
- });
- } else if(authMechanism == 'PLAIN') {
- self.s.topology.auth('plain', authdb, username, password, function(err) {
- if(err) return handleCallback(callback, err, false);
- _callback(null, true);
- });
- } else if(authMechanism == 'MONGODB-X509') {
- self.s.topology.auth('x509', authdb, username, password, function(err) {
- if(err) return handleCallback(callback, err, false);
- _callback(null, true);
- });
- } else if(authMechanism == 'SCRAM-SHA-1') {
- self.s.topology.auth('scram-sha-1', authdb, username, password, function(err) {
- if(err) return handleCallback(callback, err, false);
- _callback(null, true);
- });
- } else if(authMechanism == 'GSSAPI') {
- if(process.platform == 'win32') {
- self.s.topology.auth('sspi', authdb, username, password, options, function(err) {
- if(err) return handleCallback(callback, err, false);
- _callback(null, true);
- });
- } else {
- self.s.topology.auth('gssapi', authdb, username, password, options, function(err) {
- if(err) return handleCallback(callback, err, false);
- _callback(null, true);
- });
- }
- } else if(authMechanism == 'DEFAULT') {
- self.s.topology.auth('default', authdb, username, password, function(err) {
- if(err) return handleCallback(callback, err, false);
- _callback(null, true);
- });
- } else {
- handleCallback(callback, MongoError.create({message: f("authentication mechanism %s not supported", options.authMechanism), driver:true}));
- }
- }
- Db.prototype.authenticate = function(username, password, options, callback) {
- if(typeof options == 'function') callback = options, options = {};
- var self = this;
-
- options = shallowClone(options);
-
- if(!options.authMechanism) {
- options.authMechanism = 'DEFAULT';
- } else if(options.authMechanism != 'GSSAPI'
- && options.authMechanism != 'DEFAULT'
- && options.authMechanism != 'MONGODB-CR'
- && options.authMechanism != 'MONGODB-X509'
- && options.authMechanism != 'SCRAM-SHA-1'
- && options.authMechanism != 'PLAIN') {
- return handleCallback(callback, MongoError.create({message: "only DEFAULT, GSSAPI, PLAIN, MONGODB-X509, SCRAM-SHA-1 or MONGODB-CR is supported by authMechanism", driver:true}));
- }
-
- if(typeof callback == 'function') return authenticate(self, username, password, options, function(err, r) {
-
- if(err && err.message && err.message.indexOf('saslStart') != -1) err.code = 59;
-
- if(err) return callback(err, r);
- callback(null, r);
- });
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- authenticate(self, username, password, options, function(err, r) {
-
- if(err && err.message && err.message.indexOf('saslStart') != -1) err.code = 59;
-
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- define.classMethod('authenticate', {callback: true, promise:true});
- Db.prototype.logout = function(options, callback) {
- var self = this;
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
-
- var dbName = this.s.authSource ? this.s.authSource : this.s.databaseName;
- dbName = options.dbName ? options.dbName : dbName;
-
- if(typeof callback == 'function') {
- return self.s.topology.logout(dbName, function(err) {
- if(err) return callback(err);
- callback(null, true);
- });
- }
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- self.s.topology.logout(dbName, function(err) {
- if(err) return reject(err);
- resolve(true);
- });
- });
- }
- define.classMethod('logout', {callback: true, promise:true});
- Db.prototype.indexInformation = function(name, options, callback) {
- var self = this;
- if(typeof options == 'function') callback = options, options = {};
- options = options || {};
-
- if(typeof callback == 'function') return indexInformation(self, name, options, callback);
-
- return new this.s.promiseLibrary(function(resolve, reject) {
- indexInformation(self, name, options, function(err, r) {
- if(err) return reject(err);
- resolve(r);
- });
- });
- };
- var indexInformation = function(self, name, options, callback) {
-
- var full = options['full'] == null ? false : options['full'];
-
- if(self.serverConfig && self.serverConfig.isDestroyed()) return callback(new MongoError('topology was destroyed'));
-
- var processResults = function(indexes) {
-
- var info = {};
-
- for(var i = 0; i < indexes.length; i++) {
- var index = indexes[i];
-
- info[index.name] = [];
- for(var name in index.key) {
- info[index.name].push([name, index.key[name]]);
- }
- }
- return info;
- }
-
- self.collection(name).listIndexes().toArray(function(err, indexes) {
- if(err) return callback(toError(err));
- if(!Array.isArray(indexes)) return handleCallback(callback, null, []);
- if(full) return handleCallback(callback, null, indexes);
- handleCallback(callback, null, processResults(indexes));
- });
- }
- define.classMethod('indexInformation', {callback: true, promise:true});
- var createCreateIndexCommand = function(db, name, fieldOrSpec, options) {
- var indexParameters = parseIndexOptions(fieldOrSpec);
- var fieldHash = indexParameters.fieldHash;
-
- var indexName = typeof options.name == 'string' ? options.name : indexParameters.name;
- var selector = {
- 'ns': db.databaseName + "." + name, 'key': fieldHash, 'name': indexName
- }
-
- var finalUnique = options == null || 'object' === typeof options ? false : options;
-
- options = options == null || typeof options == 'boolean' ? {} : options;
-
- var keysToOmit = Object.keys(selector);
- for(var optionName in options) {
- if(keysToOmit.indexOf(optionName) == -1) {
- selector[optionName] = options[optionName];
- }
- }
- if(selector['unique'] == null) selector['unique'] = finalUnique;
-
- var removeKeys = ['w', 'wtimeout', 'j', 'fsync', 'readPreference'];
- for(var i = 0; i < removeKeys.length; i++) {
- delete selector[removeKeys[i]];
- }
-
- return selector;
- }
- var createIndexUsingCreateIndexes = function(self, name, fieldOrSpec, options, callback) {
-
- var indexParameters = parseIndexOptions(fieldOrSpec);
-
- var indexName = typeof options.name == 'string' ? options.name : indexParameters.name;
-
- var indexes = [{ name: indexName, key: indexParameters.fieldHash }];
-
- var keysToOmit = Object.keys(indexes[0]);
- for(var optionName in options) {
- if(keysToOmit.indexOf(optionName) == -1) {
- indexes[0][optionName] = options[optionName];
- }
-
- var removeKeys = ['w', 'wtimeout', 'j', 'fsync', 'readPreference'];
- for(var i = 0; i < removeKeys.length; i++) {
- delete indexes[0][removeKeys[i]];
- }
- }
-
- var capabilities = self.s.topology.capabilities();
-
- if(indexes[0].collation && capabilities && !capabilities.commandsTakeCollation) {
-
- var error = new MongoError(f('server/primary/mongos does not support collation'));
- error.code = 67;
-
- return callback(error);
- }
-
- var cmd = writeConcern({createIndexes: name, indexes: indexes}, self, options);
-
- decorateWithWriteConcern(cmd, self, options);
-
- options.readPreference = ReadPreference.PRIMARY;
-
- self.command(cmd, options, function(err, result) {
- if(err) return handleCallback(callback, err, null);
- if(result.ok == 0) return handleCallback(callback, toError(result), null);
-
- handleCallback(callback, null, indexName);
- });
- }
- var validateDatabaseName = function(databaseName) {
- if(typeof databaseName !== 'string') throw MongoError.create({message: "database name must be a string", driver:true});
- if(databaseName.length === 0) throw MongoError.create({message: "database name cannot be the empty string", driver:true});
- if(databaseName == '$external') return;
- var invalidChars = [" ", ".", "$", "/", "\\"];
- for(var i = 0; i < invalidChars.length; i++) {
- if(databaseName.indexOf(invalidChars[i]) != -1) throw MongoError.create({message: "database names cannot contain the character '" + invalidChars[i] + "'", driver:true});
- }
- }
- var writeConcern = function(target, db, options) {
- if(options.w != null || options.j != null || options.fsync != null) {
- var opts = {};
- if(options.w) opts.w = options.w;
- if(options.wtimeout) opts.wtimeout = options.wtimeout;
- if(options.j) opts.j = options.j;
- if(options.fsync) opts.fsync = options.fsync;
- target.writeConcern = opts;
- } else if(db.writeConcern.w != null || db.writeConcern.j != null || db.writeConcern.fsync != null) {
- target.writeConcern = db.writeConcern;
- }
- return target
- }
- var createListener = function(self, e, object) {
- var listener = function(err) {
- if(object.listeners(e).length > 0) {
- object.emit(e, err, self);
-
- for(var i = 0; i < self.s.children.length; i++) {
- self.s.children[i].emit(e, err, self.s.children[i]);
- }
- }
- }
- return listener;
- }
- Db.prototype.unref = function() {
- this.s.topology.unref();
- }
- Db.SYSTEM_NAMESPACE_COLLECTION = "system.namespaces";
- Db.SYSTEM_INDEX_COLLECTION = "system.indexes";
- Db.SYSTEM_PROFILE_COLLECTION = "system.profile";
- Db.SYSTEM_USER_COLLECTION = "system.users";
- Db.SYSTEM_COMMAND_COLLECTION = "$cmd";
- Db.SYSTEM_JS_COLLECTION = "system.js";
- module.exports = Db;
|