@nullix/zod-mongoose-studio 1.0.13 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{Db4IAypj.js → 0J_bLG6U.js} +1 -1
  3. package/.output/public/_nuxt/{Y39o1QEi.js → 5KLjKHc1.js} +1 -1
  4. package/.output/public/_nuxt/{B4zoPYF-.js → B2UrsJHg.js} +1 -1
  5. package/.output/public/_nuxt/{BSqjh6Vk.js → BC5hGokz.js} +1 -1
  6. package/.output/public/_nuxt/{BS4yU4O0.js → BJwN20Xm.js} +1 -1
  7. package/.output/public/_nuxt/{D8_n84hl.js → BMgDPqps.js} +1 -1
  8. package/.output/public/_nuxt/{CqNsQbt9.js → B_sGF2qN.js} +1 -1
  9. package/.output/public/_nuxt/{NAqlztEN.js → BeaJ7I-K.js} +10 -10
  10. package/.output/public/_nuxt/{WZ0WK9cR.js → BkE1sm_1.js} +1 -1
  11. package/.output/public/_nuxt/{kBtP64Rx.js → CADbZwE5.js} +1 -1
  12. package/.output/public/_nuxt/{B8qGaxNm.js → CB0WRoVi.js} +1 -1
  13. package/.output/public/_nuxt/{BV6uALfO.js → CCh0PVV0.js} +1 -1
  14. package/.output/public/_nuxt/{CP9Z7-_9.js → Cr0GW7rA.js} +1 -1
  15. package/.output/public/_nuxt/{DeCqoVOS.js → DJe7Wxdv.js} +1 -1
  16. package/.output/public/_nuxt/{dBZQW7JD.js → DRBrUaE5.js} +1 -1
  17. package/.output/public/_nuxt/{Db19R32y.js → DV2n9B6m.js} +1 -1
  18. package/.output/public/_nuxt/{CvnpbYw0.js → DVD-jGiv.js} +1 -1
  19. package/.output/public/_nuxt/{BWajNATZ.js → Dk9qGA5X.js} +3 -3
  20. package/.output/public/_nuxt/builds/latest.json +1 -1
  21. package/.output/public/_nuxt/builds/meta/dc761332-547d-4953-b51d-b2d453009b48.json +1 -0
  22. package/.output/public/_nuxt/error-404.64pZEiDn.css +1 -0
  23. package/.output/public/_nuxt/error-500.DHOpI0Ir.css +1 -0
  24. package/.output/public/_nuxt/{BoHhCIKW.js → koEZfutw.js} +3 -3
  25. package/.output/public/_nuxt/{B_782F4p.js → r6LdR7DN.js} +1 -1
  26. package/.output/public/_nuxt/{BEI71c6K.js → rEfOp38p.js} +1 -1
  27. package/.output/server/chunks/_/error-500.mjs.map +1 -1
  28. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  29. package/.output/server/chunks/build/{error-404-8tbBXju4.mjs → error-404-BN-7rNE4.mjs} +4 -4
  30. package/.output/server/chunks/build/error-404-BN-7rNE4.mjs.map +1 -0
  31. package/.output/server/chunks/build/error-404-styles.Byx46oLR.mjs +8 -0
  32. package/.output/server/chunks/build/error-404-styles.Byx46oLR.mjs.map +1 -0
  33. package/.output/server/chunks/build/{error-500-DxFsom1i.mjs → error-500-DhIQ-2yt.mjs} +4 -4
  34. package/.output/server/chunks/build/error-500-DhIQ-2yt.mjs.map +1 -0
  35. package/.output/server/chunks/build/error-500-styles.BL3UT0Wa.mjs +8 -0
  36. package/.output/server/chunks/build/error-500-styles.BL3UT0Wa.mjs.map +1 -0
  37. package/.output/server/chunks/build/{index-BB-Clavm.mjs → index-Dnrh1ePF.mjs} +1 -1
  38. package/.output/server/chunks/build/index-Dnrh1ePF.mjs.map +1 -0
  39. package/.output/server/chunks/build/server.mjs +9 -9
  40. package/.output/server/chunks/build/styles.mjs +4 -4
  41. package/.output/server/chunks/nitro/nitro.mjs +333 -333
  42. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  43. package/.output/server/chunks/routes/renderer.mjs.map +1 -1
  44. package/.output/server/node_modules/bson/lib/bson.cjs +157 -48
  45. package/.output/server/node_modules/bson/package.json +9 -11
  46. package/.output/server/node_modules/kareem/index.js +228 -343
  47. package/.output/server/node_modules/kareem/package.json +3 -3
  48. package/.output/server/node_modules/mongodb/lib/bson.js +27 -5
  49. package/.output/server/node_modules/mongodb/lib/bulk/common.js +7 -9
  50. package/.output/server/node_modules/mongodb/lib/change_stream.js +91 -39
  51. package/.output/server/node_modules/mongodb/lib/client-side-encryption/auto_encrypter.js +21 -14
  52. package/.output/server/node_modules/mongodb/lib/client-side-encryption/client_encryption.js +2 -5
  53. package/.output/server/node_modules/mongodb/lib/client-side-encryption/errors.js +3 -1
  54. package/.output/server/node_modules/mongodb/lib/client-side-encryption/mongocryptd_manager.js +1 -1
  55. package/.output/server/node_modules/mongodb/lib/client-side-encryption/state_machine.js +1 -0
  56. package/.output/server/node_modules/mongodb/lib/cmap/auth/aws4.js +161 -0
  57. package/.output/server/node_modules/mongodb/lib/cmap/auth/aws_temporary_credentials.js +11 -58
  58. package/.output/server/node_modules/mongodb/lib/cmap/auth/gssapi.js +4 -6
  59. package/.output/server/node_modules/mongodb/lib/cmap/auth/mongo_credentials.js +2 -16
  60. package/.output/server/node_modules/mongodb/lib/cmap/auth/mongodb_aws.js +14 -23
  61. package/.output/server/node_modules/mongodb/lib/cmap/auth/mongodb_oidc/azure_machine_workflow.js +3 -3
  62. package/.output/server/node_modules/mongodb/lib/cmap/auth/mongodb_oidc/gcp_machine_workflow.js +3 -3
  63. package/.output/server/node_modules/mongodb/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js +4 -3
  64. package/.output/server/node_modules/mongodb/lib/cmap/auth/mongodb_oidc/token_machine_workflow.js +4 -3
  65. package/.output/server/node_modules/mongodb/lib/cmap/auth/mongodb_oidc.js +4 -4
  66. package/.output/server/node_modules/mongodb/lib/cmap/auth/plain.js +1 -1
  67. package/.output/server/node_modules/mongodb/lib/cmap/auth/providers.js +0 -1
  68. package/.output/server/node_modules/mongodb/lib/cmap/auth/scram.js +53 -40
  69. package/.output/server/node_modules/mongodb/lib/cmap/commands.js +46 -39
  70. package/.output/server/node_modules/mongodb/lib/cmap/connect.js +35 -3
  71. package/.output/server/node_modules/mongodb/lib/cmap/connection.js +36 -32
  72. package/.output/server/node_modules/mongodb/lib/cmap/connection_pool.js +67 -67
  73. package/.output/server/node_modules/mongodb/lib/cmap/connection_pool_events.js +3 -3
  74. package/.output/server/node_modules/mongodb/lib/cmap/errors.js +1 -1
  75. package/.output/server/node_modules/mongodb/lib/cmap/handshake/client_metadata.js +15 -13
  76. package/.output/server/node_modules/mongodb/lib/cmap/metrics.js +3 -3
  77. package/.output/server/node_modules/mongodb/lib/cmap/wire_protocol/compression.js +26 -10
  78. package/.output/server/node_modules/mongodb/lib/cmap/wire_protocol/constants.js +3 -1
  79. package/.output/server/node_modules/mongodb/lib/cmap/wire_protocol/on_data.js +0 -1
  80. package/.output/server/node_modules/mongodb/lib/cmap/wire_protocol/on_demand/document.js +9 -9
  81. package/.output/server/node_modules/mongodb/lib/cmap/wire_protocol/responses.js +2 -2
  82. package/.output/server/node_modules/mongodb/lib/collection.js +1 -1
  83. package/.output/server/node_modules/mongodb/lib/connection_string.js +30 -15
  84. package/.output/server/node_modules/mongodb/lib/cursor/abstract_cursor.js +17 -34
  85. package/.output/server/node_modules/mongodb/lib/cursor/change_stream_cursor.js +2 -2
  86. package/.output/server/node_modules/mongodb/lib/cursor/find_cursor.js +37 -26
  87. package/.output/server/node_modules/mongodb/lib/cursor/run_command_cursor.js +1 -1
  88. package/.output/server/node_modules/mongodb/lib/db.js +6 -7
  89. package/.output/server/node_modules/mongodb/lib/deps.js +0 -13
  90. package/.output/server/node_modules/mongodb/lib/error.js +7 -10
  91. package/.output/server/node_modules/mongodb/lib/gridfs/download.js +7 -6
  92. package/.output/server/node_modules/mongodb/lib/gridfs/index.js +9 -9
  93. package/.output/server/node_modules/mongodb/lib/gridfs/upload.js +17 -23
  94. package/.output/server/node_modules/mongodb/lib/index.js +2 -4
  95. package/.output/server/node_modules/mongodb/lib/mongo_client.js +58 -67
  96. package/.output/server/node_modules/mongodb/lib/mongo_client_auth_providers.js +0 -6
  97. package/.output/server/node_modules/mongodb/lib/mongo_logger.js +11 -5
  98. package/.output/server/node_modules/mongodb/lib/mongo_types.js +1 -2
  99. package/.output/server/node_modules/mongodb/lib/operations/aggregate.js +0 -3
  100. package/.output/server/node_modules/mongodb/lib/operations/create_collection.js +0 -1
  101. package/.output/server/node_modules/mongodb/lib/operations/drop.js +8 -9
  102. package/.output/server/node_modules/mongodb/lib/operations/end_sessions.js +34 -0
  103. package/.output/server/node_modules/mongodb/lib/operations/execute_operation.js +121 -45
  104. package/.output/server/node_modules/mongodb/lib/operations/operation.js +1 -0
  105. package/.output/server/node_modules/mongodb/lib/read_preference.js +10 -14
  106. package/.output/server/node_modules/mongodb/lib/runtime_adapters.js +32 -0
  107. package/.output/server/node_modules/mongodb/lib/sdam/monitor.js +8 -8
  108. package/.output/server/node_modules/mongodb/lib/sdam/server.js +55 -43
  109. package/.output/server/node_modules/mongodb/lib/sdam/server_description.js +1 -1
  110. package/.output/server/node_modules/mongodb/lib/sdam/server_selection.js +135 -72
  111. package/.output/server/node_modules/mongodb/lib/sdam/srv_polling.js +3 -3
  112. package/.output/server/node_modules/mongodb/lib/sdam/topology.js +37 -77
  113. package/.output/server/node_modules/mongodb/lib/sdam/topology_description.js +1 -1
  114. package/.output/server/node_modules/mongodb/lib/sessions.js +150 -40
  115. package/.output/server/node_modules/mongodb/lib/transactions.js +2 -13
  116. package/.output/server/node_modules/mongodb/lib/utils.js +42 -90
  117. package/.output/server/node_modules/mongodb/package.json +49 -45
  118. package/.output/server/node_modules/mongodb-connection-string-url/lib/index.js +45 -19
  119. package/.output/server/node_modules/mongodb-connection-string-url/lib/redact.js +30 -19
  120. package/.output/server/node_modules/mongodb-connection-string-url/package.json +25 -16
  121. package/.output/server/node_modules/mongoose/lib/aggregate.js +108 -127
  122. package/.output/server/node_modules/mongoose/lib/cast/bigint.js +3 -3
  123. package/.output/server/node_modules/mongoose/lib/cast/boolean.js +3 -3
  124. package/.output/server/node_modules/mongoose/lib/cast/double.js +4 -4
  125. package/.output/server/node_modules/mongoose/lib/cast/int32.js +2 -2
  126. package/.output/server/node_modules/mongoose/lib/cast/number.js +6 -6
  127. package/.output/server/node_modules/mongoose/lib/cast/string.js +3 -3
  128. package/.output/server/node_modules/mongoose/lib/cast/uuid.js +5 -48
  129. package/.output/server/node_modules/mongoose/lib/cast.js +16 -24
  130. package/.output/server/node_modules/mongoose/lib/collection.js +6 -6
  131. package/.output/server/node_modules/mongoose/lib/connection.js +108 -105
  132. package/.output/server/node_modules/mongoose/lib/cursor/aggregationCursor.js +23 -33
  133. package/.output/server/node_modules/mongoose/lib/cursor/changeStream.js +60 -40
  134. package/.output/server/node_modules/mongoose/lib/cursor/queryCursor.js +21 -25
  135. package/.output/server/node_modules/mongoose/lib/document.js +712 -531
  136. package/.output/server/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js +85 -130
  137. package/.output/server/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js +32 -41
  138. package/.output/server/node_modules/mongoose/lib/error/cast.js +21 -12
  139. package/.output/server/node_modules/mongoose/lib/error/createCollectionsError.js +2 -2
  140. package/.output/server/node_modules/mongoose/lib/error/divergentArray.js +1 -1
  141. package/.output/server/node_modules/mongoose/lib/error/eachAsyncMultiError.js +1 -1
  142. package/.output/server/node_modules/mongoose/lib/error/index.js +3 -3
  143. package/.output/server/node_modules/mongoose/lib/error/invalidSchemaOption.js +1 -1
  144. package/.output/server/node_modules/mongoose/lib/error/messages.js +1 -0
  145. package/.output/server/node_modules/mongoose/lib/error/missingSchema.js +1 -1
  146. package/.output/server/node_modules/mongoose/lib/error/objectParameter.js +4 -5
  147. package/.output/server/node_modules/mongoose/lib/error/overwriteModel.js +1 -1
  148. package/.output/server/node_modules/mongoose/lib/error/setOptionError.js +3 -3
  149. package/.output/server/node_modules/mongoose/lib/error/strict.js +3 -3
  150. package/.output/server/node_modules/mongoose/lib/error/strictPopulate.js +2 -2
  151. package/.output/server/node_modules/mongoose/lib/error/syncIndexes.js +2 -2
  152. package/.output/server/node_modules/mongoose/lib/error/validation.js +2 -10
  153. package/.output/server/node_modules/mongoose/lib/error/validator.js +1 -1
  154. package/.output/server/node_modules/mongoose/lib/error/version.js +2 -2
  155. package/.output/server/node_modules/mongoose/lib/helpers/aggregate/prepareDiscriminatorPipeline.js +4 -4
  156. package/.output/server/node_modules/mongoose/lib/helpers/buildMiddlewareFilter.js +24 -0
  157. package/.output/server/node_modules/mongoose/lib/helpers/clone.js +35 -18
  158. package/.output/server/node_modules/mongoose/lib/helpers/common.js +5 -5
  159. package/.output/server/node_modules/mongoose/lib/helpers/cursor/eachAsync.js +4 -4
  160. package/.output/server/node_modules/mongoose/lib/helpers/discriminator/getConstructor.js +1 -1
  161. package/.output/server/node_modules/mongoose/lib/helpers/discriminator/getDiscriminatorByValue.js +1 -1
  162. package/.output/server/node_modules/mongoose/lib/helpers/discriminator/getSchemaDiscriminatorByValue.js +1 -1
  163. package/.output/server/node_modules/mongoose/lib/helpers/discriminator/mergeDiscriminatorSchema.js +10 -5
  164. package/.output/server/node_modules/mongoose/lib/helpers/document/applyDefaults.js +1 -1
  165. package/.output/server/node_modules/mongoose/lib/helpers/document/applyTimestamps.js +11 -10
  166. package/.output/server/node_modules/mongoose/lib/helpers/document/applyVirtuals.js +10 -9
  167. package/.output/server/node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js +1 -1
  168. package/.output/server/node_modules/mongoose/lib/helpers/document/compile.js +17 -14
  169. package/.output/server/node_modules/mongoose/lib/helpers/document/getDeepestSubdocumentForPath.js +3 -3
  170. package/.output/server/node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js +2 -2
  171. package/.output/server/node_modules/mongoose/lib/helpers/document/isInPathsToSave.js +24 -0
  172. package/.output/server/node_modules/mongoose/lib/helpers/get.js +1 -1
  173. package/.output/server/node_modules/mongoose/lib/helpers/indexes/decorateDiscriminatorIndexOptions.js +1 -1
  174. package/.output/server/node_modules/mongoose/lib/helpers/indexes/getRelatedIndexes.js +3 -3
  175. package/.output/server/node_modules/mongoose/lib/helpers/indexes/isIndexEqual.js +3 -4
  176. package/.output/server/node_modules/mongoose/lib/helpers/indexes/isIndexSpecEqual.js +3 -3
  177. package/.output/server/node_modules/mongoose/lib/helpers/isBsonType.js +1 -1
  178. package/.output/server/node_modules/mongoose/lib/helpers/isMongooseObject.js +1 -1
  179. package/.output/server/node_modules/mongoose/lib/helpers/isObject.js +2 -2
  180. package/.output/server/node_modules/mongoose/lib/helpers/isSimpleValidator.js +2 -2
  181. package/.output/server/node_modules/mongoose/lib/helpers/minimize.js +2 -2
  182. package/.output/server/node_modules/mongoose/lib/helpers/model/applyDefaultsToPOJO.js +2 -2
  183. package/.output/server/node_modules/mongoose/lib/helpers/model/applyHooks.js +55 -54
  184. package/.output/server/node_modules/mongoose/lib/helpers/model/applyMethods.js +2 -2
  185. package/.output/server/node_modules/mongoose/lib/helpers/model/applyStaticHooks.js +1 -48
  186. package/.output/server/node_modules/mongoose/lib/helpers/model/castBulkWrite.js +8 -12
  187. package/.output/server/node_modules/mongoose/lib/helpers/model/discriminator.js +1 -1
  188. package/.output/server/node_modules/mongoose/lib/helpers/parallelLimit.js +18 -36
  189. package/.output/server/node_modules/mongoose/lib/helpers/pluralize.js +4 -4
  190. package/.output/server/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js +4 -11
  191. package/.output/server/node_modules/mongoose/lib/helpers/populate/assignVals.js +10 -10
  192. package/.output/server/node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js +3 -3
  193. package/.output/server/node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js +197 -69
  194. package/.output/server/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js +15 -15
  195. package/.output/server/node_modules/mongoose/lib/helpers/populate/markArraySubdocsPopulated.js +1 -1
  196. package/.output/server/node_modules/mongoose/lib/helpers/populate/modelNamesFromRefPath.js +10 -2
  197. package/.output/server/node_modules/mongoose/lib/helpers/populate/setPopulatedVirtualValue.js +5 -5
  198. package/.output/server/node_modules/mongoose/lib/helpers/printJestWarning.js +1 -1
  199. package/.output/server/node_modules/mongoose/lib/helpers/processConnectionOptions.js +1 -1
  200. package/.output/server/node_modules/mongoose/lib/helpers/projection/hasIncludedChildren.js +1 -1
  201. package/.output/server/node_modules/mongoose/lib/helpers/projection/isPathExcluded.js +3 -3
  202. package/.output/server/node_modules/mongoose/lib/helpers/projection/isSubpath.js +1 -1
  203. package/.output/server/node_modules/mongoose/lib/helpers/projection/parseProjection.js +9 -4
  204. package/.output/server/node_modules/mongoose/lib/helpers/query/cast$expr.js +8 -10
  205. package/.output/server/node_modules/mongoose/lib/helpers/query/castFilterPath.js +1 -1
  206. package/.output/server/node_modules/mongoose/lib/helpers/query/castUpdate.js +44 -38
  207. package/.output/server/node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js +8 -8
  208. package/.output/server/node_modules/mongoose/lib/helpers/query/handleImmutable.js +8 -8
  209. package/.output/server/node_modules/mongoose/lib/helpers/schema/applyPlugins.js +2 -2
  210. package/.output/server/node_modules/mongoose/lib/helpers/schema/applyReadConcern.js +1 -1
  211. package/.output/server/node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js +4 -2
  212. package/.output/server/node_modules/mongoose/lib/helpers/schema/getIndexes.js +5 -11
  213. package/.output/server/node_modules/mongoose/lib/helpers/schema/getSubdocumentStrictValue.js +2 -2
  214. package/.output/server/node_modules/mongoose/lib/helpers/schema/handleIdOption.js +1 -1
  215. package/.output/server/node_modules/mongoose/lib/helpers/schema/idGetter.js +1 -1
  216. package/.output/server/node_modules/mongoose/lib/helpers/schematype/handleImmutable.js +1 -1
  217. package/.output/server/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js +26 -13
  218. package/.output/server/node_modules/mongoose/lib/helpers/timestamps/setDocumentTimestamps.js +2 -2
  219. package/.output/server/node_modules/mongoose/lib/helpers/timestamps/setupTimestamps.js +60 -38
  220. package/.output/server/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js +13 -10
  221. package/.output/server/node_modules/mongoose/lib/helpers/update/castArrayFilters.js +4 -4
  222. package/.output/server/node_modules/mongoose/lib/helpers/update/decorateUpdateWithVersionKey.js +1 -1
  223. package/.output/server/node_modules/mongoose/lib/helpers/update/modifiedPaths.js +2 -2
  224. package/.output/server/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js +7 -2
  225. package/.output/server/node_modules/mongoose/lib/helpers/updateValidators.js +58 -119
  226. package/.output/server/node_modules/mongoose/lib/model.js +940 -946
  227. package/.output/server/node_modules/mongoose/lib/mongoose.js +131 -79
  228. package/.output/server/node_modules/mongoose/lib/options/schemaArrayOptions.js +2 -2
  229. package/.output/server/node_modules/mongoose/lib/options/schemaBufferOptions.js +1 -1
  230. package/.output/server/node_modules/mongoose/lib/options/schemaDocumentArrayOptions.js +23 -0
  231. package/.output/server/node_modules/mongoose/lib/options/schemaNumberOptions.js +3 -3
  232. package/.output/server/node_modules/mongoose/lib/options/schemaObjectIdOptions.js +2 -2
  233. package/.output/server/node_modules/mongoose/lib/options/schemaStringOptions.js +6 -6
  234. package/.output/server/node_modules/mongoose/lib/options/schemaSubdocumentOptions.js +23 -0
  235. package/.output/server/node_modules/mongoose/lib/options/schemaTypeOptions.js +27 -13
  236. package/.output/server/node_modules/mongoose/lib/options/virtualOptions.js +12 -12
  237. package/.output/server/node_modules/mongoose/lib/plugins/index.js +0 -1
  238. package/.output/server/node_modules/mongoose/lib/plugins/saveSubdocs.js +84 -92
  239. package/.output/server/node_modules/mongoose/lib/plugins/sharding.js +33 -15
  240. package/.output/server/node_modules/mongoose/lib/plugins/trackTransaction.js +29 -25
  241. package/.output/server/node_modules/mongoose/lib/query.js +700 -548
  242. package/.output/server/node_modules/mongoose/lib/queryHelpers.js +34 -32
  243. package/.output/server/node_modules/mongoose/lib/schema/array.js +69 -113
  244. package/.output/server/node_modules/mongoose/lib/schema/bigint.js +18 -20
  245. package/.output/server/node_modules/mongoose/lib/schema/boolean.js +20 -22
  246. package/.output/server/node_modules/mongoose/lib/schema/buffer.js +23 -25
  247. package/.output/server/node_modules/mongoose/lib/schema/date.js +19 -21
  248. package/.output/server/node_modules/mongoose/lib/schema/decimal128.js +16 -18
  249. package/.output/server/node_modules/mongoose/lib/schema/documentArray.js +94 -131
  250. package/.output/server/node_modules/mongoose/lib/schema/documentArrayElement.js +50 -16
  251. package/.output/server/node_modules/mongoose/lib/schema/double.js +16 -18
  252. package/.output/server/node_modules/mongoose/lib/schema/index.js +1 -0
  253. package/.output/server/node_modules/mongoose/lib/schema/int32.js +17 -19
  254. package/.output/server/node_modules/mongoose/lib/schema/map.js +17 -24
  255. package/.output/server/node_modules/mongoose/lib/schema/mixed.js +14 -14
  256. package/.output/server/node_modules/mongoose/lib/schema/number.js +38 -28
  257. package/.output/server/node_modules/mongoose/lib/schema/objectId.js +18 -20
  258. package/.output/server/node_modules/mongoose/lib/schema/operators/exists.js +1 -1
  259. package/.output/server/node_modules/mongoose/lib/schema/operators/geospatial.js +1 -1
  260. package/.output/server/node_modules/mongoose/lib/schema/operators/text.js +4 -4
  261. package/.output/server/node_modules/mongoose/lib/schema/string.js +42 -27
  262. package/.output/server/node_modules/mongoose/lib/schema/subdocument.js +47 -58
  263. package/.output/server/node_modules/mongoose/lib/schema/union.js +54 -4
  264. package/.output/server/node_modules/mongoose/lib/schema/uuid.js +17 -40
  265. package/.output/server/node_modules/mongoose/lib/schema.js +235 -176
  266. package/.output/server/node_modules/mongoose/lib/schemaType.js +227 -158
  267. package/.output/server/node_modules/mongoose/lib/stateMachine.js +5 -8
  268. package/.output/server/node_modules/mongoose/lib/types/array/index.js +5 -5
  269. package/.output/server/node_modules/mongoose/lib/types/array/methods/index.js +27 -28
  270. package/.output/server/node_modules/mongoose/lib/types/arraySubdocument.js +11 -11
  271. package/.output/server/node_modules/mongoose/lib/types/buffer.js +15 -15
  272. package/.output/server/node_modules/mongoose/lib/types/decimal128.js +1 -1
  273. package/.output/server/node_modules/mongoose/lib/types/documentArray/index.js +4 -4
  274. package/.output/server/node_modules/mongoose/lib/types/documentArray/methods/index.js +15 -13
  275. package/.output/server/node_modules/mongoose/lib/types/double.js +1 -1
  276. package/.output/server/node_modules/mongoose/lib/types/map.js +20 -71
  277. package/.output/server/node_modules/mongoose/lib/types/objectid.js +1 -1
  278. package/.output/server/node_modules/mongoose/lib/types/subdocument.js +32 -81
  279. package/.output/server/node_modules/mongoose/lib/types/uuid.js +1 -1
  280. package/.output/server/node_modules/mongoose/lib/utils.js +91 -70
  281. package/.output/server/node_modules/mongoose/lib/validOptions.js +4 -3
  282. package/.output/server/node_modules/mongoose/lib/virtualType.js +18 -18
  283. package/.output/server/node_modules/mongoose/package.json +46 -63
  284. package/.output/server/node_modules/mquery/lib/collection/collection.js +3 -1
  285. package/.output/server/node_modules/mquery/lib/collection/node.js +17 -3
  286. package/.output/server/node_modules/mquery/lib/mquery.js +136 -93
  287. package/.output/server/node_modules/mquery/lib/permissions.js +21 -6
  288. package/.output/server/node_modules/mquery/package.json +4 -7
  289. package/.output/server/package.json +7 -9
  290. package/package.json +3 -3
  291. package/.output/public/_nuxt/builds/meta/0d6006db-fbb0-4297-85cf-bf92ceafcb79.json +0 -1
  292. package/.output/public/_nuxt/error-404.CTWLHxjC.css +0 -1
  293. package/.output/public/_nuxt/error-500.UBfk8-QP.css +0 -1
  294. package/.output/server/chunks/build/error-404-8tbBXju4.mjs.map +0 -1
  295. package/.output/server/chunks/build/error-404-styles.BeWfK6Py.mjs +0 -8
  296. package/.output/server/chunks/build/error-404-styles.BeWfK6Py.mjs.map +0 -1
  297. package/.output/server/chunks/build/error-500-DxFsom1i.mjs.map +0 -1
  298. package/.output/server/chunks/build/error-500-styles.CPUUvoY-.mjs +0 -8
  299. package/.output/server/chunks/build/error-500-styles.CPUUvoY-.mjs.map +0 -1
  300. package/.output/server/chunks/build/index-BB-Clavm.mjs.map +0 -1
  301. package/.output/server/node_modules/debug/package.json +0 -64
  302. package/.output/server/node_modules/debug/src/browser.js +0 -272
  303. package/.output/server/node_modules/debug/src/common.js +0 -292
  304. package/.output/server/node_modules/debug/src/index.js +0 -10
  305. package/.output/server/node_modules/debug/src/node.js +0 -263
  306. package/.output/server/node_modules/mongodb/lib/client-side-encryption/crypto_callbacks.js +0 -81
  307. package/.output/server/node_modules/mongodb/lib/resource_management.js +0 -58
  308. package/.output/server/node_modules/mongoose/lib/browserDocument.js +0 -101
  309. package/.output/server/node_modules/mongoose/lib/documentProvider.js +0 -30
  310. package/.output/server/node_modules/mongoose/lib/helpers/createJSONSchemaTypeDefinition.js +0 -24
  311. package/.output/server/node_modules/mongoose/lib/helpers/promiseOrCallback.js +0 -54
  312. package/.output/server/node_modules/mongoose/lib/plugins/validateBeforeSave.js +0 -51
  313. package/.output/server/node_modules/supports-color/index.js +0 -202
  314. package/.output/server/node_modules/supports-color/package.json +0 -64
