Skip to content

Commit d0e815b

Browse files
arsa-devachrinza
authored andcommitted
feat: upgrade mongodb driver to version 4.x
Signed-off-by: Antonio Ramón Sánchez Morales <[email protected]>
1 parent 9f9f721 commit d0e815b

File tree

4 files changed

+404
-140
lines changed

4 files changed

+404
-140
lines changed

lib/mongodb.js

+86-45
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
/*!
99
* Module dependencies
1010
*/
11-
const bson = require('bson');
1211
const g = require('strong-globalize')();
1312
const mongodb = require('mongodb');
14-
const urlParser = require('mongodb/lib/url_parser');
13+
const urlParser = require('mongodb/lib/connection_string').parseOptions;
1514
const util = require('util');
1615
const async = require('async');
1716
const Connector = require('loopback-connector').Connector;
@@ -29,7 +28,7 @@ exports.ObjectID = ObjectID;
2928
* @returns {ObjectID}
3029
*/
3130
function ObjectID(id) {
32-
if (id instanceof mongodb.ObjectID) {
31+
if (id instanceof mongodb.ObjectId) {
3332
return id;
3433
}
3534
if (typeof id !== 'string') {
@@ -40,7 +39,7 @@ function ObjectID(id) {
4039
// hex string. For LoopBack, we only allow 24-byte hex string, but 12-byte
4140
// string such as 'line-by-line' should be kept as string
4241
if (ObjectIdValueRegex.test(id)) {
43-
return new bson.ObjectID(id);
42+
return new mongodb.ObjectId(id);
4443
} else {
4544
return id;
4645
}
@@ -113,10 +112,9 @@ exports.initialize = function initializeDataSource(dataSource, callback) {
113112
fsync: s.fsync || null,
114113
};
115114
s.url = s.url || generateMongoDBURL(s);
116-
s.useNewUrlParser = s.useNewUrlParser !== false;
117-
s.useUnifiedTopology = s.useUnifiedTopology !== false;
115+
// useNewUrlParser and useUnifiedTopology are default now
118116
dataSource.connector = new MongoDB(s, dataSource);
119-
dataSource.ObjectID = mongodb.ObjectID;
117+
dataSource.ObjectID = mongodb.ObjectId;
120118

121119
if (callback) {
122120
if (s.lazyConnect) {
@@ -285,8 +283,6 @@ MongoDB.prototype.connect = function(callback) {
285283
'validateOptions',
286284
'appname',
287285
'auth',
288-
'user',
289-
'password',
290286
'authMechanism',
291287
'compression',
292288
'readPreferenceTags',
@@ -354,17 +350,45 @@ MongoDB.prototype.connect = function(callback) {
354350
}
355351
self.client = client;
356352
// The database name might be in the url
357-
return urlParser(self.settings.url, self.settings, function(err, url) {
358-
if (err) {
359-
onError(err);
360-
return;
361-
}
353+
try {
354+
const url = urlParser(self.settings.url, validOptions); // only supports the validURL options now
355+
// See https://github.com/mongodb/mongodb/blob/6.0.1/lib/mongodb.d.ts#L3854
356+
const validDbOptionNames = [
357+
'authSource',
358+
'forceServerObjectId',
359+
'readPreference',
360+
'pkFactory',
361+
'readConcern',
362+
'retryWrites',
363+
'checkKeys',
364+
'serializeFunctions',
365+
'ignoreUndefined',
366+
'promoteLongs',
367+
'promoteBuffers',
368+
'promoteValues',
369+
'fieldsAsRaw',
370+
'bsonRegExp',
371+
'raw',
372+
'writeConcern',
373+
'logger',
374+
'loggerLevel',
375+
];
376+
const dbOptions = url.db_options || self.settings;
377+
const dbOptionKeys = Object.keys(dbOptions);
378+
const validDbOptions = {};
379+
dbOptionKeys.forEach(function(option) {
380+
if (validDbOptionNames.indexOf(option) > -1) {
381+
validDbOptions[option] = dbOptions[option];
382+
}
383+
});
362384
self.db = client.db(
363385
url.dbName || self.settings.database,
364-
url.db_options || self.settings,
386+
validDbOptions,
365387
);
366388
if (callback) callback(err, self.db);
367-
});
389+
} catch (e) {
390+
onError(e);
391+
}
368392
});
369393
}
370394
};
@@ -498,7 +522,13 @@ MongoDB.prototype.execute = function(modelName, command) {
498522

499523
// Topology is destroyed when the server is disconnected
500524
// Execute if DB is connected and functional otherwise connect/reconnect first
501-
if (self.db && self.db.topology && !self.db.topology.isDestroyed()) {
525+
if (
526+
self.db && (
527+
!self.db.topology || (self.db.topology && !self.db.topology.isDestroyed())
528+
)
529+
) {
530+
doExecute();
531+
} else if (self.db && !self.db.topology) {
502532
doExecute();
503533
} else {
504534
if (self.db) {
@@ -545,7 +575,7 @@ MongoDB.prototype.execute = function(modelName, command) {
545575
'execute',
546576
context,
547577
function(context, done) {
548-
args[args.length - 1] = function(err, result) {
578+
const observerCallback = function(err, result) {
549579
if (err) {
550580
debug('Error: ', err);
551581
} else {
@@ -554,8 +584,23 @@ MongoDB.prototype.execute = function(modelName, command) {
554584
}
555585
done(err, result);
556586
};
557-
debug('MongoDB: model=%s command=%s', modelName, command, args);
558-
return collection[command].apply(collection, args);
587+
588+
// args had callback removed
589+
if (command === 'find') {
590+
// find does not support callback, remove and use a toArray with this callback
591+
args.pop();
592+
debug('MongoDB: model=%s command=%s', modelName, command, args);
593+
try {
594+
const cursor = collection[command].apply(collection, args);
595+
return observerCallback(null, cursor);
596+
} catch (err) {
597+
return observerCallback(err, null);
598+
}
599+
} else {
600+
args[args.length - 1] = observerCallback;
601+
debug('MongoDB: model=%s command=%s', modelName, command, args);
602+
return collection[command].apply(collection, args);
603+
}
559604
},
560605
callback,
561606
);
@@ -620,7 +665,7 @@ MongoDB.prototype.create = function(modelName, data, options, callback) {
620665
if (err) {
621666
return callback(err);
622667
}
623-
idValue = result.ops[0]._id;
668+
idValue = result.insertedId;
624669

625670
try {
626671
idValue = self.coerceId(modelName, idValue, options);
@@ -674,18 +719,13 @@ MongoDB.prototype.save = function(modelName, data, options, callback) {
674719
}
675720

676721
const info = {};
677-
if (result && result.result) {
678-
// create result formats:
679-
// { ok: 1, n: 1, upserted: [ [Object] ] }
680-
// { ok: 1, nModified: 0, n: 1, upserted: [ [Object] ] }
681-
//
682-
// update result formats:
683-
// { ok: 1, n: 1 }
684-
// { ok: 1, nModified: 1, n: 1 }
685-
if (result.result.ok === 1 && result.result.n === 1) {
686-
info.isNewInstance = !!result.result.upserted;
722+
if (result) {
723+
// new 4.0 result formats:
724+
// { acknowledged: true, modifiedCount: 1, upsertedCount: 1, : modifiedCount: 1}
725+
if (result.acknowledged === true && (result.matchedCount === 1 || result.upsertedCount === 1)) {
726+
info.isNewInstance = result.upsertedCount === 1;
687727
} else {
688-
debug('save result format not recognized: %j', result.result);
728+
debug('save result format not recognized: %j', result);
689729
}
690730
}
691731

@@ -854,7 +894,8 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate(
854894
data,
855895
buildOptions({
856896
upsert: true,
857-
returnOriginal: false,
897+
returnNewDocument: true,
898+
returnDocument: 'after', // ensures new document gets returned
858899
sort: [['_id', 'asc']],
859900
}, options),
860901
function(err, result) {
@@ -922,9 +963,9 @@ MongoDB.prototype.destroy = function destroy(modelName, id, options, callback) {
922963
if (self.debug) {
923964
debug('delete.callback', modelName, id, err, result);
924965
}
925-
let res = result && result.result;
966+
let res = result;
926967
if (res) {
927-
res = {count: res.n};
968+
res = {count: res.deletedCount};
928969
}
929970
if (callback) {
930971
callback(err, res);
@@ -1523,7 +1564,7 @@ MongoDB.prototype.destroyAll = function destroyAll(
15231564

15241565
if (self.debug) debug('destroyAll.callback', modelName, where, err, info);
15251566

1526-
const affectedCount = info.result ? info.result.n : undefined;
1567+
const affectedCount = info ? info.deletedCount : undefined;
15271568

15281569
if (callback) {
15291570
callback(err, {count: affectedCount});
@@ -1614,7 +1655,7 @@ MongoDB.prototype.replaceWithOptions = function(modelName, id, data, options, cb
16141655
if (err) return cb && cb(err);
16151656
let result;
16161657
const cbInfo = {};
1617-
if (info.result && info.result.n == 1) {
1658+
if (info && (info.matchedCount === 1 || info.upsertedCount === 1)) {
16181659
result = self.fromDatabase(modelName, data);
16191660
delete result._id;
16201661
result[idName] = id;
@@ -1625,8 +1666,8 @@ MongoDB.prototype.replaceWithOptions = function(modelName, id, data, options, cb
16251666
// replace result formats:
16261667
// 2.4.x: { ok: 1, n: 1 }
16271668
// { ok: 1, nModified: 1, n: 1 }
1628-
if (info.result.nModified !== undefined) {
1629-
cbInfo.isNewInstance = info.result.nModified === 0;
1669+
if (info.modifiedCount !== undefined) {
1670+
cbInfo.isNewInstance = info.modifiedCount === 0;
16301671
}
16311672
} else {
16321673
result = undefined;
@@ -1754,7 +1795,7 @@ MongoDB.prototype.update = MongoDB.prototype.updateAll = function updateAll(
17541795
if (self.debug)
17551796
debug('updateAll.callback', modelName, where, updateData, err, info);
17561797

1757-
const affectedCount = info.result ? info.result.n : undefined;
1798+
const affectedCount = info ? info.matchedCount : undefined;
17581799

17591800
if (cb) {
17601801
cb(err, {count: affectedCount});
@@ -1805,7 +1846,8 @@ MongoDB.prototype.upsertWithWhere = function upsertWithWhere(
18051846
updateData,
18061847
buildOptions({
18071848
upsert: true,
1808-
returnOriginal: false,
1849+
returnNewDocument: true,
1850+
returnDocument: 'after', // ensures new documents get returned
18091851
sort: [['_id', 'asc']],
18101852
}, options),
18111853
function(err, result) {
@@ -2040,7 +2082,7 @@ MongoDB.prototype.automigrate = function(models, cb) {
20402082
);
20412083
if (
20422084
!(
2043-
err.name === 'MongoError' &&
2085+
err.name === 'MongoServerError' &&
20442086
err.ok === 0 &&
20452087
err.errmsg === 'ns not found'
20462088
)
@@ -2188,7 +2230,7 @@ function isObjectIDProperty(modelCtor, propDef, value, options) {
21882230
(Array.isArray(value) && value.every((v) => typeof v === 'string' && v.match(ObjectIdValueRegex)))) {
21892231
if (isStoredAsObjectID(propDef)) return true;
21902232
else return !isStrictObjectIDCoercionEnabled(modelCtor, options);
2191-
} else if (value instanceof mongodb.ObjectID) {
2233+
} else if (value instanceof mongodb.ObjectId) {
21922234
return true;
21932235
} else {
21942236
return false;
@@ -2285,7 +2327,7 @@ function optimizedFindOrCreate(modelName, filter, data, options, callback) {
22852327
let value = result.value;
22862328
const created = !!result.lastErrorObject.upserted;
22872329

2288-
if (created && (value == null || Object.keys(value).length == 0)) {
2330+
if (created && (value == null || Object.keys(value).length === 0)) {
22892331
value = data;
22902332
self.setIdValue(modelName, value, result.lastErrorObject.upserted);
22912333
} else {
@@ -2336,7 +2378,6 @@ function visitAllProperties(data, modelCtor, visitor) {
23362378
} else {
23372379
visitor(modelCtor, value, def, (newValue) => { data[p] = newValue; });
23382380
}
2339-
continue;
23402381
}
23412382
}
23422383

0 commit comments

Comments
 (0)