From 01075919b1d6b49d3c96226284beaf145a6ca3f4 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 15 Sep 2024 13:52:19 -0400 Subject: [PATCH 1/2] feat(query): cast $rename Fix #3027 --- lib/helpers/query/castUpdate.js | 23 ++++++++++++++++++++--- test/model.updateOne.test.js | 25 ++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/lib/helpers/query/castUpdate.js b/lib/helpers/query/castUpdate.js index eb69bc89a09..94f374d8b58 100644 --- a/lib/helpers/query/castUpdate.js +++ b/lib/helpers/query/castUpdate.js @@ -2,6 +2,7 @@ const CastError = require('../../error/cast'); const MongooseError = require('../../error/mongooseError'); +const SchemaString = require('../../schema/string'); const StrictModeError = require('../../error/strict'); const ValidationError = require('../../error/validation'); const castNumber = require('../../cast/number'); @@ -307,6 +308,20 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) { continue; } + hasKeys = true; + } else if (op === '$rename') { + const schematype = new SchemaString(`${prefix}${key}.$rename`); + try { + obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key); + } catch (error) { + aggregatedError = _appendError(error, context, key, aggregatedError); + } + + if (obj[key] === void 0) { + delete obj[key]; + continue; + } + hasKeys = true; } else { const pathToCheck = (prefix + key); @@ -372,10 +387,12 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) { delete obj[key]; } } else { - // gh-1845 temporary fix: ignore $rename. See gh-3027 for tracking - // improving this. if (op === '$rename') { - hasKeys = true; + if (obj[key] == null) { + throw new CastError('String', obj[key], `${prefix}${key}.$rename`); + } + const schematype = new SchemaString(`${prefix}${key}.$rename`); + obj[key] = schematype.castForQuery(null, obj[key], context); continue; } diff --git a/test/model.updateOne.test.js b/test/model.updateOne.test.js index e9ae3ce43a5..8a228208823 100644 --- a/test/model.updateOne.test.js +++ b/test/model.updateOne.test.js @@ -1078,9 +1078,28 @@ describe('model: updateOne:', function() { const Model = db.model('Test', schema); const update = { $rename: { foo: 'bar' } }; - await Model.create({ foo: Date.now() }); - const res = await Model.updateOne({}, update, { multi: true }); - assert.equal(res.modifiedCount, 1); + const foo = Date.now(); + const { _id } = await Model.create({ foo }); + await Model.updateOne({}, update); + const doc = await Model.findById(_id); + assert.equal(doc.bar.valueOf(), foo.valueOf()); + }); + + it('throws CastError if $rename fails to cast to string (gh-1845)', async function() { + const schema = new Schema({ foo: Date, bar: Date }); + const Model = db.model('Test', schema); + + let err = await Model.updateOne({}, { $rename: { foo: { prop: 'baz' } } }).then(() => null, err => err); + assert.equal(err.name, 'CastError'); + assert.ok(err.message.includes('foo.$rename')); + + err = await Model.updateOne({}, { $rename: { foo: null } }).then(() => null, err => err); + assert.equal(err.name, 'CastError'); + assert.ok(err.message.includes('foo.$rename')); + + err = await Model.updateOne({}, { $rename: { foo: undefined } }).then(() => null, err => err); + assert.equal(err.name, 'CastError'); + assert.ok(err.message.includes('foo.$rename')); }); it('allows objects with positional operator (gh-3185)', async function() { From 176fb386f05218d9e672ad132f7f294f4f83887d Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 17 Sep 2024 11:25:43 -0400 Subject: [PATCH 2/2] Update test/model.updateOne.test.js Co-authored-by: hasezoey --- test/model.updateOne.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/model.updateOne.test.js b/test/model.updateOne.test.js index 8a228208823..0b4223dad5c 100644 --- a/test/model.updateOne.test.js +++ b/test/model.updateOne.test.js @@ -1083,6 +1083,7 @@ describe('model: updateOne:', function() { await Model.updateOne({}, update); const doc = await Model.findById(_id); assert.equal(doc.bar.valueOf(), foo.valueOf()); + assert.equal(doc.foo, undefined); }); it('throws CastError if $rename fails to cast to string (gh-1845)', async function() {