@@ -21,6 +21,7 @@ const ValidationError = require('./error/validation');
21
21
  const VersionError = require('./error/version');
22
22
  const ParallelSaveError = require('./error/parallelSave');
23
23
  const applyDefaultsHelper = require('./helpers/document/applyDefaults');
24
+ const isInPathsToSave = require('./helpers/document/isInPathsToSave');
24
25
  const applyDefaultsToPOJO = require('./helpers/model/applyDefaultsToPOJO');
25
26
  const applyEmbeddedDiscriminators = require('./helpers/discriminator/applyEmbeddedDiscriminators');
26
27
  const applyHooks = require('./helpers/model/applyHooks');
@@ -50,6 +51,7 @@ const immediate = require('./helpers/immediate');
50
51
  const internalToObjectOptions = require('./options').internalToObjectOptions;
51
52
  const isDefaultIdIndex = require('./helpers/indexes/isDefaultIdIndex');
52
53
  const isIndexEqual = require('./helpers/indexes/isIndexEqual');
54
+ const isIndexSpecEqual = require('./helpers/indexes/isIndexSpecEqual');
53
55
  const isTimeseriesIndex = require('./helpers/indexes/isTimeseriesIndex');
54
56
  const {
55
57
  getRelatedDBIndexes,
@@ -59,10 +61,12 @@ const decorateDiscriminatorIndexOptions = require('./helpers/indexes/decorateDis
59
61
  const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive');
60
62
  const leanPopulateMap = require('./helpers/populate/leanPopulateMap');
61
63
  const parallelLimit = require('./helpers/parallelLimit');
64
+ const parseProjection = require('./helpers/projection/parseProjection');
62
65
  const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
63
66
  const pushNestedArrayPaths = require('./helpers/model/pushNestedArrayPaths');
64
67
  const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField');
65
68
  const setDottedPath = require('./helpers/path/setDottedPath');
69
+ const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter');
66
70
  const util = require('util');
67
71
  const utils = require('./utils');
68
72
  const minimize = require('./helpers/minimize');
@@ -71,7 +75,11 @@ const ObjectExpectedError = require('./error/objectExpected');
71
75
  const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult');
72
76
  const modelCollectionSymbol = Symbol('mongoose#Model#collection');
73
77
  const modelDbSymbol = Symbol('mongoose#Model#db');
74
- const modelSymbol = require('./helpers/symbols').modelSymbol;
78
+ const {
79
+ arrayAtomicsBackupSymbol,
80
+ arrayAtomicsSymbol,
81
+ modelSymbol
82
+ } = require('./helpers/symbols');
75
83
  const subclassedSymbol = Symbol('mongoose#Model#subclassed');
76
84
 
77
85
  const { VERSION_INC, VERSION_WHERE, VERSION_ALL } = Document;
@@ -102,9 +110,11 @@ const saveToObjectOptions = Object.assign({}, internalToObjectOptions, {
102
110
  * // You also use a model to create queries:
103
111
  * const userFromDb = await UserModel.findOne({ name: 'Foo' });
104
112
  *
105
- * @param {Object} doc values for initial set
106
- * @param {Object} [fields] optional object containing the fields that were selected in the query which returned this document. You do **not** need to set this parameter to ensure Mongoose handles your [query projection](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()).
107
- * @param {Boolean} [skipId=false] optional boolean. If true, mongoose doesn't add an `_id` field to the document.
113
+ * @param {object} doc values for initial set
114
+ * @param {object} [fields] optional object containing the fields that were selected in the query which returned this document. You do **not** need to set this parameter to ensure Mongoose handles your [query projection](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()).
115
+ * @param {object} [options] optional object containing the options for the document.
116
+ * @param {boolean} [options.defaults=true] if `false`, skip applying default values to this document.
117
+ * @param {boolean} [options.skipId=false] By default, Mongoose document if one is not provided and the document's schema does not override Mongoose's default `_id`. Set `skipId` to `true` to skip this generation step.
108
118
  * @inherits Document https://mongoosejs.com/docs/api/document.html
109
119
  * @event `error`: If listening to this event, 'error' is emitted when a document was saved and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model.
110
120
  * @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event.
@@ -113,7 +123,7 @@ const saveToObjectOptions = Object.assign({}, internalToObjectOptions, {
113
123
  * @api public
114
124
  */
115
125
 
116
- function Model(doc, fields, skipId) {
126
+ function Model(doc, fields, options) {
117
127
  if (fields instanceof Schema) {
118
128
  throw new TypeError('2nd argument to `Model` constructor must be a POJO or string, ' +
119
129
  '**not** a schema. Make sure you\'re calling `mongoose.model()`, not ' +
@@ -124,7 +134,7 @@ function Model(doc, fields, skipId) {
124
134
  '**not** a string. Make sure you\'re calling `mongoose.model()`, not ' +
125
135
  '`mongoose.Model()`.');
126
136
  }
127
- Document.call(this, doc, fields, skipId);
137
+ Document.call(this, doc, fields, options);
128
138
  }
129
139
 
130
140
  /**
@@ -173,14 +183,17 @@ Model.prototype.db;
173
183
  * If you use `useConnection()` to switch a model's connection, the model will still have the old connection's plugins.
174
184
  *
175
185
  * @function useConnection
176
- * @param [Connection] connection The new connection to use
177
- * @return [Model] this
186
+ * @param {Connection} connection The new connection to use
187
+ * @return {Model} this
178
188
  * @api public
179
189
  */
180
190
 
181
191
  Model.useConnection = function useConnection(connection) {
182
- if (!connection) {
183
- throw new Error('Please provide a connection.');
192
+ if (typeof connection?.model !== 'function' || typeof connection.collection !== 'function' || typeof connection.base?.version !== 'string') {
193
+ throw new MongooseError('`useConnection()` requires a Mongoose connection.');
194
+ }
195
+ if (this.db?.base?.version && this.db?.base?.version !== connection.base?.version) {
196
+ throw new MongooseError(`The connection passed to \`useConnection()\` has a different version of Mongoose (${connection.base?.version}) than the model you are using (${this.db?.base?.version}).`);
184
197
  }
185
198
  if (this.db) {
186
199
  delete this.db.models[this.modelName];
@@ -316,11 +329,10 @@ function _applyCustomWhere(doc, where) {
316
329
  /*!
317
330
  * ignore
318
331
  */
319
-
320
- Model.prototype.$__handleSave = function(options, callback) {
332
+ function _createSaveOptions(doc, options) {
321
333
  const saveOptions = {};
322
334
 
323
- applyWriteConcern(this.$__schema, options);
335
+ applyWriteConcern(doc.$__schema, options);
324
336
  if (typeof options.writeConcern !== 'undefined') {
325
337
  saveOptions.writeConcern = {};
326
338
  if ('w' in options.writeConcern) {
@@ -347,211 +359,223 @@ Model.prototype.$__handleSave = function(options, callback) {
347
359
  saveOptions.checkKeys = options.checkKeys;
348
360
  }
349
361
 
350
- const session = this.$session();
351
- const asyncLocalStorage = this[modelDbSymbol].base.transactionAsyncLocalStorage?.getStore();
362
+ const session = doc.$session();
363
+ const asyncLocalStorage = doc[modelDbSymbol].base.transactionAsyncLocalStorage?.getStore();
352
364
  if (session != null) {
353
365
  saveOptions.session = session;
354
366
  } else if (!Object.hasOwn(options, 'session') && asyncLocalStorage?.session != null) {
355
367
  // Only set session from asyncLocalStorage if `session` option wasn't originally passed in options
356
368
  saveOptions.session = asyncLocalStorage.session;
357
369
  }
358
- if (this.$isNew) {
359
- // send entire doc
360
- const obj = this.toObject(saveToObjectOptions);
361
- if ((obj || {})._id === void 0) {
362
- // documents must have an _id else mongoose won't know
363
- // what to update later if more changes are made. the user
364
- // wouldn't know what _id was generated by mongodb either
365
- // nor would the ObjectId generated by mongodb necessarily
366
- // match the schema definition.
367
- immediate(function() {
368
- callback(new MongooseError('document must have an _id before saving'));
369
- });
370
- return;
371
- }
372
370
 
373
- this.$__version(true, obj);
374
- this[modelCollectionSymbol].insertOne(obj, saveOptions).then(
375
- ret => callback(null, ret),
376
- err => {
377
- _setIsNew(this, true);
371
+ return saveOptions;
372
+ }
378
373
 
379
- callback(err, null);
380
- }
381
- );
374
+ /*!
375
+ * ignore
376
+ */
377
+
378
+ Model.prototype.$__save = async function $__save(options) {
379
+ try {
380
+ const hasValidateBeforeSaveOption = options &&
381
+ (typeof options === 'object') &&
382
+ ('validateBeforeSave' in options);
383
+ const shouldValidateBeforeSave = hasValidateBeforeSaveOption ?
384
+ !!options.validateBeforeSave :
385
+ this.$__schema.options.validateBeforeSave;
386
+ if (shouldValidateBeforeSave) {
387
+ const hasValidateModifiedOnlyOption = options != null &&
388
+ typeof options === 'object' &&
389
+ Object.hasOwn(options, 'validateModifiedOnly');
390
+ const validateOptions = hasValidateModifiedOnlyOption ?
391
+ { validateModifiedOnly: options.validateModifiedOnly } :
392
+ null;
393
+ await this.$validate(validateOptions);
394
+ this.$op = 'save';
395
+ }
382
396
 
383
- this.$__reset();
384
- _setIsNew(this, false);
385
- // Make it possible to retry the insert
386
- this.$__.inserting = true;
397
+ await this._execDocumentPreHooks('save', options, [options]);
398
+ } catch (error) {
399
+ await this._execDocumentPostHooks('save', options, error);
387
400
  return;
388
401
  }
389
402
 
390
- // Make sure we don't treat it as a new object on error,
391
- // since it already exists
392
- this.$__.inserting = false;
393
- const delta = this.$__delta();
394
403
 
395
- if (options.pathsToSave) {
396
- for (const key in delta[1]['$set']) {
397
- if (options.pathsToSave.includes(key)) {
398
- continue;
399
- } else if (options.pathsToSave.some(pathToSave => key.slice(0, pathToSave.length) === pathToSave && key.charAt(pathToSave.length) === '.')) {
400
- continue;
401
- } else {
402
- delete delta[1]['$set'][key];
404
+ let result = null;
405
+ let where = null;
406
+ try {
407
+ const saveOptions = _createSaveOptions(this, options);
408
+
409
+ if (this.$isNew) {
410
+ // send entire doc
411
+ const obj = this.$__hasOnlyPrimitiveValues() ?
412
+ this.$__toObjectShallow() :
413
+ this.toObject(saveToObjectOptions);
414
+ if ((obj || {})._id === void 0) {
415
+ // documents must have an _id else mongoose won't know
416
+ // what to update later if more changes are made. the user
417
+ // wouldn't know what _id was generated by mongodb either
418
+ // nor would the ObjectId generated by mongodb necessarily
419
+ // match the schema definition.
420
+ throw new MongooseError('document must have an _id before saving');
403
421
  }
404
- }
405
- }
406
- if (delta) {
407
- if (delta instanceof MongooseError) {
408
- callback(delta);
409
- return;
410
- }
411
422
 
412
- const where = this.$__where(delta[0]);
413
- if (where instanceof MongooseError) {
414
- callback(where);
415
- return;
416
- }
417
-
418
- _applyCustomWhere(this, where);
419
-
420
- const update = delta[1];
421
- if (this.$__schema.options.minimize) {
422
- for (const updateOp of Object.values(update)) {
423
- if (updateOp == null) {
424
- continue;
425
- }
426
- for (const key of Object.keys(updateOp)) {
427
- if (updateOp[key] == null || typeof updateOp[key] !== 'object') {
428
- continue;
429
- }
430
- if (!utils.isPOJO(updateOp[key])) {
431
- continue;
432
- }
433
- minimize(updateOp[key]);
434
- if (Object.keys(updateOp[key]).length === 0) {
435
- delete updateOp[key];
436
- update.$unset = update.$unset || {};
437
- update.$unset[key] = 1;
423
+ this.$__version(true, obj);
424
+ this.$__reset();
425
+ _setIsNew(this, false);
426
+ // Make it possible to retry the insert
427
+ this.$__.inserting = true;
428
+ result = await this[modelCollectionSymbol].insertOne(obj, saveOptions).catch(err => {
429
+ _setIsNew(this, true);
430
+ throw err;
431
+ });
432
+ } else {
433
+ // Make sure we don't treat it as a new object on error,
434
+ // since it already exists
435
+ this.$__.inserting = false;
436
+ const pathsToSave = Array.isArray(options.pathsToSave) ? options.pathsToSave : null;
437
+ const pathsToSaveSet = pathsToSave != null ? new Set(pathsToSave) : null;
438
+ const delta = this.$__delta(pathsToSave, pathsToSaveSet);
439
+ const unsavedDirty = pathsToSave != null ? (delta != null ? delta[2] : this.$__dirty()) : null;
440
+ const unsavedDefaultPaths = pathsToSave != null
441
+ ? Object.keys(this.$__.activePaths.getStatePaths('default')).filter(path => !isInPathsToSave(path, pathsToSaveSet, pathsToSave))
442
+ : null;
443
+
444
+ if (delta) {
445
+ where = this.$__where(delta[0]);
446
+ _applyCustomWhere(this, where);
447
+
448
+ const update = delta[1];
449
+ if (this.$__schema.options.minimize) {
450
+ for (const updateOp of Object.values(update)) {
451
+ if (updateOp == null) {
452
+ continue;
453
+ }
454
+ for (const key of Object.keys(updateOp)) {
455
+ if (updateOp[key] == null || typeof updateOp[key] !== 'object') {
456
+ continue;
457
+ }
458
+ if (!utils.isPOJO(updateOp[key])) {
459
+ continue;
460
+ }
461
+ minimize(updateOp[key]);
462
+ if (utils.hasOwnKeys(updateOp[key]) === false) {
463
+ delete updateOp[key];
464
+ update.$unset = update.$unset || {};
465
+ update.$unset[key] = 1;
466
+ }
467
+ }
438
468
  }
439
469
  }
440
- }
441
- }
442
470
 
443
- this[modelCollectionSymbol].updateOne(where, update, saveOptions).then(
444
- ret => {
445
- if (ret == null) {
446
- ret = { $where: where };
447
- } else {
448
- ret.$where = where;
471
+ // store the modified paths before the document is reset
472
+ this.$__.modifiedPaths = this.modifiedPaths();
473
+ this.$__reset();
474
+ restoreUnsavedState(this, unsavedDirty, unsavedDefaultPaths);
475
+
476
+ _setIsNew(this, false);
477
+ result = await this[modelCollectionSymbol].updateOne(where, update, saveOptions).catch(err => {
478
+ this.$__undoReset();
479
+ throw err;
480
+ });
481
+ } else {
482
+ where = this.$__where();
483
+ _applyCustomWhere(this, where);
484
+ if (this.$__.version) {
485
+ this.$__version(where, delta);
449
486
  }
450
- callback(null, ret);
451
- },
452
- err => {
453
- this.$__undoReset();
454
487
 
455
- callback(err);
488
+ applyReadConcern(this.$__schema, saveOptions);
489
+ result = await this.constructor.collection.findOne(where, { ...saveOptions, projection: { _id: 1 } })
490
+ .then(documentExists => ({ matchedCount: !documentExists ? 0 : 1 }));
456
491
  }
457
- );
458
- } else {
459
- handleEmptyUpdate.call(this);
492
+ }
493
+ } catch (err) {
494
+ const error = this.$__schema._transformDuplicateKeyError(err);
495
+ await this._execDocumentPostHooks('save', options, error);
460
496
  return;
461
497
  }
462
498
 
463
- // store the modified paths before the document is reset in case we need to generate version error.
464
- this.$__.modifiedPaths = this.modifiedPaths().concat(Object.keys(this.$__.activePaths.getStatePaths('default')));
465
- this.$__reset();
466
-
467
- _setIsNew(this, false);
499
+ let numAffected = 0;
500
+ const writeConcern = options != null ?
501
+ options.writeConcern != null ?
502
+ options.writeConcern.w :
503
+ options.w :
504
+ 0;
505
+ if (writeConcern !== 0) {
506
+ // Skip checking if write succeeded if writeConcern is set to
507
+ // unacknowledged writes, because otherwise `numAffected` will always be 0
508
+ if (result != null) {
509
+ if (Array.isArray(result)) {
510
+ numAffected = result.length;
511
+ } else if (result.matchedCount != null) {
512
+ numAffected = result.matchedCount;
513
+ } else {
514
+ numAffected = result;
515
+ }
516
+ }
468
517
 
469
- function handleEmptyUpdate() {
470
- const optionsWithCustomValues = Object.assign({}, options, saveOptions);
471
- const where = this.$__where();
472
- const optimisticConcurrency = this.$__schema.options.optimisticConcurrency;
473
- if (optimisticConcurrency && !Array.isArray(optimisticConcurrency)) {
518
+ const versionBump = this.$__.version;
519
+ // was this an update that required a version bump?
520
+ if (versionBump && !this.$__.inserting) {
521
+ const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version);
522
+ this.$__.version = undefined;
474
523
  const key = this.$__schema.options.versionKey;
475
- const val = this.$__getValue(key);
476
- if (val != null) {
477
- where[key] = val;
524
+ const version = this.$__getValue(key) || 0;
525
+ if (numAffected <= 0) {
526
+ // the update failed. pass an error back
527
+ this.$__undoReset();
528
+ const err = this.$__.$versionError ||
529
+ new VersionError(this, version, this.$__.modifiedPaths);
530
+ await this._execDocumentPostHooks('save', options, err);
531
+ return;
478
532
  }
479
- }
480
533
 
481
- applyReadConcern(this.$__schema, optionsWithCustomValues);
482
- this.constructor.collection.findOne(where, optionsWithCustomValues)
483
- .then(documentExists => {
484
- const matchedCount = !documentExists ? 0 : 1;
485
- callback(null, { $where: where, matchedCount });
486
- })
487
- .catch(callback);
534
+ // increment version if was successful
535
+ if (doIncrement) {
536
+ this.$__setValue(key, version + 1);
537
+ }
538
+ }
539
+ if (result != null && numAffected <= 0) {
540
+ this.$__undoReset();
541
+ const error = new DocumentNotFoundError(where, this.constructor.modelName, numAffected, result);
542
+ await this._execDocumentPostHooks('save', options, error);
543
+ return;
544
+ }
488
545
  }
546
+ this.$__.saving = undefined;
547
+ this.$__.savedState = {};
548
+ this.$emit('save', this, numAffected);
549
+ this.constructor.emit('save', this, numAffected);
550
+ await this._execDocumentPostHooks('save', options);
489
551
  };
490
552
 
491
553
  /*!
492
- * ignore
554
+ * Restores $__.activePaths state and any atomics for paths that failed
555
+ * to save.
556
+ *
557
+ * @param {Document} doc
558
+ * @param {object[]} unsavedDirty
559
+ * @param {string[]} unsavedDefaultPaths
493
560
  */
494
561
 
495
- Model.prototype.$__save = function(options, callback) {
496
- this.$__handleSave(options, (error, result) => {
497
- if (error) {
498
- error = this.$__schema._transformDuplicateKeyError(error);
499
- const hooks = this.$__schema.s.hooks;
500
- return hooks.execPost('save:error', this, [this], { error: error }, (error) => {
501
- callback(error, this);
502
- });
503
- }
504
- let numAffected = 0;
505
- const writeConcern = options != null ?
506
- options.writeConcern != null ?
507
- options.writeConcern.w :
508
- options.w :
509
- 0;
510
- if (writeConcern !== 0) {
511
- // Skip checking if write succeeded if writeConcern is set to
512
- // unacknowledged writes, because otherwise `numAffected` will always be 0
513
- if (result != null) {
514
- if (Array.isArray(result)) {
515
- numAffected = result.length;
516
- } else if (result.matchedCount != null) {
517
- numAffected = result.matchedCount;
518
- } else {
519
- numAffected = result;
520
- }
521
- }
522
-
523
- const versionBump = this.$__.version;
524
- // was this an update that required a version bump?
525
- if (versionBump && !this.$__.inserting) {
526
- if (numAffected <= 0) {
527
- const key = this.$__schema.options.versionKey;
528
- const version = this.$__getValue(key) || 0;
529
- // the update failed. pass an error back
530
- this.$__undoReset();
531
- const err = this.$__.$versionError ||
532
- new VersionError(this, version, this.$__.modifiedPaths);
533
- return callback(err, this);
534
- }
562
+ function restoreUnsavedState(doc, unsavedDirty, unsavedDefaultPaths) {
563
+ if (unsavedDirty == null) {
564
+ return;
565
+ }
535
566
 
536
- this._applyVersionIncrement();
537
- }
538
- if (result != null && numAffected <= 0) {
539
- this.$__undoReset();
540
- error = new DocumentNotFoundError(result.$where,
541
- this.constructor.modelName, numAffected, result);
542
- const hooks = this.$__schema.s.hooks;
543
- return hooks.execPost('save:error', this, [this], { error: error }, (error) => {
544
- callback(error, this);
545
- });
546
- }
567
+ for (const dirty of unsavedDirty) {
568
+ doc.$__.activePaths.modify(dirty.path);
569
+ if (dirty.value?.[arrayAtomicsBackupSymbol]) {
570
+ dirty.value[arrayAtomicsSymbol] = dirty.value[arrayAtomicsBackupSymbol];
571
+ dirty.value[arrayAtomicsBackupSymbol] = null;
547
572
  }
548
- this.$__.saving = undefined;
549
- this.$__.savedState = {};
550
- this.$emit('save', this, numAffected);
551
- this.constructor.emit('save', this, numAffected);
552
- callback(null, this);
553
- });
554
- };
573
+ }
574
+
575
+ for (const path of unsavedDefaultPaths) {
576
+ doc.$__.activePaths.default(path);
577
+ }
578
+ }
555
579
 
556
580
  /*!
557
581
  * ignore
@@ -583,17 +607,20 @@ function generateVersionError(doc, modifiedPaths, defaultPaths) {
583
607
  * const newProduct = await product.save();
584
608
  * newProduct === product; // true
585
609
  *
586
- * @param {Object} [options] options optional options
610
+ * @param {object} [options] options optional options
587
611
  * @param {Session} [options.session=null] the [session](https://www.mongodb.com/docs/manual/reference/server-sessions/) associated with this save operation. If not specified, defaults to the [document's associated session](https://mongoosejs.com/docs/api/document.html#Document.prototype.session()).
588
- * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](https://mongoosejs.com/docs/guide.html#safe). Use the `w` option instead.
589
- * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
590
- * @param {Boolean} [options.validateModifiedOnly=false] if `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths.
591
- * @param {Number|String} [options.w] set the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#w-option). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern)
592
- * @param {Boolean} [options.j] set to true for MongoDB to wait until this `save()` has been [journaled before resolving the returned promise](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern)
593
- * @param {Number} [options.wtimeout] sets a [timeout for the write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern).
594
- * @param {Boolean} [options.checkKeys=true] the MongoDB driver prevents you from saving keys that start with '$' or contain '.' by default. Set this option to `false` to skip that check. See [restrictions on field names](https://docs.mongodb.com/manual/reference/limits/#mongodb-limit-Restrictions-on-Field-Names)
595
- * @param {Boolean} [options.timestamps=true] if `false` and [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this `save()`.
612
+ * @param {object} [options.safe] (DEPRECATED) overrides [schema's safe option](https://mongoosejs.com/docs/guide.html#safe). Use the `w` option instead.
613
+ * @param {boolean} [options.validateBeforeSave] set to false to save without validating.
614
+ * @param {boolean} [options.validateModifiedOnly=false] if `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths.
615
+ * @param {number|string} [options.w] set the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#w-option). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern)
616
+ * @param {boolean} [options.j] set to true for MongoDB to wait until this `save()` has been [journaled before resolving the returned promise](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern)
617
+ * @param {number} [options.wtimeout] sets a [timeout for the write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern).
618
+ * @param {boolean} [options.checkKeys=true] the MongoDB driver prevents you from saving keys that start with '$' or contain '.' by default. Set this option to `false` to skip that check. See [restrictions on field names](https://docs.mongodb.com/manual/reference/limits/#mongodb-limit-Restrictions-on-Field-Names)
619
+ * @param {boolean} [options.timestamps=true] if `false` and [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this `save()`.
596
620
  * @param {Array} [options.pathsToSave] An array of paths that tell mongoose to only validate and save the paths in `pathsToSave`.
621
+ * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware
622
+ * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
623
+ * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks
597
624
  * @throws {DocumentNotFoundError} if this [save updates an existing document](https://mongoosejs.com/docs/api/document.html#Document.prototype.isNew) but the document doesn't exist in the database. For example, you will get this error if the document is [deleted between when you retrieved the document and when you saved it](documents.html#updating).
598
625
  * @return {Promise}
599
626
  * @api public
@@ -611,7 +638,7 @@ Model.prototype.save = async function save(options) {
611
638
  if (this.$__.saving) {
612
639
  parallelSave = new ParallelSaveError(this);
613
640
  } else {
614
- this.$__.saving = new ParallelSaveError(this);
641
+ this.$__.saving = true;
615
642
  }
616
643
 
617
644
  options = new SaveOptions(options);
@@ -621,11 +648,13 @@ Model.prototype.save = async function save(options) {
621
648
  if (this.$__.timestamps != null) {
622
649
  options.timestamps = this.$__.timestamps;
623
650
  }
624
- this.$__.$versionError = generateVersionError(
625
- this,
626
- this.modifiedPaths(),
627
- Object.keys(this.$__.activePaths.getStatePaths('default'))
628
- );
651
+ if (!this.$isNew) {
652
+ this.$__.$versionError = generateVersionError(
653
+ this,
654
+ this.modifiedPaths(),
655
+ Object.keys(this.$__.activePaths.getStatePaths('default'))
656
+ );
657
+ }
629
658
 
630
659
  if (parallelSave) {
631
660
  this.$__handleReject(parallelSave);
@@ -634,20 +663,17 @@ Model.prototype.save = async function save(options) {
634
663
 
635
664
  this.$__.saveOptions = options;
636
665
 
637
- await new Promise((resolve, reject) => {
638
- this.$__save(options, error => {
639
- this.$__.saving = null;
640
- this.$__.saveOptions = null;
641
- this.$__.$versionError = null;
642
- this.$op = null;
643
- if (error != null) {
644
- this.$__handleReject(error);
645
- return reject(error);
646
- }
647
-
648
- resolve();
649
- });
650
- });
666
+ try {
667
+ await this.$__save(options);
668
+ } catch (error) {
669
+ this.$__handleReject(error);
670
+ throw error;
671
+ } finally {
672
+ this.$__.saving = null;
673
+ this.$__.saveOptions = null;
674
+ this.$__.$versionError = null;
675
+ this.$op = null;
676
+ }
651
677
 
652
678
  return this;
653
679
  };
@@ -745,7 +771,7 @@ Model.prototype.$__where = function _where(where) {
745
771
  }
746
772
 
747
773
  if (this._doc._id === void 0) {
748
- return new MongooseError('No _id found on document!');
774
+ throw new MongooseError('No _id found on document!');
749
775
  }
750
776
 
751
777
  return where;
@@ -786,9 +812,6 @@ Model.prototype.deleteOne = function deleteOne(options) {
786
812
 
787
813
  const self = this;
788
814
  const where = this.$__where();
789
- if (where instanceof Error) {
790
- throw where;
791
- }
792
815
  const query = self.constructor.deleteOne();
793
816
 
794
817
  if (this.$session() != null) {
@@ -797,38 +820,37 @@ Model.prototype.deleteOne = function deleteOne(options) {
797
820
  }
798
821
  }
799
822
 
823
+ const preFilter = buildMiddlewareFilter(options, 'pre');
824
+ const postFilter = buildMiddlewareFilter(options, 'post');
825
+
800
826
  query.pre(async function queryPreDeleteOne() {
801
- await new Promise((resolve, reject) => {
802
- self.constructor._middleware.execPre('deleteOne', self, [self, options], err => {
803
- query.deleteOne(where, options);
804
- if (err) reject(err);
805
- else resolve();
806
- });
807
- });
827
+ const res = await self.constructor._middleware.execPre('deleteOne', self, [self, options], { filter: preFilter });
828
+ // `self` is passed to pre hooks as argument for backwards compatibility, but that
829
+ // isn't the actual arguments passed to the wrapped function.
830
+ if (res[0] !== self || res[1] !== options) {
831
+ throw new MongooseError('Document deleteOne pre hooks cannot overwrite arguments');
832
+ }
833
+ query.deleteOne(where, options);
808
834
  // Apply custom where conditions _after_ document deleteOne middleware for
809
835
  // consistency with save() - sharding plugin needs to set $where
810
836
  if (self.$where != null) {
811
837
  this.where(self.$where);
812
838
  }
839
+ return res;
813
840
  });
814
- query.pre(function callSubdocPreHooks(cb) {
815
- each(self.$getAllSubdocs(), (subdoc, cb) => {
816
- subdoc.constructor._middleware.execPre('deleteOne', subdoc, [subdoc, options], cb);
817
- }, cb);
841
+ query.pre(function callSubdocPreHooks() {
842
+ return Promise.all(self.$getAllSubdocs().map(subdoc => subdoc.constructor._middleware.execPre('deleteOne', subdoc, [subdoc], { filter: preFilter })));
818
843
  });
819
- query.pre(function skipIfAlreadyDeleted(cb) {
844
+ query.pre(function skipIfAlreadyDeleted() {
820
845
  if (self.$__.isDeleted) {
821
- return cb(Kareem.skipWrappedFunction());
846
+ throw new Kareem.skipWrappedFunction();
822
847
  }
823
- return cb();
824
848
  });
825
- query.post(function callSubdocPostHooks(cb) {
826
- each(self.$getAllSubdocs(), (subdoc, cb) => {
827
- subdoc.constructor._middleware.execPost('deleteOne', subdoc, [subdoc], {}, cb);
828
- }, cb);
849
+ query.post(function callSubdocPostHooks() {
850
+ return Promise.all(self.$getAllSubdocs().map(subdoc => subdoc.constructor._middleware.execPost('deleteOne', subdoc, [subdoc], { filter: postFilter })));
829
851
  });
830
- query.post(function queryPostDeleteOne(cb) {
831
- self.constructor._middleware.execPost('deleteOne', self, [self], {}, cb);
852
+ query.post(function queryPostDeleteOne() {
853
+ return self.constructor._middleware.execPost('deleteOne', self, [self], { filter: postFilter });
832
854
  });
833
855
  query.transform(function setIsDeleted(result) {
834
856
  if (result?.deletedCount > 0) {
@@ -850,7 +872,7 @@ Model.prototype.deleteOne = function deleteOne(options) {
850
872
  * doc.$model() === Tank; // true
851
873
  * await doc.$model('User').findById(id);
852
874
  *
853
- * @param {String} [name] model name
875
+ * @param {string} [name] model name
854
876
  * @method $model
855
877
  * @api public
856
878
  * @return {Model}
@@ -873,7 +895,7 @@ Model.prototype.$model = function $model(name) {
873
895
  * doc.$model() === Tank; // true
874
896
  * await doc.$model('User').findById(id);
875
897
  *
876
- * @param {String} [name] model name
898
+ * @param {string} [name] model name
877
899
  * @method model
878
900
  * @api public
879
901
  * @return {Model}
@@ -900,8 +922,8 @@ Model.prototype.model = Model.prototype.$model;
900
922
  *
901
923
  * - `findOne()`
902
924
  *
903
- * @param {Object} filter
904
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
925
+ * @param {object} filter
926
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
905
927
  * @return {Query}
906
928
  */
907
929
 
@@ -945,14 +967,14 @@ Model.exists = function exists(filter, options) {
945
967
  * const Employee = Person.discriminator('Employee', employeeSchema, 'staff');
946
968
  * new Employee().__t; // "staff" because of 3rd argument above
947
969
  *
948
- * @param {String} name discriminator model name
970
+ * @param {string} name discriminator model name
949
971
  * @param {Schema} schema discriminator model schema
950
- * @param {Object|String} [options] If string, same as `options.value`.
951
- * @param {String} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
952
- * @param {Boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning.
953
- * @param {Boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name.
954
- * @param {Boolean} [options.mergeHooks=true] By default, Mongoose merges the base schema's hooks with the discriminator schema's hooks. Set this option to `false` to make Mongoose use the discriminator schema's hooks instead.
955
- * @param {Boolean} [options.mergePlugins=true] By default, Mongoose merges the base schema's plugins with the discriminator schema's plugins. Set this option to `false` to make Mongoose use the discriminator schema's plugins instead.
972
+ * @param {object|string} [options] If string, same as `options.value`.
973
+ * @param {string} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
974
+ * @param {boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning.
975
+ * @param {boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name.
976
+ * @param {boolean} [options.mergeHooks=true] By default, Mongoose merges the base schema's hooks with the discriminator schema's hooks. Set this option to `false` to make Mongoose use the discriminator schema's hooks instead.
977
+ * @param {boolean} [options.mergePlugins=true] By default, Mongoose merges the base schema's plugins with the discriminator schema's plugins. Set this option to `false` to make Mongoose use the discriminator schema's plugins instead.
956
978
  * @return {Model} The newly created discriminator model
957
979
  * @api public
958
980
  */
@@ -1175,7 +1197,7 @@ Model.init = function init() {
1175
1197
  * });
1176
1198
  *
1177
1199
  * @api public
1178
- * @param {Object} [options] see [MongoDB driver docs](https://mongodb.github.io/node-mongodb-native/4.9/classes/Db.html#createCollection)
1200
+ * @param {object} [options] see [MongoDB driver docs](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#createCollection)
1179
1201
  * @returns {Promise}
1180
1202
  */
1181
1203
 
@@ -1185,37 +1207,32 @@ Model.createCollection = async function createCollection(options) {
1185
1207
  throw new MongooseError('Model.createCollection() no longer accepts a callback');
1186
1208
  }
1187
1209
 
1188
- const shouldSkip = await new Promise((resolve, reject) => {
1189
- this.hooks.execPre('createCollection', this, [options], (err) => {
1190
- if (err != null) {
1191
- if (err instanceof Kareem.skipWrappedFunction) {
1192
- return resolve(true);
1193
- }
1194
- return reject(err);
1195
- }
1196
- resolve();
1197
- });
1210
+ const preFilter = buildMiddlewareFilter(options, 'pre');
1211
+ const postFilter = buildMiddlewareFilter(options, 'post');
1212
+
1213
+ // Remove middleware option before passing to MongoDB
1214
+ if (options?.middleware != null) {
1215
+ options = { ...options };
1216
+ delete options.middleware;
1217
+ }
1218
+
1219
+ [options] = await this.hooks.execPre('createCollection', this, [options], { filter: preFilter }).catch(err => {
1220
+ if (err instanceof Kareem.skipWrappedFunction) {
1221
+ return [err];
1222
+ }
1223
+ throw err;
1198
1224
  });
1199
1225
 
1200
- const collectionOptions = this &&
1201
- this.schema &&
1202
- this.schema.options &&
1203
- this.schema.options.collectionOptions;
1226
+ const collectionOptions = this?.schema?.options?.collectionOptions;
1204
1227
  if (collectionOptions != null) {
1205
1228
  options = Object.assign({}, collectionOptions, options);
1206
1229
  }
1207
1230
 
1208
- const schemaCollation = this &&
1209
- this.schema &&
1210
- this.schema.options &&
1211
- this.schema.options.collation;
1231
+ const schemaCollation = this?.schema?.options?.collation;
1212
1232
  if (schemaCollation != null) {
1213
1233
  options = Object.assign({ collation: schemaCollation }, options);
1214
1234
  }
1215
- const capped = this &&
1216
- this.schema &&
1217
- this.schema.options &&
1218
- this.schema.options.capped;
1235
+ const capped = this?.schema?.options?.capped;
1219
1236
  if (capped != null) {
1220
1237
  if (typeof capped === 'number') {
1221
1238
  options = Object.assign({ capped: true, size: capped }, options);
@@ -1223,10 +1240,7 @@ Model.createCollection = async function createCollection(options) {
1223
1240
  options = Object.assign({ capped: true }, capped, options);
1224
1241
  }
1225
1242
  }
1226
- const timeseries = this &&
1227
- this.schema &&
1228
- this.schema.options &&
1229
- this.schema.options.timeseries;
1243
+ const timeseries = this?.schema?.options?.timeseries;
1230
1244
  if (timeseries != null) {
1231
1245
  options = Object.assign({ timeseries }, options);
1232
1246
  if (options.expireAfterSeconds != null) {
@@ -1241,40 +1255,22 @@ Model.createCollection = async function createCollection(options) {
1241
1255
  }
1242
1256
  }
1243
1257
 
1244
- const clusteredIndex = this &&
1245
- this.schema &&
1246
- this.schema.options &&
1247
- this.schema.options.clusteredIndex;
1258
+ const clusteredIndex = this?.schema?.options?.clusteredIndex;
1248
1259
  if (clusteredIndex != null) {
1249
1260
  options = Object.assign({ clusteredIndex: { ...clusteredIndex, unique: true } }, options);
1250
1261
  }
1251
1262
 
1252
1263
  try {
1253
- if (!shouldSkip) {
1264
+ if (!(options instanceof Kareem.skipWrappedFunction)) {
1254
1265
  await this.db.createCollection(this.$__collection.collectionName, options);
1255
1266
  }
1256
1267
  } catch (err) {
1257
1268
  if (err != null && (err.name !== 'MongoServerError' || err.code !== 48)) {
1258
- await new Promise((resolve, reject) => {
1259
- const _opts = { error: err };
1260
- this.hooks.execPost('createCollection', this, [null], _opts, (err) => {
1261
- if (err != null) {
1262
- return reject(err);
1263
- }
1264
- resolve();
1265
- });
1266
- });
1269
+ await this.hooks.execPost('createCollection', this, [null], { error: err, filter: postFilter });
1267
1270
  }
1268
1271
  }
1269
1272
 
1270
- await new Promise((resolve, reject) => {
1271
- this.hooks.execPost('createCollection', this, [this.$__collection], (err) => {
1272
- if (err != null) {
1273
- return reject(err);
1274
- }
1275
- resolve();
1276
- });
1277
- });
1273
+ await this.hooks.execPost('createCollection', this, [this.$__collection], { filter: postFilter });
1278
1274
 
1279
1275
  return this.$__collection;
1280
1276
  };
@@ -1307,9 +1303,8 @@ Model.createCollection = async function createCollection(options) {
1307
1303
  * toDrop; // Array of strings containing names of indexes that `syncIndexes()` will drop
1308
1304
  * toCreate; // Array of strings containing names of indexes that `syncIndexes()` will create
1309
1305
  *
1310
- * @param {Object} [options] options to pass to `ensureIndexes()`
1311
- * @param {Boolean} [options.background=null] if specified, overrides each index's `background` property
1312
- * @param {Boolean} [options.hideIndexes=false] set to `true` to hide indexes instead of dropping. Requires MongoDB server 4.4 or higher
1306
+ * @param {object} [options] options to pass to `ensureIndexes()`
1307
+ * @param {boolean} [options.hideIndexes=false] set to `true` to hide indexes instead of dropping. Requires MongoDB server 4.4 or higher
1313
1308
  * @return {Promise}
1314
1309
  * @api public
1315
1310
  */
@@ -1353,9 +1348,9 @@ Model.syncIndexes = async function syncIndexes(options) {
1353
1348
  * const Customer = mongoose.model('Customer', schema);
1354
1349
  * await Customer.createSearchIndex({ name: 'test', definition: { mappings: { dynamic: true } } });
1355
1350
  *
1356
- * @param {Object} description index options, including `name` and `definition`
1357
- * @param {String} description.name
1358
- * @param {Object} description.definition
1351
+ * @param {object} description index options, including `name` and `definition`
1352
+ * @param {string} description.name
1353
+ * @param {object} description.definition
1359
1354
  * @return {Promise}
1360
1355
  * @api public
1361
1356
  */
@@ -1376,8 +1371,8 @@ Model.createSearchIndex = async function createSearchIndex(description) {
1376
1371
  * const Customer = mongoose.model('Customer', schema);
1377
1372
  * await Customer.updateSearchIndex('test', { mappings: { dynamic: true } });
1378
1373
  *
1379
- * @param {String} name
1380
- * @param {Object} definition
1374
+ * @param {string} name
1375
+ * @param {object} definition
1381
1376
  * @return {Promise}
1382
1377
  * @api public
1383
1378
  */
@@ -1398,7 +1393,7 @@ Model.updateSearchIndex = async function updateSearchIndex(name, definition) {
1398
1393
  * const Customer = mongoose.model('Customer', schema);
1399
1394
  * await Customer.dropSearchIndex('test');
1400
1395
  *
1401
- * @param {String} name
1396
+ * @param {string} name
1402
1397
  * @return {Promise}
1403
1398
  * @api public
1404
1399
  */
@@ -1421,7 +1416,7 @@ Model.dropSearchIndex = async function dropSearchIndex(name) {
1421
1416
  * await Customer.createSearchIndex({ name: 'test', definition: { mappings: { dynamic: true } } });
1422
1417
  * const res = await Customer.listSearchIndexes(); // Includes `[{ name: 'test' }]`
1423
1418
  *
1424
- * @param {Object} [options]
1419
+ * @param {object} [options]
1425
1420
  * @return {Promise<Array>}
1426
1421
  * @api public
1427
1422
  */
@@ -1443,9 +1438,9 @@ Model.listSearchIndexes = async function listSearchIndexes(options) {
1443
1438
  * toDrop; // Array of strings containing names of indexes that `syncIndexes()` will drop
1444
1439
  * toCreate; // Array of index specs containing the keys of indexes that `syncIndexes()` will create
1445
1440
  *
1446
- * @param {Object} [options]
1447
- * @param {Boolean} [options.indexOptionsToCreate=false] If true, `toCreate` will include both the index spec and the index options, not just the index spec
1448
- * @return {Promise<Object>} contains the indexes that would be dropped in MongoDB and indexes that would be created in MongoDB as `{ toDrop: string[], toCreate: string[] }`.
1441
+ * @param {object} [options]
1442
+ * @param {boolean} [options.indexOptionsToCreate=false] If true, `toCreate` will include both the index spec and the index options, not just the index spec
1443
+ * @return {Promise<object>} contains the indexes that would be dropped in MongoDB and indexes that would be created in MongoDB as `{ toDrop: string[], toCreate: string[] }`.
1449
1444
  */
1450
1445
 
1451
1446
  Model.diffIndexes = async function diffIndexes(options) {
@@ -1548,10 +1543,10 @@ function getIndexesToDrop(schema, schemaIndexes, dbIndexes) {
1548
1543
  *
1549
1544
  * The returned promise resolves to a list of the dropped indexes' names as an array
1550
1545
  *
1551
- * @param {Object} [options]
1552
- * @param {Array<String>} [options.toDrop] if specified, contains a list of index names to drop
1553
- * @param {Boolean} [options.hideIndexes=false] set to `true` to hide indexes instead of dropping. Requires MongoDB server 4.4 or higher
1554
- * @return {Promise<Array<String>>} list of dropped or hidden index names
1546
+ * @param {object} [options]
1547
+ * @param {string[]} [options.toDrop] if specified, contains a list of index names to drop
1548
+ * @param {boolean} [options.hideIndexes=false] set to `true` to hide indexes instead of dropping. Requires MongoDB server 4.4 or higher
1549
+ * @return {Promise<string[]>} list of dropped or hidden index names
1555
1550
  * @api public
1556
1551
  */
1557
1552
 
@@ -1562,7 +1557,7 @@ Model.cleanIndexes = async function cleanIndexes(options) {
1562
1557
  }
1563
1558
  const model = this;
1564
1559
 
1565
- if (Array.isArray(options && options.toDrop)) {
1560
+ if (Array.isArray(options?.toDrop)) {
1566
1561
  const res = await _dropIndexes(options.toDrop, model, options);
1567
1562
  return res;
1568
1563
  }
@@ -1577,7 +1572,7 @@ async function _dropIndexes(toDrop, model, options) {
1577
1572
  }
1578
1573
 
1579
1574
  const collection = model.$__collection;
1580
- if (options && options.hideIndexes) {
1575
+ if (options?.hideIndexes) {
1581
1576
  await Promise.all(toDrop.map(indexName => {
1582
1577
  return model.db.db.command({
1583
1578
  collMod: collection.collectionName,
@@ -1637,7 +1632,7 @@ Model.listIndexes = async function listIndexes() {
1637
1632
  *
1638
1633
  * _NOTE: It is not recommended that you run this in production. Index creation may impact database performance depending on your load. Use with caution._
1639
1634
  *
1640
- * @param {Object} [options] internal options
1635
+ * @param {object} [options] internal options
1641
1636
  * @return {Promise}
1642
1637
  * @api public
1643
1638
  */
@@ -1659,10 +1654,10 @@ Model.ensureIndexes = async function ensureIndexes(options) {
1659
1654
  };
1660
1655
 
1661
1656
  /**
1662
- * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](https://mongodb.github.io/node-mongodb-native/4.9/classes/Db.html#createIndex)
1657
+ * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#createIndex)
1663
1658
  * function.
1664
1659
  *
1665
- * @param {Object} [options] internal options
1660
+ * @param {object} [options] internal options
1666
1661
  * @return {Promise}
1667
1662
  * @api public
1668
1663
  */
@@ -1704,14 +1699,32 @@ function _ensureIndexes(model, options, callback) {
1704
1699
  }
1705
1700
  }
1706
1701
 
1702
+ // Check for duplicate index definitions (gh-15056)
1703
+ const seenIndexes = [];
1704
+ for (const index of indexes) {
1705
+ const fields = index[0];
1706
+ const indexOptions = index[1];
1707
+ if (indexOptions.name == null) {
1708
+ for (const existingIndex of seenIndexes) {
1709
+ if (existingIndex[1].name == null && isIndexSpecEqual(existingIndex[0], fields)) {
1710
+ utils.warn('mongoose: Duplicate schema index on ' + JSON.stringify(fields) +
1711
+ ' for model "' + model.modelName + '". ' +
1712
+ 'This is often due to declaring an index using both "index: true" and "schema.index()". ' +
1713
+ 'Please remove the duplicate index definition.');
1714
+ break;
1715
+ }
1716
+ }
1717
+ }
1718
+ seenIndexes.push(index);
1719
+ }
1720
+
1707
1721
  if (!indexes.length) {
1708
1722
  immediate(function() {
1709
1723
  done();
1710
1724
  });
1711
1725
  return;
1712
1726
  }
1713
- // Indexes are created one-by-one to support how MongoDB < 2.4 deals
1714
- // with background indexes.
1727
+ // Indexes are created one-by-one
1715
1728
 
1716
1729
  const indexSingleDone = function(err, fields, options, name) {
1717
1730
  model.emit('index-single-done', err, fields, options, name);
@@ -1763,10 +1776,6 @@ function _ensureIndexes(model, options, callback) {
1763
1776
 
1764
1777
  indexSingleStart(indexFields, options);
1765
1778
 
1766
- if ('background' in options) {
1767
- indexOptions.background = options.background;
1768
- }
1769
-
1770
1779
  // Just in case `createIndex()` throws a sync error
1771
1780
  let promise = null;
1772
1781
  try {
@@ -1912,9 +1921,9 @@ Model.discriminators;
1912
1921
  *
1913
1922
  * Only translate arguments of object type anything else is returned raw
1914
1923
  *
1915
- * @param {Object} fields fields/conditions that may contain aliased keys
1916
- * @param {Boolean} [errorOnDuplicates] if true, throw an error if there's both a key and an alias for that key in `fields`
1917
- * @return {Object} the translated 'pure' fields/conditions
1924
+ * @param {object} fields fields/conditions that may contain aliased keys
1925
+ * @param {boolean} [errorOnDuplicates] if true, throw an error if there's both a key and an alias for that key in `fields`
1926
+ * @return {object} the translated 'pure' fields/conditions
1918
1927
  */
1919
1928
  Model.translateAliases = function translateAliases(fields, errorOnDuplicates) {
1920
1929
  _checkContext(this, 'translateAliases');
@@ -1926,7 +1935,7 @@ Model.translateAliases = function translateAliases(fields, errorOnDuplicates) {
1926
1935
  let currentSchema = this.schema;
1927
1936
  for (const i in fieldKeys) {
1928
1937
  const name = fieldKeys[i];
1929
- if (currentSchema && currentSchema.aliases[name]) {
1938
+ if (currentSchema?.aliases[name]) {
1930
1939
  alias = currentSchema.aliases[name];
1931
1940
  if (errorOnDuplicates && alias in fields) {
1932
1941
  throw new MongooseError(`Provided object has both field "${name}" and its alias "${alias}"`);
@@ -1940,7 +1949,7 @@ Model.translateAliases = function translateAliases(fields, errorOnDuplicates) {
1940
1949
  }
1941
1950
 
1942
1951
  // Check if aliased path is a schema
1943
- if (currentSchema && currentSchema.paths[alias]) {
1952
+ if (currentSchema?.paths[alias]) {
1944
1953
  currentSchema = currentSchema.paths[alias].schema;
1945
1954
  }
1946
1955
  else
@@ -2010,9 +2019,9 @@ Model.translateAliases = function translateAliases(fields, errorOnDuplicates) {
2010
2019
  * This function triggers `deleteOne` query hooks. Read the
2011
2020
  * [middleware docs](https://mongoosejs.com/docs/middleware.html#naming) to learn more.
2012
2021
  *
2013
- * @param {Object} conditions
2014
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2015
- * @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.
2022
+ * @param {object} conditions
2023
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2024
+ * @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.
2016
2025
  * @return {Query}
2017
2026
  * @api public
2018
2027
  */
@@ -2043,9 +2052,9 @@ Model.deleteOne = function deleteOne(conditions, options) {
2043
2052
  * This function triggers `deleteMany` query hooks. Read the
2044
2053
  * [middleware docs](https://mongoosejs.com/docs/middleware.html#naming) to learn more.
2045
2054
  *
2046
- * @param {Object} conditions
2047
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2048
- * @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.
2055
+ * @param {object} conditions
2056
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2057
+ * @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.
2049
2058
  * @return {Query}
2050
2059
  * @api public
2051
2060
  */
@@ -2084,10 +2093,10 @@ Model.deleteMany = function deleteMany(conditions, options) {
2084
2093
  * // passing options
2085
2094
  * await MyModel.find({ name: /john/i }, null, { skip: 10 }).exec();
2086
2095
  *
2087
- * @param {Object|ObjectId} filter
2088
- * @param {Object|String|String[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2089
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2090
- * @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.
2096
+ * @param {object|ObjectId} filter
2097
+ * @param {object|string|string[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2098
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2099
+ * @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.
2091
2100
  * @return {Query}
2092
2101
  * @see field selection https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
2093
2102
  * @see query casting https://mongoosejs.com/docs/tutorials/query_casting.html
@@ -2124,9 +2133,9 @@ Model.find = function find(conditions, projection, options) {
2124
2133
  * // select only the adventures name and length
2125
2134
  * await Adventure.findById(id, 'name length').exec();
2126
2135
  *
2127
- * @param {Any} id value of `_id` to query by
2128
- * @param {Object|String|String[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2129
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2136
+ * @param {any} id value of `_id` to query by
2137
+ * @param {object|string|string[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2138
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2130
2139
  * @return {Query}
2131
2140
  * @see field selection https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
2132
2141
  * @see lean queries https://mongoosejs.com/docs/tutorials/lean.html
@@ -2162,10 +2171,10 @@ Model.findById = function findById(id, projection, options) {
2162
2171
  * // Select only the adventures name and length
2163
2172
  * await Adventure.findOne({ country: 'Croatia' }, 'name length').exec();
2164
2173
  *
2165
- * @param {Object} [conditions]
2166
- * @param {Object|String|String[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2167
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2168
- * @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.
2174
+ * @param {object} [conditions]
2175
+ * @param {object|string|string[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2176
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2177
+ * @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.
2169
2178
  * @return {Query}
2170
2179
  * @see field selection https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
2171
2180
  * @see lean queries https://mongoosejs.com/docs/tutorials/lean.html
@@ -2195,7 +2204,7 @@ Model.findOne = function findOne(conditions, projection, options) {
2195
2204
  *
2196
2205
  * const numAdventures = await Adventure.estimatedDocumentCount();
2197
2206
  *
2198
- * @param {Object} [options]
2207
+ * @param {object} [options]
2199
2208
  * @return {Query}
2200
2209
  * @api public
2201
2210
  */
@@ -2222,7 +2231,7 @@ Model.estimatedDocumentCount = function estimatedDocumentCount(options) {
2222
2231
  * a full collection scan and **not** use any indexes.
2223
2232
  *
2224
2233
  * The `countDocuments()` function is similar to `count()`, but there are a
2225
- * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#countDocuments).
2234
+ * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#countDocuments).
2226
2235
  * Below are the operators that `count()` supports but `countDocuments()` does not,
2227
2236
  * and the suggested replacement:
2228
2237
  *
@@ -2230,7 +2239,7 @@ Model.estimatedDocumentCount = function estimatedDocumentCount(options) {
2230
2239
  * - `$near`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$center`](https://www.mongodb.com/docs/manual/reference/operator/query/center/#op._S_center)
2231
2240
  * - `$nearSphere`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://www.mongodb.com/docs/manual/reference/operator/query/centerSphere/#op._S_centerSphere)
2232
2241
  *
2233
- * @param {Object} filter
2242
+ * @param {object} filter
2234
2243
  * @return {Query}
2235
2244
  * @api public
2236
2245
  */
@@ -2258,9 +2267,9 @@ Model.countDocuments = function countDocuments(conditions, options) {
2258
2267
  * const query = Link.distinct('url');
2259
2268
  * query.exec();
2260
2269
  *
2261
- * @param {String} field
2262
- * @param {Object} [conditions] optional
2263
- * @param {Object} [options] optional
2270
+ * @param {string} field
2271
+ * @param {object} [conditions] optional
2272
+ * @param {object} [options] optional
2264
2273
  * @return {Query}
2265
2274
  * @api public
2266
2275
  */
@@ -2297,8 +2306,8 @@ Model.distinct = function distinct(field, conditions, options) {
2297
2306
  * .where('name', /^b/i)
2298
2307
  * ... etc
2299
2308
  *
2300
- * @param {String} path
2301
- * @param {Object} [val] optional value
2309
+ * @param {string} path
2310
+ * @param {object} [val] optional value
2302
2311
  * @return {Query}
2303
2312
  * @api public
2304
2313
  */
@@ -2316,9 +2325,9 @@ Model.where = function where(path, val) {
2316
2325
  *
2317
2326
  * Sometimes you need to query for things in mongodb using a JavaScript expression. You can do so via `find({ $where: javascript })`, or you can use the mongoose shortcut method $where via a Query chain or from your mongoose Model.
2318
2327
  *
2319
- * Blog.$where('this.username.indexOf("val") !== -1').exec(function (err, docs) {});
2328
+ * const result = await Blog.$where('this.username.indexOf("val") !== -1').exec();
2320
2329
  *
2321
- * @param {String|Function} argument is a javascript string or anonymous function
2330
+ * @param {string|Function} argument is a javascript string or anonymous function
2322
2331
  * @method $where
2323
2332
  * @memberOf Model
2324
2333
  * @return {Query}
@@ -2373,25 +2382,25 @@ Model.$where = function $where() {
2373
2382
  * doc.name = 'jason bourne';
2374
2383
  * await doc.save();
2375
2384
  *
2376
- * @param {Object} [conditions]
2377
- * @param {Object} [update]
2378
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2379
- * @param {String} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2380
- * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
2385
+ * @param {object} [conditions]
2386
+ * @param {object} [update]
2387
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2388
+ * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2389
+ * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
2381
2390
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
2382
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2383
- * @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.
2384
- * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
2385
- * @param {Object|String|String[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2386
- * @param {Boolean} [options.new=false] if true, return the modified document rather than the original
2387
- * @param {Object|String} [options.fields] Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
2388
- * @param {Number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0
2389
- * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.
2390
- * @param {Boolean} [options.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
2391
- * @param {Boolean} [options.setDefaultsOnInsert=true] 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
2392
- * @param {Boolean} [options.includeResultMetadata] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html)
2393
- * @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.
2394
- * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
2391
+ * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2392
+ * @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.
2393
+ * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document
2394
+ * @param {object|string|string[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2395
+ * @param {boolean} [options.new=false] if true, return the modified document rather than the original
2396
+ * @param {object|string} [options.fields] Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
2397
+ * @param {number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0
2398
+ * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.
2399
+ * @param {boolean} [options.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
2400
+ * @param {boolean} [options.setDefaultsOnInsert=true] 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
2401
+ * @param {boolean} [options.includeResultMetadata] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html)
2402
+ * @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.
2403
+ * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
2395
2404
  * @return {Query}
2396
2405
  * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html
2397
2406
  * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/
@@ -2409,13 +2418,6 @@ Model.findOneAndUpdate = function(conditions, update, options) {
2409
2418
  fields = options.fields || options.projection;
2410
2419
  }
2411
2420
 
2412
- update = clone(update, {
2413
- depopulate: true,
2414
- _isNested: true
2415
- });
2416
-
2417
- decorateUpdateWithVersionKey(update, options, this.schema.options.versionKey);
2418
-
2419
2421
  const mq = new this.Query({}, {}, this, this.$__collection);
2420
2422
  mq.select(fields);
2421
2423
 
@@ -2462,23 +2464,23 @@ Model.findOneAndUpdate = function(conditions, update, options) {
2462
2464
  * doc.name = 'jason bourne';
2463
2465
  * await doc.save();
2464
2466
  *
2465
- * @param {Object|Number|String} id value of `_id` to query by
2466
- * @param {Object} [update]
2467
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2468
- * @param {String} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2469
- * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
2467
+ * @param {object|number|string} id value of `_id` to query by
2468
+ * @param {object} [update]
2469
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2470
+ * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2471
+ * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
2470
2472
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
2471
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2472
- * @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.
2473
- * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.
2474
- * @param {Boolean} [options.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
2475
- * @param {Boolean} [options.setDefaultsOnInsert=true] 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
2476
- * @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
2477
- * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
2478
- * @param {Boolean} [options.new=false] if true, return the modified document rather than the original
2479
- * @param {Object|String} [options.select] sets the document fields to return.
2480
- * @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.
2481
- * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
2473
+ * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2474
+ * @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.
2475
+ * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.
2476
+ * @param {boolean} [options.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
2477
+ * @param {boolean} [options.setDefaultsOnInsert=true] 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
2478
+ * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document
2479
+ * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document
2480
+ * @param {boolean} [options.new=false] if true, return the modified document rather than the original
2481
+ * @param {object|string} [options.select] sets the document fields to return.
2482
+ * @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.
2483
+ * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
2482
2484
  * @return {Query}
2483
2485
  * @see Model.findOneAndUpdate https://mongoosejs.com/docs/api/model.html#Model.findOneAndUpdate()
2484
2486
  * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/
@@ -2524,16 +2526,16 @@ Model.findByIdAndUpdate = function(id, update, options) {
2524
2526
  * doc.name = 'jason bourne';
2525
2527
  * await doc.save();
2526
2528
  *
2527
- * @param {Object} conditions
2528
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2529
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2530
- * @param {Object|String|String[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2529
+ * @param {object} conditions
2530
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2531
+ * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2532
+ * @param {object|string|string[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2531
2533
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
2532
- * @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
2533
- * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.
2534
- * @param {Object|String} [options.select] sets the document fields to return.
2535
- * @param {Number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0
2536
- * @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.
2534
+ * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document
2535
+ * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.
2536
+ * @param {object|string} [options.select] sets the document fields to return.
2537
+ * @param {number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0
2538
+ * @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.
2537
2539
  * @return {Query}
2538
2540
  * @api public
2539
2541
  */
@@ -2566,10 +2568,10 @@ Model.findOneAndDelete = function(conditions, options) {
2566
2568
  *
2567
2569
  * - `findOneAndDelete()`
2568
2570
  *
2569
- * @param {Object|Number|String} id value of `_id` to query by
2570
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2571
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2572
- * @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.
2571
+ * @param {object|number|string} id value of `_id` to query by
2572
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2573
+ * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2574
+ * @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.
2573
2575
  * @return {Query}
2574
2576
  * @see Model.findOneAndDelete https://mongoosejs.com/docs/api/model.html#Model.findOneAndDelete()
2575
2577
  * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/
@@ -2600,20 +2602,20 @@ Model.findByIdAndDelete = function(id, options) {
2600
2602
  * A.findOneAndReplace(filter, replacement) // returns Query
2601
2603
  * A.findOneAndReplace() // returns Query
2602
2604
  *
2603
- * @param {Object} filter Replace the first document that matches this filter
2604
- * @param {Object} [replacement] Replace with this document
2605
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2606
- * @param {String} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2607
- * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
2605
+ * @param {object} filter Replace the first document that matches this filter
2606
+ * @param {object} [replacement] Replace with this document
2607
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
2608
+ * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
2609
+ * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
2608
2610
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
2609
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2610
- * @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.
2611
- * @param {Object|String|String[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2612
- * @param {Object|String} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.
2613
- * @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
2614
- * @param {Object|String} [options.select] sets the document fields to return.
2615
- * @param {Number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0
2616
- * @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.
2611
+ * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
2612
+ * @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.
2613
+ * @param {object|string|string[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
2614
+ * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.
2615
+ * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document
2616
+ * @param {object|string} [options.select] sets the document fields to return.
2617
+ * @param {number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0
2618
+ * @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.
2617
2619
  * @return {Query}
2618
2620
  * @api public
2619
2621
  */
@@ -2659,10 +2661,10 @@ Model.findOneAndReplace = function(filter, replacement, options) {
2659
2661
  * // specify options.
2660
2662
  * await Character.create([{ name: 'Jean-Luc Picard' }], { session });
2661
2663
  *
2662
- * @param {Array|Object} docs Documents to insert, as a spread or array
2663
- * @param {Object} [options] Options passed down to `save()`. To specify `options`, `docs` **must** be an array, not a spread. See [Model.save](https://mongoosejs.com/docs/api/model.html#Model.prototype.save()) for available options.
2664
- * @param {Boolean} [options.ordered] saves the docs in series rather than parallel.
2665
- * @param {Boolean} [options.aggregateErrors] Aggregate Errors instead of throwing the first one that occurs. Default: false
2664
+ * @param {Array|object} docs Documents to insert, as a spread or array
2665
+ * @param {object} [options] Options passed down to `save()`. To specify `options`, `docs` **must** be an array, not a spread. See [Model.save](https://mongoosejs.com/docs/api/model.html#Model.prototype.save()) for available options.
2666
+ * @param {boolean} [options.ordered] saves the docs in series rather than parallel.
2667
+ * @param {boolean} [options.aggregateErrors] Aggregate Errors instead of throwing the first one that occurs. Default: false
2666
2668
  * @return {Promise}
2667
2669
  * @api public
2668
2670
  */
@@ -2727,6 +2729,26 @@ Model.create = async function create(doc, options) {
2727
2729
  throw new MongooseError('Cannot call `create()` with a session and multiple documents unless `ordered: true` is set');
2728
2730
  }
2729
2731
 
2732
+ if (!Array.isArray(doc) && args.length === 1) {
2733
+ let toSave = doc;
2734
+
2735
+ const Model = this.discriminators && doc[discriminatorKey] != null ?
2736
+ this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) :
2737
+ this;
2738
+ if (Model == null) {
2739
+ throw new MongooseError(`Discriminator "${doc[discriminatorKey]}" not ` +
2740
+ `found for model "${this.modelName}"`);
2741
+ }
2742
+
2743
+ if (!(toSave instanceof Model)) {
2744
+ toSave = new Model(toSave);
2745
+ }
2746
+
2747
+ await toSave.$save(options);
2748
+
2749
+ return toSave;
2750
+ }
2751
+
2730
2752
  if (options.ordered) {
2731
2753
  for (let i = 0; i < args.length; i++) {
2732
2754
  try {
@@ -2805,11 +2827,6 @@ Model.create = async function create(doc, options) {
2805
2827
  }
2806
2828
  }
2807
2829
 
2808
-
2809
- if (!Array.isArray(doc) && args.length === 1) {
2810
- return res[0];
2811
- }
2812
-
2813
2830
  return res;
2814
2831
  };
2815
2832
 
@@ -2831,8 +2848,8 @@ Model.create = async function create(doc, options) {
2831
2848
  * // Create a new character within a transaction.
2832
2849
  * await Character.insertOne({ name: 'Jean-Luc Picard' }, { session });
2833
2850
  *
2834
- * @param {Object|Document} doc Document to insert, as a POJO or Mongoose document
2835
- * @param {Object} [options] Options passed down to `save()`.
2851
+ * @param {object|Document} doc Document to insert, as a POJO or Mongoose document
2852
+ * @param {object} [options] Options passed down to `save()`.
2836
2853
  * @return {Promise<Document>} resolves to the saved document
2837
2854
  * @api public
2838
2855
  */
@@ -2840,6 +2857,10 @@ Model.create = async function create(doc, options) {
2840
2857
  Model.insertOne = async function insertOne(doc, options) {
2841
2858
  _checkContext(this, 'insertOne');
2842
2859
 
2860
+ if (doc == null || typeof doc !== 'object') {
2861
+ throw new ObjectParameterError(doc, 'doc', 'insertOne');
2862
+ }
2863
+
2843
2864
  const discriminatorKey = this.schema.options.discriminatorKey;
2844
2865
  const Model = this.discriminators && doc[discriminatorKey] != null ?
2845
2866
  this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) :
@@ -2883,8 +2904,8 @@ Model.insertOne = async function insertOne(doc, options) {
2883
2904
  * await doc.deleteOne();
2884
2905
  *
2885
2906
  * @param {Array} [pipeline]
2886
- * @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#watch)
2887
- * @param {Boolean} [options.hydrate=false] if true and `fullDocument: 'updateLookup'` is set, Mongoose will automatically hydrate `fullDocument` into a fully fledged Mongoose document
2907
+ * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#watch)
2908
+ * @param {boolean} [options.hydrate=false] if true and `fullDocument: 'updateLookup'` is set, Mongoose will automatically hydrate `fullDocument` into a fully fledged Mongoose document
2888
2909
  * @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter
2889
2910
  * @api public
2890
2911
  */
@@ -2892,27 +2913,21 @@ Model.insertOne = async function insertOne(doc, options) {
2892
2913
  Model.watch = function(pipeline, options) {
2893
2914
  _checkContext(this, 'watch');
2894
2915
 
2895
- const changeStreamThunk = cb => {
2896
- pipeline = pipeline || [];
2897
- prepareDiscriminatorPipeline(pipeline, this.schema, 'fullDocument');
2898
- if (this.$__collection.buffer) {
2899
- this.$__collection.addQueue(() => {
2900
- if (this.closed) {
2901
- return;
2902
- }
2903
- const driverChangeStream = this.$__collection.watch(pipeline, options);
2904
- cb(null, driverChangeStream);
2905
- });
2906
- } else {
2907
- const driverChangeStream = this.$__collection.watch(pipeline, options);
2908
- cb(null, driverChangeStream);
2909
- }
2910
- };
2911
-
2912
2916
  options = options || {};
2917
+ const watchOptions = options?.hydrate !== undefined ?
2918
+ utils.omit(options, ['hydrate']) :
2919
+ { ...options };
2913
2920
  options.model = this;
2914
2921
 
2915
- return new ChangeStream(changeStreamThunk, pipeline, options);
2922
+
2923
+ pipeline = pipeline || [];
2924
+ prepareDiscriminatorPipeline(pipeline, this.schema, 'fullDocument');
2925
+
2926
+ const changeStreamPromise = this.db._waitForConnect().then(
2927
+ () => this.$__collection.watch(pipeline, watchOptions)
2928
+ );
2929
+
2930
+ return new ChangeStream(changeStreamPromise, pipeline, options);
2916
2931
  };
2917
2932
 
2918
2933
  /**
@@ -2935,8 +2950,8 @@ Model.watch = function(pipeline, options) {
2935
2950
  * // secondary that is experiencing replication lag.
2936
2951
  * doc = await Person.findOne({ name: 'Ned Stark' }, null, { session, readPreference: 'secondary' });
2937
2952
  *
2938
- * @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/4.9/classes/MongoClient.html#startSession)
2939
- * @param {Boolean} [options.causalConsistency=true] set to false to disable causal consistency
2953
+ * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html#startSession)
2954
+ * @param {boolean} [options.causalConsistency=true] set to false to disable causal consistency
2940
2955
  * @return {Promise<ClientSession>} promise that resolves to a MongoDB driver `ClientSession`
2941
2956
  * @api public
2942
2957
  */
@@ -2978,14 +2993,17 @@ Model.startSession = function() {
2978
2993
  * { name: 'The Empire Strikes Back' }
2979
2994
  * ], { rawResult: true });
2980
2995
  *
2981
- * @param {Array|Object|*} doc(s)
2982
- * @param {Object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#insertMany)
2983
- * @param {Boolean} [options.ordered=true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`.
2984
- * @param {Boolean} [options.rawResult=false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/InsertManyResult.html) with a `mongoose` property that contains `validationErrors` and `results` if this is an unordered `insertMany`.
2985
- * @param {Boolean} [options.lean=false] if `true`, skips hydrating the documents. This means Mongoose will **not** cast, validate, or apply defaults to any of the documents passed to `insertMany()`. This option is useful if you need the extra performance, but comes with data integrity risk. Consider using with [`castObject()`](https://mongoosejs.com/docs/api/model.html#Model.castObject()) and [`applyDefaults()`](https://mongoosejs.com/docs/api/model.html#Model.applyDefaults()).
2986
- * @param {Number} [options.limit=null] this limits the number of documents being processed (validation/casting) by mongoose in parallel, this does **NOT** send the documents in batches to MongoDB. Use this option if you're processing a large number of documents and your app is running out of memory.
2987
- * @param {String|Object|Array} [options.populate=null] populates the result documents. This option is a no-op if `rawResult` is set.
2988
- * @param {Boolean} [options.throwOnValidationError=false] If true and `ordered: false`, throw an error if one of the operations failed validation, but all valid operations completed successfully.
2996
+ * @param {Array|object|any} doc(s)
2997
+ * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#insertMany)
2998
+ * @param {boolean} [options.ordered=true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`.
2999
+ * @param {boolean} [options.rawResult=false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/InsertManyResult.html) with a `mongoose` property that contains `validationErrors` and `results` if this is an unordered `insertMany`.
3000
+ * @param {boolean} [options.lean=false] if `true`, skips hydrating the documents. This means Mongoose will **not** cast, validate, or apply defaults to any of the documents passed to `insertMany()`. This option is useful if you need the extra performance, but comes with data integrity risk. Consider using with [`castObject()`](https://mongoosejs.com/docs/api/model.html#Model.castObject()) and [`applyDefaults()`](https://mongoosejs.com/docs/api/model.html#Model.applyDefaults()).
3001
+ * @param {number} [options.limit=null] this limits the number of documents being processed (validation/casting) by mongoose in parallel, this does **NOT** send the documents in batches to MongoDB. Use this option if you're processing a large number of documents and your app is running out of memory.
3002
+ * @param {string|object|Array} [options.populate=null] populates the result documents. This option is a no-op if `rawResult` is set.
3003
+ * @param {boolean} [options.throwOnValidationError=false] If true and `ordered: false`, throw an error if one of the operations failed validation, but all valid operations completed successfully.
3004
+ * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware
3005
+ * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
3006
+ * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks
2989
3007
  * @return {Promise} resolving to the raw result from the MongoDB driver if `options.rawResult` was `true`, or the documents that passed validation, otherwise
2990
3008
  * @api public
2991
3009
  */
@@ -2997,37 +3015,16 @@ Model.insertMany = async function insertMany(arr, options) {
2997
3015
  throw new MongooseError('Model.insertMany() no longer accepts a callback');
2998
3016
  }
2999
3017
 
3000
- return new Promise((resolve, reject) => {
3001
- this.$__insertMany(arr, options, (err, res) => {
3002
- if (err != null) {
3003
- return reject(err);
3004
- }
3005
- resolve(res);
3006
- });
3007
- });
3008
- };
3009
-
3010
- /**
3011
- * ignore
3012
- *
3013
- * @param {Array} arr
3014
- * @param {Object} options
3015
- * @param {Function} callback
3016
- * @api private
3017
- * @memberOf Model
3018
- * @method $__insertMany
3019
- * @static
3020
- */
3018
+ options = options || {};
3019
+ const preFilter = buildMiddlewareFilter(options, 'pre');
3020
+ const postFilter = buildMiddlewareFilter(options, 'post');
3021
3021
 
3022
- Model.$__insertMany = function(arr, options, callback) {
3023
- const _this = this;
3024
- if (typeof options === 'function') {
3025
- callback = options;
3026
- options = null;
3022
+ try {
3023
+ [arr] = await this._middleware.execPre('insertMany', this, [arr], { filter: preFilter });
3024
+ } catch (error) {
3025
+ await this._middleware.execPost('insertMany', this, [arr], { error, filter: postFilter });
3027
3026
  }
3028
-
3029
- callback = callback || utils.noop;
3030
- options = options || {};
3027
+ const ThisModel = this;
3031
3028
  const limit = options.limit || 1000;
3032
3029
  const rawResult = !!options.rawResult;
3033
3030
  const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
@@ -3046,238 +3043,213 @@ Model.$__insertMany = function(arr, options, callback) {
3046
3043
  const validationErrors = [];
3047
3044
  const validationErrorsToOriginalOrder = new Map();
3048
3045
  const results = ordered ? null : new Array(arr.length);
3049
- const toExecute = arr.map((doc, index) =>
3050
- callback => {
3051
- // If option `lean` is set to true bypass validation and hydration
3052
- if (lean) {
3053
- // we have to execute callback at the nextTick to be compatible
3054
- // with parallelLimit, as `results` variable has TDZ issue if we
3055
- // execute the callback synchronously
3056
- return immediate(() => callback(null, doc));
3057
- }
3058
- let createdNewDoc = false;
3059
- if (!(doc instanceof _this)) {
3060
- if (doc != null && typeof doc !== 'object') {
3061
- return callback(new ObjectParameterError(doc, 'arr.' + index, 'insertMany'));
3062
- }
3063
- try {
3064
- doc = new _this(doc);
3065
- createdNewDoc = true;
3066
- } catch (err) {
3067
- return callback(err);
3068
- }
3046
+ async function validateDoc(doc, index) {
3047
+ // If option `lean` is set to true bypass validation and hydration
3048
+ if (lean) {
3049
+ return doc;
3050
+ }
3051
+ let createdNewDoc = false;
3052
+ if (!(doc instanceof ThisModel)) {
3053
+ if (doc != null && typeof doc !== 'object') {
3054
+ throw new ObjectParameterError(doc, 'arr.' + index, 'insertMany');
3069
3055
  }
3056
+ doc = new ThisModel(doc);
3057
+ createdNewDoc = true;
3058
+ }
3070
3059
 
3071
- if (options.session != null) {
3072
- doc.$session(options.session);
3073
- }
3074
- // If option `lean` is set to true bypass validation
3075
- if (lean) {
3076
- // we have to execute callback at the nextTick to be compatible
3077
- // with parallelLimit, as `results` variable has TDZ issue if we
3078
- // execute the callback synchronously
3079
- return immediate(() => callback(null, doc));
3080
- }
3081
- doc.$validate(createdNewDoc ? { _skipParallelValidateCheck: true } : null).then(
3082
- () => { callback(null, doc); },
3083
- error => {
3084
- if (ordered === false) {
3085
- // Add index to validation error so users can identify which document failed
3086
- error.index = index;
3087
- validationErrors.push(error);
3088
- validationErrorsToOriginalOrder.set(error, index);
3089
- results[index] = error;
3090
- return callback(null, null);
3091
- }
3092
- callback(error);
3060
+ if (options.session != null) {
3061
+ doc.$session(options.session);
3062
+ }
3063
+ return doc.$validate(createdNewDoc ? { _skipParallelValidateCheck: true } : null)
3064
+ .then(() => doc)
3065
+ .catch(error => {
3066
+ if (ordered === false) {
3067
+ error.index = index;
3068
+ validationErrors.push(error);
3069
+ validationErrorsToOriginalOrder.set(error, index);
3070
+ results[index] = error;
3071
+ return;
3093
3072
  }
3094
- );
3095
- });
3073
+ throw error;
3074
+ });
3075
+ }
3096
3076
 
3097
- parallelLimit(toExecute, limit, function(error, docs) {
3098
- if (error) {
3099
- callback(error, null);
3100
- return;
3101
- }
3077
+ const docs = await parallelLimit(arr, validateDoc, limit);
3102
3078
 
3103
- const originalDocIndex = new Map();
3104
- const validDocIndexToOriginalIndex = new Map();
3105
- for (let i = 0; i < docs.length; ++i) {
3106
- originalDocIndex.set(docs[i], i);
3107
- }
3079
+ const originalDocIndex = new Map();
3080
+ const validDocIndexToOriginalIndex = new Map();
3081
+ for (let i = 0; i < docs.length; ++i) {
3082
+ originalDocIndex.set(docs[i], i);
3083
+ }
3108
3084
 
3109
- // We filter all failed pre-validations by removing nulls
3110
- const docAttributes = docs.filter(function(doc) {
3111
- return doc != null;
3085
+ // We filter all failed pre-validations by removing nulls
3086
+ const docAttributes = docs.filter(function(doc) {
3087
+ return doc != null;
3088
+ });
3089
+ for (let i = 0; i < docAttributes.length; ++i) {
3090
+ validDocIndexToOriginalIndex.set(i, originalDocIndex.get(docAttributes[i]));
3091
+ }
3092
+
3093
+ // Make sure validation errors are in the same order as the
3094
+ // original documents, so if both doc1 and doc2 both fail validation,
3095
+ // `Model.insertMany([doc1, doc2])` will always have doc1's validation
3096
+ // error before doc2's. Re: gh-12791.
3097
+ if (validationErrors.length > 0) {
3098
+ validationErrors.sort((err1, err2) => {
3099
+ return validationErrorsToOriginalOrder.get(err1) - validationErrorsToOriginalOrder.get(err2);
3112
3100
  });
3113
- for (let i = 0; i < docAttributes.length; ++i) {
3114
- validDocIndexToOriginalIndex.set(i, originalDocIndex.get(docAttributes[i]));
3115
- }
3101
+ }
3116
3102
 
3117
- // Make sure validation errors are in the same order as the
3118
- // original documents, so if both doc1 and doc2 both fail validation,
3119
- // `Model.insertMany([doc1, doc2])` will always have doc1's validation
3120
- // error before doc2's. Re: gh-12791.
3121
- if (validationErrors.length > 0) {
3122
- validationErrors.sort((err1, err2) => {
3123
- return validationErrorsToOriginalOrder.get(err1) - validationErrorsToOriginalOrder.get(err2);
3124
- });
3103
+ // Quickly escape while there aren't any valid docAttributes
3104
+ if (docAttributes.length === 0) {
3105
+ if (throwOnValidationError) {
3106
+ throw new MongooseBulkWriteError(
3107
+ validationErrors,
3108
+ results,
3109
+ null,
3110
+ 'insertMany'
3111
+ );
3125
3112
  }
3126
-
3127
- // Quickly escape while there aren't any valid docAttributes
3128
- if (docAttributes.length === 0) {
3129
- if (throwOnValidationError) {
3130
- return callback(new MongooseBulkWriteError(
3131
- validationErrors,
3132
- results,
3133
- null,
3134
- 'insertMany'
3135
- ));
3136
- }
3137
- if (rawResult) {
3138
- const res = {
3139
- acknowledged: true,
3140
- insertedCount: 0,
3141
- insertedIds: {}
3142
- };
3143
- decorateBulkWriteResult(res, validationErrors, validationErrors);
3144
- return callback(null, res);
3145
- }
3146
- callback(null, []);
3147
- return;
3113
+ if (rawResult) {
3114
+ const res = {
3115
+ acknowledged: true,
3116
+ insertedCount: 0,
3117
+ insertedIds: {}
3118
+ };
3119
+ decorateBulkWriteResult(res, validationErrors, validationErrors);
3120
+ return res;
3148
3121
  }
3149
- const docObjects = lean ? docAttributes : docAttributes.map(function(doc) {
3150
- if (doc.$__schema.options.versionKey) {
3151
- doc[doc.$__schema.options.versionKey] = 0;
3152
- }
3153
- const shouldSetTimestamps = (!options || options.timestamps !== false) && doc.initializeTimestamps && (!doc.$__ || doc.$__.timestamps !== false);
3154
- if (shouldSetTimestamps) {
3155
- doc.initializeTimestamps();
3156
- }
3157
- if (doc.$__hasOnlyPrimitiveValues()) {
3158
- return doc.$__toObjectShallow();
3159
- }
3160
- return doc.toObject(internalToObjectOptions);
3161
- });
3162
-
3163
- _this.$__collection.insertMany(docObjects, options).then(
3164
- res => {
3165
- if (!lean) {
3166
- for (const attribute of docAttributes) {
3167
- attribute.$__reset();
3168
- _setIsNew(attribute, false);
3169
- }
3170
- }
3122
+ return [];
3123
+ }
3124
+ const docObjects = lean ? docAttributes : docAttributes.map(function(doc) {
3125
+ if (doc.$__schema.options.versionKey) {
3126
+ doc[doc.$__schema.options.versionKey] = 0;
3127
+ }
3128
+ const shouldSetTimestamps = options?.timestamps !== false && doc.initializeTimestamps && (!doc.$__ || doc.$__.timestamps !== false);
3129
+ if (shouldSetTimestamps) {
3130
+ doc.initializeTimestamps(options?.timestamps);
3131
+ }
3132
+ if (doc.$__hasOnlyPrimitiveValues()) {
3133
+ return doc.$__toObjectShallow();
3134
+ }
3135
+ return doc.toObject(internalToObjectOptions);
3136
+ });
3171
3137
 
3172
- if (ordered === false && throwOnValidationError && validationErrors.length > 0) {
3173
- for (let i = 0; i < results.length; ++i) {
3174
- if (results[i] === void 0) {
3175
- results[i] = docs[i];
3176
- }
3177
- }
3178
- return callback(new MongooseBulkWriteError(
3179
- validationErrors,
3180
- results,
3181
- res,
3182
- 'insertMany'
3183
- ));
3184
- }
3138
+ let res;
3139
+ try {
3140
+ res = await this.$__collection.insertMany(docObjects, options);
3141
+ } catch (error) {
3142
+ // `writeErrors` is a property reported by the MongoDB driver,
3143
+ // just not if there's only 1 error.
3144
+ if (error.writeErrors == null &&
3145
+ error.result?.result?.writeErrors != null) {
3146
+ error.writeErrors = error.result.result.writeErrors;
3147
+ }
3185
3148
 
3186
- if (rawResult) {
3187
- if (ordered === false) {
3188
- for (let i = 0; i < results.length; ++i) {
3189
- if (results[i] === void 0) {
3190
- results[i] = docs[i];
3191
- }
3192
- }
3149
+ // `insertedDocs` is a Mongoose-specific property
3150
+ const hasWriteErrors = error?.writeErrors;
3151
+ const erroredIndexes = new Set((error?.writeErrors || []).map(err => err.index));
3193
3152
 
3194
- // Decorate with mongoose validation errors in case of unordered,
3195
- // because then still do `insertMany()`
3196
- decorateBulkWriteResult(res, validationErrors, results);
3197
- }
3198
- return callback(null, res);
3153
+ if (error.writeErrors != null) {
3154
+ for (let i = 0; i < error.writeErrors.length; ++i) {
3155
+ const originalIndex = validDocIndexToOriginalIndex.get(error.writeErrors[i].index);
3156
+ error.writeErrors[i] = { ...error.writeErrors[i], index: originalIndex };
3157
+ if (!ordered) {
3158
+ results[originalIndex] = error.writeErrors[i];
3199
3159
  }
3160
+ }
3161
+ }
3200
3162
 
3201
- if (options.populate != null) {
3202
- return _this.populate(docAttributes, options.populate).then(
3203
- docs => { callback(null, docs); },
3204
- err => {
3205
- if (err != null) {
3206
- err.insertedDocs = docAttributes;
3207
- }
3208
- throw err;
3209
- }
3210
- );
3163
+ if (!ordered) {
3164
+ for (let i = 0; i < results.length; ++i) {
3165
+ if (results[i] === void 0) {
3166
+ results[i] = docs[i];
3211
3167
  }
3168
+ }
3212
3169
 
3213
- callback(null, docAttributes);
3214
- },
3215
- error => {
3216
- // `writeErrors` is a property reported by the MongoDB driver,
3217
- // just not if there's only 1 error.
3218
- if (error.writeErrors == null &&
3219
- (error.result && error.result.result && error.result.result.writeErrors) != null) {
3220
- error.writeErrors = error.result.result.writeErrors;
3221
- }
3170
+ error.results = results;
3171
+ }
3222
3172
 
3223
- // `insertedDocs` is a Mongoose-specific property
3224
- const hasWriteErrors = error && error.writeErrors;
3225
- const erroredIndexes = new Set((error && error.writeErrors || []).map(err => err.index));
3173
+ let firstErroredIndex = -1;
3174
+ error.insertedDocs = docAttributes.
3175
+ filter((doc, i) => {
3176
+ const isErrored = !hasWriteErrors || erroredIndexes.has(i);
3226
3177
 
3227
- if (error.writeErrors != null) {
3228
- for (let i = 0; i < error.writeErrors.length; ++i) {
3229
- const originalIndex = validDocIndexToOriginalIndex.get(error.writeErrors[i].index);
3230
- error.writeErrors[i] = { ...error.writeErrors[i], index: originalIndex };
3231
- if (!ordered) {
3232
- results[originalIndex] = error.writeErrors[i];
3233
- }
3178
+ if (ordered) {
3179
+ if (firstErroredIndex > -1) {
3180
+ return i < firstErroredIndex;
3234
3181
  }
3235
- }
3236
3182
 
3237
- if (!ordered) {
3238
- for (let i = 0; i < results.length; ++i) {
3239
- if (results[i] === void 0) {
3240
- results[i] = docs[i];
3241
- }
3183
+ if (isErrored) {
3184
+ firstErroredIndex = i;
3242
3185
  }
3186
+ }
3243
3187
 
3244
- error.results = results;
3188
+ return !isErrored;
3189
+ }).
3190
+ map(function setIsNewForInsertedDoc(doc) {
3191
+ if (lean) {
3192
+ return doc;
3245
3193
  }
3194
+ doc.$__reset();
3195
+ _setIsNew(doc, false);
3196
+ return doc;
3197
+ });
3246
3198
 
3247
- let firstErroredIndex = -1;
3248
- error.insertedDocs = docAttributes.
3249
- filter((doc, i) => {
3250
- const isErrored = !hasWriteErrors || erroredIndexes.has(i);
3199
+ if (rawResult && ordered === false) {
3200
+ decorateBulkWriteResult(error, validationErrors, results);
3201
+ }
3251
3202
 
3252
- if (ordered) {
3253
- if (firstErroredIndex > -1) {
3254
- return i < firstErroredIndex;
3255
- }
3203
+ await this._middleware.execPost('insertMany', this, [arr], { error, filter: postFilter });
3204
+ }
3256
3205
 
3257
- if (isErrored) {
3258
- firstErroredIndex = i;
3259
- }
3260
- }
3206
+ if (!lean) {
3207
+ for (const attribute of docAttributes) {
3208
+ attribute.$__reset();
3209
+ _setIsNew(attribute, false);
3210
+ }
3211
+ }
3261
3212
 
3262
- return !isErrored;
3263
- }).
3264
- map(function setIsNewForInsertedDoc(doc) {
3265
- if (lean) {
3266
- return doc;
3267
- }
3268
- doc.$__reset();
3269
- _setIsNew(doc, false);
3270
- return doc;
3271
- });
3213
+ if (ordered === false && throwOnValidationError && validationErrors.length > 0) {
3214
+ for (let i = 0; i < results.length; ++i) {
3215
+ if (results[i] === void 0) {
3216
+ results[i] = docs[i];
3217
+ }
3218
+ }
3219
+ throw new MongooseBulkWriteError(
3220
+ validationErrors,
3221
+ results,
3222
+ res,
3223
+ 'insertMany'
3224
+ );
3225
+ }
3272
3226
 
3273
- if (rawResult && ordered === false) {
3274
- decorateBulkWriteResult(error, validationErrors, results);
3227
+ if (rawResult) {
3228
+ if (ordered === false) {
3229
+ for (let i = 0; i < results.length; ++i) {
3230
+ if (results[i] === void 0) {
3231
+ results[i] = docs[i];
3275
3232
  }
3233
+ }
3234
+
3235
+ // Decorate with mongoose validation errors in case of unordered,
3236
+ // because then still do `insertMany()`
3237
+ decorateBulkWriteResult(res, validationErrors, results);
3238
+ }
3239
+ return res;
3240
+ }
3276
3241
 
3277
- callback(error, null);
3242
+ if (options.populate != null) {
3243
+ return this.populate(docAttributes, options.populate).catch(err => {
3244
+ if (err != null) {
3245
+ err.insertedDocs = docAttributes;
3278
3246
  }
3279
- );
3280
- });
3247
+ throw err;
3248
+ });
3249
+ }
3250
+
3251
+ const [result] = await this._middleware.execPost('insertMany', this, [docAttributes], { filter: postFilter });
3252
+ return result;
3281
3253
  };
3282
3254
 
3283
3255
  /*!
@@ -3359,38 +3331,43 @@ function _setIsNew(doc, val) {
3359
3331
  * - `replaceOne`
3360
3332
  *
3361
3333
  * @param {Array} ops
3362
- * @param {Object} [ops.insertOne.document] The document to insert
3363
- * @param {Object} [ops.insertOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3364
- * @param {Object} [ops.updateOne.filter] Update the first document that matches this filter
3365
- * @param {Object} [ops.updateOne.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/)
3366
- * @param {Boolean} [ops.updateOne.upsert=false] If true, insert a doc if none match
3367
- * @param {Boolean} [ops.updateOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3368
- * @param {Object} [ops.updateOne.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use
3334
+ * @param {object} [ops.insertOne.document] The document to insert
3335
+ * @param {object} [ops.insertOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3336
+ * @param {object} [ops.updateOne.filter] Update the first document that matches this filter
3337
+ * @param {object} [ops.updateOne.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/)
3338
+ * @param {boolean} [ops.updateOne.upsert=false] If true, insert a doc if none match
3339
+ * @param {boolean} [ops.updateOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3340
+ * @param {boolean} [ops.updateOne.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 {object} [ops.updateOne.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use
3369
3342
  * @param {Array} [ops.updateOne.arrayFilters] The [array filters](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html) used in `update`
3370
- * @param {Object} [ops.updateMany.filter] Update all the documents that match this filter
3371
- * @param {Object} [ops.updateMany.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/)
3372
- * @param {Boolean} [ops.updateMany.upsert=false] If true, insert a doc if no documents match `filter`
3373
- * @param {Boolean} [ops.updateMany.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3374
- * @param {Object} [ops.updateMany.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use
3343
+ * @param {object} [ops.updateMany.filter] Update all the documents that match this filter
3344
+ * @param {object} [ops.updateMany.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/)
3345
+ * @param {boolean} [ops.updateMany.upsert=false] If true, insert a doc if no documents match `filter`
3346
+ * @param {boolean} [ops.updateMany.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3347
+ * @param {boolean} [ops.updateMany.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.
3348
+ * @param {object} [ops.updateMany.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use
3375
3349
  * @param {Array} [ops.updateMany.arrayFilters] The [array filters](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html) used in `update`
3376
- * @param {Object} [ops.deleteOne.filter] Delete the first document that matches this filter
3377
- * @param {Object} [ops.deleteMany.filter] Delete all documents that match this filter
3378
- * @param {Object} [ops.replaceOne.filter] Replace the first document that matches this filter
3379
- * @param {Object} [ops.replaceOne.replacement] The replacement document
3380
- * @param {Boolean} [ops.replaceOne.upsert=false] If true, insert a doc if no documents match `filter`
3381
- * @param {Object} [ops.replaceOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3382
- * @param {Object} [options]
3383
- * @param {Boolean} [options.ordered=true] If true, execute writes in order and stop at the first error. If false, execute writes in parallel and continue until all writes have either succeeded or errored.
3384
- * @param {Boolean} [options.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to any operations. Can be overridden at the operation-level.
3350
+ * @param {object} [ops.deleteOne.filter] Delete the first document that matches this filter
3351
+ * @param {object} [ops.deleteMany.filter] Delete all documents that match this filter
3352
+ * @param {object} [ops.replaceOne.filter] Replace the first document that matches this filter
3353
+ * @param {object} [ops.replaceOne.replacement] The replacement document
3354
+ * @param {boolean} [ops.replaceOne.upsert=false] If true, insert a doc if no documents match `filter`
3355
+ * @param {object} [ops.replaceOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation
3356
+ * @param {object} [options]
3357
+ * @param {boolean} [options.ordered=true] If true, execute writes in order and stop at the first error. If false, execute writes in parallel and continue until all writes have either succeeded or errored.
3358
+ * @param {boolean} [options.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to any operations. Can be overridden at the operation-level.
3385
3359
  * @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
3386
- * @param {String|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information.
3360
+ * @param {string|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information.
3387
3361
  * @param {number} [options.wtimeout=null] The [write concern timeout](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout).
3388
- * @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option)
3389
- * @param {Boolean} [options.skipValidation=false] Set to true to skip Mongoose schema validation on bulk write operations. Mongoose currently runs validation on `insertOne` and `replaceOne` operations by default.
3390
- * @param {Boolean} [options.bypassDocumentValidation=false] If true, disable [MongoDB server-side schema validation](https://www.mongodb.com/docs/manual/core/schema-validation/) for all writes in this bulk.
3391
- * @param {Boolean} [options.throwOnValidationError=false] If true and `ordered: false`, throw an error if one of the operations failed validation, but all valid operations completed successfully. Note that Mongoose will still send all valid operations to the MongoDB server.
3392
- * @param {Boolean|"throw"} [options.strict=null] Overwrites the [`strict` option](https://mongoosejs.com/docs/guide.html#strict) on schema. If false, allows filtering and writing fields not defined in the schema for all writes in this bulk.
3393
- * @return {Promise} resolves to a [`BulkWriteOpResult`](https://mongodb.github.io/node-mongodb-native/4.9/classes/BulkWriteResult.html) if the operation succeeds
3362
+ * @param {boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option)
3363
+ * @param {boolean} [options.skipValidation=false] Set to true to skip Mongoose schema validation on bulk write operations. Mongoose currently runs validation on `insertOne` and `replaceOne` operations by default.
3364
+ * @param {boolean} [options.bypassDocumentValidation=false] If true, disable [MongoDB server-side schema validation](https://www.mongodb.com/docs/manual/core/schema-validation/) for all writes in this bulk.
3365
+ * @param {boolean} [options.throwOnValidationError=false] If true and `ordered: false`, throw an error if one of the operations failed validation, but all valid operations completed successfully. Note that Mongoose will still send all valid operations to the MongoDB server.
3366
+ * @param {boolean|"throw"} [options.strict=null] Overwrites the [`strict` option](https://mongoosejs.com/docs/guide.html#strict) on schema. If false, allows filtering and writing fields not defined in the schema for all writes in this bulk.
3367
+ * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware
3368
+ * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
3369
+ * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks
3370
+ * @return {Promise} resolves to a [`BulkWriteOpResult`](https://mongodb.github.io/node-mongodb-native/7.0/classes/BulkWriteResult.html) if the operation succeeds
3394
3371
  * @api public
3395
3372
  */
3396
3373
 
@@ -3402,21 +3379,21 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3402
3379
  throw new MongooseError('Model.bulkWrite() no longer accepts a callback');
3403
3380
  }
3404
3381
  options = options || {};
3382
+ const preFilter = buildMiddlewareFilter(options, 'pre');
3383
+ const postFilter = buildMiddlewareFilter(options, 'post');
3405
3384
 
3406
- const shouldSkip = await new Promise((resolve, reject) => {
3407
- this.hooks.execPre('bulkWrite', this, [ops, options], (err) => {
3408
- if (err != null) {
3409
- if (err instanceof Kareem.skipWrappedFunction) {
3410
- return resolve(err);
3411
- }
3412
- return reject(err);
3413
- }
3414
- resolve();
3415
- });
3416
- });
3385
+ try {
3386
+ [ops, options] = await this.hooks.execPre('bulkWrite', this, [ops, options], { filter: preFilter });
3387
+ } catch (err) {
3388
+ if (err instanceof Kareem.skipWrappedFunction) {
3389
+ ops = err;
3390
+ } else {
3391
+ await this.hooks.execPost('bulkWrite', this, [null], { error: err, filter: postFilter });
3392
+ }
3393
+ }
3417
3394
 
3418
- if (shouldSkip) {
3419
- return shouldSkip.args[0];
3395
+ if (ops instanceof Kareem.skipWrappedFunction) {
3396
+ return ops.args[0];
3420
3397
  }
3421
3398
 
3422
3399
  const ordered = options.ordered == null ? true : options.ordered;
@@ -3450,15 +3427,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3450
3427
  try {
3451
3428
  res = await this.$__collection.bulkWrite(ops, options);
3452
3429
  } catch (error) {
3453
- await new Promise((resolve, reject) => {
3454
- const _opts = { error: error };
3455
- this.hooks.execPost('bulkWrite', this, [null], _opts, (err) => {
3456
- if (err != null) {
3457
- return reject(err);
3458
- }
3459
- resolve();
3460
- });
3461
- });
3430
+ await this.hooks.execPost('bulkWrite', this, [null], { error, filter: postFilter });
3462
3431
  }
3463
3432
  } else {
3464
3433
  let validOpIndexes = [];
@@ -3490,7 +3459,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3490
3459
  sort((v1, v2) => v1.index - v2.index).
3491
3460
  map(v => v.error);
3492
3461
 
3493
- const validOps = validOpIndexes.sort().map(index => ops[index]);
3462
+ const validOps = validOpIndexes.sort((a, b) => a - b).map(index => ops[index]);
3494
3463
 
3495
3464
  if (validOps.length === 0) {
3496
3465
  if (options.throwOnValidationError && validationErrors.length) {
@@ -3527,15 +3496,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3527
3496
  decorateBulkWriteResult(error, validationErrors, results);
3528
3497
  }
3529
3498
 
3530
- await new Promise((resolve, reject) => {
3531
- const _opts = { error: error };
3532
- this.hooks.execPost('bulkWrite', this, [null], _opts, (err) => {
3533
- if (err != null) {
3534
- return reject(err);
3535
- }
3536
- resolve();
3537
- });
3538
- });
3499
+ await this.hooks.execPost('bulkWrite', this, [null], { error, filter: postFilter });
3539
3500
  }
3540
3501
 
3541
3502
  if (validationErrors.length > 0) {
@@ -3552,14 +3513,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3552
3513
  }
3553
3514
  }
3554
3515
 
3555
- await new Promise((resolve, reject) => {
3556
- this.hooks.execPost('bulkWrite', this, [res], (err) => {
3557
- if (err != null) {
3558
- return reject(err);
3559
- }
3560
- resolve();
3561
- });
3562
- });
3516
+ await this.hooks.execPost('bulkWrite', this, [res], { filter: postFilter });
3563
3517
 
3564
3518
  return res;
3565
3519
  };
@@ -3578,14 +3532,17 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3578
3532
  *
3579
3533
  * Note that `bulkSave()` will **not** throw an error if only some of the `save()` calls succeeded.
3580
3534
  *
3581
- * @param {Array<Document>} documents
3582
- * @param {Object} [options] options passed to the underlying `bulkWrite()`
3583
- * @param {Boolean} [options.timestamps] defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
3535
+ * @param {Document[]} documents
3536
+ * @param {object} [options] options passed to the underlying `bulkWrite()`
3537
+ * @param {boolean} [options.timestamps] defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
3584
3538
  * @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
3585
- * @param {String|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information.
3539
+ * @param {string|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information.
3586
3540
  * @param {number} [options.wtimeout=null] The [write concern timeout](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout).
3587
- * @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option)
3588
- * @param {Boolean} [options.validateBeforeSave=true] set to `false` to skip Mongoose validation on all documents
3541
+ * @param {boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option)
3542
+ * @param {boolean} [options.validateBeforeSave=true] set to `false` to skip Mongoose validation on all documents
3543
+ * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware
3544
+ * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
3545
+ * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks
3589
3546
  * @return {BulkWriteResult} the return value from `bulkWrite()`
3590
3547
  */
3591
3548
  Model.bulkSave = async function bulkSave(documents, options) {
@@ -3631,7 +3588,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
3631
3588
  const successfulDocuments = [];
3632
3589
  for (let i = 0; i < documents.length; i++) {
3633
3590
  const document = documents[i];
3634
- const documentError = bulkWriteError && bulkWriteError.writeErrors.find(writeError => {
3591
+ const documentError = bulkWriteError?.writeErrors.find(writeError => {
3635
3592
  const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id;
3636
3593
  return writeErrorDocumentId.toString() === document._doc._id.toString();
3637
3594
  });
@@ -3640,7 +3597,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
3640
3597
  successfulDocuments.push(document);
3641
3598
  }
3642
3599
  }
3643
- await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document)));
3600
+ await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document, options)));
3644
3601
 
3645
3602
  if (bulkWriteError != null) {
3646
3603
  throw bulkWriteError;
@@ -3649,43 +3606,30 @@ Model.bulkSave = async function bulkSave(documents, options) {
3649
3606
  return bulkWriteResult;
3650
3607
  };
3651
3608
 
3652
- function buildPreSavePromise(document, options) {
3653
- return new Promise((resolve, reject) => {
3654
- document.schema.s.hooks.execPre('save', document, [options], (err) => {
3655
- if (err) {
3656
- reject(err);
3657
- return;
3658
- }
3659
- resolve();
3660
- });
3661
- });
3609
+ async function buildPreSavePromise(document, options) {
3610
+ const preFilter = buildMiddlewareFilter(options, 'pre');
3611
+ const [newOptions] = await document.schema.s.hooks.execPre('save', document, [options], { filter: preFilter });
3612
+ if (newOptions !== options) {
3613
+ throw new MongooseError('Cannot overwrite options in pre("save") hook on bulkSave()');
3614
+ }
3662
3615
  }
3663
3616
 
3664
- function handleSuccessfulWrite(document) {
3665
- return new Promise((resolve, reject) => {
3666
- if (document.$isNew) {
3667
- _setIsNew(document, false);
3668
- }
3669
-
3670
- document.$__reset();
3671
- document._applyVersionIncrement();
3672
-
3673
- document.schema.s.hooks.execPost('save', document, [document], {}, (err) => {
3674
- if (err) {
3675
- reject(err);
3676
- return;
3677
- }
3678
- resolve();
3679
- });
3617
+ async function handleSuccessfulWrite(document, options) {
3618
+ if (document.$isNew) {
3619
+ _setIsNew(document, false);
3620
+ }
3680
3621
 
3681
- });
3622
+ document.$__reset();
3623
+ document._applyVersionIncrement();
3624
+ const postFilter = buildMiddlewareFilter(options, 'post');
3625
+ return document.schema.s.hooks.execPost('save', document, [document], { filter: postFilter });
3682
3626
  }
3683
3627
 
3684
3628
  /**
3685
3629
  * Apply defaults to the given document or POJO.
3686
3630
  *
3687
- * @param {Object|Document} obj object or document to apply defaults on
3688
- * @returns {Object|Document}
3631
+ * @param {object|Document} obj object or document to apply defaults on
3632
+ * @returns {object|Document}
3689
3633
  * @api public
3690
3634
  */
3691
3635
 
@@ -3722,9 +3666,9 @@ Model.applyDefaults = function applyDefaults(doc) {
3722
3666
  * obj.name; // 'John'
3723
3667
  * obj.upper; // 'JOHN', Mongoose applied the return value of the virtual to the given object
3724
3668
  *
3725
- * @param {Object} obj object or document to apply virtuals on
3726
- * @param {Array<string>} [virtualsToApply] optional whitelist of virtuals to apply
3727
- * @returns {Object} obj
3669
+ * @param {object} obj object or document to apply virtuals on
3670
+ * @param {string[]} [virtualsToApply] optional whitelist of virtuals to apply
3671
+ * @returns {object} obj
3728
3672
  * @api public
3729
3673
  */
3730
3674
 
@@ -3755,11 +3699,11 @@ Model.applyVirtuals = function applyVirtuals(obj, virtualsToApply) {
3755
3699
  * obj.createdAt; // 2024-06-01T18:00:00.000Z
3756
3700
  * obj.updatedAt; // 2024-06-01T18:00:00.000Z
3757
3701
  *
3758
- * @param {Object} obj object or document to apply virtuals on
3759
- * @param {Object} [options]
3760
- * @param {Boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt
3702
+ * @param {object} obj object or document to apply virtuals on
3703
+ * @param {object} [options]
3704
+ * @param {boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt
3761
3705
  * @param {Function} [options.currentTime] if set, Mongoose will call this function to get the current time.
3762
- * @returns {Object} obj
3706
+ * @returns {object} obj
3763
3707
  * @api public
3764
3708
  */
3765
3709
 
@@ -3789,10 +3733,10 @@ Model.applyTimestamps = function applyTimestamps(obj, options) {
3789
3733
  *
3790
3734
  * Test.castObject({ num: 'not a number' }); // Throws a ValidationError
3791
3735
  *
3792
- * @param {Object} obj object or document to cast
3793
- * @param {Object} options options passed to castObject
3794
- * @param {Boolean} options.ignoreCastErrors If set to `true` will not throw a ValidationError and only return values that were successfully cast.
3795
- * @returns {Object} POJO casted to the model's schema
3736
+ * @param {object} obj object or document to cast
3737
+ * @param {object} options options passed to castObject
3738
+ * @param {boolean} options.ignoreCastErrors If set to `true` will not throw a ValidationError and only return values that were successfully cast.
3739
+ * @returns {object} POJO casted to the model's schema
3796
3740
  * @throws {ValidationError} if casting failed for at least one path
3797
3741
  * @api public
3798
3742
  */
@@ -3810,7 +3754,7 @@ Model.castObject = function castObject(obj, options) {
3810
3754
 
3811
3755
  for (const path of paths) {
3812
3756
  const schemaType = schema.path(path);
3813
- if (!schemaType || !schemaType.$isMongooseArray) {
3757
+ if (!schemaType?.$isMongooseArray) {
3814
3758
  continue;
3815
3759
  }
3816
3760
 
@@ -3851,7 +3795,7 @@ Model.castObject = function castObject(obj, options) {
3851
3795
  }
3852
3796
  } else {
3853
3797
  cur[pieces[pieces.length - 1]] = [
3854
- Model.castObject.call(schemaType.caster, val)
3798
+ Model.castObject.call(schemaType.Constructor, val)
3855
3799
  ];
3856
3800
  }
3857
3801
 
@@ -3860,7 +3804,7 @@ Model.castObject = function castObject(obj, options) {
3860
3804
  }
3861
3805
  if (schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) {
3862
3806
  try {
3863
- val = Model.castObject.call(schemaType.caster, val);
3807
+ val = Model.castObject.call(schemaType.Constructor, val);
3864
3808
  } catch (err) {
3865
3809
  if (!options.ignoreCastErrors) {
3866
3810
  error = error || new ValidationError();
@@ -3896,17 +3840,17 @@ Model.castObject = function castObject(obj, options) {
3896
3840
  /**
3897
3841
  * Build bulk write operations for `bulkSave()`.
3898
3842
  *
3899
- * @param {Array<Document>} documents The array of documents to build write operations of
3900
- * @param {Object} options
3901
- * @param {Boolean} options.skipValidation defaults to `false`, when set to true, building the write operations will bypass validating the documents.
3902
- * @param {Boolean} options.timestamps defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
3903
- * @return {Array<Promise>} Returns a array of all Promises the function executes to be awaited.
3843
+ * @param {Document[]} documents The array of documents to build write operations of
3844
+ * @param {object} options
3845
+ * @param {boolean} options.skipValidation defaults to `false`, when set to true, building the write operations will bypass validating the documents.
3846
+ * @param {boolean} options.timestamps defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
3847
+ * @return {Promise[]} Returns a array of all Promises the function executes to be awaited.
3904
3848
  * @api private
3905
3849
  */
3906
3850
 
3907
3851
  Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, options) {
3908
3852
  if (!Array.isArray(documents)) {
3909
- throw new Error(`bulkSave expects an array of documents to be passed, received \`${documents}\` instead`);
3853
+ throw new MongooseError(`bulkSave expects an array of documents to be passed, received \`${documents}\` instead`);
3910
3854
  }
3911
3855
 
3912
3856
  setDefaultOptions();
@@ -3914,7 +3858,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
3914
3858
  const writeOperations = documents.map((document, i) => {
3915
3859
  if (!options.skipValidation) {
3916
3860
  if (!(document instanceof Document)) {
3917
- throw new Error(`documents.${i} was not a mongoose document, documents must be an array of mongoose documents (instanceof mongoose.Document).`);
3861
+ throw new MongooseError(`documents.${i} was not a mongoose document, documents must be an array of mongoose documents (instanceof mongoose.Document).`);
3918
3862
  }
3919
3863
  if (options.validateBeforeSave == null || options.validateBeforeSave) {
3920
3864
  const err = document.validateSync();
@@ -3981,18 +3925,18 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
3981
3925
  * // hydrate previous data into a Mongoose document
3982
3926
  * const mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c', type: 'jelly bean' });
3983
3927
  *
3984
- * @param {Object} obj
3985
- * @param {Object|String|String[]} [projection] optional projection containing which fields should be selected for this document
3986
- * @param {Object} [options] optional options
3987
- * @param {Boolean} [options.setters=false] if true, apply schema setters when hydrating
3988
- * @param {Boolean} [options.hydratedPopulatedDocs=false] if true, populates the docs if passing pre-populated data
3989
- * @param {Boolean} [options.virtuals=false] if true, sets any virtuals present on `obj`
3990
- * @param {Boolean|String} [options.strict=false] configure strict mode for the hydrated document. In particular, if strict is false, fields not in the schema won't be stripped out; if strict is 'throw', `hydrate()` will throw an error if there are any fields that are not in the schema. Defaults to true (silently strip out fields not in the schema).
3928
+ * @param {object} obj
3929
+ * @param {object|string|string[]} [projection] optional projection containing which fields should be selected for this document
3930
+ * @param {object} [options] optional options
3931
+ * @param {boolean} [options.setters=false] if true, apply schema setters when hydrating
3932
+ * @param {boolean} [options.hydratedPopulatedDocs=false] if true, populates the docs if passing pre-populated data
3933
+ * @param {boolean} [options.virtuals=false] if true, sets any virtuals present on `obj`
3934
+ * @param {boolean|'throw'} [options.strict=false] configure strict mode for the hydrated document. In particular, if strict is false, fields not in the schema won't be stripped out; if strict is 'throw', `hydrate()` will throw an error if there are any fields that are not in the schema. Defaults to true (silently strip out fields not in the schema).
3991
3935
  * @return {Document} document instance
3992
3936
  * @api public
3993
3937
  */
3994
3938
 
3995
- Model.hydrate = function(obj, projection, options) {
3939
+ Model.hydrate = function hydrate(obj, projection, options) {
3996
3940
  _checkContext(this, 'hydrate');
3997
3941
 
3998
3942
  if (options?.virtuals && options?.hydratedPopulatedDocs === false) {
@@ -4000,9 +3944,10 @@ Model.hydrate = function(obj, projection, options) {
4000
3944
  }
4001
3945
 
4002
3946
  if (projection != null) {
4003
- if (obj != null && obj.$__ != null) {
3947
+ if (obj?.$__ != null) {
4004
3948
  obj = obj.toObject(internalToObjectOptions);
4005
3949
  }
3950
+ projection = parseProjection(projection);
4006
3951
  obj = applyProjection(obj, projection);
4007
3952
  }
4008
3953
  const document = require('./queryHelpers').createModel(this, obj, projection, projection, options);
@@ -4035,19 +3980,19 @@ Model.hydrate = function(obj, projection, options) {
4035
3980
  *
4036
3981
  * - `updateMany()`
4037
3982
  *
4038
- * @param {Object} filter
4039
- * @param {Object|Array} update. If array, this update will be treated as an update pipeline and not casted.
4040
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
4041
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4042
- * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
4043
- * @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)
4044
- * @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.
4045
- * @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.
4046
- * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
3983
+ * @param {object} filter
3984
+ * @param {object|Array} update. If array, this update will be treated as an update pipeline and not casted.
3985
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
3986
+ * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3987
+ * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document
3988
+ * @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)
3989
+ * @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.
3990
+ * @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.
3991
+ * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
4047
3992
  * @return {Query}
4048
3993
  * @see Query docs https://mongoosejs.com/docs/queries.html
4049
3994
  * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output
4050
- * @see UpdateResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/UpdateResult.html
3995
+ * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html
4051
3996
  * @api public
4052
3997
  */
4053
3998
 
@@ -4083,19 +4028,19 @@ Model.updateMany = function updateMany(conditions, update, options) {
4083
4028
  *
4084
4029
  * - `updateOne()`
4085
4030
  *
4086
- * @param {Object} filter
4087
- * @param {Object|Array} update. If array, this update will be treated as an update pipeline and not casted.
4088
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
4089
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4090
- * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
4091
- * @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)
4092
- * @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.
4093
- * @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.
4094
- * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
4031
+ * @param {object} filter
4032
+ * @param {object|Array} update. If array, this update will be treated as an update pipeline and not casted.
4033
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
4034
+ * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4035
+ * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document
4036
+ * @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)
4037
+ * @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.
4038
+ * @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.
4039
+ * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
4095
4040
  * @return {Query}
4096
4041
  * @see Query docs https://mongoosejs.com/docs/queries.html
4097
4042
  * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output
4098
- * @see UpdateResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/UpdateResult.html
4043
+ * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html
4099
4044
  * @api public
4100
4045
  */
4101
4046
 
@@ -4121,17 +4066,17 @@ Model.updateOne = function updateOne(conditions, doc, options) {
4121
4066
  *
4122
4067
  * - `replaceOne()`
4123
4068
  *
4124
- * @param {Object} filter
4125
- * @param {Object} doc
4126
- * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
4127
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4128
- * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
4129
- * @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)
4130
- * @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.
4131
- * @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.
4069
+ * @param {object} filter
4070
+ * @param {object} doc
4071
+ * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
4072
+ * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4073
+ * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document
4074
+ * @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)
4075
+ * @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.
4076
+ * @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.
4132
4077
  * @return {Query}
4133
4078
  * @see Query docs https://mongoosejs.com/docs/queries.html
4134
- * @see UpdateResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/UpdateResult.html
4079
+ * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html
4135
4080
  * @return {Query}
4136
4081
  * @api public
4137
4082
  */
@@ -4139,7 +4084,7 @@ Model.updateOne = function updateOne(conditions, doc, options) {
4139
4084
  Model.replaceOne = function replaceOne(conditions, doc, options) {
4140
4085
  _checkContext(this, 'replaceOne');
4141
4086
 
4142
- const versionKey = this && this.schema && this.schema.options && this.schema.options.versionKey || null;
4087
+ const versionKey = this?.schema?.options?.versionKey || null;
4143
4088
  if (versionKey && !doc[versionKey]) {
4144
4089
  doc[versionKey] = 0;
4145
4090
  }
@@ -4165,10 +4110,7 @@ function _update(model, op, conditions, doc, options) {
4165
4110
  }
4166
4111
  options = typeof options === 'function' ? options : clone(options);
4167
4112
 
4168
- const versionKey = model &&
4169
- model.schema &&
4170
- model.schema.options &&
4171
- model.schema.options.versionKey || null;
4113
+ const versionKey = model?.schema?.options?.versionKey ?? null;
4172
4114
  decorateUpdateWithVersionKey(doc, options, versionKey);
4173
4115
 
4174
4116
  return mq[op](conditions, doc, options);
@@ -4214,7 +4156,7 @@ function _update(model, op, conditions, doc, options) {
4214
4156
  * @see Aggregate https://mongoosejs.com/docs/api/aggregate.html#Aggregate()
4215
4157
  * @see MongoDB https://www.mongodb.com/docs/manual/applications/aggregation/
4216
4158
  * @param {Array} [pipeline] aggregation pipeline as an array of objects
4217
- * @param {Object} [options] aggregation options
4159
+ * @param {object} [options] aggregation options
4218
4160
  * @return {Aggregate}
4219
4161
  * @api public
4220
4162
  */
@@ -4253,10 +4195,10 @@ Model.aggregate = function aggregate(pipeline, options) {
4253
4195
  * Object.keys(err.errors); // ['name']
4254
4196
  * }
4255
4197
  *
4256
- * @param {Object} obj
4257
- * @param {Object|Array|String} pathsOrOptions
4258
- * @param {Object} [context]
4259
- * @return {Promise<Object>} casted and validated copy of `obj` if validation succeeded
4198
+ * @param {object} obj
4199
+ * @param {object|Array|string} pathsOrOptions
4200
+ * @param {object} [context]
4201
+ * @return {Promise<object>} casted and validated copy of `obj` if validation succeeded
4260
4202
  * @api public
4261
4203
  */
4262
4204
 
@@ -4307,7 +4249,7 @@ Model.validate = async function validate(obj, pathsOrOptions, context) {
4307
4249
 
4308
4250
  for (const path of paths) {
4309
4251
  const schemaType = schema.path(path);
4310
- if (!schemaType || !schemaType.$isMongooseArray || schemaType.$isMongooseDocumentArray) {
4252
+ if (!schemaType?.$isMongooseArray || schemaType.$isMongooseDocumentArray) {
4311
4253
  continue;
4312
4254
  }
4313
4255
 
@@ -4327,51 +4269,34 @@ Model.validate = async function validate(obj, pathsOrOptions, context) {
4327
4269
  }
4328
4270
  }
4329
4271
 
4330
- let remaining = paths.size;
4331
-
4332
- return new Promise((resolve, reject) => {
4333
- if (remaining === 0) {
4334
- return settle();
4272
+ const promises = [];
4273
+ for (const path of paths) {
4274
+ const schemaType = schema.path(path);
4275
+ if (schemaType == null) {
4276
+ continue;
4335
4277
  }
4336
4278
 
4337
- for (const path of paths) {
4338
- const schemaType = schema.path(path);
4339
- if (schemaType == null) {
4340
- _checkDone();
4341
- continue;
4342
- }
4343
-
4344
- const pieces = path.indexOf('.') === -1 ? [path] : path.split('.');
4345
- let cur = obj;
4346
- for (let i = 0; i < pieces.length - 1; ++i) {
4347
- cur = cur[pieces[i]];
4348
- }
4349
-
4350
- const val = get(obj, path, void 0);
4351
-
4352
- schemaType.doValidate(val, err => {
4353
- if (err) {
4354
- error = error || new ValidationError();
4355
- error.addError(path, err);
4356
- }
4357
- _checkDone();
4358
- }, context, { path: path });
4279
+ const pieces = path.indexOf('.') === -1 ? [path] : path.split('.');
4280
+ let cur = obj;
4281
+ for (let i = 0; i < pieces.length - 1; ++i) {
4282
+ cur = cur[pieces[i]];
4359
4283
  }
4360
4284
 
4361
- function settle() {
4362
- if (error) {
4363
- reject(error);
4364
- } else {
4365
- resolve(obj);
4366
- }
4367
- }
4285
+ const val = get(obj, path, void 0);
4286
+ promises.push(
4287
+ schemaType.doValidate(val, context, { path: path }).catch(err => {
4288
+ error = error || new ValidationError();
4289
+ error.addError(path, err);
4290
+ })
4291
+ );
4292
+ }
4368
4293
 
4369
- function _checkDone() {
4370
- if (--remaining <= 0) {
4371
- return settle();
4372
- }
4373
- }
4374
- });
4294
+ await Promise.all(promises);
4295
+ if (error != null) {
4296
+ throw error;
4297
+ }
4298
+
4299
+ return obj;
4375
4300
  };
4376
4301
 
4377
4302
  /**
@@ -4414,20 +4339,20 @@ Model.validate = async function validate(obj, pathsOrOptions, context) {
4414
4339
  * users[0].dog.breed; // undefined because of `select`
4415
4340
  *
4416
4341
  * @param {Document|Array} docs Either a single document or array of documents to populate.
4417
- * @param {Object|String} options Either the paths to populate or an object specifying all parameters
4342
+ * @param {object|string} options Either the paths to populate or an object specifying all parameters
4418
4343
  * @param {string} [options.path=null] The path to populate.
4419
4344
  * @param {string|PopulateOptions} [options.populate=null] Recursively populate paths in the populated documents. See [deep populate docs](https://mongoosejs.com/docs/populate.html#deep-populate).
4420
4345
  * @param {boolean} [options.retainNullValues=false] By default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries.
4421
4346
  * @param {boolean} [options.getters=false] If true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](https://mongoosejs.com/docs/schematypes.html#schematype-options).
4422
4347
  * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them.
4423
- * @param {Object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://www.mongodb.com/docs/manual/tutorial/query-documents/), or a function that returns a filter object.
4424
- * @param {Boolean} [options.skipInvalidIds=false] By default, Mongoose throws a cast error if `localField` and `foreignField` schemas don't line up. If you enable this option, Mongoose will instead filter out any `localField` properties that cannot be casted to `foreignField`'s schema type.
4425
- * @param {Number} [options.perDocumentLimit=null] For legacy reasons, `limit` with `populate()` may give incorrect results because it only executes a single query for every document being populated. If you set `perDocumentLimit`, Mongoose will ensure correct `limit` per document by executing a separate query for each document to `populate()`. For example, `.find().populate({ path: 'test', perDocumentLimit: 2 })` will execute 2 additional queries if `.find()` returns 2 documents.
4426
- * @param {Boolean} [options.strictPopulate=true] Set to false to allow populating paths that aren't defined in the given model's schema.
4427
- * @param {Object} [options.options=null] Additional options like `limit` and `lean`.
4348
+ * @param {object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://www.mongodb.com/docs/manual/tutorial/query-documents/), or a function that returns a filter object.
4349
+ * @param {boolean} [options.skipInvalidIds=false] By default, Mongoose throws a cast error if `localField` and `foreignField` schemas don't line up. If you enable this option, Mongoose will instead filter out any `localField` properties that cannot be casted to `foreignField`'s schema type.
4350
+ * @param {number} [options.perDocumentLimit=null] For legacy reasons, `limit` with `populate()` may give incorrect results because it only executes a single query for every document being populated. If you set `perDocumentLimit`, Mongoose will ensure correct `limit` per document by executing a separate query for each document to `populate()`. For example, `.find().populate({ path: 'test', perDocumentLimit: 2 })` will execute 2 additional queries if `.find()` returns 2 documents.
4351
+ * @param {boolean} [options.strictPopulate=true] Set to false to allow populating paths that aren't defined in the given model's schema.
4352
+ * @param {object} [options.options=null] Additional options like `limit` and `lean`.
4428
4353
  * @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document.
4429
- * @param {Boolean} [options.forceRepopulate=true] Set to `false` to prevent Mongoose from repopulating paths that are already populated
4430
- * @param {Boolean} [options.ordered=false] Set to `true` to execute any populate queries one at a time, as opposed to in parallel. Set this option to `true` if populating multiple paths or paths with multiple models in transactions.
4354
+ * @param {boolean} [options.forceRepopulate=true] Set to `false` to prevent Mongoose from repopulating paths that are already populated
4355
+ * @param {boolean} [options.ordered=false] Set to `true` to execute any populate queries one at a time, as opposed to in parallel. Set this option to `true` if populating multiple paths or paths with multiple models in transactions.
4431
4356
  * @return {Promise}
4432
4357
  * @api public
4433
4358
  */
@@ -4472,7 +4397,7 @@ const excludeIdRegGlobal = /\s?-_id\s?/g;
4472
4397
 
4473
4398
  async function _populatePath(model, docs, populateOptions) {
4474
4399
  if (populateOptions.strictPopulate == null) {
4475
- if (populateOptions._localModel != null && populateOptions._localModel.schema._userProvidedOptions.strictPopulate != null) {
4400
+ if (populateOptions._localModel?.schema._userProvidedOptions.strictPopulate != null) {
4476
4401
  populateOptions.strictPopulate = populateOptions._localModel.schema._userProvidedOptions.strictPopulate;
4477
4402
  } else if (populateOptions._localModel != null && model.base.options.strictPopulate != null) {
4478
4403
  populateOptions.strictPopulate = model.base.options.strictPopulate;
@@ -4556,7 +4481,7 @@ async function _populatePath(model, docs, populateOptions) {
4556
4481
  }
4557
4482
  }
4558
4483
 
4559
- if (mod.options.options && mod.options.options.limit != null) {
4484
+ if (mod.options.options?.limit != null) {
4560
4485
  assignmentOpts.originalLimit = mod.options.options.limit;
4561
4486
  } else if (mod.options.limit != null) {
4562
4487
  assignmentOpts.originalLimit = mod.options.limit;
@@ -4579,20 +4504,35 @@ async function _populatePath(model, docs, populateOptions) {
4579
4504
  return;
4580
4505
  }
4581
4506
 
4507
+ // Track deferred populates per-param (per model) to avoid mixing them
4508
+ const deferredPopulatesPerParam = new Map();
4509
+
4582
4510
  if (populateOptions.ordered) {
4583
4511
  // Populate in series, primarily for transactions because MongoDB doesn't support multiple operations on
4584
4512
  // one transaction in parallel.
4585
- for (const arr of params) {
4586
- await _execPopulateQuery.apply(null, arr).then(valsFromDb => { vals = vals.concat(valsFromDb); });
4513
+ for (let i = 0; i < params.length; i++) {
4514
+ const arr = params[i];
4515
+ const { docs, deferredPopulates } = await _execPopulateQuery.apply(null, arr);
4516
+ vals = vals.concat(docs);
4517
+ if (deferredPopulates.length > 0) {
4518
+ deferredPopulatesPerParam.set(i, deferredPopulates);
4519
+ }
4587
4520
  }
4588
4521
  } else {
4589
4522
  // By default, populate in parallel
4590
4523
  const promises = [];
4591
4524
  for (const arr of params) {
4592
- promises.push(_execPopulateQuery.apply(null, arr).then(valsFromDb => { vals = vals.concat(valsFromDb); }));
4525
+ promises.push(_execPopulateQuery.apply(null, arr));
4593
4526
  }
4594
4527
 
4595
- await Promise.all(promises);
4528
+ const results = await Promise.all(promises);
4529
+ for (let i = 0; i < results.length; i++) {
4530
+ const { docs, deferredPopulates } = results[i];
4531
+ vals = vals.concat(docs);
4532
+ if (deferredPopulates.length > 0) {
4533
+ deferredPopulatesPerParam.set(i, deferredPopulates);
4534
+ }
4535
+ }
4596
4536
  }
4597
4537
 
4598
4538
 
@@ -4605,12 +4545,49 @@ async function _populatePath(model, docs, populateOptions) {
4605
4545
  _assign(model, vals, mod, assignmentOpts);
4606
4546
  }
4607
4547
 
4548
+ // Handle deferred populate for cases with per-document match functions.
4549
+ // We defer populate so sift filtering can compare ObjectIds (not populated docs).
4550
+ // This handles both explicit sub-populate and populate added by pre('find') hooks.
4551
+ // Each param's deferred populates are tracked separately to avoid mixing them
4552
+ // between different models (e.g., when using refPath with multiple model types).
4553
+ if (deferredPopulatesPerParam.size > 0) {
4554
+ for (let i = 0; i < params.length; i++) {
4555
+ const arr = params[i];
4556
+ const mod = arr[0];
4557
+ if (!Array.isArray(mod.match)) {
4558
+ continue;
4559
+ }
4560
+
4561
+ const paramDeferredPopulates = deferredPopulatesPerParam.get(i);
4562
+ if (!paramDeferredPopulates || paramDeferredPopulates.length === 0) {
4563
+ continue;
4564
+ }
4565
+
4566
+ // Get the assigned (filtered) children from parent docs
4567
+ const childDocsToPopulate = [];
4568
+ for (const doc of mod.docs) {
4569
+ const childVal = doc.$__ != null ? doc.get(mod.options.path) : doc[mod.options.path];
4570
+ if (Array.isArray(childVal)) {
4571
+ childDocsToPopulate.push(...childVal);
4572
+ } else if (childVal != null) {
4573
+ childDocsToPopulate.push(childVal);
4574
+ }
4575
+ }
4576
+
4577
+ if (childDocsToPopulate.length > 0) {
4578
+ for (const pop of paramDeferredPopulates) {
4579
+ await mod.model.populate(childDocsToPopulate, pop);
4580
+ }
4581
+ }
4582
+ }
4583
+ }
4584
+
4608
4585
  for (const arr of params) {
4609
4586
  removeDeselectedForeignField(arr[0].foreignField, arr[0].options, vals);
4610
4587
  }
4611
4588
  for (const arr of params) {
4612
4589
  const mod = arr[0];
4613
- if (mod.options && mod.options.options && mod.options.options._leanTransform) {
4590
+ if (mod.options?.options?._leanTransform) {
4614
4591
  for (const doc of vals) {
4615
4592
  mod.options.options._leanTransform(doc);
4616
4593
  }
@@ -4647,6 +4624,12 @@ function _execPopulateQuery(mod, match, select) {
4647
4624
  queryOptions.limit = queryOptions.limit * mod.ids.length;
4648
4625
  }
4649
4626
 
4627
+ // When there's a per-document match function, defer any populate (including from hooks)
4628
+ // until after sift filtering, otherwise sift compares ObjectIds against populated docs.
4629
+ if (Array.isArray(mod.match)) {
4630
+ queryOptions._deferPopulate = true;
4631
+ }
4632
+
4650
4633
  const query = mod.model.find(match, select, queryOptions);
4651
4634
  // If we're doing virtual populate and projection is inclusive and foreign
4652
4635
  // field is not selected, automatically select it because mongoose needs it.
@@ -4668,7 +4651,14 @@ function _execPopulateQuery(mod, match, select) {
4668
4651
  }
4669
4652
  }
4670
4653
 
4671
- // If we need to sub-populate, call populate recursively
4654
+ // If we need to sub-populate, call populate recursively.
4655
+ // However, if we have a per-document match function (Array.isArray(mod.match)),
4656
+ // defer sub-populate until after sift filtering, otherwise sift compares
4657
+ // ObjectIds against already-populated docs and fails to match.
4658
+ const shouldDeferSubPopulate = subPopulate && Array.isArray(mod.match);
4659
+
4660
+ // Prepare sub-populate options with necessary metadata (_fullPath, strictPopulate, _localModel).
4661
+ // This must be done for both immediate and deferred sub-populate cases.
4672
4662
  if (subPopulate) {
4673
4663
  // If subpopulating on a discriminator, skip check for non-existent
4674
4664
  // paths. Because the discriminator may not have the path defined.
@@ -4686,11 +4676,21 @@ function _execPopulateQuery(mod, match, select) {
4686
4676
  if (Array.isArray(subPopulate)) {
4687
4677
  for (const pop of subPopulate) {
4688
4678
  pop._fullPath = basePath + '.' + pop.path;
4679
+ // Set _localModel for deferred populates so strictPopulate works correctly
4680
+ if (shouldDeferSubPopulate && pop._localModel == null) {
4681
+ pop._localModel = mod.model;
4682
+ }
4689
4683
  }
4690
4684
  } else if (typeof subPopulate === 'object') {
4691
4685
  subPopulate._fullPath = basePath + '.' + subPopulate.path;
4686
+ // Set _localModel for deferred populates so strictPopulate works correctly
4687
+ if (shouldDeferSubPopulate && subPopulate._localModel == null) {
4688
+ subPopulate._localModel = mod.model;
4689
+ }
4692
4690
  }
4691
+ }
4693
4692
 
4693
+ if (subPopulate && !shouldDeferSubPopulate) {
4694
4694
  query.populate(subPopulate);
4695
4695
  }
4696
4696
 
@@ -4699,7 +4699,16 @@ function _execPopulateQuery(mod, match, select) {
4699
4699
  for (const val of docs) {
4700
4700
  leanPopulateMap.set(val, mod.model);
4701
4701
  }
4702
- return docs;
4702
+ // Return both docs and any deferred populates (from hooks or explicit sub-populate)
4703
+ const deferredPopulates = [];
4704
+ if (query._deferredPopulate) {
4705
+ deferredPopulates.push(...query._deferredPopulate);
4706
+ delete query._deferredPopulate;
4707
+ }
4708
+ if (shouldDeferSubPopulate) {
4709
+ deferredPopulates.push(subPopulate);
4710
+ }
4711
+ return { docs, deferredPopulates };
4703
4712
  }
4704
4713
  );
4705
4714
  }
@@ -4741,14 +4750,7 @@ function _assign(model, vals, mod, assignmentOpts) {
4741
4750
  if (__val instanceof Document) {
4742
4751
  __val = __val._doc._id;
4743
4752
  }
4744
- if (__val?.constructor?.name === 'Binary' && __val.sub_type === 4 && typeof __val.toUUID === 'function') {
4745
- // Workaround for gh-15315 because Mongoose UUIDs don't use BSON UUIDs yet.
4746
- key = String(__val.toUUID());
4747
- } else if (__val?.constructor?.name === 'Buffer' && __val._subtype === 4 && typeof __val.toUUID === 'function') {
4748
- key = String(__val.toUUID());
4749
- } else {
4750
- key = String(__val);
4751
- }
4753
+ key = String(__val);
4752
4754
  if (rawDocs[key]) {
4753
4755
  if (Array.isArray(rawDocs[key])) {
4754
4756
  rawDocs[key].push(val);
@@ -4771,14 +4773,7 @@ function _assign(model, vals, mod, assignmentOpts) {
4771
4773
  if (_val instanceof Document) {
4772
4774
  _val = _val._doc._id;
4773
4775
  }
4774
- if (_val?.constructor?.name === 'Binary' && _val.sub_type === 4 && typeof _val.toUUID === 'function') {
4775
- // Workaround for gh-15315 because Mongoose UUIDs don't use BSON UUIDs yet.
4776
- key = String(_val.toUUID());
4777
- } else if (_val?.constructor?.name === 'Buffer' && _val._subtype === 4 && typeof _val.toUUID === 'function') {
4778
- key = String(_val.toUUID());
4779
- } else {
4780
- key = String(_val);
4781
- }
4776
+ key = String(_val);
4782
4777
  if (rawDocs[key]) {
4783
4778
  if (Array.isArray(rawDocs[key])) {
4784
4779
  rawDocs[key].push(val);
@@ -4830,9 +4825,9 @@ function _assign(model, vals, mod, assignmentOpts) {
4830
4825
  /**
4831
4826
  * Compiler utility.
4832
4827
  *
4833
- * @param {String|Function} name model name or class extending Model
4828
+ * @param {string|Function} name model name or class extending Model
4834
4829
  * @param {Schema} schema
4835
- * @param {String} collectionName
4830
+ * @param {string} collectionName
4836
4831
  * @param {Connection} connection
4837
4832
  * @param {Mongoose} base mongoose instance
4838
4833
  * @api private
@@ -4899,7 +4894,6 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4899
4894
  model.events = new EventEmitter();
4900
4895
 
4901
4896
  schema._preCompile();
4902
-
4903
4897
  const _userProvidedOptions = schema._userProvidedOptions || {};
4904
4898
 
4905
4899
  const collectionOptions = {
@@ -4955,7 +4949,7 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4955
4949
  Model.clientEncryption = function clientEncryption() {
4956
4950
  const ClientEncryption = this.base.driver.get().ClientEncryption;
4957
4951
  if (!ClientEncryption) {
4958
- throw new Error('The mongodb driver must be used to obtain a ClientEncryption object.');
4952
+ throw new MongooseError('The mongodb driver must be used to obtain a ClientEncryption object.');
4959
4953
  }
4960
4954
 
4961
4955
  const client = this.collection?.conn?.client;
@@ -5026,7 +5020,7 @@ function applyQueryMethods(model, methods) {
5026
5020
  *
5027
5021
  * @param {Connection} conn
5028
5022
  * @param {Schema} [schema]
5029
- * @param {String} [collection]
5023
+ * @param {string} [collection]
5030
5024
  * @return {Model}
5031
5025
  * @api private
5032
5026
  * @memberOf Model
@@ -5179,7 +5173,7 @@ Model._applyQueryMiddleware = function _applyQueryMiddleware() {
5179
5173
  return !!contexts.query;
5180
5174
  }
5181
5175
  if (hook.name === 'deleteOne' || hook.name === 'updateOne') {
5182
- return !!contexts.query || Object.keys(contexts).length === 0;
5176
+ return !!contexts.query || utils.hasOwnKeys(contexts) === false;
5183
5177
  }
5184
5178
  if (hook.query != null || hook.document != null) {
5185
5179
  return !!hook.query;