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(mongoose): add setDriver() function to allow overwriting driver in a more consistent way #11900

Merged
merged 8 commits into from
Jun 16, 2022
2 changes: 1 addition & 1 deletion lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,7 @@ Connection.prototype.collection = function(name, options) {
};
options = Object.assign({}, defaultOptions, options ? utils.clone(options) : {});
options.$wasForceClosed = this.$wasForceClosed;
const Collection = driver.get().Collection;
const Collection = this.base && this.base.__driver ? this.base.__driver.Collection : driver.get().Collection;
vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved
if (!(name in this.collections)) {
this.collections[name] = new Collection(name, this, options);
}
Expand Down
72 changes: 56 additions & 16 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const shardingPlugin = require('./plugins/sharding');
const trusted = require('./helpers/query/trusted').trusted;
const sanitizeFilter = require('./helpers/query/sanitizeFilter');
const isBsonType = require('./helpers/isBsonType');
const MongooseError = require('./error/mongooseError');

const defaultMongooseSymbol = Symbol.for('mongoose:default');

Expand Down Expand Up @@ -62,6 +63,7 @@ function Mongoose(options) {
this.connections = [];
this.models = {};
this.events = new EventEmitter();
this.__driver = driver.get();
// default global options
this.options = Object.assign({
pluralization: true,
Expand Down Expand Up @@ -135,13 +137,44 @@ Mongoose.prototype.ConnectionStates = STATES;
* uses to communicate with the database. A driver is a Mongoose-specific interface that defines functions
* like `find()`.
*
* @deprecated
* @memberOf Mongoose
* @property driver
* @api public
*/

Mongoose.prototype.driver = driver;

/**
* Overwrites the current driver used by this Mongoose instance. A driver is a
* Mongoose-specific interface that defines functions like `find()`.
*
* @memberOf Mongoose
* @method setDriver
* @api public
*/

Mongoose.prototype.setDriver = function setDriver(driver) {
const _mongoose = this instanceof Mongoose ? this : mongoose;

if (_mongoose.__driver === driver) {
return;
vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved
}

const openConnection = _mongoose.connections && _mongoose.connections.find(conn => conn.readyState !== STATES.disconnected);
if (openConnection) {
const msg = 'Cannot modify Mongoose driver if a connection is already open. ' +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: why do we need a one time variable? We could directly pass it to MongooseError

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better readability IMO. Easier to break up the long message if there's a temporary variable rather than putting it as a param.

'Call `mongoose.disconnect()` before modifying the driver';
throw new MongooseError(msg);
}
_mongoose.__driver = driver;

const Connection = driver.getConnection();
_mongoose.connections = [new Connection(_mongoose)];

return _mongoose;
};

/**
* Sets mongoose options
*
Expand Down Expand Up @@ -275,7 +308,7 @@ Mongoose.prototype.get = Mongoose.prototype.set;
Mongoose.prototype.createConnection = function(uri, options, callback) {
const _mongoose = this instanceof Mongoose ? this : mongoose;

const Connection = driver.get().getConnection();
const Connection = _mongoose.__driver.getConnection();
const conn = new Connection(_mongoose);
if (typeof options === 'function') {
callback = options;
Expand Down Expand Up @@ -688,7 +721,7 @@ Mongoose.prototype.__defineGetter__('connection', function() {
});

Mongoose.prototype.__defineSetter__('connection', function(v) {
if (v instanceof Connection) {
if (v instanceof this.__driver.getConnection()) {
this.connections[0] = v;
this.models = v.models;
}
Expand Down Expand Up @@ -717,18 +750,6 @@ Mongoose.prototype.__defineSetter__('connection', function(v) {

Mongoose.prototype.connections;

/*!
* Connection
*/

const Connection = driver.get().getConnection();

/*!
* Collection
*/

const Collection = driver.get().Collection;

/**
* The Mongoose Aggregate constructor
*
Expand All @@ -745,7 +766,14 @@ Mongoose.prototype.Aggregate = Aggregate;
* @api public
*/

Mongoose.prototype.Collection = Collection;
Object.defineProperty(Mongoose.prototype, 'Collection', {
get: function() {
return this.__driver.Collection;
},
set: function(Collection) {
this.__driver.Collection = Collection;
}
});

/**
* The Mongoose [Connection](#connection_Connection) constructor
Expand All @@ -756,7 +784,19 @@ Mongoose.prototype.Collection = Collection;
* @api public
*/

Mongoose.prototype.Connection = Connection;

vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved
Object.defineProperty(Mongoose.prototype, 'Connection', {
get: function() {
return this.__driver.getConnection();
},
set: function(Connection) {
if (Connection === this.__driver.getConnection()) {
return;
}

this.__driver.getConnection = () => Connection;
}
});

/**
* The Mongoose version
Expand Down
25 changes: 25 additions & 0 deletions test/types/populate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,29 @@ async function _11532() {
if (!leanResult) return;
expectType<string>(leanResult.child.name);
expectError(leanResult?.__v);
}

async function gh11710() {
vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved

// `Parent` represents the object as it is stored in MongoDB
interface Parent {
child?: Types.ObjectId,
name?: string
}
interface Child {
name: string;
}
interface PopulatedParent {
child: Child | null;
}
const ParentModel = model<Parent>('Parent', new Schema({
child: { type: Schema.Types.ObjectId, ref: 'Child' },
name: String
}));
const childSchema: Schema = new Schema({ name: String });
const ChildModel = model<Child>('Child', childSchema);

// Populate with `Paths` generic `{ child: Child }` to override `child` path
const doc = await ParentModel.findOne({}).populate<Pick<PopulatedParent, 'child'>>('child').orFail();
expectType<Child | null>(doc.child);
}
6 changes: 6 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ declare module 'mongoose' {
/** Returns an array of model names created on this instance of Mongoose. */
export function modelNames(): Array<string>;

/**
* Overwrites the current driver used by this Mongoose instance. A driver is a
* Mongoose-specific interface that defines functions like `find()`.
*/
export function setDriver(driver: any): Mongoose;

/** The node-mongodb-native driver Mongoose uses. */
export const mongo: typeof mongodb;

Expand Down