Skip to content

Commit

Permalink
Merge pull request #13591 from Automattic/vkarpov15/mongodb-57
Browse files Browse the repository at this point in the history
feat: upgrade to MongoDB Node.js driver 5.7.0
  • Loading branch information
vkarpov15 committed Jul 10, 2023
2 parents 1d2c79f + d0ade4e commit a4feabb
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 101 deletions.
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/
);
});
});

0 comments on commit a4feabb

Please sign in to comment.