diff --git a/lib/types/map.js b/lib/types/map.js index 194982f4e1..d3043be058 100644 --- a/lib/types/map.js +++ b/lib/types/map.js @@ -9,6 +9,7 @@ const handleSpreadDoc = require('../helpers/document/handleSpreadDoc'); const util = require('util'); const specialProperties = require('../helpers/specialProperties'); const isBsonType = require('../helpers/isBsonType'); +const cleanModifiedSubpaths = require('../helpers/document/cleanModifiedSubpaths'); const populateModelSymbol = require('../helpers/symbols').populateModelSymbol; @@ -157,7 +158,13 @@ class MongooseMap extends Map { super.set(key, value); if (parent != null && parent.$__ != null && !deepEqual(value, priorVal)) { - parent.markModified(fullPath.call(this)); + const path = fullPath.call(this); + parent.markModified(path); + // If overwriting the full document array or subdoc, make sure to clean up any paths that were modified + // before re: #15108 + if (this.$__schemaType.$isMongooseDocumentArray || this.$__schemaType.$isSingleNested) { + cleanModifiedSubpaths(parent, path); + } } // Delay calculating full path unless absolutely necessary, because string diff --git a/test/types.map.test.js b/test/types.map.test.js index 3b22beccc5..c6486e507a 100644 --- a/test/types.map.test.js +++ b/test/types.map.test.js @@ -1103,4 +1103,51 @@ describe('Map', function() { assert.equal(doc.addresses.get('home').length, 1); assert.equal(doc.addresses.get('home')[0].city, 'London'); }); + + it('clears nested changes in subdocs (gh-15108)', async function() { + const CarSchema = new mongoose.Schema({ + owners: { + type: Map, + of: { + name: String + } + } + }); + const CarModel = db.model('Car', CarSchema); + const car = await CarModel.create({ + owners: { abc: { name: 'John' } } + }); + + car.owners.get('abc').name = undefined; + car.owners.delete('abc'); + assert.deepStrictEqual(car.getChanges(), { $unset: { 'owners.abc': 1 } }); + await car.save(); + + const doc = await CarModel.findById(car._id); + assert.strictEqual(doc.owners.get('abc'), undefined); + }); + + it('clears nested changes in doc arrays (gh-15108)', async function() { + const CarSchema = new mongoose.Schema({ + owners: { + type: Map, + of: [{ + _id: false, + name: String + }] + } + }); + const CarModel = db.model('Car', CarSchema); + const car = await CarModel.create({ + owners: { abc: [{ name: 'John' }] } + }); + + car.owners.get('abc')[0].name = undefined; + car.owners.set('abc', [{ name: 'Bill' }]); + assert.deepStrictEqual(car.getChanges(), { $inc: { __v: 1 }, $set: { 'owners.abc': [{ name: 'Bill' }] } }); + await car.save(); + + const doc = await CarModel.findById(car._id); + assert.deepStrictEqual(doc.owners.get('abc').toObject(), [{ name: 'Bill' }]); + }); });