Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: useConnection(connection) function #14802

Open
wants to merge 4 commits into
base: 8.8
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 58 additions & 3 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,62 @@ Model.prototype.$isMongooseModelPrototype = true;

Model.prototype.db;

/**
* Changes the Connection instance this model uses to make requests to MongoDB.
* This function is most useful for changing the Connection that a Model defined using `mongoose.model()` uses
* after initialization.
*
* #### Example:
*
* await mongoose.connect('mongodb://127.0.0.1:27017/db1');
* const UserModel = mongoose.model('User', mongoose.Schema({ name: String }));
* UserModel.connection === mongoose.connection; // true
*
* const conn2 = await mongoose.createConnection('mongodb://127.0.0.1:27017/db2').asPromise();
* UserModel.useConnection(conn2); // `UserModel` now stores documents in `db2`, not `db1`
*
* UserModel.connection === mongoose.connection; // false
* UserModel.connection === conn2; // true
*
* conn2.model('User') === UserModel; // true
* mongoose.model('User'); // Throws 'MissingSchemaError'
*
* Note: `useConnection()` does **not** apply any [connection-level plugins](https://mongoosejs.com/docs/api/connection.html#Connection.prototype.plugin()) from the new connection.
* If you use `useConnection()` to switch a model's connection, the model will still have the old connection's plugins.
*
* @function useConnection
* @param [Connection] connection The new connection to use
* @return [Model] this
* @api public
*/

Model.useConnection = function useConnection(connection) {
if (!connection) {
throw new Error('Please provide a connection.');
}
if (this.db) {
delete this.db.models[this.modelName];
delete this.prototype.db;
delete this.prototype[modelDbSymbol];
delete this.prototype.collection;
delete this.prototype.$collection;
delete this.prototype[modelCollectionSymbol];
}

this.db = connection;
const collection = connection.collection(this.modelName, connection.options);
this.prototype.collection = collection;
this.prototype.$collection = collection;
this.prototype[modelCollectionSymbol] = collection;
this.prototype.db = connection;
this.prototype[modelDbSymbol] = connection;
this.collection = collection;
this.$__collection = collection;
connection.models[this.modelName] = this;
vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved

return this;
};

/**
* The collection instance this model uses.
* A Mongoose collection is a thin wrapper around a [MongoDB Node.js driver collection]([MongoDB Node.js driver collection](https://mongodb.github.io/node-mongodb-native/Next/classes/Collection.html)).
Expand Down Expand Up @@ -2069,9 +2125,8 @@ Model.estimatedDocumentCount = function estimatedDocumentCount(options) {
*
* #### Example:
*
* Adventure.countDocuments({ type: 'jungle' }, function (err, count) {
* console.log('there are %d jungle adventures', count);
* });
* const count = await Adventure.countDocuments({ type: 'jungle' });
* console.log('there are %d jungle adventures', count);
*
* If you want to count all documents in a large collection,
* use the [`estimatedDocumentCount()` function](https://mongoosejs.com/docs/api/model.html#Model.estimatedDocumentCount())
Expand Down
43 changes: 43 additions & 0 deletions test/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7821,6 +7821,49 @@ describe('Model', function() {
assert.strictEqual(doc.__v, 0);
});

describe('Model.useConnection() (gh-14802)', function() {
it('updates the model\'s db property to point to the provided connection instance and vice versa (gh-14802))', async function() {
const schema = new mongoose.Schema({
name: String
});
const Model = db.model('Test', schema);
assert.equal(db.model('Test'), Model);
const original = Model.find();
assert.equal(original.model.collection.conn.name, 'mongoose_test');
await Model.create({ name: 'gh-14802 test' });
let docs = await original;
assert.equal(docs.length, 1);
assert.strictEqual(docs[0].name, 'gh-14802 test');

const connection = start({ uri: start.uri2 });
await connection.asPromise();
await Model.useConnection(connection);
assert.equal(db.models[Model.modelName], undefined);
assert(connection.models[Model.modelName]);
const query = Model.find();
assert.equal(query.model.collection.conn.name, 'mongoose_test_2');

await Model.deleteMany({});
await Model.create({ name: 'gh-14802 test 2' });
docs = await query;
assert.equal(docs.length, 1);
assert.strictEqual(docs[0].name, 'gh-14802 test 2');

assert.equal(connection.model('Test'), Model);
assert.throws(() => db.model('Test'), /MissingSchemaError/);
});

it('should throw an error if no connection is passed', async function() {
const schema = new mongoose.Schema({
name: String
});
const Model = db.model('Test', schema);
assert.throws(() => {
Model.useConnection();
}, { message: 'Please provide a connection.' });
});
});

it('insertMany should throw an error if there were operations that failed validation, ' +
'but all operations that passed validation succeeded (gh-13256)', async function() {
const userSchema = new Schema({
Expand Down
11 changes: 11 additions & 0 deletions test/types/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import mongoose, {
Schema,
Document,
Model,
createConnection,
connection,
model,
Types,
Expand Down Expand Up @@ -977,3 +978,13 @@ function testWithLevel1NestedPaths() {
'foo.one': string | null | undefined
}>({} as Test2);
}

async function gh14802() {
const schema = new mongoose.Schema({
name: String
});
const Model = model('Test', schema);

const conn2 = mongoose.createConnection('mongodb://127.0.0.1:27017/mongoose_test');
Model.useConnection(conn2);
}
7 changes: 7 additions & 0 deletions types/models.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,13 @@ declare module 'mongoose' {
*/
updateSearchIndex(name: string, definition: AnyObject): Promise<void>;

/**
* Changes the Connection instance this model uses to make requests to MongoDB.
* This function is most useful for changing the Connection that a Model defined using `mongoose.model()` uses
* after initialization.
*/
useConnection(connection: Connection): this;

/** Casts and validates the given object against this model's schema, passing the given `context` to custom validators. */
validate(): Promise<void>;
validate(obj: any): Promise<void>;
Expand Down