3 var parse = require('./url_parser')
4 , Server = require('./server')
5 , Mongos = require('./mongos')
6 , ReplSet = require('./replset')
7 , Define = require('./metadata')
8 , ReadPreference = require('./read_preference')
9 , Logger = require('mongodb-core').Logger
10 , MongoError = require('mongodb-core').MongoError
11 , Db = require('./db')
12 , dns = require('dns')
13 , f = require('util').format
14 , shallowClone = require('./utils').shallowClone;
17 * @fileOverview The **MongoClient** class is a class that allows for making Connections to MongoDB.
20 * var MongoClient = require('mongodb').MongoClient,
21 * test = require('assert');
23 * var url = 'mongodb://localhost:27017/test';
24 * // Connect using MongoClient
25 * MongoClient.connect(url, function(err, db) {
26 * // Get an additional db
32 * Creates a new MongoClient instance
34 * @return {MongoClient} a MongoClient instance.
36 function MongoClient() {
38 * The callback format for results
39 * @callback MongoClient~connectCallback
40 * @param {MongoError} error An error instance representing the error during the execution.
41 * @param {Db} db The connected database.
45 * Connect to MongoDB using a url as documented at
47 * docs.mongodb.org/manual/reference/connection-string/
49 * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
52 * @param {string} url The connection URI string
53 * @param {object} [options=null] Optional settings.
54 * @param {boolean} [options.uri_decode_auth=false] Uri decode the user name and password for authentication
55 * @param {object} [options.db=null] A hash of options to set on the db object, see **Db constructor**
56 * @param {object} [options.server=null] A hash of options to set on the server objects, see **Server** constructor**
57 * @param {object} [options.replSet=null] A hash of options to set on the replSet object, see **ReplSet** constructor**
58 * @param {object} [options.mongos=null] A hash of options to set on the mongos object, see **Mongos** constructor**
59 * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
60 * @param {MongoClient~connectCallback} [callback] The command result callback
61 * @return {Promise} returns Promise if no callback passed
63 this.connect = MongoClient.connect;
66 var define = MongoClient.define = new Define('MongoClient', MongoClient, false);
69 * Connect to MongoDB using a url as documented at
71 * docs.mongodb.org/manual/reference/connection-string/
73 * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
77 * @param {string} url The connection URI string
78 * @param {object} [options=null] Optional settings.
79 * @param {boolean} [options.uri_decode_auth=false] Uri decode the user name and password for authentication
80 * @param {object} [options.db=null] A hash of options to set on the db object, see **Db constructor**
81 * @param {object} [options.server=null] A hash of options to set on the server objects, see **Server** constructor**
82 * @param {object} [options.replSet=null] A hash of options to set on the replSet object, see **ReplSet** constructor**
83 * @param {object} [options.mongos=null] A hash of options to set on the mongos object, see **Mongos** constructor**
84 * @param {object} [options.promiseLibrary=null] A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
85 * @param {MongoClient~connectCallback} [callback] The command result callback
86 * @return {Promise} returns Promise if no callback passed
88 MongoClient.connect = function(url, options, callback) {
89 var args = Array.prototype.slice.call(arguments, 1);
90 callback = typeof args[args.length - 1] == 'function' ? args.pop() : null;
91 options = args.length ? args.shift() : null;
92 options = options || {};
94 // Get the promiseLibrary
95 var promiseLibrary = options.promiseLibrary;
97 // No promise library selected fall back
99 promiseLibrary = typeof global.Promise == 'function' ?
100 global.Promise : require('es6-promise').Promise;
104 if(typeof callback != 'function') {
105 return new promiseLibrary(function(resolve, reject) {
106 connect(url, options, function(err, db) {
107 if(err) return reject(err);
113 // Fallback to callback based connect
114 connect(url, options, callback);
117 define.staticMethod('connect', {callback: true, promise:true});
119 var mergeOptions = function(target, source, flatten) {
120 for(var name in source) {
121 if(source[name] && typeof source[name] == 'object' && flatten) {
122 target = mergeOptions(target, source[name], flatten);
124 target[name] = source[name];
131 var createUnifiedOptions = function(finalOptions, options) {
132 var childOptions = ['mongos', 'server', 'db'
133 , 'replset', 'db_options', 'server_options', 'rs_options', 'mongos_options'];
136 for(var name in options) {
137 if(noMerge.indexOf(name.toLowerCase()) != -1) {
138 finalOptions[name] = options[name];
139 } else if(childOptions.indexOf(name.toLowerCase()) != -1) {
140 finalOptions = mergeOptions(finalOptions, options[name], false);
142 if(options[name] && typeof options[name] == 'object' && !Buffer.isBuffer(options[name]) && !Array.isArray(options[name])) {
143 finalOptions = mergeOptions(finalOptions, options[name], true);
145 finalOptions[name] = options[name];
153 function translateOptions(options) {
154 // If we have a readPreference passed in by the db options
155 if(typeof options.readPreference == 'string' || typeof options.read_preference == 'string') {
156 options.readPreference = new ReadPreference(options.readPreference || options.read_preference);
159 // Do we have readPreference tags, add them
160 if(options.readPreference && (options.readPreferenceTags || options.read_preference_tags)) {
161 options.readPreference.tags = options.readPreferenceTags || options.read_preference_tags;
164 // Do we have maxStalenessMS
165 if(options.maxStalenessMS) {
166 options.readPreference.maxStalenessMS = options.maxStalenessMS;
169 // Set the socket and connection timeouts
170 if(!options.socketTimeoutMS == null) options.socketTimeoutMS = 30000;
171 if(!options.connectTimeoutMS == null) options.connectTimeoutMS = 30000;
173 // Create server instances
174 return options.servers.map(function(serverObj) {
175 return serverObj.domain_socket ?
176 new Server(serverObj.domain_socket, 27017, options)
177 : new Server(serverObj.host, serverObj.port, options);
181 function createReplicaset(options, callback) {
182 // Set default options
183 var servers = translateOptions(options);
184 // Create Db instance
185 new Db(options.dbName, new ReplSet(servers, options), options).open(callback);
188 function createMongos(options, callback) {
189 // Set default options
190 var servers = translateOptions(options);
191 // Create Db instance
192 new Db(options.dbName, new Mongos(servers, options), options).open(callback);
195 function createServer(options, callback) {
196 // Set default options
197 var servers = translateOptions(options);
198 // Create Db instance
199 new Db(options.dbName, servers[0], options).open(function(err, db) {
200 if(err) return callback(err);
201 // Check if we are really speaking to a mongos
202 var ismaster = db.serverConfig.lastIsMaster();
204 // Do we actually have a mongos
205 if(ismaster && ismaster.msg == 'isdbgrid') {
206 // Destroy the current connection
208 // Create mongos connection instead
209 return createMongos(options, callback);
212 // Otherwise callback
217 function connectHandler(options, callback) {
218 return function (err, db) {
220 return process.nextTick(function() {
230 // No authentication just reconnect
232 return process.nextTick(function() {
242 // What db to authenticate against
243 var authentication_db = db;
244 if(options.authSource) {
245 authentication_db = db.db(options.authSource);
249 authentication_db.authenticate(options.user, options.password, options, function(err, success){
251 process.nextTick(function() {
261 process.nextTick(function() {
263 callback(err ? err : new Error('Could not authenticate user ' + options.auth[0]), null);
275 * Connect using MongoClient
277 var connect = function(url, options, callback) {
278 options = options || {};
279 options = shallowClone(options);
281 // If callback is null throw an exception
282 if(callback == null) {
283 throw new Error("no callback function provided");
286 // Get a logger for MongoClient
287 var logger = Logger('MongoClient', options);
290 var object = parse(url, options);
291 var _finalOptions = createUnifiedOptions({}, object);
292 _finalOptions = mergeOptions(_finalOptions, object, false);
293 _finalOptions = createUnifiedOptions(_finalOptions, options);
295 // Check if we have connection and socket timeout set
296 if(!_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 120000;
297 if(!_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 120000;
300 if(object.servers.length == 0) {
301 throw new Error("connection string must contain at least one seed host");
304 function connectCallback(err, db) {
305 if(err && err.message == 'no mongos proxies found in seed list') {
306 if(logger.isWarn()) {
307 logger.warn(f('seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name'));
310 // Return a more specific error message for MongoClient.connect
311 return callback(new MongoError('seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name'));
314 // Return the error and db instance
318 // Do we have a replicaset then skip discovery and go straight to connectivity
319 if(_finalOptions.replicaSet || _finalOptions.rs_name) {
320 return createReplicaset(_finalOptions, connectHandler(_finalOptions, connectCallback));
321 } else if(object.servers.length > 1) {
322 return createMongos(_finalOptions, connectHandler(_finalOptions, connectCallback));
324 return createServer(_finalOptions, connectHandler(_finalOptions, connectCallback));
328 module.exports = MongoClient