diff --git a/lib/mongoose.js b/lib/mongoose.js index cfac2331ced..9a9c26d2bb1 100644 --- a/lib/mongoose.js +++ b/lib/mongoose.js @@ -33,6 +33,7 @@ const SetOptionError = require('./error/setOptionError'); const applyEmbeddedDiscriminators = require('./helpers/discriminator/applyEmbeddedDiscriminators'); const defaultMongooseSymbol = Symbol.for('mongoose:default'); +const defaultConnectionSymbol = Symbol('mongoose:defaultConnection'); require('./helpers/printJestWarning'); @@ -72,8 +73,7 @@ function Mongoose(options) { }, options); const createInitialConnection = utils.getOption('createInitialConnection', this.options) ?? true; if (createInitialConnection && this.__driver != null) { - const conn = this.createConnection(); // default connection - conn.models = this.models; + _createDefaultConnection(this); } if (this.options.pluralization) { @@ -292,6 +292,14 @@ Mongoose.prototype.set = function(key, value) { } else if (!optionValue && _mongoose.transactionAsyncLocalStorage) { delete _mongoose.transactionAsyncLocalStorage; } + } else if (optionKey === 'createInitialConnection') { + if (optionValue && !_mongoose.connection) { + _createDefaultConnection(_mongoose); + } else if (optionValue === false && _mongoose.connection && _mongoose.connection[defaultConnectionSymbol]) { + if (_mongoose.connection.readyState === STATES.disconnected && Object.keys(_mongoose.connection.models).length === 0) { + _mongoose.connections.shift(); + } + } } } @@ -424,6 +432,9 @@ Mongoose.prototype.connect = async function connect(uri, options) { } const _mongoose = this instanceof Mongoose ? this : mongoose; + if (_mongoose.connection == null) { + _createDefaultConnection(_mongoose); + } const conn = _mongoose.connection; return conn.openUri(uri, options).then(() => _mongoose); @@ -1315,6 +1326,20 @@ Mongoose.prototype.overwriteMiddlewareResult = Kareem.overwriteResult; Mongoose.prototype.omitUndefined = require('./helpers/omitUndefined'); +/*! + * Create a new default connection (`mongoose.connection`) for a Mongoose instance. + * No-op if there is already a default connection. + */ + +function _createDefaultConnection(mongoose) { + if (mongoose.connection) { + return; + } + const conn = mongoose.createConnection(); // default connection + conn[defaultConnectionSymbol] = true; + conn.models = mongoose.models; +} + /** * The exports object is an instance of Mongoose. * diff --git a/lib/validOptions.js b/lib/validOptions.js index 2654a7521ed..15fd3e634e7 100644 --- a/lib/validOptions.js +++ b/lib/validOptions.js @@ -15,6 +15,7 @@ const VALID_OPTIONS = Object.freeze([ 'bufferCommands', 'bufferTimeoutMS', 'cloneSchemas', + 'createInitialConnection', 'debug', 'id', 'timestamps.createdAt.immutable', diff --git a/test/index.test.js b/test/index.test.js index 4eccc90a206..cfbd644f1f3 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1180,4 +1180,52 @@ describe('mongoose module:', function() { } }); }); + + describe('createInitialConnection (gh-8302)', function() { + let m; + + beforeEach(function() { + m = new mongoose.Mongoose(); + }); + + afterEach(async function() { + await m.disconnect(); + }); + + it('should delete existing connection when setting createInitialConnection to false', function() { + assert.ok(m.connection); + m.set('createInitialConnection', false); + assert.strictEqual(m.connection, undefined); + }); + + it('should create connection when createConnection is called', function() { + m.set('createInitialConnection', false); + const conn = m.createConnection(); + assert.equal(conn, m.connection); + }); + + it('should create a new connection automatically when connect() is called if no existing default connection', async function() { + assert.ok(m.connection); + m.set('createInitialConnection', false); + assert.strictEqual(m.connection, undefined); + + await m.connect(start.uri); + assert.ok(m.connection); + }); + + it('should not delete default connection if it has models', async function() { + assert.ok(m.connection); + m.model('Test', new m.Schema({ name: String })); + m.set('createInitialConnection', false); + assert.ok(m.connection); + }); + + it('should not delete default connection if it is connected', async function() { + assert.ok(m.connection); + await m.connect(start.uri); + m.set('createInitialConnection', false); + assert.ok(m.connection); + assert.equal(m.connection.readyState, 1); + }); + }); });