From f2cf85a142f9836518eaa299f024444d0b2654a6 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 1 May 2023 14:23:02 -0400 Subject: [PATCH] fix(query): apply schema-level paths before calculating projection for findOneAndUpdate() Fix #13340 --- lib/query.js | 52 ++++++++-------------------- test/model.findOneAndReplace.test.js | 1 - test/query.test.js | 2 +- 3 files changed, 15 insertions(+), 40 deletions(-) diff --git a/lib/query.js b/lib/query.js index 4d6f3accc86..4dc900f4f78 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2003,9 +2003,13 @@ Query.prototype._optionsForExec = function(model) { } } - const projection = this._fieldsForExec(); - if (projection != null) { - options.projection = projection; + this._applyPaths(); + if (this._fields != null) { + this._fields = this._castFields(this._fields); + const projection = this._fieldsForExec(); + if (projection != null) { + options.projection = projection; + } } return options; @@ -2258,10 +2262,6 @@ Query.prototype._find = wrapThunk(function(callback) { callback = _wrapThunkCallback(this, callback); - this._applyPaths(); - this._fields = this._castFields(this._fields); - - const fields = this._fieldsForExec(); const mongooseOptions = this._mongooseOptions; const _this = this; const userProvidedFields = _this._userProvidedFields || {}; @@ -2276,6 +2276,10 @@ Query.prototype._find = wrapThunk(function(callback) { lean: mongooseOptions.lean || null }); + const options = this._optionsForExec(); + const filter = this._conditions; + const fields = options.projection; + const cb = (err, docs) => { if (err) { return callback(err); @@ -2317,8 +2321,6 @@ Query.prototype._find = wrapThunk(function(callback) { }); }; - const options = this._optionsForExec(); - const filter = this._conditions; this._collection.collection.find(filter, options, (err, cursor) => { if (err != null) { @@ -2531,8 +2533,6 @@ Query.prototype._findOne = wrapThunk(function(callback) { return null; } - this._applyPaths(); - this._fields = this._castFields(this._fields); applyGlobalMaxTimeMS(this.options, this.model); applyGlobalDiskUse(this.options, this.model); @@ -3852,17 +3852,6 @@ Query.prototype._findOneAndReplace = wrapThunk(function(callback) { const filter = this._conditions; const options = this._optionsForExec(); convertNewToReturnDocument(options); - let fields = null; - - this._applyPaths(); - if (this._fields != null) { - options.projection = this._castFields(utils.clone(this._fields)); - fields = options.projection; - if (fields instanceof Error) { - callback(fields); - return null; - } - } const runValidators = _getOption(this, 'runValidators', false); if (runValidators === false) { @@ -3991,7 +3980,6 @@ Query.prototype._findAndModify = function(type, callback) { const model = this.model; const schema = model.schema; const _this = this; - let fields; const castedQuery = castQuery(this); if (castedQuery instanceof Error) { @@ -4062,18 +4050,6 @@ Query.prototype._findAndModify = function(type, callback) { } } - this._applyPaths(); - if (this._fields) { - this._fields = this._castFields(this._fields); - fields = this._fieldsForExec(); - if (fields != null) { - opts.projection = fields; - } - if (opts.projection instanceof Error) { - return callback(opts.projection); - } - } - if (opts.sort) convertSortToArray(opts); const cb = function(err, doc, res) { @@ -5442,6 +5418,9 @@ Query.prototype._castFields = function _castFields(fields) { */ Query.prototype._applyPaths = function applyPaths() { + if (!this.model) { + return; + } this._fields = this._fields || {}; helpers.applyPaths(this._fields, this.model.schema); @@ -5500,9 +5479,6 @@ Query.prototype._applyPaths = function applyPaths() { */ Query.prototype.cursor = function cursor(opts) { - this._applyPaths(); - this._fields = this._castFields(this._fields); - if (opts) { this.setOptions(opts); } diff --git a/test/model.findOneAndReplace.test.js b/test/model.findOneAndReplace.test.js index c303bb598f8..f8188663f42 100644 --- a/test/model.findOneAndReplace.test.js +++ b/test/model.findOneAndReplace.test.js @@ -371,7 +371,6 @@ describe('model: findOneAndReplace:', function() { const schema = new Schema({ name: String, age: { type: Number, select: false } }); const Model = db.model('Test', schema); - const doc = await Model.findOneAndReplace({}, { name: 'Jean-Luc Picard', age: 59 }, { upsert: true, returnOriginal: false diff --git a/test/query.test.js b/test/query.test.js index 967a4251a12..163bc1352f3 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1268,7 +1268,7 @@ describe('Query', function() { const q = new Query(); q.hint(hint); - const options = q._optionsForExec({ schema: { options: {} } }); + const options = q._optionsForExec(); assert.equal(JSON.stringify(options), a); done(); });