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

Mongodb driver 4.x Support #639

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 87 additions & 42 deletions lib/mongodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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') {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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);
}
});
}
};
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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
args.pop();
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,
);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -838,7 +882,8 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate(
data,
buildOptions({
upsert: true,
returnOriginal: false,
returnNewDocument: true,
returnDocument: 'after', // ensures new document gets returned
sort: [['_id', 'asc']],
}, options),
function(err, result) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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});
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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});
Expand Down Expand Up @@ -1778,7 +1823,8 @@ MongoDB.prototype.upsertWithWhere = function upsertWithWhere(
updateData,
{
upsert: true,
returnOriginal: false,
returnNewDocument: true,
returnDocument: 'after', // ensures new documents get returned
sort: [['_id', 'asc']],
},
function(err, result) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -2265,7 +2311,6 @@ function visitAllProperties(data, modelCtor, visitor) {
} else {
visitor(modelCtor, value, def, (newValue) => { data[p] = newValue; });
}
continue;
}
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down