Skip to content

Commit 635e87c

Browse files
committed
Merge branch '6.4' into vkarpov15/gh-11426
2 parents 969fd1d + 6db2e4a commit 635e87c

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
@@ -36,6 +36,7 @@ const shardingPlugin = require('./plugins/sharding');
3636
const trusted = require('./helpers/query/trusted').trusted;
3737
const sanitizeFilter = require('./helpers/query/sanitizeFilter');
3838
const isBsonType = require('./helpers/isBsonType');
39+
const MongooseError = require('./error/mongooseError');
3940

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

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

144147
Mongoose.prototype.driver = driver;
145148

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

282-
const Connection = driver.get().getConnection();
315+
const Connection = _mongoose.__driver.getConnection();
283316
const conn = new Connection(_mongoose);
284317
if (typeof options === 'function') {
285318
callback = options;
@@ -475,7 +508,6 @@ Mongoose.prototype.pluralize = function(fn) {
475508
*/
476509

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

481513
if (typeof schema === 'string') {
@@ -692,7 +724,7 @@ Mongoose.prototype.__defineGetter__('connection', function() {
692724
});
693725

694726
Mongoose.prototype.__defineSetter__('connection', function(v) {
695-
if (v instanceof Connection) {
727+
if (v instanceof this.__driver.getConnection()) {
696728
this.connections[0] = v;
697729
this.models = v.models;
698730
}
@@ -721,18 +753,6 @@ Mongoose.prototype.__defineSetter__('connection', function(v) {
721753

722754
Mongoose.prototype.connections;
723755

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

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

754781
/**
755782
* The Mongoose [Connection](#connection_Connection) constructor
@@ -760,7 +787,18 @@ Mongoose.prototype.Collection = Collection;
760787
* @api public
761788
*/
762789

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

765803
/**
766804
* 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)