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

feat: upgrade to MongoDB Node.js driver 5.7.0 #13591

Merged
merged 1 commit into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
110 changes: 16 additions & 94 deletions docs/deprecations.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,108 +9,30 @@ cause any problems for your application. Please [report any issues on GitHub](ht

To fix all deprecation warnings, follow the below steps:

* Replace `update()` with `updateOne()`, `updateMany()`, or `replaceOne()`
* Replace `remove()` with `deleteOne()` or `deleteMany()`.
* Replace `count()` with `countDocuments()`, unless you want to count how many documents are in the whole collection (no filter). In the latter case, use `estimatedDocumentCount()`.
* Replace `rawResult: true` with `includeResultMetadata: false` in `findOneAndUpdate()`, `findOneAndReplace()`, `findOneAndDelete()` calls.

Read below for more a more detailed description of each deprecation warning.

<h2 id="remove"><a href="#remove"><code>remove()</code></a></h2>
<h2 id="rawresult"><a href="#rawresult"><code>rawResult</code></a></h2>

The MongoDB driver's [`remove()` function](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#remove) is deprecated in favor of `deleteOne()` and `deleteMany()`. This is to comply with
the [MongoDB CRUD specification](https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst),
which aims to provide a consistent API for CRUD operations across all MongoDB
drivers.

```txt
DeprecationWarning: collection.remove is deprecated. Use deleteOne,
deleteMany, or bulkWrite instead.
```

To remove this deprecation warning, replace any usage of `remove()` with
`deleteMany()`, *unless* you specify the [`single` option to `remove()`](api/model.html#model_Model-remove). The `single`
option limited `remove()` to deleting at most one document, so you should
replace `remove(filter, { single: true })` with `deleteOne(filter)`.
As of Mongoose 7.4.0, the `rawResult` option to `findOneAndUpdate()` is deprecated.
You should instead use the `includeResultMetadata` option, which the MongoDB Node.js driver's new option that replaces `rawResult`.

```javascript
// Replace this:
MyModel.remove({ foo: 'bar' });
// With this:
MyModel.deleteMany({ foo: 'bar' });

// Replace this:
MyModel.remove({ answer: 42 }, { single: true });
// With this:
MyModel.deleteOne({ answer: 42 });
```
const doc = await Test.findOneAndUpdate(
{ name: 'Test' },
{ name: 'Test Testerson' },
{ rawResult: true }
);

<h2 id="update"><a href="#update"><code>update()</code></a></h2>

Like `remove()`, the [`update()` function](api/model.html#model_Model-update) is deprecated in favor
of the more explicit [`updateOne()`](api/model.html#model_Model-updateOne), [`updateMany()`](api/model.html#model_Model-updateMany), and [`replaceOne()`](api/model.html#model_Model-replaceOne) functions. You should replace
`update()` with `updateOne()`, unless you use the [`multi` or `overwrite` options](api/model.html#model_Model-update).

```txt
collection.update is deprecated. Use updateOne, updateMany, or bulkWrite
instead.
```

```javascript
// Replace this:
MyModel.update({ foo: 'bar' }, { answer: 42 });
// With this:
MyModel.updateOne({ foo: 'bar' }, { answer: 42 });

// If you use `overwrite: true`, you should use `replaceOne()` instead:
MyModel.update(filter, update, { overwrite: true });
// Replace with this:
MyModel.replaceOne(filter, update);

// If you use `multi: true`, you should use `updateMany()` instead:
MyModel.update(filter, update, { multi: true });
// Replace with this:
MyModel.updateMany(filter, update);
const doc = await Test.findOneAndUpdate(
{ name: 'Test' },
{ name: 'Test Testerson' },
{ includeResultMetadata: false }
);
```

<h2 id="count"><a href="#count"><code>count()</code></a></h2>

The MongoDB server has deprecated the `count()` function in favor of two
separate functions, [`countDocuments()`](api/query.html#Query.prototype.countDocuments()) and
[`estimatedDocumentCount()`](api/query.html#Query.prototype.estimatedDocumentCount()).

```txt
DeprecationWarning: collection.count is deprecated, and will be removed in a future version. Use collection.countDocuments or collection.estimatedDocumentCount instead
```

The difference between the two is `countDocuments()` can accept a filter
parameter like [`find()`](api/query.html#Query.prototype.find()). The `estimatedDocumentCount()`
function is faster, but can only tell you the total number of documents in
a collection. You cannot pass a `filter` to `estimatedDocumentCount()`.

To migrate, replace `count()` with `countDocuments()` *unless* you do not
pass any arguments to `count()`. If you use `count()` to count all documents
in a collection as opposed to counting documents that match a query, use
`estimatedDocumentCount()` instead of `countDocuments()`.

```javascript
// Replace this:
MyModel.count({ answer: 42 });
// With this:
MyModel.countDocuments({ answer: 42 });

// If you're counting all documents in the collection, use
// `estimatedDocumentCount()` instead.
MyModel.count();
// Replace with:
MyModel.estimatedDocumentCount();

// Replace this:
MyModel.find({ answer: 42 }).count().exec();
// With this:
MyModel.find({ answer: 42 }).countDocuments().exec();

// Replace this:
MyModel.find().count().exec();
// With this, since there's no filter
MyModel.find().estimatedDocumentCount().exec();
```
The `rawResult` option only affects Mongoose; the MongoDB Node.js driver still returns the full result metadata, Mongoose just parses out the raw document.
The `includeResultMetadata` option also tells the MongoDB Node.js driver to only return the document, not the full `ModifyResult` object.
33 changes: 30 additions & 3 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,10 @@ Query.prototype.setOptions = function(options, overwrite) {
delete options.translateAliases;
}

if ('rawResult' in options) {
printRawResultDeprecationWarning();
}

if (options.lean == null && this.schema && 'lean' in this.schema.options) {
this._mongooseOptions.lean = this.schema.options.lean;
}
Expand Down Expand Up @@ -1670,6 +1674,15 @@ Query.prototype.setOptions = function(options, overwrite) {
return this;
};

/*!
* ignore
*/

const printRawResultDeprecationWarning = util.deprecate(
function printRawResultDeprecationWarning() {},
'The `rawResult` option for Mongoose queries is deprecated. Use `includeResultMetadata: false` as a replacement for `rawResult: true`.'
);

/**
* Sets the [`explain` option](https://www.mongodb.com/docs/manual/reference/method/cursor.explain/),
* which makes this query return detailed execution stats instead of the actual
Expand Down Expand Up @@ -3287,6 +3300,10 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
applyGlobalMaxTimeMS(this.options, this.model);
applyGlobalDiskUse(this.options, this.model);

if (this.options.rawResult && this.options.includeResultMetadata === false) {
throw new MongooseError('Cannot set `rawResult` option when `includeResultMetadata` is false');
}

if ('strict' in this.options) {
this._mongooseOptions.strict = this.options.strict;
}
Expand Down Expand Up @@ -3337,7 +3354,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
for (const fn of this._transforms) {
res = fn(res);
}
const doc = res.value;
const doc = options.includeResultMetadata === false ? res : res.value;

return new Promise((resolve, reject) => {
this._completeOne(doc, res, _wrapThunkCallback(this, (err, res) => {
Expand Down Expand Up @@ -3478,6 +3495,11 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
throw this.error();
}

const includeResultMetadata = this.options.includeResultMetadata;
if (this.options.rawResult && includeResultMetadata === false) {
throw new MongooseError('Cannot set `rawResult` option when `includeResultMetadata` is false');
}

const filter = this._conditions;
const options = this._optionsForExec(this.model);
this._applyTranslateAliases(options);
Expand All @@ -3486,7 +3508,7 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
for (const fn of this._transforms) {
res = fn(res);
}
const doc = res.value;
const doc = includeResultMetadata === false ? res : res.value;

return new Promise((resolve, reject) => {
this._completeOne(doc, res, _wrapThunkCallback(this, (err, res) => {
Expand Down Expand Up @@ -3613,6 +3635,11 @@ Query.prototype._findOneAndReplace = async function _findOneAndReplace() {
this._applyTranslateAliases(options);
convertNewToReturnDocument(options);

const includeResultMetadata = this.options.includeResultMetadata;
if (this.options.rawResult && includeResultMetadata === false) {
throw new MongooseError('Cannot set `rawResult` option when `includeResultMetadata` is false');
}

const modelOpts = { skipId: true };
if ('strict' in this._mongooseOptions) {
modelOpts.strict = this._mongooseOptions.strict;
Expand Down Expand Up @@ -3643,7 +3670,7 @@ Query.prototype._findOneAndReplace = async function _findOneAndReplace() {
res = fn(res);
}

const doc = res.value;
const doc = includeResultMetadata === false ? res : res.value;
return new Promise((resolve, reject) => {
this._completeOne(doc, res, _wrapThunkCallback(this, (err, res) => {
if (err) {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
],
"license": "MIT",
"dependencies": {
"bson": "^5.3.0",
"bson": "^5.4.0",
"kareem": "2.5.1",
"mongodb": "5.6.0",
"mongodb": "5.7.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
Expand Down
38 changes: 38 additions & 0 deletions test/model.findOneAndDelete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,42 @@ describe('model: findOneAndDelete:', function() {
assert.equal(postCount, 1);
});
});

it('supports the `includeResultMetadata` option (gh-13539)', async function() {
const testSchema = new mongoose.Schema({
name: String
});
const Test = db.model('Test', testSchema);
await Test.create({ name: 'Test' });
const doc = await Test.findOneAndDelete(
{ name: 'Test' },
{ includeResultMetadata: false }
);
assert.equal(doc.ok, undefined);
assert.equal(doc.name, 'Test');

await Test.create({ name: 'Test' });
let data = await Test.findOneAndDelete(
{ name: 'Test' },
{ includeResultMetadata: true }
);
assert(data.ok);
assert.equal(data.value.name, 'Test');

await Test.create({ name: 'Test' });
data = await Test.findOneAndDelete(
{ name: 'Test' },
{ includeResultMetadata: true, rawResult: true }
);
assert(data.ok);
assert.equal(data.value.name, 'Test');

await assert.rejects(
() => Test.findOneAndDelete(
{ name: 'Test' },
{ includeResultMetadata: false, rawResult: true }
),
/Cannot set `rawResult` option when `includeResultMetadata` is false/
);
});
});
42 changes: 42 additions & 0 deletions test/model.findOneAndReplace.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,4 +451,46 @@ describe('model: findOneAndReplace:', function() {
assert.ok(!Object.keys(opts).includes('overwrite'));
assert.ok(!Object.keys(opts).includes('timestamps'));
});

it('supports the `includeResultMetadata` option (gh-13539)', async function() {
const testSchema = new mongoose.Schema({
name: String
});
const Test = db.model('Test', testSchema);
await Test.create({
name: 'Test'
});
const doc = await Test.findOneAndReplace(
{ name: 'Test' },
{ name: 'Test Testerson' },
{ new: true, upsert: true, includeResultMetadata: false }
);
assert.equal(doc.ok, undefined);
assert.equal(doc.name, 'Test Testerson');

let data = await Test.findOneAndReplace(
{ name: 'Test Testerson' },
{ name: 'Test' },
{ new: true, upsert: true, includeResultMetadata: true }
);
assert(data.ok);
assert.equal(data.value.name, 'Test');

data = await Test.findOneAndReplace(
{ name: 'Test Testerson' },
{ name: 'Test' },
{ new: true, upsert: true, includeResultMetadata: true, rawResult: true }
);
assert(data.ok);
assert.equal(data.value.name, 'Test');

await assert.rejects(
() => Test.findOneAndReplace(
{ name: 'Test Testerson' },
{ name: 'Test' },
{ new: true, upsert: true, includeResultMetadata: false, rawResult: true }
),
/Cannot set `rawResult` option when `includeResultMetadata` is false/
);
});
});
30 changes: 28 additions & 2 deletions test/model.findOneAndUpdate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2151,11 +2151,37 @@ describe('model: findOneAndUpdate:', function() {
await Test.create({
name: 'Test'
});
const doc = await Test.findOneAndUpdate({ name: 'Test' }, { name: 'Test Testerson' }, { new: true, upsert: true, includeResultMetadata: false });
const doc = await Test.findOneAndUpdate(
{ name: 'Test' },
{ name: 'Test Testerson' },
{ new: true, upsert: true, includeResultMetadata: false }
);
assert.equal(doc.ok, undefined);
assert.equal(doc.name, 'Test Testerson');
const data = await Test.findOneAndUpdate({ name: 'Test Testerson' }, { name: 'Test' }, { new: true, upsert: true, includeResultMetadata: true });

let data = await Test.findOneAndUpdate(
{ name: 'Test Testerson' },
{ name: 'Test' },
{ new: true, upsert: true, includeResultMetadata: true }
);
assert(data.ok);
assert.equal(data.value.name, 'Test');

data = await Test.findOneAndUpdate(
{ name: 'Test Testerson' },
{ name: 'Test' },
{ new: true, upsert: true, includeResultMetadata: true, rawResult: true }
);
assert(data.ok);
assert.equal(data.value.name, 'Test');

await assert.rejects(
() => Test.findOneAndUpdate(
{ name: 'Test Testerson' },
{ name: 'Test' },
{ new: true, upsert: true, includeResultMetadata: false, rawResult: true }
),
/Cannot set `rawResult` option when `includeResultMetadata` is false/
);
});
});