diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6ead84d1eab..e22404cb71f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,7 +39,7 @@ jobs: strategy: fail-fast: false matrix: - node: [14, 16, 18, 20] + node: [16, 18, 20] os: [ubuntu-20.04, ubuntu-22.04] mongodb: [4.4.18, 5.0.14, 6.0.4] include: @@ -108,7 +108,7 @@ jobs: - name: Setup Deno uses: denoland/setup-deno@v1 with: - deno-version: v1.34.x + deno-version: v1.36.x - run: deno --version - run: npm install - name: Run Deno tests diff --git a/docs/middleware.md b/docs/middleware.md index 433e1fb149d..923379ff9f2 100644 --- a/docs/middleware.md +++ b/docs/middleware.md @@ -48,7 +48,6 @@ In query middleware functions, `this` refers to the query. * [find](api/query.html#query_Query-find) * [findOne](api/query.html#query_Query-findOne) * [findOneAndDelete](api/query.html#query_Query-findOneAndDelete) -* [findOneAndRemove](api/query.html#query_Query-findOneAndRemove) * [findOneAndReplace](api/query.html#query_Query-findOneAndReplace) * [findOneAndUpdate](api/query.html#query_Query-findOneAndUpdate) * [remove](api/model.html#model_Model-remove) @@ -81,7 +80,6 @@ Here are the possible strings that can be passed to `pre()` * find * findOne * findOneAndDelete -* findOneAndRemove * findOneAndReplace * findOneAndUpdate * init diff --git a/docs/migrating_to_8.md b/docs/migrating_to_8.md new file mode 100644 index 00000000000..50fe962ed39 --- /dev/null +++ b/docs/migrating_to_8.md @@ -0,0 +1,56 @@ +# Migrating from 7.x to 8.x + + + +There are several backwards-breaking changes +you should be aware of when migrating from Mongoose 7.x to Mongoose 8.x. + +If you're still on Mongoose 6.x or earlier, please read the [Mongoose 6.x to 7.x migration guide](migrating_to_7.html) and upgrade to Mongoose 7.x first before upgrading to Mongoose 8. + +* [Removed `rawResult` option for `findOneAndUpdate()`](#removed-rawresult-option-for-findoneandupdate) +* [Changed behavior for `findOneAndUpdate()` with `orFail()` and upsert](#changed-behavior-for-findoneandupdate-with-orfail-and-upsert) +* [MongoDB Node Driver 6.0](#mongodb-node-driver-6) +* [Removed `findOneAndRemove()`](#removed-findoneandremove) + +

Removed rawResult option for findOneAndUpdate()

+ +The `rawResult` option for `findOneAndUpdate()`, `findOneAndReplace()`, and `findOneAndDelete()` has been replaced by the `includeResultMetadata` option. + +```javascript +const filter = { name: 'Will Riker' }; +const update = { age: 29 }; + +const res = await Character.findOneAndUpdate(filter, update, { + new: true, + upsert: true, + // Replace `rawResult: true` with `includeResultMetadata: true` + includeResultMetadata: true +}); +``` + +`includeResultMetadata` in Mongoose 8 behaves identically to `rawResult`. + +

Changed behavior for findOneAndUpdate() with orFail() and upsert

+ +In Mongoose 7, `findOneAndUpdate(filter, update, { upsert: true }).orFail()` would throw a `DocumentNotFoundError` if a new document was upserted. +In other words, `findOneAndUpdate().orFail()` always threw an error if no document was found, even if a new document was upserted. + +In Mongoose 8, `findOneAndUpdate(filter, update, { upsert: true }).orFail()` always succeeds. +`findOneAndUpdate().orFail()` now throws a `DocumentNotFoundError` if there's no document returned, rather than if no document was found. + +

MongoDB Node Driver 6

+ +Mongoose 8 uses [v6.x of the MongoDB Node driver](https://github.com/mongodb/node-mongodb-native/blob/main/HISTORY.md#600-2023-08-28). +There's a few noteable changes in MongoDB Node driver v6 that affect Mongoose: + +1. The `ObjectId` constructor no longer accepts strings of length 12. In Mongoose 7, `new mongoose.Types.ObjectId('12charstring')` was perfectly valid. In Mongoose 8, `new mongoose.Types.ObjectId('12charstring')` throws an error. + +

Removed findOneAndRemove()

+ +In Mongoose 7, `findOneAndRemove()` was an alias for `findOneAndDelete()` that Mongoose supported for backwards compatibility. +Mongoose 8 no longer supports `findOneAndRemove()`. +Use `findOneAndDelete()` instead. diff --git a/docs/models.md b/docs/models.md index b046303aa06..84c53cca0d1 100644 --- a/docs/models.md +++ b/docs/models.md @@ -193,7 +193,7 @@ If you attempt to `save()` a document from a View, you will get an error from th ## Yet more -The [API docs](api/model.html#model_Model) cover many additional methods available like [count](api/model.html#model_Model-count), [mapReduce](api/model.html#model_Model-mapReduce), [aggregate](api/model.html#model_Model-aggregate), and [more](api/model.html#model_Model-findOneAndRemove). +The [API docs](api/model.html#model_Model) cover many additional methods available like [count](api/model.html#model_Model-count), [mapReduce](api/model.html#model_Model-mapReduce), [aggregate](api/model.html#model_Model-aggregate), and more. ## Next Up diff --git a/docs/queries.md b/docs/queries.md index 8ca6cf1345a..27936197f6a 100644 --- a/docs/queries.md +++ b/docs/queries.md @@ -14,7 +14,6 @@ Each of these functions returns a * [`Model.findByIdAndUpdate()`](api.html#model_Model-findByIdAndUpdate) * [`Model.findOne()`](api.html#model_Model-findOne) * [`Model.findOneAndDelete()`](api.html#model_Model-findOneAndDelete) -* [`Model.findOneAndRemove()`](api.html#model_Model-findOneAndRemove) * [`Model.findOneAndReplace()`](api.html#model_Model-findOneAndReplace) * [`Model.findOneAndUpdate()`](api.html#model_Model-findOneAndUpdate) * [`Model.replaceOne()`](api.html#model_Model-replaceOne) diff --git a/docs/source/index.js b/docs/source/index.js index 1e5aaf50a6c..138853ff93a 100644 --- a/docs/source/index.js +++ b/docs/source/index.js @@ -65,6 +65,7 @@ docs['docs/migration.md'] = { guide: true, title: 'Migration Guide', markdown: t docs['docs/migrating_to_5.md'] = { guide: true, title: 'Migrating to Mongoose 5', markdown: true }; docs['docs/migrating_to_6.md'] = { guide: true, title: 'Migrating to Mongoose 6', markdown: true }; docs['docs/migrating_to_7.md'] = { guide: true, title: 'Migrating to Mongoose 7', markdown: true }; +docs['docs/migrating_to_8.md'] = { guide: true, title: 'Migrating to Mongoose 8', markdown: true }; docs['docs/connections.md'] = { guide: true, title: 'Connecting to MongoDB', markdown: true }; docs['docs/lambda.md'] = { guide: true, title: 'Using Mongoose With AWS Lambda', markdown: true }; docs['docs/geojson.md'] = { guide: true, title: 'Using GeoJSON', acquit: true, markdown: true }; diff --git a/lib/helpers/query/validOps.js b/lib/helpers/query/validOps.js index cb17549dbbd..2eb4375a93f 100644 --- a/lib/helpers/query/validOps.js +++ b/lib/helpers/query/validOps.js @@ -16,6 +16,5 @@ module.exports = Object.freeze([ // Delete 'deleteMany', 'deleteOne', - 'findOneAndDelete', - 'findOneAndRemove' + 'findOneAndDelete' ]); diff --git a/lib/model.js b/lib/model.js index 4f8d7a284e2..583d4f1a9e6 100644 --- a/lib/model.js +++ b/lib/model.js @@ -1019,6 +1019,13 @@ Model.prototype.deleteOne = function deleteOne(options) { throw where; } const query = self.constructor.deleteOne(where, options); + + if (this.$session() != null) { + if (!('session' in query.options)) { + query.options.session = this.$session(); + } + } + query.pre(function queryPreDeleteOne(cb) { self.constructor._middleware.execPre('deleteOne', self, [self], cb); }); @@ -2374,7 +2381,7 @@ Model.$where = function $where() { * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. * @param {Boolean} [options.runValidators] if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema * @param {Boolean} [options.setDefaultsOnInsert=true] If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) + * @param {Boolean} [options.includeResultMetadata] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. * @return {Query} * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html @@ -2490,7 +2497,7 @@ function _decorateUpdateWithVersionKey(update, options, versionKey) { * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. * @param {Boolean} [options.runValidators] if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema * @param {Boolean} [options.setDefaultsOnInsert=true] If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) + * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document * @param {Boolean} [options.new=false] if true, return the modified document rather than the original * @param {Object|String} [options.select] sets the document fields to return. @@ -2524,12 +2531,6 @@ Model.findByIdAndUpdate = function(id, update, options) { * * - `findOneAndDelete()` * - * This function differs slightly from `Model.findOneAndRemove()` in that - * `findOneAndRemove()` becomes a [MongoDB `findAndModify()` command](https://www.mongodb.com/docs/manual/reference/method/db.collection.findAndModify/), - * as opposed to a `findOneAndDelete()` command. For most mongoose use cases, - * this distinction is purely pedantic. You should use `findOneAndDelete()` - * unless you have a good reason not to. - * * #### Example: * * A.findOneAndDelete(conditions, options) // return Query @@ -2551,7 +2552,7 @@ Model.findByIdAndUpdate = function(id, update, options) { * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) * @param {Object|String|String[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) + * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. * @param {Object|String} [options.select] sets the document fields to return. * @param {Number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0 @@ -2593,7 +2594,7 @@ Model.findOneAndDelete = function(conditions, options) { * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. * @return {Query} - * @see Model.findOneAndRemove https://mongoosejs.com/docs/api/model.html#Model.findOneAndRemove() + * @see Model.findOneAndDelete https://mongoosejs.com/docs/api/model.html#Model.findOneAndDelete() * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/ */ @@ -2632,7 +2633,7 @@ Model.findByIdAndDelete = function(id, options) { * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. * @param {Object|String|String[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) + * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document * @param {Object|String} [options.select] sets the document fields to return. * @param {Number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0 * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. @@ -2659,104 +2660,6 @@ Model.findOneAndReplace = function(filter, replacement, options) { return mq.findOneAndReplace(filter, replacement, options); }; -/** - * Issue a mongodb findOneAndRemove command. - * - * Finds a matching document, removes it, and returns the found document (if any). - * - * This function triggers the following middleware. - * - * - `findOneAndRemove()` - * - * #### Example: - * - * A.findOneAndRemove(conditions, options) // return Query - * A.findOneAndRemove(conditions) // returns Query - * A.findOneAndRemove() // returns Query - * - * `findOneAndX` and `findByIdAndX` functions support limited validation. You can - * enable validation by setting the `runValidators` option. - * - * If you need full-fledged validation, use the traditional approach of first - * retrieving the document. - * - * const doc = await Model.findById(id); - * doc.name = 'jason bourne'; - * await doc.save(); - * - * @param {Object} conditions - * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) - * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). - * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) - * @param {Object|String|String[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) - * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) - * @param {Object|String} [options.select] sets the document fields to return. - * @param {Number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0 - * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. - * @return {Query} - * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/ - * @api public - */ - -Model.findOneAndRemove = function(conditions, options) { - _checkContext(this, 'findOneAndRemove'); - - if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') { - throw new MongooseError('Model.findOneAndRemove() no longer accepts a callback'); - } - - let fields; - if (options) { - fields = options.select; - options.select = undefined; - } - - const mq = new this.Query({}, {}, this, this.$__collection); - mq.select(fields); - - return mq.findOneAndRemove(conditions, options); -}; - -/** - * Issue a mongodb findOneAndRemove command by a document's _id field. `findByIdAndRemove(id, ...)` is equivalent to `findOneAndRemove({ _id: id }, ...)`. - * - * Finds a matching document, removes it, and returns the found document (if any). - * - * This function triggers the following middleware. - * - * - `findOneAndRemove()` - * - * #### Example: - * - * A.findByIdAndRemove(id, options) // return Query - * A.findByIdAndRemove(id) // returns Query - * A.findByIdAndRemove() // returns Query - * - * @param {Object|Number|String} id value of `_id` to query by - * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) - * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) - * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). - * @param {Object|String|String[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) - * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) - * @param {Object|String} [options.select] sets the document fields to return. - * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. - * @return {Query} - * @see Model.findOneAndRemove https://mongoosejs.com/docs/api/model.html#Model.findOneAndRemove() - * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/ - */ - -Model.findByIdAndRemove = function(id, options) { - _checkContext(this, 'findByIdAndRemove'); - - if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') { - throw new MongooseError('Model.findByIdAndRemove() no longer accepts a callback'); - } - - return this.findOneAndRemove({ _id: id }, options); -}; - /** * Shortcut for saving one or more documents to the database. * `MyModel.create(docs)` does `new MyModel(doc).save()` for every doc in diff --git a/lib/query.js b/lib/query.js index 469871bcf81..0e33882933d 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1219,7 +1219,6 @@ Query.prototype.toString = function toString() { this.op === 'deleteMany' || this.op === 'deleteOne' || this.op === 'findOneAndDelete' || - this.op === 'findOneAndRemove' || this.op === 'remove') { return `${this.model.modelName}.${this.op}(${util.inspect(this._conditions)})`; } @@ -1550,13 +1549,13 @@ Query.prototype.getOptions = function() { * * - [maxTimeMS](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) * - * The following options are for `find()`, `findOne()`, `findOneAndUpdate()`, `findOneAndRemove()`, `findOneAndDelete()`, `updateOne()`, and `deleteOne()`: + * The following options are for `find()`, `findOne()`, `findOneAndUpdate()`, `findOneAndDelete()`, `updateOne()`, and `deleteOne()`: * * - [sort](https://www.mongodb.com/docs/manual/reference/method/cursor.sort/) * - * The following options are for `findOneAndUpdate()` and `findOneAndRemove()` + * The following options are for `findOneAndUpdate()` and `findOneAndDelete()` * - * - rawResult + * - includeResultMetadata * * The following options are for all operations: * @@ -1637,10 +1636,6 @@ 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; } @@ -1675,15 +1670,6 @@ 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`.' -); - /*! * ignore */ @@ -2455,7 +2441,7 @@ Query.prototype.collation = function(value) { */ Query.prototype._completeOne = function(doc, res, callback) { - if (!doc && !this.options.rawResult && !this.options.includeResultMetadata) { + if (!doc && !this.options.includeResultMetadata) { return callback(null, null); } @@ -2464,7 +2450,7 @@ Query.prototype._completeOne = function(doc, res, callback) { const userProvidedFields = this._userProvidedFields || {}; // `populate`, `lean` const mongooseOptions = this._mongooseOptions; - // `rawResult` + const options = this.options; if (!options.lean && mongooseOptions.lean) { options.lean = mongooseOptions.lean; @@ -3103,7 +3089,7 @@ Query.prototype._deleteMany = async function _deleteMany() { */ function completeOne(model, doc, res, options, fields, userProvidedFields, pop, callback) { - if ((options.rawResult || options.includeResultMetadata) && doc == null) { + if (options.includeResultMetadata && doc == null) { _init(null); return null; } @@ -3116,7 +3102,7 @@ function completeOne(model, doc, res, options, fields, userProvidedFields, pop, } - if (options.rawResult || options.includeResultMetadata) { + if (options.includeResultMetadata) { if (doc && casted) { if (options.session != null) { casted.$session(options.session); @@ -3171,7 +3157,6 @@ function prepareDiscriminatorCriteria(query) { * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0 * - `runValidators`: if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema. * - `setDefaultsOnInsert`: `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. - * - `rawResult`: if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) * * #### Example: * @@ -3186,7 +3171,7 @@ function prepareDiscriminatorCriteria(query) { * @param {Object|Query} [filter] * @param {Object} [doc] * @param {Object} [options] - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) + * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors. @@ -3295,10 +3280,6 @@ 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; } @@ -3349,7 +3330,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() { for (const fn of this._transforms) { res = fn(res); } - const doc = options.includeResultMetadata === false ? res : res.value; + const doc = !options.includeResultMetadata ? res : res.value; return new Promise((resolve, reject) => { this._completeOne(doc, res, _wrapThunkCallback(this, (err, res) => { @@ -3361,60 +3342,6 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() { }); }; -/** - * Legacy alias for `findOneAndDelete()`. - * - * Finds a matching document, removes it, returns the found document (if any). - * - * This function triggers the following middleware. - * - * - `findOneAndRemove()` - * - * #### Available options - * - * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update - * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0 - * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) - * - * #### Example: - * - * A.where().findOneAndRemove(conditions, options) // return Query - * A.where().findOneAndRemove(conditions) // returns Query - * A.where().findOneAndRemove() // returns Query - * - * @method findOneAndRemove - * @memberOf Query - * @instance - * @param {Object} [conditions] - * @param {Object} [options] - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) - * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). - * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) - * @return {Query} this - * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/ - * @api public - */ - -Query.prototype.findOneAndRemove = function(conditions, options) { - if (typeof conditions === 'function' || - typeof options === 'function' || - typeof arguments[2] === 'function') { - throw new MongooseError('Query.prototype.findOneAndRemove() no longer accepts a callback'); - } - - this.op = 'findOneAndRemove'; - this._validateOp(); - this._validate(); - - if (mquery.canMerge(conditions)) { - this.merge(conditions); - } - - options && this.setOptions(options); - - return this; -}; - /** * Issues a MongoDB [findOneAndDelete](https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndDelete/) command. * @@ -3428,7 +3355,6 @@ Query.prototype.findOneAndRemove = function(conditions, options) { * * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0 - * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) * * #### Callback Signature * @@ -3447,7 +3373,7 @@ Query.prototype.findOneAndRemove = function(conditions, options) { * @memberOf Query * @param {Object} [filter] * @param {Object} [options] - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) + * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) * @return {Query} this @@ -3491,9 +3417,6 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() { } 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); @@ -3503,7 +3426,7 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() { for (const fn of this._transforms) { res = fn(res); } - const doc = includeResultMetadata === false ? res : res.value; + const doc = !includeResultMetadata ? res : res.value; return new Promise((resolve, reject) => { this._completeOne(doc, res, _wrapThunkCallback(this, (err, res) => { @@ -3528,7 +3451,7 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() { * * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0 - * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) + * - `includeResultMetadata`: if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document * * #### Callback Signature * @@ -3548,7 +3471,7 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() { * @param {Object} [filter] * @param {Object} [replacement] * @param {Object} [options] - * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) + * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) * @param {Boolean} [options.new=false] By default, `findOneAndUpdate()` returns the document as it was **before** `update` was applied. If you set `new: true`, `findOneAndUpdate()` will instead give you the object after `update` was applied. @@ -3628,9 +3551,6 @@ Query.prototype._findOneAndReplace = async function _findOneAndReplace() { 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) { @@ -3662,7 +3582,7 @@ Query.prototype._findOneAndReplace = async function _findOneAndReplace() { res = fn(res); } - const doc = includeResultMetadata === false ? res : res.value; + const doc = !includeResultMetadata ? res : res.value; return new Promise((resolve, reject) => { this._completeOne(doc, res, _wrapThunkCallback(this, (err, res) => { if (err) { @@ -3694,19 +3614,6 @@ function convertNewToReturnDocument(options) { } } -/** - * Execute a `findOneAndRemove`. Alias for `findOneAndDelete` - * - * @return {Query} this - * @method _findOneAndRemove - * @memberOf Query - * @instance - * @api private - */ -Query.prototype._findOneAndRemove = async function _findOneAndRemove() { - return this._findOneAndDelete(); -}; - /** * Get options from query opts, falling back to the base mongoose object. * @param {Query} query @@ -3757,7 +3664,7 @@ function _completeOneLean(schema, doc, path, res, opts, callback) { return; } } - if (opts.rawResult) { + if (opts.includeResultMetadata) { return callback(null, res); } return callback(null, doc); @@ -4289,14 +4196,12 @@ Query.prototype.orFail = function(err) { } break; case 'findOneAndDelete': - case 'findOneAndRemove': - if ((res && res.lastErrorObject && res.lastErrorObject.n) === 0) { - throw _orFailError(err, this); - } - break; case 'findOneAndUpdate': case 'findOneAndReplace': - if ((res && res.lastErrorObject && res.lastErrorObject.updatedExisting) === false) { + if (this.options.includeResultMetadata && res != null && res.value == null) { + throw _orFailError(err, this); + } + if (!this.options.includeResultMetadata && res == null) { throw _orFailError(err, this); } break; diff --git a/package.json b/package.json index 2817e4211bb..49ba8faafda 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ ], "license": "MIT", "dependencies": { - "bson": "^5.4.0", + "bson": "^6.0.0", "kareem": "2.5.1", - "mongodb": "5.8.1", + "mongodb": "6.0.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -119,7 +119,7 @@ "main": "./index.js", "types": "./types/index.d.ts", "engines": { - "node": ">=14.20.1" + "node": ">=16.20.1" }, "bugs": { "url": "https://github.com/Automattic/mongoose/issues/new" diff --git a/test/docs/findoneandupdate.test.js b/test/docs/findoneandupdate.test.js index e6cbaf8fe96..2fde28cd688 100644 --- a/test/docs/findoneandupdate.test.js +++ b/test/docs/findoneandupdate.test.js @@ -139,7 +139,7 @@ describe('Tutorial: findOneAndUpdate()', function() { // acquit:ignore:end }); - it('rawResult', async function() { + it('includeResultMetadata', async function() { const filter = { name: 'Will Riker' }; const update = { age: 29 }; @@ -151,7 +151,8 @@ describe('Tutorial: findOneAndUpdate()', function() { const res = await Character.findOneAndUpdate(filter, update, { new: true, upsert: true, - rawResult: true // Return the raw result from the MongoDB driver + // Return additional properties about the operation, not just the document + includeResultMetadata: true }); res.value instanceof Character; // true diff --git a/test/document.test.js b/test/document.test.js index 3fb0fafd4d0..d096d349bc9 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -1945,7 +1945,7 @@ describe('document', function() { const Person = db.model('Person', personSchema); const createdPerson = await Person.create({ name: 'Hafez' }); - const removedPerson = await Person.findOneAndRemove({ _id: createdPerson._id }); + const removedPerson = await Person.findOneAndDelete({ _id: createdPerson._id }); removedPerson.isNew = true; @@ -3188,13 +3188,6 @@ describe('document', function() { assert.equal(doc.child.name, 'Anakin'); }); - it('strings of length 12 are valid oids (gh-3365)', async function() { - const schema = new Schema({ myId: mongoose.Schema.Types.ObjectId }); - const M = db.model('Test', schema); - const doc = new M({ myId: 'blablablabla' }); - await doc.validate(); - }); - it('set() empty obj unmodifies subpaths (gh-4182)', async function() { const omeletteSchema = new Schema({ topping: { diff --git a/test/index.test.js b/test/index.test.js index 0976aab3479..76ef810c2d1 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -466,7 +466,7 @@ describe('mongoose module:', function() { const M = mongoose.model('gh6760', schema); - const doc = new M({ testId: 'length12str0', testNum: 123, mixed: {} }); + const doc = new M({ testId: '0'.repeat(24), testNum: 123, mixed: {} }); assert.ok(doc.testId instanceof mongoose.Types.ObjectId); assert.ok(doc.testNum instanceof mongoose.Types.Decimal128); @@ -739,7 +739,7 @@ describe('mongoose module:', function() { }); it('isValidObjectId (gh-3823)', function() { - assert.ok(mongoose.isValidObjectId('0123456789ab')); + assert.ok(!mongoose.isValidObjectId('0123456789ab')); assert.ok(mongoose.isValidObjectId('5f5c2d56f6e911019ec2acdc')); assert.ok(mongoose.isValidObjectId('608DE01F32B6A93BBA314159')); assert.ok(mongoose.isValidObjectId(new mongoose.Types.ObjectId())); @@ -920,7 +920,7 @@ describe('mongoose module:', function() { const goodIdString = '1'.repeat(24); assert.deepStrictEqual(mongoose.isValidObjectId(goodIdString), true); const goodIdString2 = '1'.repeat(12); - assert.deepStrictEqual(mongoose.isValidObjectId(goodIdString2), true); + assert.deepStrictEqual(mongoose.isValidObjectId(goodIdString2), false); }); it('Allows a syncIndexes shorthand mongoose.syncIndexes (gh-10893)', async function() { const m = new mongoose.Mongoose(); diff --git a/test/model.findOneAndDelete.test.js b/test/model.findOneAndDelete.test.js index 38986a9ca33..de77c5ceb05 100644 --- a/test/model.findOneAndDelete.test.js +++ b/test/model.findOneAndDelete.test.js @@ -350,27 +350,11 @@ describe('model: findOneAndDelete:', function() { assert.equal(doc.name, 'Test'); await Test.create({ name: 'Test' }); - let data = await Test.findOneAndDelete( + const 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/ - ); }); }); diff --git a/test/model.findOneAndRemove.test.js b/test/model.findOneAndRemove.test.js deleted file mode 100644 index 667f7115a96..00000000000 --- a/test/model.findOneAndRemove.test.js +++ /dev/null @@ -1,349 +0,0 @@ -'use strict'; - -/** - * Test dependencies. - */ - -const start = require('./common'); - -const assert = require('assert'); - -const mongoose = start.mongoose; -const Schema = mongoose.Schema; -const ObjectId = Schema.Types.ObjectId; -const DocumentObjectId = mongoose.Types.ObjectId; - -describe('model: findOneAndRemove:', async function() { - let Comments; - let BlogPost; - let db; - - before(function() { - db = start(); - }); - - after(async function() { - await db.close(); - }); - - beforeEach(() => db.deleteModel(/.*/)); - afterEach(() => require('./util').clearTestData(db)); - afterEach(() => require('./util').stopRemainingOps(db)); - - beforeEach(function() { - Comments = new Schema(); - - Comments.add({ - title: String, - date: Date, - body: String, - comments: [Comments] - }); - - BlogPost = new Schema({ - title: String, - author: String, - slug: String, - date: Date, - meta: { - date: Date, - visitors: Number - }, - published: Boolean, - mixed: {}, - numbers: [Number], - owners: [ObjectId], - comments: [Comments] - }); - - BlogPost.virtual('titleWithAuthor') - .get(function() { - return this.get('title') + ' by ' + this.get('author'); - }) - .set(function(val) { - const split = val.split(' by '); - this.set('title', split[0]); - this.set('author', split[1]); - }); - - BlogPost.method('cool', function() { - return this; - }); - - BlogPost.static('woot', function() { - return this; - }); - - BlogPost = db.model('BlogPost', BlogPost); - }); - - it('returns the original document', async function() { - const M = BlogPost; - const title = 'remove muah'; - - const post = new M({ title: title }); - - await post.save(); - - const doc = await M.findOneAndRemove({ title: title }); - - assert.equal(post.id, doc.id); - - const gone = await M.findById(post.id); - assert.equal(gone, null); - }); - - it('options/conditions/doc are merged when no callback is passed', function(done) { - const M = BlogPost; - - const now = new Date(); - let query; - - // Model.findOneAndRemove - query = M.findOneAndRemove({ author: 'aaron' }, { select: 'author' }); - assert.equal(query._fields.author, 1); - assert.equal(query._conditions.author, 'aaron'); - - query = M.findOneAndRemove({ author: 'aaron' }); - assert.equal(query._fields, undefined); - assert.equal(query._conditions.author, 'aaron'); - - query = M.findOneAndRemove(); - assert.equal(query.options.new, undefined); - assert.equal(query._fields, undefined); - assert.equal(query._conditions.author, undefined); - - // Query.findOneAndRemove - query = M.where('author', 'aaron').findOneAndRemove({ date: now }); - assert.equal(query._fields, undefined); - assert.equal(query._conditions.date, now); - assert.equal(query._conditions.author, 'aaron'); - - query = M.find().findOneAndRemove({ author: 'aaron' }, { select: 'author' }); - assert.equal(query._fields.author, 1); - assert.equal(query._conditions.author, 'aaron'); - - query = M.find().findOneAndRemove(); - assert.equal(query._fields, undefined); - assert.equal(query._conditions.author, undefined); - done(); - }); - - it('returns the original document', async function() { - const M = BlogPost; - const title = 'remove muah pleez'; - - const post = new M({ title: title }); - await post.save(); - const doc = await M.findByIdAndRemove(post.id); - assert.equal(post.id, doc.id); - const gone = await M.findById(post.id); - assert.equal(gone, null); - }); - - it('options/conditions/doc are merged when no callback is passed', function(done) { - const M = BlogPost; - const _id = new DocumentObjectId(); - - let query; - - // Model.findByIdAndRemove - query = M.findByIdAndRemove(_id, { select: 'author' }); - assert.equal(query._fields.author, 1); - assert.equal(query._conditions._id.toString(), _id.toString()); - - query = M.findByIdAndRemove(_id.toString()); - assert.equal(query._fields, undefined); - assert.equal(query._conditions._id, _id.toString()); - - query = M.findByIdAndRemove(); - assert.equal(query.options.new, undefined); - assert.equal(query._fields, undefined); - assert.equal(query._conditions._id, undefined); - done(); - }); - - it('supports v3 select string syntax', function(done) { - const M = BlogPost; - const _id = new DocumentObjectId(); - - let query; - - query = M.findByIdAndRemove(_id, { select: 'author -title' }); - query._applyPaths(); - assert.strictEqual(1, query._fields.author); - assert.strictEqual(0, query._fields.title); - - query = M.findOneAndRemove({}, { select: 'author -title' }); - query._applyPaths(); - assert.strictEqual(1, query._fields.author); - assert.strictEqual(0, query._fields.title); - done(); - }); - - it('supports v3 select object syntax', function(done) { - const M = BlogPost; - const _id = new DocumentObjectId(); - - let query; - - query = M.findByIdAndRemove(_id, { select: { author: 1, title: 0 } }); - assert.strictEqual(1, query._fields.author); - assert.strictEqual(0, query._fields.title); - - query = M.findOneAndRemove({}, { select: { author: 1, title: 0 } }); - assert.strictEqual(1, query._fields.author); - assert.strictEqual(0, query._fields.title); - done(); - }); - - it('supports v3 sort string syntax', function(done) { - const M = BlogPost; - const _id = new DocumentObjectId(); - - let query; - - query = M.findByIdAndRemove(_id, { sort: 'author -title' }); - assert.equal(Object.keys(query.options.sort).length, 2); - assert.equal(query.options.sort.author, 1); - assert.equal(query.options.sort.title, -1); - - query = M.findOneAndRemove({}, { sort: 'author -title' }); - assert.equal(Object.keys(query.options.sort).length, 2); - assert.equal(query.options.sort.author, 1); - assert.equal(query.options.sort.title, -1); - done(); - }); - - it('supports v3 sort object syntax', function(done) { - const M = BlogPost; - const _id = new DocumentObjectId(); - - let query; - - query = M.findByIdAndRemove(_id, { sort: { author: 1, title: -1 } }); - assert.equal(Object.keys(query.options.sort).length, 2); - assert.equal(query.options.sort.author, 1); - assert.equal(query.options.sort.title, -1); - - query = M.findOneAndRemove(_id, { sort: { author: 1, title: -1 } }); - assert.equal(Object.keys(query.options.sort).length, 2); - assert.equal(query.options.sort.author, 1); - assert.equal(query.options.sort.title, -1); - done(); - }); - - it('supports population (gh-1395)', async function() { - const M = db.model('Test1', { name: String }); - const N = db.model('Test2', { a: { type: Schema.ObjectId, ref: 'Test1' }, i: Number }); - - const a = await M.create({ name: 'i am an A' }); - const b = await N.create({ a: a._id, i: 10 }); - - const doc = await N.findOneAndRemove({ _id: b._id }, { select: 'a -_id' }) - .populate('a') - .exec(); - - assert.ok(doc); - assert.equal(doc._id, undefined); - assert.ok(doc.a); - assert.equal('i am an A', doc.a.name); - }); - - it('only calls setters once (gh-6203)', async function() { - - const calls = []; - const userSchema = new mongoose.Schema({ - name: String, - foo: { - type: String, - set: function(val) { - calls.push(val); - return val + val; - } - } - }); - const Model = db.model('Test', userSchema); - - await Model.findOneAndRemove({ foo: '123' }, { name: 'bar' }); - - assert.deepEqual(calls, ['123']); - }); - - it('with orFail() (gh-9381)', function() { - const User = db.model('User', Schema({ name: String })); - - return User.findOneAndRemove({ name: 'not found' }).orFail(). - then(() => null, err => err). - then(err => { - assert.ok(err); - assert.equal(err.name, 'DocumentNotFoundError'); - }); - }); - - describe('middleware', function() { - - it('works', async function() { - const s = new Schema({ - topping: { type: String, default: 'bacon' }, - base: String - }); - - let preCount = 0; - s.pre('findOneAndRemove', function() { - ++preCount; - }); - - let postCount = 0; - s.post('findOneAndRemove', function() { - ++postCount; - }); - - const Breakfast = db.model('Test', s); - const breakfast = new Breakfast({ - base: 'eggs' - }); - - await breakfast.save(); - - const breakfast2 = await Breakfast.findOneAndRemove( - { base: 'eggs' }, - {} - ); - - assert.equal(breakfast2.base, 'eggs'); - assert.equal(preCount, 1); - assert.equal(postCount, 1); - }); - - it('works with exec() (gh-439)', async() => { - const s = new Schema({ - topping: { type: String, default: 'bacon' }, - base: String - }); - - let preCount = 0; - s.pre('findOneAndRemove', function() { - ++preCount; - }); - - let postCount = 0; - s.post('findOneAndRemove', function() { - ++postCount; - }); - - const Breakfast = db.model('Test', s); - const breakfast = new Breakfast({ - base: 'eggs' - }); - - await breakfast.save(); - - const breakfast2 = await Breakfast.findOneAndRemove({ base: 'eggs' }, {}); - - assert.equal(breakfast2.base, 'eggs'); - assert.equal(preCount, 1); - assert.equal(postCount, 1); - }); - }); -}); diff --git a/test/model.findOneAndReplace.test.js b/test/model.findOneAndReplace.test.js index 5550f198915..e8fa3764ca4 100644 --- a/test/model.findOneAndReplace.test.js +++ b/test/model.findOneAndReplace.test.js @@ -468,29 +468,12 @@ describe('model: findOneAndReplace:', function() { assert.equal(doc.ok, undefined); assert.equal(doc.name, 'Test Testerson'); - let data = await Test.findOneAndReplace( + const 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/ - ); }); }); diff --git a/test/model.findOneAndUpdate.test.js b/test/model.findOneAndUpdate.test.js index eee320aa968..ab3121a2581 100644 --- a/test/model.findOneAndUpdate.test.js +++ b/test/model.findOneAndUpdate.test.js @@ -666,7 +666,7 @@ describe('model: findOneAndUpdate:', function() { assert.ok(fruit instanceof mongoose.Document); }); - it('return rawResult when doing an upsert & new=false gh-7770', async function() { + it('return includeResultMetadata when doing an upsert & new=false gh-7770', async function() { const thingSchema = new Schema({ _id: String, flag: { @@ -678,13 +678,13 @@ describe('model: findOneAndUpdate:', function() { const Thing = db.model('Test', thingSchema); const key = 'some-new-id'; - const rawResult = await Thing.findOneAndUpdate({ _id: key }, { $set: { flag: false } }, { upsert: true, new: false, rawResult: true }).exec(); - assert.equal(rawResult.lastErrorObject.updatedExisting, false); + const res1 = await Thing.findOneAndUpdate({ _id: key }, { $set: { flag: false } }, { upsert: true, new: false, includeResultMetadata: true }).exec(); + assert.equal(res1.lastErrorObject.updatedExisting, false); - const rawResult2 = await Thing.findOneAndUpdate({ _id: key }, { $set: { flag: true } }, { upsert: true, new: false, rawResult: true }).exec(); - assert.equal(rawResult2.lastErrorObject.updatedExisting, true); - assert.equal(rawResult2.value._id, key); - assert.equal(rawResult2.value.flag, false); + const res2 = await Thing.findOneAndUpdate({ _id: key }, { $set: { flag: true } }, { upsert: true, new: false, includeResultMetadata: true }).exec(); + assert.equal(res2.lastErrorObject.updatedExisting, true); + assert.equal(res2.value._id, key); + assert.equal(res2.value.flag, false); }); @@ -1261,13 +1261,13 @@ describe('model: findOneAndUpdate:', function() { }); describe('bug fixes', function() { - it('passes raw result if rawResult specified (gh-4925)', async function() { + it('passes raw result if includeResultMetadata specified (gh-4925)', async function() { const testSchema = new mongoose.Schema({ test: String }); const TestModel = db.model('Test', testSchema); - const options = { upsert: true, new: true, rawResult: true }; + const options = { upsert: true, new: true, includeResultMetadata: true }; const update = { $set: { test: 'abc' } }; const res = await TestModel.findOneAndUpdate({}, update, options).exec(); @@ -2159,30 +2159,13 @@ describe('model: findOneAndUpdate:', function() { assert.equal(doc.ok, undefined); assert.equal(doc.name, 'Test Testerson'); - let data = await Test.findOneAndUpdate( + const 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/ - ); }); it('successfully runs findOneAndUpdate with no update and versionKey set to false (gh-13783)', async function() { diff --git a/test/model.middleware.preposttypes.test.js b/test/model.middleware.preposttypes.test.js index 3d8f6fdd77e..952bc901001 100644 --- a/test/model.middleware.preposttypes.test.js +++ b/test/model.middleware.preposttypes.test.js @@ -187,7 +187,7 @@ describe('pre/post hooks, type of this', function() { const MongooseDistinctQueryMiddleware = [ 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', - 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', + 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany']; const MongooseDefaultQueryMiddleware = [...MongooseDistinctQueryMiddleware, 'updateOne', 'deleteOne']; const MongooseQueryMiddleware = [...MongooseDistinctQueryMiddleware, ...MongooseQueryAndDocumentMiddleware]; @@ -285,7 +285,6 @@ describe('pre/post hooks, type of this', function() { await Doc.find({}).exec(); await Doc.findOne({}).exec(); await Doc.findOneAndDelete({}).exec(); await Doc.create({ data: 'value' }); - await Doc.findOneAndRemove({}).exec(); await Doc.create({ data: 'value' }); await Doc.findOneAndReplace({}, { data: 'valueRep' }).exec(); await Doc.findOneAndUpdate({}, { data: 'valueUpd' }).exec(); await Doc.replaceOne({}, { data: 'value' }).exec(); diff --git a/test/model.test.js b/test/model.test.js index d657c14a2e6..0a0903715cc 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -5050,10 +5050,6 @@ describe('Model', function() { await Model.createCollection(); const collectionName = Model.collection.name; - // If the collection is not created, the following will throw - // MongoServerError: Collection [mongoose_test.User] not found. - await db.collection(collectionName).stats(); - await Model.create([{ name: 'alpha' }, { name: 'Zeta' }]); // Ensure that the default collation is set. Mongoose will set the diff --git a/test/query.test.js b/test/query.test.js index 56567e72a6b..d018c5fee77 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1915,7 +1915,7 @@ describe('Query', function() { const ops = [ 'find', 'findOne', - 'findOneAndRemove', + 'findOneAndDelete', 'findOneAndUpdate', 'replaceOne', 'updateOne', diff --git a/test/schema.select.test.js b/test/schema.select.test.js index a6a2ae29a32..70631978c76 100644 --- a/test/schema.select.test.js +++ b/test/schema.select.test.js @@ -57,10 +57,10 @@ describe('schema select option', function() { assert.equal(findUpdateDoc.isSelected('name'), false); assert.equal(findUpdateDoc.isSelected('docs.name'), false); assert.strictEqual(undefined, findUpdateDoc.name); - const findAndRemoveDoc = await Test.findOneAndRemove({ _id: doc._id }); - assert.equal(findAndRemoveDoc.isSelected('name'), false); - assert.equal(findAndRemoveDoc.isSelected('docs.name'), false); - assert.strictEqual(undefined, findAndRemoveDoc.name); + const findAndDeleteDoc = await Test.findOneAndDelete({ _id: doc._id }); + assert.equal(findAndDeleteDoc.isSelected('name'), false); + assert.equal(findAndDeleteDoc.isSelected('docs.name'), false); + assert.strictEqual(undefined, findAndDeleteDoc.name); }); it('including paths through schematype', async function() { @@ -87,10 +87,10 @@ describe('schema select option', function() { assert.strictEqual(true, findOneAndUpdateDoc.isSelected('name')); assert.strictEqual(true, findOneAndUpdateDoc.isSelected('docs.name')); assert.equal(findOneAndUpdateDoc.name, 'the included'); - const findOneAndRemoveDoc = await S.findOneAndRemove({ _id: doc._id }); - assert.strictEqual(true, findOneAndRemoveDoc.isSelected('name')); - assert.strictEqual(true, findOneAndRemoveDoc.isSelected('docs.name')); - assert.equal(findOneAndRemoveDoc.name, 'the included'); + const findOneAndDeleteDoc = await S.findOneAndDelete({ _id: doc._id }); + assert.strictEqual(true, findOneAndDeleteDoc.isSelected('name')); + assert.strictEqual(true, findOneAndDeleteDoc.isSelected('docs.name')); + assert.equal(findOneAndDeleteDoc.name, 'the included'); }); describe('overriding schematype select options', function() { @@ -210,16 +210,16 @@ describe('schema select option', function() { assert.ok(findOneAndUpdateDoc.thin); assert.ok(findOneAndUpdateDoc.docs[0].bool); }); - it('with findOneAndRemove', async function() { - const findOneAndRemoveDoc = await E.findOneAndRemove({ _id: exclusionDoc._id }).select('-name -docs.name'); - assert.equal(findOneAndRemoveDoc.isSelected('name'), false); - assert.equal(findOneAndRemoveDoc.isSelected('thin'), true); - assert.equal(findOneAndRemoveDoc.isSelected('docs.name'), false); - assert.equal(findOneAndRemoveDoc.isSelected('docs.bool'), true); - assert.strictEqual(undefined, findOneAndRemoveDoc.name); - assert.strictEqual(undefined, findOneAndRemoveDoc.docs[0].name); - assert.strictEqual(true, findOneAndRemoveDoc.thin); - assert.strictEqual(true, findOneAndRemoveDoc.docs[0].bool); + it('with findOneAndDelete', async function() { + const findOneAndDeleteDoc = await E.findOneAndDelete({ _id: exclusionDoc._id }).select('-name -docs.name'); + assert.equal(findOneAndDeleteDoc.isSelected('name'), false); + assert.equal(findOneAndDeleteDoc.isSelected('thin'), true); + assert.equal(findOneAndDeleteDoc.isSelected('docs.name'), false); + assert.equal(findOneAndDeleteDoc.isSelected('docs.bool'), true); + assert.strictEqual(undefined, findOneAndDeleteDoc.name); + assert.strictEqual(undefined, findOneAndDeleteDoc.docs[0].name); + assert.strictEqual(true, findOneAndDeleteDoc.thin); + assert.strictEqual(true, findOneAndDeleteDoc.docs[0].bool); }); }); }); diff --git a/test/schema.test.js b/test/schema.test.js index 1bb7495cf35..495a79393a2 100644 --- a/test/schema.test.js +++ b/test/schema.test.js @@ -2650,7 +2650,7 @@ describe('schema', function() { myStr: { type: String, cast: v => '' + v } }); - assert.equal(schema.path('myId').cast('12charstring').toHexString(), '313263686172737472696e67'); + assert.equal(schema.path('myId').cast('0'.repeat(24)).toHexString(), '0'.repeat(24)); assert.equal(schema.path('myNum').cast(3.14), 4); assert.equal(schema.path('myDate').cast('2012-06-01').getFullYear(), 2012); assert.equal(schema.path('myBool').cast('hello'), true); diff --git a/test/schematype.cast.test.js b/test/schematype.cast.test.js index acf2658f3af..77f28e2d9a7 100644 --- a/test/schematype.cast.test.js +++ b/test/schematype.cast.test.js @@ -29,6 +29,9 @@ describe('SchemaType.cast() (gh-7045)', function() { class CustomObjectId extends Schema.ObjectId {} CustomObjectId.cast(v => { + if (v === 'special') { + return original.objectid('0'.repeat(24)); + } assert.ok(v == null || (typeof v === 'string' && v.length === 24)); return original.objectid(v); }); @@ -38,7 +41,7 @@ describe('SchemaType.cast() (gh-7045)', function() { let threw = false; try { - objectid.cast('12charstring'); + baseObjectId.cast('special'); } catch (error) { threw = true; assert.equal(error.name, 'CastError'); @@ -46,7 +49,7 @@ describe('SchemaType.cast() (gh-7045)', function() { assert.doesNotThrow(function() { objectid.cast('000000000000000000000000'); - baseObjectId.cast('12charstring'); + objectid.cast('special'); baseObjectId.cast('000000000000000000000000'); }); diff --git a/test/types/middleware.preposttypes.test.ts b/test/types/middleware.preposttypes.test.ts index 6ee1ec35017..006adb2dd6f 100644 --- a/test/types/middleware.preposttypes.test.ts +++ b/test/types/middleware.preposttypes.test.ts @@ -468,51 +468,6 @@ schema.post('findOneAndDelete', { document: false, query: false }, function(res) expectNotType>(res); }); -schema.pre('findOneAndRemove', function() { - expectType>(this); -}); - -schema.post('findOneAndRemove', function(res) { - expectType>(this); - expectNotType>(res); -}); - -schema.pre('findOneAndRemove', { document: false, query: true }, function() { - expectType>(this); -}); - -schema.post('findOneAndRemove', { document: false, query: true }, function(res) { - expectType>(this); - expectNotType>(res); -}); - -schema.pre('findOneAndRemove', { document: true, query: true }, function() { - expectType>(this); -}); - -schema.post('findOneAndRemove', { document: true, query: true }, function(res) { - expectType>(this); - expectNotType>(res); -}); - -schema.pre('findOneAndRemove', { document: true, query: false }, function() { - expectType(this); -}); - -schema.post('findOneAndRemove', { document: true, query: false }, function(res) { - expectType(this); - expectNotType>(res); -}); - -schema.pre('findOneAndRemove', { document: false, query: false }, function() { - expectType(this); -}); - -schema.post('findOneAndRemove', { document: false, query: false }, function(res) { - expectType(this); - expectNotType>(res); -}); - schema.pre('findOneAndReplace', function() { expectType>(this); }); @@ -828,92 +783,92 @@ schema.post(['save', 'init'], { document: false, query: false }, function(res) { expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: false, query: true }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: false, query: true }, function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: false, query: true }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: false, query: true }, function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: true, query: true }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: true, query: true }, function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: true, query: true }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: true, query: true }, function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: true, query: false }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: true, query: false }, function() { expectType(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: true, query: false }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: true, query: false }, function(res) { expectType(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: false, query: false }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: false, query: false }, function() { expectType(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: false, query: false }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany'], { document: false, query: false }, function(res) { expectType(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: false, query: true }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: false, query: true }, function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: false, query: true }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: false, query: true }, function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: true, query: false }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: true, query: false }, function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: true, query: false }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: true, query: false }, function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: true, query: true }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: true, query: true }, function() { expectType|HydratedDocument>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: true, query: true }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: true, query: true }, function(res) { expectType|HydratedDocument>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: false, query: false }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: false, query: false }, function() { expectType(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: false, query: false }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne'], { document: false, query: false }, function(res) { expectType(this); expectNotType>(res); }); @@ -963,92 +918,92 @@ schema.post(['save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], function() { expectType|HydratedDocument>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], function(res) { expectType|HydratedDocument>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: false, query: true }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: false, query: true }, function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: false, query: true }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: false, query: true }, function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: true, query: false }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: true, query: false }, function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: true, query: false }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: true, query: false }, function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: true, query: true }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: true, query: true }, function() { expectType|HydratedDocument>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: true, query: true }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: true, query: true }, function(res) { expectType|HydratedDocument>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: false, query: false }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: false, query: false }, function() { expectType(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: false, query: false }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'updateOne', 'deleteOne', 'validate'], { document: false, query: false }, function(res) { expectType(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], function() { expectType|HydratedDocument>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], function(res) { expectType|HydratedDocument>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: false, query: true }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: false, query: true }, function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: false, query: true }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: false, query: true }, function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: true, query: false }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: true, query: false }, function() { expectType>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: true, query: false }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: true, query: false }, function(res) { expectType>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: true, query: true }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: true, query: true }, function() { expectType|HydratedDocument>(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: true, query: true }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: true, query: true }, function(res) { expectType|HydratedDocument>(this); expectNotType>(res); }); -schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: false, query: false }, function() { +schema.pre(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: false, query: false }, function() { expectType(this); }); -schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndRemove', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: false, query: false }, function(res) { +schema.post(['count', 'estimatedDocumentCount', 'countDocuments', 'deleteMany', 'distinct', 'find', 'findOne', 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'replaceOne', 'updateMany', 'save', 'init', 'updateOne', 'deleteOne', 'validate'], { document: false, query: false }, function(res) { expectType(this); expectNotType>(res); }); diff --git a/test/types/queries.test.ts b/test/types/queries.test.ts index 2b6175dfd30..ff5f7c18cba 100644 --- a/test/types/queries.test.ts +++ b/test/types/queries.test.ts @@ -115,10 +115,10 @@ Test.findOneAndUpdate({ name: 'test' }, { name: 'test3' }, { upsert: true, new: Test.findOneAndUpdate({ name: 'test' }, { name: 'test3' }, { upsert: true, returnOriginal: false }).then((res: ITest) => { res.name = 'test4'; }); -Test.findOneAndUpdate({ name: 'test' }, { name: 'test3' }, { rawResult: true }).then((res: any) => { +Test.findOneAndUpdate({ name: 'test' }, { name: 'test3' }, { includeResultMetadata: true }).then((res: any) => { console.log(res.ok); }); -Test.findOneAndUpdate({ name: 'test' }, { name: 'test3' }, { new: true, upsert: true, rawResult: true }).then((res: any) => { +Test.findOneAndUpdate({ name: 'test' }, { name: 'test3' }, { new: true, upsert: true, includeResultMetadata: true }).then((res: any) => { console.log(res.ok); }); @@ -423,7 +423,7 @@ async function gh11602(): Promise { const updateResult = await ModelType.findOneAndUpdate(query, { $inc: { occurence: 1 } }, { upsert: true, returnDocument: 'after', - rawResult: true + includeResultMetadata: true }); expectError(updateResult.lastErrorObject?.modifiedCount); diff --git a/types/middlewares.d.ts b/types/middlewares.d.ts index 43ca1974b81..2d305252908 100644 --- a/types/middlewares.d.ts +++ b/types/middlewares.d.ts @@ -5,7 +5,7 @@ declare module 'mongoose' { type MongooseDistinctDocumentMiddleware = 'save' | 'init' | 'validate'; type MongooseDocumentMiddleware = MongooseDistinctDocumentMiddleware | MongooseQueryAndDocumentMiddleware; - type MongooseDistinctQueryMiddleware = 'count' | 'estimatedDocumentCount' | 'countDocuments' | 'deleteMany' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndRemove' | 'findOneAndReplace' | 'findOneAndUpdate' | 'replaceOne' | 'updateMany'; + type MongooseDistinctQueryMiddleware = 'count' | 'estimatedDocumentCount' | 'countDocuments' | 'deleteMany' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndReplace' | 'findOneAndUpdate' | 'replaceOne' | 'updateMany'; type MongooseDefaultQueryMiddleware = MongooseDistinctQueryMiddleware | 'updateOne' | 'deleteOne'; type MongooseQueryMiddleware = MongooseDistinctQueryMiddleware | MongooseQueryAndDocumentMiddleware; diff --git a/types/models.d.ts b/types/models.d.ts index 1d001640565..6af56a5656d 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -551,7 +551,7 @@ declare module 'mongoose' { findByIdAndUpdate( id: mongodb.ObjectId | any, update: UpdateQuery, - options: QueryOptions & { rawResult: true } + options: QueryOptions & { includeResultMetadata: true } ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate'>; findByIdAndUpdate( id: mongodb.ObjectId | any, @@ -589,12 +589,6 @@ declare module 'mongoose' { options?: QueryOptions | null ): QueryWithHelpers; - /** Creates a `findOneAndRemove` query: atomically finds the given document and deletes it. */ - findOneAndRemove( - filter?: FilterQuery, - options?: QueryOptions | null - ): QueryWithHelpers; - /** Creates a `findOneAndReplace` query: atomically finds the given document and replaces it with `replacement`. */ findOneAndReplace( filter: FilterQuery, @@ -610,7 +604,7 @@ declare module 'mongoose' { findOneAndReplace( filter: FilterQuery, replacement: TRawDocType | AnyObject, - options: QueryOptions & { rawResult: true } + options: QueryOptions & { includeResultMetadata: true } ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndReplace'>; findOneAndReplace( filter: FilterQuery, @@ -643,7 +637,7 @@ declare module 'mongoose' { findOneAndUpdate( filter: FilterQuery, update: UpdateQuery, - options: QueryOptions & { rawResult: true } + options: QueryOptions & { includeResultMetadata: true } ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate'>; findOneAndUpdate( filter: FilterQuery, diff --git a/types/query.d.ts b/types/query.d.ts index 2fb346fdd5c..008f82d2d9d 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -124,9 +124,9 @@ declare module 'mongoose' { overwriteDiscriminatorKey?: boolean; projection?: ProjectionType; /** - * if true, returns the raw result from the MongoDB driver + * if true, returns the full ModifyResult rather than just the document */ - rawResult?: boolean; + includeResultMetadata?: boolean; readPreference?: string | mongodb.ReadPreferenceMode; /** * An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. @@ -392,17 +392,11 @@ declare module 'mongoose' { options?: QueryOptions | null ): QueryWithHelpers; - /** Creates a `findOneAndRemove` query: atomically finds the given document and deletes it. */ - findOneAndRemove( - filter?: FilterQuery, - options?: QueryOptions | null - ): QueryWithHelpers; - /** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */ findOneAndUpdate( filter: FilterQuery, update: UpdateQuery, - options: QueryOptions & { rawResult: true } + options: QueryOptions & { includeResultMetadata: true } ): QueryWithHelpers, DocType, THelpers, RawDocType, 'findOneAndUpdate'>; findOneAndUpdate( filter: FilterQuery, @@ -439,7 +433,7 @@ declare module 'mongoose' { findByIdAndUpdate( id: mongodb.ObjectId | any, update: UpdateQuery, - options: QueryOptions & { rawResult: true } + options: QueryOptions & { includeResultMetadata: true } ): QueryWithHelpers; findByIdAndUpdate( id: mongodb.ObjectId | any,