Skip to content

Commit 3cfd3f6

Browse files
committed
Merge branch '6.x' into 7.x
2 parents 0deb234 + becd799 commit 3cfd3f6

File tree

5 files changed

+72
-2
lines changed

5 files changed

+72
-2
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
6.13.2 / 2024-09-12
2+
===================
3+
* fix(document): make set() respect merge option on deeply nested objects #14870 #14878
4+
15
7.8.1 / 2024-08-19
26
==================
37
* fix(query): handle casting $switch in $expr #14761
@@ -12,6 +16,10 @@
1216
==================
1317
* feat(model): add throwOnValidationError option for opting into getting MongooseBulkWriteError if all valid operations succeed in bulkWrite() and insertMany() #14599 #14587 #14572 #13410
1418

19+
6.13.1 / 2024-09-06
20+
===================
21+
* fix: remove empty $and, $or, $not that were made empty by scrict mode #14749 #13086 [0x0a0d](https://github.com/0x0a0d)
22+
1523
6.13.0 / 2024-06-06
1624
===================
1725
* feat(model): add throwOnValidationError option for opting into getting MongooseBulkWriteError if all valid operations succeed in bulkWrite() and insertMany() #14599 #14587 #14572 #13410

lib/cast.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,27 @@ module.exports = function cast(schema, obj, options, context) {
6565
if (!Array.isArray(val)) {
6666
throw new CastError('Array', val, path);
6767
}
68-
for (let k = 0; k < val.length; ++k) {
68+
for (let k = val.length - 1; k >= 0; k--) {
6969
if (val[k] == null || typeof val[k] !== 'object') {
7070
throw new CastError('Object', val[k], path + '.' + k);
7171
}
72+
const beforeCastKeysLength = Object.keys(val[k]).length;
7273
const discriminatorValue = val[k][schema.options.discriminatorKey];
7374
if (discriminatorValue == null) {
7475
val[k] = cast(schema, val[k], options, context);
7576
} else {
7677
const discriminatorSchema = getSchemaDiscriminatorByValue(context.schema, discriminatorValue);
7778
val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context);
7879
}
80+
81+
if (Object.keys(val[k]).length === 0 && beforeCastKeysLength !== 0) {
82+
val.splice(k, 1);
83+
}
84+
}
85+
86+
// delete empty: {$or: []} -> {}
87+
if (val.length === 0) {
88+
delete obj[path];
7989
}
8090
} else if (path === '$where') {
8191
type = typeof val;

lib/document.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
12041204
this.$__setValue(path, null);
12051205
cleanModifiedSubpaths(this, path);
12061206
} else {
1207-
return this.$set(val, path, constructing);
1207+
return this.$set(val, path, constructing, options);
12081208
}
12091209

12101210
const keys = getKeysInSchemaOrder(this.$__schema, val, path);

test/docs/cast.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,26 @@ describe('Cast Tutorial', function() {
137137
// acquit:ignore:end
138138
});
139139

140+
it('strictQuery removes casted empty objects', async function() {
141+
mongoose.deleteModel('Character');
142+
const schema = new mongoose.Schema({ name: String, age: Number }, {
143+
strictQuery: true
144+
});
145+
Character = mongoose.model('Character', schema);
146+
147+
const query = Character.findOne({
148+
$or: [{ notInSchema: { $lt: 'not a number' } }],
149+
$and: [{ name: 'abc' }, { age: { $gt: 18 } }, { notInSchema: { $lt: 'not a number' } }],
150+
$nor: [{}] // should be kept
151+
});
152+
153+
await query.exec();
154+
query.getFilter(); // Empty object `{}`, Mongoose removes `notInSchema`
155+
// acquit:ignore:start
156+
assert.deepEqual(query.getFilter(), { $and: [{ name: 'abc' }, { age: { $gt: 18 } }], $nor: [{}] });
157+
// acquit:ignore:end
158+
});
159+
140160
it('implicit in', async function() {
141161
// Normally wouldn't find anything because `name` is a string, but
142162
// Mongoose automatically inserts `$in`

test/document.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8048,6 +8048,38 @@ describe('document', function() {
80488048
await person.save();
80498049
});
80508050

8051+
it('set() merge option with double nested', async function() {
8052+
const PersonSchema = new Schema({
8053+
info: {
8054+
address: {
8055+
city: String,
8056+
country: { type: String, default: 'UK' },
8057+
postcode: String
8058+
}
8059+
}
8060+
});
8061+
8062+
const Person = db.model('Person', PersonSchema);
8063+
8064+
8065+
const person = new Person({
8066+
info: {
8067+
address: {
8068+
country: 'United States',
8069+
city: 'New York'
8070+
}
8071+
}
8072+
});
8073+
8074+
const update = { info: { address: { postcode: '12H' } } };
8075+
8076+
person.set(update, undefined, { merge: true });
8077+
8078+
assert.equal(person.info.address.city, 'New York');
8079+
assert.equal(person.info.address.postcode, '12H');
8080+
assert.equal(person.info.address.country, 'United States');
8081+
});
8082+
80518083
it('setting single nested subdoc with timestamps (gh-8251)', async function() {
80528084
const ActivitySchema = Schema({ description: String }, { timestamps: true });
80538085
const RequestSchema = Schema({ activity: ActivitySchema });

0 commit comments

Comments
 (0)