Skip to content

Commit 16ae35d

Browse files
committed
fix(query): handle casting array filter paths underneath array filter paths with embedded discriminators
Fix #15386
1 parent b1ac0f5 commit 16ae35d

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

lib/helpers/query/getEmbeddedDiscriminatorPath.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const updatedPathsByArrayFilter = require('../update/updatedPathsByArrayFilter')
1717
*/
1818

1919
module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, path, options) {
20-
const parts = path.split('.');
20+
const parts = path.indexOf('.') === -1 ? [path] : path.split('.');
2121
let schematype = null;
2222
let type = 'adhocOrUndefined';
2323

@@ -26,9 +26,10 @@ module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, p
2626
const arrayFilters = options != null && Array.isArray(options.arrayFilters) ?
2727
options.arrayFilters : [];
2828
const updatedPathsByFilter = updatedPathsByArrayFilter(update);
29+
let startIndex = 0;
2930

3031
for (let i = 0; i < parts.length; ++i) {
31-
const originalSubpath = parts.slice(0, i + 1).join('.');
32+
const originalSubpath = parts.slice(startIndex, i + 1).join('.');
3233
const subpath = cleanPositionalOperators(originalSubpath);
3334
schematype = schema.path(subpath);
3435
if (schematype == null) {
@@ -89,6 +90,8 @@ module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, p
8990

9091
const rest = parts.slice(i + 1).join('.');
9192
schematype = discriminatorSchema.path(rest);
93+
schema = discriminatorSchema;
94+
startIndex = i + 1;
9295
if (schematype != null) {
9396
type = discriminatorSchema._getPathType(rest);
9497
break;

lib/helpers/schema/getPath.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const numberRE = /^\d+$/;
88
* @api private
99
*/
1010

11-
module.exports = function getPath(schema, path) {
11+
module.exports = function getPath(schema, path, discriminatorValueMap) {
1212
let schematype = schema.path(path);
1313
if (schematype != null) {
1414
return schematype;
@@ -26,10 +26,14 @@ module.exports = function getPath(schema, path) {
2626
schematype = schema.path(cur);
2727
if (schematype != null && schematype.schema) {
2828
schema = schematype.schema;
29+
const currentPath = cur;
2930
cur = '';
3031
if (!isArray && schematype.$isMongooseDocumentArray) {
3132
isArray = true;
3233
}
34+
if (discriminatorValueMap && discriminatorValueMap[currentPath]) {
35+
schema = schema.discriminators[discriminatorValueMap[currentPath]] ?? schema;
36+
}
3337
}
3438
}
3539

lib/helpers/update/castArrayFilters.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt
3333
return;
3434
}
3535

36+
// Map of embedded discriminator values set in the array filters
37+
const discriminatorValueMap = {};
38+
3639
for (const filter of arrayFilters) {
3740
if (filter == null) {
3841
throw new Error(`Got null array filter in ${arrayFilters}`);
@@ -58,12 +61,13 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt
5861
updatedPathsByFilter[filterWildcardPath]
5962
);
6063

61-
const baseSchematype = getPath(schema, baseFilterPath);
64+
const baseSchematype = getPath(schema, baseFilterPath, discriminatorValueMap);
6265
let filterBaseSchema = baseSchematype != null ? baseSchematype.schema : null;
6366
if (filterBaseSchema != null &&
6467
filterBaseSchema.discriminators != null &&
6568
filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey]) {
6669
filterBaseSchema = filterBaseSchema.discriminators[filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey]] || filterBaseSchema;
70+
discriminatorValueMap[baseFilterPath] = filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey];
6771
}
6872

6973
for (const key of keys) {
@@ -83,7 +87,7 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt
8387
// If there are multiple array filters in the path being updated, make sure
8488
// to replace them so we can get the schema path.
8589
filterPathRelativeToBase = cleanPositionalOperators(filterPathRelativeToBase);
86-
schematype = getPath(filterBaseSchema, filterPathRelativeToBase);
90+
schematype = getPath(filterBaseSchema, filterPathRelativeToBase, discriminatorValueMap);
8791
}
8892

8993
if (schematype == null) {

test/helpers/update.castArrayFilters.test.js

+48
Original file line numberDiff line numberDiff line change
@@ -349,4 +349,52 @@ describe('castArrayFilters', function() {
349349

350350
assert.strictEqual(q.getUpdate().$set['groups.$[group].tags.$[tag]'], '42');
351351
});
352+
353+
it('casts paths underneath embedded discriminators (gh-15386)', async function() {
354+
const eventSchema = new Schema({ message: String }, { discriminatorKey: 'kind', _id: false });
355+
const batchSchema = new Schema({ events: [eventSchema] });
356+
357+
const docArray = batchSchema.path('events');
358+
docArray.discriminator('Clicked', new Schema({ element: { type: String, required: true } }, { _id: false }));
359+
360+
const productSchema = new Schema({
361+
name: String,
362+
price: Number
363+
});
364+
365+
docArray.discriminator(
366+
'Purchased',
367+
new Schema({
368+
products: {
369+
type: [productSchema],
370+
required: true
371+
}
372+
})
373+
);
374+
375+
const q = new Query();
376+
q.schema = batchSchema;
377+
378+
const filter = {};
379+
const update = {
380+
$set: {
381+
'events.$[event].products.$[product].price': '20'
382+
}
383+
};
384+
const purchasedId = new Types.ObjectId();
385+
const productId = new Types.ObjectId();
386+
const opts = {
387+
arrayFilters: [
388+
{ 'event._id': purchasedId, 'event.kind': 'Purchased' },
389+
{ 'product._id': productId.toString() }
390+
]
391+
};
392+
393+
q.updateOne(filter, update, opts);
394+
castArrayFilters(q);
395+
q._update = q._castUpdate(q._update, false);
396+
397+
assert.strictEqual(q.getOptions().arrayFilters[1]['product._id'].toHexString(), productId.toHexString());
398+
assert.strictEqual(q.getUpdate().$set['events.$[event].products.$[product].price'], 20);
399+
});
352400
});

0 commit comments

Comments
 (0)