diff --git a/CHANGELOG.md b/CHANGELOG.md index a4fc4ddc61b..67e0ae75260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +7.8.2 / 2024-09-25 +================== + * fix(projection): avoid setting projection to unknown exclusive/inclusive if elemMatch on a Date, ObjectId, etc. #14894 #14893 + 8.6.3 / 2024-09-17 ================== * fix: make getters convert uuid to string when calling toObject() and toJSON() #14890 #14869 diff --git a/lib/helpers/projection/isExclusive.js b/lib/helpers/projection/isExclusive.js index b55cf468458..e6ca3cad5ec 100644 --- a/lib/helpers/projection/isExclusive.js +++ b/lib/helpers/projection/isExclusive.js @@ -1,6 +1,7 @@ 'use strict'; const isDefiningProjection = require('./isDefiningProjection'); +const isPOJO = require('../isPOJO'); /*! * ignore @@ -22,10 +23,12 @@ module.exports = function isExclusive(projection) { // Explicitly avoid `$meta` and `$slice` const key = keys[ki]; if (key !== '_id' && isDefiningProjection(projection[key])) { - exclude = (projection[key] != null && typeof projection[key] === 'object') ? - isExclusive(projection[key]) : + exclude = isPOJO(projection[key]) ? + (isExclusive(projection[key]) ?? exclude) : !projection[key]; - break; + if (exclude != null) { + break; + } } } } diff --git a/lib/helpers/projection/isInclusive.js b/lib/helpers/projection/isInclusive.js index eebb412c4a3..c53bac02873 100644 --- a/lib/helpers/projection/isInclusive.js +++ b/lib/helpers/projection/isInclusive.js @@ -1,6 +1,7 @@ 'use strict'; const isDefiningProjection = require('./isDefiningProjection'); +const isPOJO = require('../isPOJO'); /*! * ignore @@ -26,7 +27,7 @@ module.exports = function isInclusive(projection) { // If field is truthy (1, true, etc.) and not an object, then this // projection must be inclusive. If object, assume its $meta, $slice, etc. if (isDefiningProjection(projection[prop]) && !!projection[prop]) { - if (projection[prop] != null && typeof projection[prop] === 'object') { + if (isPOJO(projection[prop])) { return isInclusive(projection[prop]); } else { return !!projection[prop]; diff --git a/test/helpers/projection.isExclusive.test.js b/test/helpers/projection.isExclusive.test.js new file mode 100644 index 00000000000..2fc4a16b990 --- /dev/null +++ b/test/helpers/projection.isExclusive.test.js @@ -0,0 +1,12 @@ +'use strict'; + +const assert = require('assert'); + +require('../common'); // required for side-effect setup (so that the default driver is set-up) +const isExclusive = require('../../lib/helpers/projection/isExclusive'); + +describe('isExclusive', function() { + it('handles $elemMatch (gh-14893)', function() { + assert.strictEqual(isExclusive({ field: { $elemMatch: { test: new Date('2024-06-01') } }, otherProp: 1 }), false); + }); +}); diff --git a/test/helpers/projection.isInclusive.test.js b/test/helpers/projection.isInclusive.test.js new file mode 100644 index 00000000000..3bb93635a50 --- /dev/null +++ b/test/helpers/projection.isInclusive.test.js @@ -0,0 +1,12 @@ +'use strict'; + +const assert = require('assert'); + +require('../common'); // required for side-effect setup (so that the default driver is set-up) +const isInclusive = require('../../lib/helpers/projection/isInclusive'); + +describe('isInclusive', function() { + it('handles $elemMatch (gh-14893)', function() { + assert.strictEqual(isInclusive({ field: { $elemMatch: { test: new Date('2024-06-01') } }, otherProp: 1 }), true); + }); +});