Skip to content

Commit 19fa17b

Browse files
committed
Add requireFilter for all methods with comprehensive tests, fix beforeEach timeout (#14913)
1 parent ce4c3d5 commit 19fa17b

File tree

2 files changed

+362
-46
lines changed

2 files changed

+362
-46
lines changed

lib/query.js

+39-7
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ function Query(conditions, options, model, collection) {
165165
}
166166
}
167167

168+
// Helper function to check empty/invalid filter
169+
function checkRequireFilter(filter, options) {
170+
if (options && options.requireFilter &&
171+
(filter == null ||
172+
(typeof filter === 'object' && filter !== null && !Array.isArray(filter) && Object.keys(filter).length === 0))) {
173+
throw new Error('Empty or invalid filter not allowed with requireFilter enabled');
174+
}
175+
}
176+
168177
/*!
169178
* inherit mquery
170179
*/
@@ -3111,6 +3120,7 @@ function _handleSortValue(val, key) {
31113120
*
31123121
* @param {Object|Query} [filter] mongodb selector
31133122
* @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
3123+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
31143124
* @return {Query} this
31153125
* @see DeleteResult https://mongodb.github.io/node-mongodb-native/6.15/interfaces/DeleteResult.html
31163126
* @see deleteOne https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteOne
@@ -3148,6 +3158,9 @@ Query.prototype._deleteOne = async function _deleteOne() {
31483158
this._applyTranslateAliases();
31493159
this._castConditions();
31503160

3161+
// Check for empty/invalid filter with requireFilter option
3162+
checkRequireFilter(this._conditions, this.options);
3163+
31513164
if (this.error() != null) {
31523165
throw this.error();
31533166
}
@@ -3183,6 +3196,7 @@ Query.prototype._deleteOne = async function _deleteOne() {
31833196
*
31843197
* @param {Object|Query} [filter] mongodb selector
31853198
* @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
3199+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
31863200
* @return {Query} this
31873201
* @see DeleteResult https://mongodb.github.io/node-mongodb-native/6.15/interfaces/DeleteResult.html
31883202
* @see deleteMany https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteMany
@@ -3220,6 +3234,9 @@ Query.prototype._deleteMany = async function _deleteMany() {
32203234
this._applyTranslateAliases();
32213235
this._castConditions();
32223236

3237+
// Check for empty/invalid filter with requireFilter option
3238+
checkRequireFilter(this._conditions, this.options);
3239+
32233240
if (this.error() != null) {
32243241
throw this.error();
32253242
}
@@ -3311,7 +3328,7 @@ function prepareDiscriminatorCriteria(query) {
33113328
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
33123329
* - `runValidators`: if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
33133330
* - `setDefaultsOnInsert`: `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created.
3314-
* - `strictFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
3331+
* - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
33153332
*
33163333
* #### Example:
33173334
*
@@ -3338,7 +3355,7 @@ function prepareDiscriminatorCriteria(query) {
33383355
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
33393356
* @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
33403357
* @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
3341-
* @param {Boolean} [options.strictFilter=false] If true, throws an error if the filter is empty (`{}`)
3358+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
33423359
* @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html
33433360
* @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
33443361
* @see ModifyResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html
@@ -3355,11 +3372,6 @@ Query.prototype.findOneAndUpdate = function(filter, doc, options) {
33553372
throw new MongooseError('Query.prototype.findOneAndUpdate() no longer accepts a callback');
33563373
}
33573374

3358-
// Check for empty filter with strictFilter option
3359-
if (options && options.strictFilter && filter && Object.keys(filter).length === 0) {
3360-
return Promise.reject(new Error('Empty filter not allowed in findOneAndUpdate with strictFilter enabled'));
3361-
}
3362-
33633375
this.op = 'findOneAndUpdate';
33643376
this._validateOp();
33653377
this._validate();
@@ -3424,6 +3436,9 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
34243436
this._applyTranslateAliases();
34253437
this._castConditions();
34263438

3439+
// Check for empty/invalid filter with requireFilter option
3440+
checkRequireFilter(this._conditions, this.options);
3441+
34273442
_castArrayFilters(this);
34283443

34293444
if (this.error()) {
@@ -3507,6 +3522,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
35073522
*
35083523
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
35093524
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
3525+
* - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
35103526
*
35113527
* #### Example:
35123528
*
@@ -3519,6 +3535,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
35193535
* @param {Object} [filter]
35203536
* @param {Object} [options]
35213537
* @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
3538+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
35223539
* @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
35233540
* @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
35243541
* @return {Query} this
@@ -3558,6 +3575,9 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
35583575
this._applyTranslateAliases();
35593576
this._castConditions();
35603577

3578+
// Check for empty/invalid filter with requireFilter option
3579+
checkRequireFilter(this._conditions, this.options);
3580+
35613581
if (this.error() != null) {
35623582
throw this.error();
35633583
}
@@ -3597,6 +3617,7 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
35973617
* - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
35983618
* - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
35993619
* - `includeResultMetadata`: if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
3620+
* - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
36003621
*
36013622
* #### Example:
36023623
*
@@ -3619,6 +3640,7 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
36193640
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.
36203641
* @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
36213642
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
3643+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
36223644
* @return {Query} this
36233645
* @api public
36243646
*/
@@ -3674,6 +3696,10 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options) {
36743696
Query.prototype._findOneAndReplace = async function _findOneAndReplace() {
36753697
this._applyTranslateAliases();
36763698
this._castConditions();
3699+
3700+
// Check for empty/invalid filter with requireFilter option
3701+
checkRequireFilter(this._conditions, this.options);
3702+
36773703
if (this.error() != null) {
36783704
throw this.error();
36793705
}
@@ -3976,6 +4002,9 @@ async function _updateThunk(op) {
39764002

39774003
this._castConditions();
39784004

4005+
// Check for empty/invalid filter with requireFilter option
4006+
checkRequireFilter(this._conditions, this.options);
4007+
39794008
_castArrayFilters(this);
39804009

39814010
if (this.error() != null) {
@@ -4126,6 +4155,7 @@ Query.prototype._replaceOne = async function _replaceOne() {
41264155
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
41274156
* @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
41284157
* @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
4158+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
41294159
* @return {Query} this
41304160
* @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
41314161
* @see Query docs https://mongoosejs.com/docs/queries.html
@@ -4200,6 +4230,7 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) {
42004230
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
42014231
* @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
42024232
* @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
4233+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
42034234
* @return {Query} this
42044235
* @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
42054236
* @see Query docs https://mongoosejs.com/docs/queries.html
@@ -4266,6 +4297,7 @@ Query.prototype.updateOne = function(conditions, doc, options, callback) {
42664297
* @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
42674298
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
42684299
* @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
4300+
* @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
42694301
* @return {Query} this
42704302
* @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
42714303
* @see Query docs https://mongoosejs.com/docs/queries.html

0 commit comments

Comments
 (0)