diff --git a/lib/helpers/populate/assignVals.js b/lib/helpers/populate/assignVals.js index 651a48e4887..da40f396e2a 100644 --- a/lib/helpers/populate/assignVals.js +++ b/lib/helpers/populate/assignVals.js @@ -52,6 +52,11 @@ module.exports = function assignVals(o) { const _allIds = o.allIds[i]; + if (o.path.endsWith('.$*')) { + // Skip maps re: gh-12494 + return valueFilter(val, options, populateOptions, _allIds); + } + if (o.justOne === true && Array.isArray(val)) { // Might be an embedded discriminator (re: gh-9244) with multiple models, so make sure to pick the right // model before assigning. diff --git a/lib/types/map.js b/lib/types/map.js index 3c68026fa0e..6516d2e4038 100644 --- a/lib/types/map.js +++ b/lib/types/map.js @@ -97,15 +97,27 @@ class MongooseMap extends Map { const fullPath = this.$__path + '.' + key; const populated = this.$__parent != null && this.$__parent.$__ ? - this.$__parent.$populated(fullPath) || this.$__parent.$populated(this.$__path) : + this.$__parent.$populated(fullPath, true) || this.$__parent.$populated(this.$__path, true) : null; const priorVal = this.get(key); if (populated != null) { - if (value.$__ == null) { - value = new populated.options[populateModelSymbol](value); + if (Array.isArray(value) && this.$__schemaType.$isMongooseArray) { + value = value.map(v => { + if (v.$__ == null) { + v = new populated.options[populateModelSymbol](v); + } + // Doesn't support single nested "in-place" populate + v.$__.wasPopulated = { value: v._id }; + return v; + }); + } else { + if (value.$__ == null) { + value = new populated.options[populateModelSymbol](value); + } + // Doesn't support single nested "in-place" populate + value.$__.wasPopulated = { value: value._id }; } - value.$__.wasPopulated = { value: populated.value }; } else { try { value = this.$__schemaType. diff --git a/test/types.map.test.js b/test/types.map.test.js index 9edd721b068..c3415a6c3bf 100644 --- a/test/types.map.test.js +++ b/test/types.map.test.js @@ -1050,4 +1050,45 @@ describe('Map', function() { const res = doc.toObject({ flattenMaps: true }); assert.equal(res.l1.l1key.l2.l2key.value, 'abc'); }); + + it('handles populating map of arrays (gh-12494)', async function() { + const User = new mongoose.Schema({ + name: String, + addresses: { + type: Map, + of: [{ + type: mongoose.Schema.Types.ObjectId, + ref: 'Address' + }], + default: {} + } + }); + + const Address = new mongoose.Schema({ + city: String + }); + + const UserModel = db.model('User', User); + const AddressModel = db.model('Address', Address); + + const address = await new AddressModel({ city: 'London' }).save(); + + const { _id } = await new UserModel({ + name: 'Name', + addresses: { + home: [address._id] + } + }).save(); + + const query = UserModel.findById(_id); + + query.populate({ + path: 'addresses.$*' + }); + + const doc = await query.exec(); + assert.ok(Array.isArray(doc.addresses.get('home'))); + assert.equal(doc.addresses.get('home').length, 1); + assert.equal(doc.addresses.get('home')[0].city, 'London'); + }); });