@@ -65,6 +65,25 @@ const queryOptionMethods = new Set([
6565 'wtimeout'
6666] ) ;
6767
68+ // Map from operation name to the name of the function that executes the actual operation against MongoDB.
69+ // Called a thunk for legacy reasons, "thunk" means function that takes exactly 1 param, a callback.
70+ // Currently `_countDocuments()`, etc. are async functions that take no params.
71+ const opToThunk = new Map ( [
72+ [ 'countDocuments' , '_countDocuments' ] ,
73+ [ 'distinct' , '__distinct' ] ,
74+ [ 'estimatedDocumentCount' , '_estimatedDocumentCount' ] ,
75+ [ 'find' , '_find' ] ,
76+ [ 'findOne' , '_findOne' ] ,
77+ [ 'findOneAndReplace' , '_findOneAndReplace' ] ,
78+ [ 'findOneAndUpdate' , '_findOneAndUpdate' ] ,
79+ [ 'replaceOne' , '_replaceOne' ] ,
80+ [ 'updateMany' , '_updateMany' ] ,
81+ [ 'updateOne' , '_updateOne' ] ,
82+ [ 'deleteMany' , '_deleteMany' ] ,
83+ [ 'deleteOne' , '_deleteOne' ] ,
84+ [ 'findOneAndDelete' , '_findOneAndDelete' ]
85+ ] ) ;
86+
6887/**
6988 * Query constructor used for building queries. You do not need
7089 * to instantiate a `Query` directly. Instead use Model functions like
@@ -2337,18 +2356,17 @@ Query.prototype._find = async function _find() {
23372356 }
23382357
23392358 const mongooseOptions = this . _mongooseOptions ;
2340- const _this = this ;
2341- const userProvidedFields = _this . _userProvidedFields || { } ;
2359+ const userProvidedFields = this . _userProvidedFields || { } ;
23422360
23432361 applyGlobalMaxTimeMS ( this . options , this . model . db . options , this . model . base . options ) ;
23442362 applyGlobalDiskUse ( this . options , this . model . db . options , this . model . base . options ) ;
23452363
23462364 // Separate options to pass down to `completeMany()` in case we need to
23472365 // set a session on the document
2348- const completeManyOptions = Object . assign ( { } , {
2366+ const completeManyOptions = {
23492367 session : this && this . options && this . options . session || null ,
23502368 lean : mongooseOptions . lean || null
2351- } ) ;
2369+ } ;
23522370
23532371 const options = this . _optionsForExec ( ) ;
23542372
@@ -2366,7 +2384,7 @@ Query.prototype._find = async function _find() {
23662384 }
23672385
23682386 if ( ! mongooseOptions . populate ) {
2369- const versionKey = _this . schema . options . versionKey ;
2387+ const versionKey = this . schema . options . versionKey ;
23702388 if ( mongooseOptions . lean && mongooseOptions . lean . versionKey === false && versionKey ) {
23712389 docs . forEach ( ( doc ) => {
23722390 if ( versionKey in doc ) {
@@ -2375,17 +2393,17 @@ Query.prototype._find = async function _find() {
23752393 } ) ;
23762394 }
23772395 return mongooseOptions . lean ?
2378- _completeManyLean ( _this . model . schema , docs , null , completeManyOptions ) :
2379- _this . _completeMany ( docs , fields , userProvidedFields , completeManyOptions ) ;
2396+ _completeManyLean ( this . model . schema , docs , null , completeManyOptions ) :
2397+ this . _completeMany ( docs , fields , userProvidedFields , completeManyOptions ) ;
23802398 }
23812399
2382- const pop = helpers . preparePopulationOptionsMQ ( _this , mongooseOptions ) ;
2400+ const pop = helpers . preparePopulationOptionsMQ ( this , mongooseOptions ) ;
23832401
23842402 if ( mongooseOptions . lean ) {
2385- return _this . model . populate ( docs , pop ) ;
2403+ return this . model . populate ( docs , pop ) ;
23862404 }
23872405
2388- docs = await _this . _completeMany ( docs , fields , userProvidedFields , completeManyOptions ) ;
2406+ docs = await this . _completeMany ( docs , fields , userProvidedFields , completeManyOptions ) ;
23892407 await this . model . populate ( docs , pop ) ;
23902408
23912409 return docs ;
@@ -4397,22 +4415,14 @@ Query.prototype.exec = async function exec(op) {
43974415 if ( this . model == null ) {
43984416 throw new MongooseError ( 'Query must have an associated model before executing' ) ;
43994417 }
4400- this . _validateOp ( ) ;
4401-
4402- if ( ! this . op ) {
4403- return ;
4404- }
44054418
4406- if ( this . options && this . options . sort ) {
4407- const keys = Object . keys ( this . options . sort ) ;
4408- if ( keys . includes ( '' ) ) {
4409- throw new Error ( 'Invalid field "" passed to sort()' ) ;
4410- }
4419+ const thunk = opToThunk . get ( this . op ) ;
4420+ if ( ! thunk ) {
4421+ throw new MongooseError ( 'Query has invalid `op`: "' + this . op + '"' ) ;
44114422 }
44124423
4413- let thunk = '_' + this . op ;
4414- if ( this . op === 'distinct' ) {
4415- thunk = '__distinct' ;
4424+ if ( this . options && this . options . sort && typeof this . options . sort === 'object' && this . options . sort . hasOwnProperty ( '' ) ) {
4425+ throw new Error ( 'Invalid field "" passed to sort()' ) ;
44164426 }
44174427
44184428 if ( this . _executionStack != null ) {
0 commit comments