Skip to content

Commit 6db2e4a

Browse files
authored
Merge pull request #11900 from Automattic/vkarpov15/driver-fixes
feat(mongoose): add `setDriver()` function to allow overwriting `driver` in a more consistent way
2 parents 40626d1 + 567631a commit 6db2e4a

File tree

7 files changed

+132
-19
lines changed

7 files changed

+132
-19
lines changed

lib/connection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,7 @@ Connection.prototype.collection = function(name, options) {
10681068
};
10691069
options = Object.assign({}, defaultOptions, options ? utils.clone(options) : {});
10701070
options.$wasForceClosed = this.$wasForceClosed;
1071-
const Collection = driver.get().Collection;
1071+
const Collection = this.base && this.base.__driver && this.base.__driver.Collection || driver.get().Collection;
10721072
if (!(name in this.collections)) {
10731073
this.collections[name] = new Collection(name, this, options);
10741074
}

lib/index.js

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const shardingPlugin = require('./plugins/sharding');
3535
const trusted = require('./helpers/query/trusted').trusted;
3636
const sanitizeFilter = require('./helpers/query/sanitizeFilter');
3737
const isBsonType = require('./helpers/isBsonType');
38+
const MongooseError = require('./error/mongooseError');
3839

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

@@ -62,6 +63,7 @@ function Mongoose(options) {
6263
this.connections = [];
6364
this.models = {};
6465
this.events = new EventEmitter();
66+
this.__driver = driver.get();
6567
// default global options
6668
this.options = Object.assign({
6769
pluralization: true,
@@ -135,13 +137,44 @@ Mongoose.prototype.ConnectionStates = STATES;
135137
* uses to communicate with the database. A driver is a Mongoose-specific interface that defines functions
136138
* like `find()`.
137139
*
140+
* @deprecated
138141
* @memberOf Mongoose
139142
* @property driver
140143
* @api public
141144
*/
142145

143146
Mongoose.prototype.driver = driver;
144147

148+
/**
149+
* Overwrites the current driver used by this Mongoose instance. A driver is a
150+
* Mongoose-specific interface that defines functions like `find()`.
151+
*
152+
* @memberOf Mongoose
153+
* @method setDriver
154+
* @api public
155+
*/
156+
157+
Mongoose.prototype.setDriver = function setDriver(driver) {
158+
const _mongoose = this instanceof Mongoose ? this : mongoose;
159+
160+
if (_mongoose.__driver === driver) {
161+
return _mongoose;
162+
}
163+
164+
const openConnection = _mongoose.connections && _mongoose.connections.find(conn => conn.readyState !== STATES.disconnected);
165+
if (openConnection) {
166+
const msg = 'Cannot modify Mongoose driver if a connection is already open. ' +
167+
'Call `mongoose.disconnect()` before modifying the driver';
168+
throw new MongooseError(msg);
169+
}
170+
_mongoose.__driver = driver;
171+
172+
const Connection = driver.getConnection();
173+
_mongoose.connections = [new Connection(_mongoose)];
174+
175+
return _mongoose;
176+
};
177+
145178
/**
146179
* Sets mongoose options
147180
*
@@ -278,7 +311,7 @@ Mongoose.prototype.get = Mongoose.prototype.set;
278311
Mongoose.prototype.createConnection = function(uri, options, callback) {
279312
const _mongoose = this instanceof Mongoose ? this : mongoose;
280313

281-
const Connection = driver.get().getConnection();
314+
const Connection = _mongoose.__driver.getConnection();
282315
const conn = new Connection(_mongoose);
283316
if (typeof options === 'function') {
284317
callback = options;
@@ -474,7 +507,6 @@ Mongoose.prototype.pluralize = function(fn) {
474507
*/
475508

476509
Mongoose.prototype.model = function(name, schema, collection, options) {
477-
478510
const _mongoose = this instanceof Mongoose ? this : mongoose;
479511

480512
if (typeof schema === 'string') {
@@ -691,7 +723,7 @@ Mongoose.prototype.__defineGetter__('connection', function() {
691723
});
692724

693725
Mongoose.prototype.__defineSetter__('connection', function(v) {
694-
if (v instanceof Connection) {
726+
if (v instanceof this.__driver.getConnection()) {
695727
this.connections[0] = v;
696728
this.models = v.models;
697729
}
@@ -720,18 +752,6 @@ Mongoose.prototype.__defineSetter__('connection', function(v) {
720752

721753
Mongoose.prototype.connections;
722754

723-
/*!
724-
* Connection
725-
*/
726-
727-
const Connection = driver.get().getConnection();
728-
729-
/*!
730-
* Collection
731-
*/
732-
733-
const Collection = driver.get().Collection;
734-
735755
/**
736756
* The Mongoose Aggregate constructor
737757
*
@@ -748,7 +768,14 @@ Mongoose.prototype.Aggregate = Aggregate;
748768
* @api public
749769
*/
750770

751-
Mongoose.prototype.Collection = Collection;
771+
Object.defineProperty(Mongoose.prototype, 'Collection', {
772+
get: function() {
773+
return this.__driver.Collection;
774+
},
775+
set: function(Collection) {
776+
this.__driver.Collection = Collection;
777+
}
778+
});
752779

753780
/**
754781
* The Mongoose [Connection](#connection_Connection) constructor
@@ -759,7 +786,18 @@ Mongoose.prototype.Collection = Collection;
759786
* @api public
760787
*/
761788

762-
Mongoose.prototype.Connection = Connection;
789+
Object.defineProperty(Mongoose.prototype, 'Connection', {
790+
get: function() {
791+
return this.__driver.getConnection();
792+
},
793+
set: function(Connection) {
794+
if (Connection === this.__driver.getConnection()) {
795+
return;
796+
}
797+
798+
this.__driver.getConnection = () => Connection;
799+
}
800+
});
763801

764802
/**
765803
* The Mongoose version

lib/query.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2197,7 +2197,6 @@ function _castArrayFilters(query) {
21972197
* @api private
21982198
*/
21992199
Query.prototype._find = wrapThunk(function(callback) {
2200-
22012200
this._castConditions();
22022201

22032202
if (this.error() != null) {

lib/utils.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,9 @@ exports.getOption = function(name) {
935935
const sources = Array.prototype.slice.call(arguments, 1);
936936

937937
for (const source of sources) {
938+
if (source == null) {
939+
continue;
940+
}
938941
if (source[name] != null) {
939942
return source[name];
940943
}

test/index.test.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const start = require('./common');
77
const assert = require('assert');
88
const random = require('./util').random;
99
const stream = require('stream');
10+
const { EventEmitter } = require('events');
1011

1112
const collection = 'blogposts_' + random();
1213

@@ -1044,4 +1045,45 @@ describe('mongoose module:', function() {
10441045
});
10451046
});
10461047
});
1048+
1049+
describe('custom drivers', function() {
1050+
it('can set custom driver (gh-11900)', async function() {
1051+
const m = new mongoose.Mongoose();
1052+
1053+
class Collection {
1054+
findOne(filter, options, cb) {
1055+
cb(null, { answer: 42 });
1056+
}
1057+
}
1058+
class Connection extends EventEmitter {
1059+
constructor(base) {
1060+
super();
1061+
this.base = base;
1062+
this.models = {};
1063+
}
1064+
1065+
collection() {
1066+
return new Collection();
1067+
}
1068+
1069+
openUri(uri, opts, callback) {
1070+
this.readyState = mongoose.ConnectionStates.connected;
1071+
callback();
1072+
}
1073+
}
1074+
const driver = {
1075+
Collection,
1076+
getConnection: () => Connection
1077+
};
1078+
1079+
m.setDriver(driver);
1080+
1081+
await m.connect();
1082+
1083+
const Test = m.model('Test', m.Schema({ answer: Number }));
1084+
1085+
const res = await Test.findOne();
1086+
assert.deepEqual(res.toObject(), { answer: 42 });
1087+
});
1088+
});
10471089
});

test/types/populate.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,31 @@ async function _11532() {
247247
expectError(leanResult?.__v);
248248
}
249249

250+
async function gh11710() {
251+
252+
// `Parent` represents the object as it is stored in MongoDB
253+
interface Parent {
254+
child?: Types.ObjectId,
255+
name?: string
256+
}
257+
interface Child {
258+
name: string;
259+
}
260+
interface PopulatedParent {
261+
child: Child | null;
262+
}
263+
const ParentModel = model<Parent>('Parent', new Schema({
264+
child: { type: Schema.Types.ObjectId, ref: 'Child' },
265+
name: String
266+
}));
267+
const childSchema: Schema = new Schema({ name: String });
268+
const ChildModel = model<Child>('Child', childSchema);
269+
270+
// Populate with `Paths` generic `{ child: Child }` to override `child` path
271+
const doc = await ParentModel.findOne({}).populate<Pick<PopulatedParent, 'child'>>('child').orFail();
272+
expectType<Child | null>(doc.child);
273+
}
274+
250275
function gh11758() {
251276
interface NestedChild {
252277
name: string

types/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ declare module 'mongoose' {
8181
/** Returns an array of model names created on this instance of Mongoose. */
8282
export function modelNames(): Array<string>;
8383

84+
/**
85+
* Overwrites the current driver used by this Mongoose instance. A driver is a
86+
* Mongoose-specific interface that defines functions like `find()`.
87+
*/
88+
export function setDriver(driver: any): Mongoose;
89+
8490
/** The node-mongodb-native driver Mongoose uses. */
8591
export const mongo: typeof mongodb;
8692

0 commit comments

Comments
 (0)