From 06d7ccf6576ac472fb06cd11cef9e531752ba462 Mon Sep 17 00:00:00 2001 From: Davis Date: Fri, 13 Aug 2021 12:27:37 -0700 Subject: [PATCH 1/3] collection.findOneAndUpdate option [returnOriginal] is deprecated for [returnNewDocument] --- lib/mongodb.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mongodb.js b/lib/mongodb.js index 5545b49af..94d883aaf 100644 --- a/lib/mongodb.js +++ b/lib/mongodb.js @@ -838,7 +838,7 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate( data, buildOptions({ upsert: true, - returnOriginal: false, + returnNewDocument: true, sort: [['_id', 'asc']], }, options), function(err, result) { @@ -1778,7 +1778,7 @@ MongoDB.prototype.upsertWithWhere = function upsertWithWhere( updateData, { upsert: true, - returnOriginal: false, + returnNewDocument: true, sort: [['_id', 'asc']], }, function(err, result) { From fcd76022ebae9567f2f885c3b938a436c3dc0b4f Mon Sep 17 00:00:00 2001 From: Davis Date: Tue, 17 Aug 2021 13:08:47 -0700 Subject: [PATCH 2/3] mongodb 4.1.0 compatibility changes --- lib/mongodb.js | 125 +++++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 86 insertions(+), 41 deletions(-) diff --git a/lib/mongodb.js b/lib/mongodb.js index 94d883aaf..221a30825 100644 --- a/lib/mongodb.js +++ b/lib/mongodb.js @@ -11,7 +11,7 @@ const bson = require('bson'); const g = require('strong-globalize')(); const mongodb = require('mongodb'); -const urlParser = require('mongodb/lib/url_parser'); +const urlParser = require('mongodb/lib/connection_string').parseOptions; const util = require('util'); const async = require('async'); const Connector = require('loopback-connector').Connector; @@ -23,13 +23,14 @@ const ObjectIdValueRegex = /^[0-9a-fA-F]{24}$/; const ObjectIdTypeRegex = /objectid/i; exports.ObjectID = ObjectID; +exports.ObjectId = ObjectID; /*! * Convert the id to be a BSON ObjectID if it is compatible * @param {*} id The id value * @returns {ObjectID} */ function ObjectID(id) { - if (id instanceof mongodb.ObjectID) { + if (id instanceof mongodb.ObjectId) { return id; } if (typeof id !== 'string') { @@ -113,10 +114,9 @@ exports.initialize = function initializeDataSource(dataSource, callback) { fsync: s.fsync || null, }; s.url = s.url || generateMongoDBURL(s); - s.useNewUrlParser = s.useNewUrlParser !== false; - s.useUnifiedTopology = s.useUnifiedTopology !== false; + // useNewUrlParser and useUnifiedTopology are default now dataSource.connector = new MongoDB(s, dataSource); - dataSource.ObjectID = mongodb.ObjectID; + dataSource.ObjectID = mongodb.ObjectId; if (callback) { if (s.lazyConnect) { @@ -278,8 +278,8 @@ MongoDB.prototype.connect = function(callback) { 'validateOptions', 'appname', 'auth', - 'user', - 'password', + // 'user', // no longer supported, only in `auth` + // 'password', // no longer supported , only in `auth` 'authMechanism', 'compression', 'readPreferenceTags', @@ -338,17 +338,45 @@ MongoDB.prototype.connect = function(callback) { } self.client = client; // The database name might be in the url - return urlParser(self.settings.url, self.settings, function(err, url) { - if (err) { - onError(err); - return; - } + try { + const url = urlParser(self.settings.url, validOptions); // only supports the validURL options now + // See https://github.com/mongodb/mongodb/blob/6.0.1/lib/mongodb.d.ts#L3854 + const validDbOptionNames = [ + 'authSource', + 'forceServerObjectId', + 'readPreference', + 'pkFactory', + 'readConcern', + 'retryWrites', + 'checkKeys', + 'serializeFunctions', + 'ignoreUndefined', + 'promoteLongs', + 'promoteBuffers', + 'promoteValues', + 'fieldsAsRaw', + 'bsonRegExp', + 'raw', + 'writeConcern', + 'logger', + 'loggerLevel', + ]; + const dbOptions = url.db_options || self.settings; + const dbOptionKeys = Object.keys(dbOptions); + const validDbOptions = {}; + dbOptionKeys.forEach(function(option) { + if (validDbOptionNames.indexOf(option) > -1) { + validDbOptions[option] = dbOptions[option]; + } + }); self.db = client.db( url.dbName || self.settings.database, - url.db_options || self.settings, + validDbOptions, ); if (callback) callback(err, self.db); - }); + } catch (e) { + onError(e); + } }); } }; @@ -482,7 +510,13 @@ MongoDB.prototype.execute = function(modelName, command) { // Topology is destroyed when the server is disconnected // Execute if DB is connected and functional otherwise connect/reconnect first - if (self.db && self.db.topology && !self.db.topology.isDestroyed()) { + if ( + self.db && ( + !self.db.topology || (self.db.topology && !self.db.topology.isDestroyed()) + ) + ) { + doExecute(); + } else if (self.db && !self.db.topology) { doExecute(); } else { if (self.db) { @@ -529,7 +563,7 @@ MongoDB.prototype.execute = function(modelName, command) { 'execute', context, function(context, done) { - args[args.length - 1] = function(err, result) { + const observerCallback = function(err, result) { if (err) { debug('Error: ', err); } else { @@ -538,8 +572,23 @@ MongoDB.prototype.execute = function(modelName, command) { } done(err, result); }; - debug('MongoDB: model=%s command=%s', modelName, command, args); - return collection[command].apply(collection, args); + + // args had callback removed + if (command === 'find') { + // find does not support callback, remove and use a toArray with this callback + delete args[args.length - 1]; + debug('MongoDB: model=%s command=%s', modelName, command, args); + try { + const cursor = collection[command].apply(collection, args); + return observerCallback(null, cursor); + } catch (err) { + return observerCallback(err, null); + } + } else { + args[args.length - 1] = observerCallback; + debug('MongoDB: model=%s command=%s', modelName, command, args); + return collection[command].apply(collection, args); + } }, callback, ); @@ -604,7 +653,7 @@ MongoDB.prototype.create = function(modelName, data, options, callback) { if (err) { return callback(err); } - idValue = result.ops[0]._id; + idValue = result.insertedId; try { idValue = self.coerceId(modelName, idValue, options); @@ -658,18 +707,13 @@ MongoDB.prototype.save = function(modelName, data, options, callback) { } const info = {}; - if (result && result.result) { - // create result formats: - // { ok: 1, n: 1, upserted: [ [Object] ] } - // { ok: 1, nModified: 0, n: 1, upserted: [ [Object] ] } - // - // update result formats: - // { ok: 1, n: 1 } - // { ok: 1, nModified: 1, n: 1 } - if (result.result.ok === 1 && result.result.n === 1) { - info.isNewInstance = !!result.result.upserted; + if (result) { + // new 4.0 result formats: + // { acknowledged: true, modifiedCount: 1, upsertedCount: 1, : modifiedCount: 1} + if (result.acknowledged === true && result.matchedCount === 1) { + info.isNewInstance = result.upsertedCount === 1; } else { - debug('save result format not recognized: %j', result.result); + debug('save result format not recognized: %j', result); } } @@ -839,6 +883,7 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate( buildOptions({ upsert: true, returnNewDocument: true, + returnDocument: 'after', // ensures new document gets returned sort: [['_id', 'asc']], }, options), function(err, result) { @@ -906,9 +951,9 @@ MongoDB.prototype.destroy = function destroy(modelName, id, options, callback) { if (self.debug) { debug('delete.callback', modelName, id, err, result); } - let res = result && result.result; + let res = result; if (res) { - res = {count: res.n}; + res = {count: res.deletedCount}; } if (callback) { callback(err, res); @@ -1507,7 +1552,7 @@ MongoDB.prototype.destroyAll = function destroyAll( if (self.debug) debug('destroyAll.callback', modelName, where, err, info); - const affectedCount = info.result ? info.result.n : undefined; + const affectedCount = info ? info.deletedCount : undefined; if (callback) { callback(err, {count: affectedCount}); @@ -1587,7 +1632,7 @@ MongoDB.prototype.replaceWithOptions = function(modelName, id, data, options, cb if (err) return cb && cb(err); let result; const cbInfo = {}; - if (info.result && info.result.n == 1) { + if (info && info.matchedCount === 1) { result = self.fromDatabase(modelName, data); delete result._id; result[idName] = id; @@ -1598,8 +1643,8 @@ MongoDB.prototype.replaceWithOptions = function(modelName, id, data, options, cb // replace result formats: // 2.4.x: { ok: 1, n: 1 } // { ok: 1, nModified: 1, n: 1 } - if (info.result.nModified !== undefined) { - cbInfo.isNewInstance = info.result.nModified === 0; + if (info.modifiedCount !== undefined) { + cbInfo.isNewInstance = info.modifiedCount === 0; } } else { result = undefined; @@ -1727,7 +1772,7 @@ MongoDB.prototype.update = MongoDB.prototype.updateAll = function updateAll( if (self.debug) debug('updateAll.callback', modelName, where, updateData, err, info); - const affectedCount = info.result ? info.result.n : undefined; + const affectedCount = info ? info.matchedCount : undefined; if (cb) { cb(err, {count: affectedCount}); @@ -1779,6 +1824,7 @@ MongoDB.prototype.upsertWithWhere = function upsertWithWhere( { upsert: true, returnNewDocument: true, + returnDocument: 'after', // ensures new documents get returned sort: [['_id', 'asc']], }, function(err, result) { @@ -2117,7 +2163,7 @@ function isObjectIDProperty(modelCtor, propDef, value, options) { (Array.isArray(value) && value.every((v) => v.match(ObjectIdValueRegex)))) { if (isStoredAsObjectID(propDef)) return true; else return !isStrictObjectIDCoercionEnabled(modelCtor, options); - } else if (value instanceof mongodb.ObjectID) { + } else if (value instanceof mongodb.ObjectId) { return true; } else { return false; @@ -2214,7 +2260,7 @@ function optimizedFindOrCreate(modelName, filter, data, options, callback) { let value = result.value; const created = !!result.lastErrorObject.upserted; - if (created && (value == null || Object.keys(value).length == 0)) { + if (created && (value == null || Object.keys(value).length === 0)) { value = data; self.setIdValue(modelName, value, result.lastErrorObject.upserted); } else { @@ -2265,7 +2311,6 @@ function visitAllProperties(data, modelCtor, visitor) { } else { visitor(modelCtor, value, def, (newValue) => { data[p] = newValue; }); } - continue; } } diff --git a/package.json b/package.json index 7aa2c711d..14ec38366 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "bson": "^1.0.6", "debug": "^4.1.0", "loopback-connector": "^5.0.0", - "mongodb": "^3.2.4", + "mongodb": "^4.1.0", "strong-globalize": "^6.0.0" }, "devDependencies": { From e335fdc00668003f71830c33c39a214afea27fb5 Mon Sep 17 00:00:00 2001 From: Davis Date: Wed, 18 Aug 2021 13:00:04 -0700 Subject: [PATCH 3/3] fix deleting callback from find --- lib/mongodb.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mongodb.js b/lib/mongodb.js index 221a30825..803cd082d 100644 --- a/lib/mongodb.js +++ b/lib/mongodb.js @@ -576,7 +576,7 @@ MongoDB.prototype.execute = function(modelName, command) { // args had callback removed if (command === 'find') { // find does not support callback, remove and use a toArray with this callback - delete args[args.length - 1]; + args.pop(); debug('MongoDB: model=%s command=%s', modelName, command, args); try { const cursor = collection[command].apply(collection, args);