Skip to content

Commit c1d5b76

Browse files
authored
Merge pull request #13774 from Automattic/vkarpov15/gh-13748
fix(document): make array getters avoid unintentionally modifying array, defer getters until index access instead
2 parents fafa5d5 + 65245a4 commit c1d5b76

File tree

4 files changed

+57
-8
lines changed

4 files changed

+57
-8
lines changed

lib/document.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4091,6 +4091,14 @@ function applyGetters(self, json, options) {
40914091
if (ii === last) {
40924092
const val = self.$get(path);
40934093
branch[part] = clone(val, options);
4094+
if (Array.isArray(branch[part]) && schema.paths[path].$embeddedSchemaType) {
4095+
for (let i = 0; i < branch[part].length; ++i) {
4096+
branch[part][i] = schema.paths[path].$embeddedSchemaType.applyGetters(
4097+
branch[part][i],
4098+
self
4099+
);
4100+
}
4101+
}
40944102
} else if (v == null) {
40954103
if (part in cur) {
40964104
branch[part] = v;

lib/schema/array.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -289,13 +289,6 @@ SchemaArray.prototype.applyGetters = function(value, scope) {
289289
}
290290

291291
const ret = SchemaType.prototype.applyGetters.call(this, value, scope);
292-
if (Array.isArray(ret)) {
293-
const rawValue = utils.isMongooseArray(ret) ? ret.__array : ret;
294-
const len = rawValue.length;
295-
for (let i = 0; i < len; ++i) {
296-
rawValue[i] = this.caster.applyGetters(rawValue[i], scope);
297-
}
298-
}
299292
return ret;
300293
};
301294

lib/types/array/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ function MongooseArray(values, path, doc, schematype) {
9090
if (mongooseArrayMethods.hasOwnProperty(prop)) {
9191
return mongooseArrayMethods[prop];
9292
}
93+
if (typeof prop === 'string' && numberRE.test(prop) && schematype?.$embeddedSchemaType != null) {
94+
return schematype.$embeddedSchemaType.applyGetters(__array[prop], doc);
95+
}
9396

9497
return __array[prop];
9598
},

test/document.test.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5564,6 +5564,7 @@ describe('document', function() {
55645564
const Test = db.model('Test', testSchema);
55655565

55665566
const doc = new Test({ arr: [new mongoose.Types.ObjectId()] });
5567+
assert.equal(called, 0);
55675568
assert.deepEqual(doc.toObject({ getters: true }).arr, [42]);
55685569
assert.equal(called, 1);
55695570
});
@@ -5582,7 +5583,6 @@ describe('document', function() {
55825583

55835584
const Test = db.model('Test', testSchema);
55845585

5585-
55865586
let doc = await Test.create({ arr: [new mongoose.Types.ObjectId()] });
55875587
assert.equal(called, 1);
55885588

@@ -12343,6 +12343,51 @@ describe('document', function() {
1234312343
assert.strictEqual(Object.polluted, undefined);
1234412344
});
1234512345

12346+
it('does not modify array when calling getters (gh-13748)', async function() {
12347+
// create simple setter to add a sufix
12348+
const addSufix = (name) => {
12349+
return name + '-sufix';
12350+
};
12351+
12352+
// create simple gettrer to remove last 6 letters (should be "-sufix")
12353+
const removeSufix = (name) => {
12354+
return ('' + name).slice(0, -6);
12355+
};
12356+
12357+
const userSchema = new mongoose.Schema(
12358+
{
12359+
name: String,
12360+
age: Number,
12361+
profession: {
12362+
type: String,
12363+
get: removeSufix,
12364+
set: addSufix
12365+
},
12366+
hobbies: [{ type: String, get: removeSufix, set: addSufix }]
12367+
},
12368+
{
12369+
toObject: { getters: true },
12370+
toJSON: { getters: true }
12371+
}
12372+
);
12373+
const User = db.model('User', userSchema);
12374+
12375+
const usr = await User.create({
12376+
name: 'John',
12377+
age: 18,
12378+
profession: 'teacher',
12379+
hobbies: ['swimming', 'football']
12380+
});
12381+
12382+
const oneUser = await User.findById(usr._id).orFail();
12383+
assert.equal(oneUser.profession, 'teacher');
12384+
assert.equal(oneUser.profession, 'teacher');
12385+
assert.equal(oneUser.profession, 'teacher');
12386+
assert.equal(oneUser.hobbies[0], 'swimming');
12387+
assert.equal(oneUser.hobbies[0], 'swimming');
12388+
assert.equal(oneUser.hobbies[0], 'swimming');
12389+
});
12390+
1234612391
it('sets defaults on subdocs with subdoc projection (gh-13720)', async function() {
1234712392
const subSchema = new mongoose.Schema({
1234812393
propertyA: { type: String, default: 'A' },

0 commit comments

Comments
 (0)