mongoose 6.4.6 → 6.5.1

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 (50) hide show
  1. package/lib/aggregate.js +32 -30
  2. package/lib/browser.js +1 -1
  3. package/lib/cast.js +2 -2
  4. package/lib/collection.js +1 -1
  5. package/lib/connection.js +56 -18
  6. package/lib/cursor/ChangeStream.js +39 -1
  7. package/lib/document.js +293 -249
  8. package/lib/error/index.js +1 -0
  9. package/lib/error/validation.js +9 -0
  10. package/lib/helpers/document/applyDefaults.js +115 -0
  11. package/lib/helpers/document/cleanModifiedSubpaths.js +3 -3
  12. package/lib/helpers/document/compile.js +7 -6
  13. package/lib/helpers/firstKey.js +8 -0
  14. package/lib/helpers/model/applyDefaultsToPOJO.js +52 -0
  15. package/lib/helpers/model/castBulkWrite.js +1 -1
  16. package/lib/helpers/model/pushNestedArrayPaths.js +15 -0
  17. package/lib/helpers/populate/markArraySubdocsPopulated.js +1 -0
  18. package/lib/helpers/projection/hasIncludedChildren.js +1 -0
  19. package/lib/helpers/update/applyTimestampsToChildren.js +6 -2
  20. package/lib/index.js +3 -2
  21. package/lib/internal.js +3 -1
  22. package/lib/model.js +233 -94
  23. package/lib/options/SchemaArrayOptions.js +19 -0
  24. package/lib/options/SchemaNumberOptions.js +2 -0
  25. package/lib/options/SchemaObjectIdOptions.js +1 -0
  26. package/lib/plugins/trackTransaction.js +2 -2
  27. package/lib/query.js +24 -10
  28. package/lib/schema/SubdocumentPath.js +10 -0
  29. package/lib/schema/array.js +2 -1
  30. package/lib/schema/documentarray.js +14 -1
  31. package/lib/schema/string.js +3 -0
  32. package/lib/schema.js +22 -3
  33. package/lib/schematype.js +100 -82
  34. package/lib/statemachine.js +23 -9
  35. package/lib/types/buffer.js +21 -19
  36. package/lib/types/map.js +2 -0
  37. package/lib/utils.js +8 -0
  38. package/lib/validoptions.js +1 -0
  39. package/lib/virtualtype.js +14 -16
  40. package/package.json +12 -12
  41. package/types/connection.d.ts +2 -2
  42. package/types/document.d.ts +8 -1
  43. package/types/expressions.d.ts +1 -1
  44. package/types/index.d.ts +16 -25
  45. package/types/inferschematype.d.ts +3 -20
  46. package/types/models.d.ts +58 -54
  47. package/types/mongooseoptions.d.ts +6 -0
  48. package/types/schemaoptions.d.ts +15 -4
  49. package/types/utility.d.ts +19 -0
  50. package/types/virtuals.d.ts +14 -0
package/lib/aggregate.js CHANGED
@@ -19,7 +19,7 @@ const validRedactStringValues = new Set(['$$DESCEND', '$$PRUNE', '$$KEEP']);
19
19
 
20
20
  /**
21
21
  * Aggregate constructor used for building aggregation pipelines. Do not
22
- * instantiate this class directly, use [Model.aggregate()](/docs/api.html#model_Model.aggregate) instead.
22
+ * instantiate this class directly, use [Model.aggregate()](/docs/api.html#model_Model-aggregate) instead.
23
23
  *
24
24
  * #### Example:
25
25
  *
@@ -38,11 +38,9 @@ const validRedactStringValues = new Set(['$$DESCEND', '$$PRUNE', '$$KEEP']);
38
38
  * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
39
39
  * - Mongoose does **not** cast pipeline stages. The below will **not** work unless `_id` is a string in the database
40
40
  *
41
- * ```javascript
42
- * new Aggregate([{ $match: { _id: '00000000000000000000000a' } }]);
43
- * // Do this instead to cast to an ObjectId
44
- * new Aggregate([{ $match: { _id: new mongoose.Types.ObjectId('00000000000000000000000a') } }]);
45
- * ```
41
+ * new Aggregate([{ $match: { _id: '00000000000000000000000a' } }]);
42
+ * // Do this instead to cast to an ObjectId
43
+ * new Aggregate([{ $match: { _id: new mongoose.Types.ObjectId('00000000000000000000000a') } }]);
46
44
  *
47
45
  * @see MongoDB https://docs.mongodb.org/manual/applications/aggregation/
48
46
  * @see driver https://mongodb.github.com/node-mongodb-native/api-generated/collection.html#aggregate
@@ -72,8 +70,8 @@ function Aggregate(pipeline, model) {
72
70
  * - [`cursor`](./api.html#aggregate_Aggregate-cursor)
73
71
  * - [`explain`](./api.html#aggregate_Aggregate-explain)
74
72
  * - `fieldsAsRaw`
75
- * - hint
76
- * - let
73
+ * - `hint`
74
+ * - `let`
77
75
  * - `maxTimeMS`
78
76
  * - `raw`
79
77
  * - `readConcern`
@@ -92,6 +90,7 @@ Aggregate.prototype.options;
92
90
  * Get/set the model that this aggregation will execute on.
93
91
  *
94
92
  * #### Example:
93
+ *
95
94
  * const aggregate = MyModel.aggregate([{ $match: { answer: 42 } }]);
96
95
  * aggregate.model() === MyModel; // true
97
96
  *
@@ -99,7 +98,7 @@ Aggregate.prototype.options;
99
98
  * aggregate.model(SomeOtherModel);
100
99
  * aggregate.model() === SomeOtherModel; // true
101
100
  *
102
- * @param {Model} [model] set the model associated with this aggregate.
101
+ * @param {Model} [model] Set the model associated with this aggregate. If not provided, returns the already stored model.
103
102
  * @return {Model}
104
103
  * @api public
105
104
  */
@@ -135,7 +134,7 @@ Aggregate.prototype.model = function(model) {
135
134
  * const pipeline = [{ $match: { daw: 'Logic Audio X' }} ];
136
135
  * aggregate.append(pipeline);
137
136
  *
138
- * @param {Object} ops operator(s) to append
137
+ * @param {...Object|Object[]} ops operator(s) to append. Can either be a spread of objects or a single parameter of a object array.
139
138
  * @return {Aggregate}
140
139
  * @api public
141
140
  */
@@ -364,7 +363,7 @@ Aggregate.prototype.near = function(arg) {
364
363
  * aggregate.unwind({ path: '$tags', preserveNullAndEmptyArrays: true });
365
364
  *
366
365
  * @see $unwind https://docs.mongodb.org/manual/reference/aggregation/unwind/
367
- * @param {String|Object} fields the field(s) to unwind, either as field names or as [objects with options](https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/#document-operand-with-options). If passing a string, prefixing the field name with '$' is optional. If passing an object, `path` must start with '$'.
366
+ * @param {String|Object|String[]|Object[]} fields the field(s) to unwind, either as field names or as [objects with options](https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/#document-operand-with-options). If passing a string, prefixing the field name with '$' is optional. If passing an object, `path` must start with '$'.
368
367
  * @return {Aggregate}
369
368
  * @api public
370
369
  */
@@ -495,6 +494,7 @@ Aggregate.prototype.lookup = function(options) {
495
494
  * Note that graphLookup can only consume at most 100MB of memory, and does not allow disk use even if `{ allowDiskUse: true }` is specified.
496
495
  *
497
496
  * #### Examples:
497
+ *
498
498
  * // Suppose we have a collection of courses, where a document might look like `{ _id: 0, name: 'Calculus', prerequisite: 'Trigonometry'}` and `{ _id: 0, name: 'Trigonometry', prerequisite: 'Algebra' }`
499
499
  * aggregate.graphLookup({ from: 'courses', startWith: '$prerequisite', connectFromField: 'prerequisite', connectToField: 'name', as: 'prerequisites', maxDepth: 3 }) // this will recursively search the 'courses' collection up to 3 prerequisites
500
500
  *
@@ -602,7 +602,8 @@ Aggregate.prototype.sort = function(arg) {
602
602
  *
603
603
  * @see $unionWith https://docs.mongodb.com/manual/reference/operator/aggregation/unionWith
604
604
  * @param {Object} options to $unionWith query as described in the above link
605
- * @return {Aggregate}* @api public
605
+ * @return {Aggregate}
606
+ * @api public
606
607
  */
607
608
 
608
609
  Aggregate.prototype.unionWith = function(options) {
@@ -617,8 +618,8 @@ Aggregate.prototype.unionWith = function(options) {
617
618
  *
618
619
  * await Model.aggregate(pipeline).read('primaryPreferred');
619
620
  *
620
- * @param {String} pref one of the listed preference options or their aliases
621
- * @param {Array} [tags] optional tags for this query
621
+ * @param {String|ReadPreference} pref one of the listed preference options or their aliases
622
+ * @param {Array} [tags] optional tags for this query. DEPRECATED
622
623
  * @return {Aggregate} this
623
624
  * @api public
624
625
  * @see mongodb https://docs.mongodb.org/manual/applications/replication/#read-preference
@@ -703,9 +704,9 @@ Aggregate.prototype.redact = function(expression, thenExpr, elseExpr) {
703
704
  *
704
705
  * Model.aggregate(..).explain(callback)
705
706
  *
706
- * @param {String} verbosity
707
- * @param {Function} callback
708
- * @return {Promise}
707
+ * @param {String} [verbosity]
708
+ * @param {Function} [callback] The callback function to call, if not specified, will return a Promise instead.
709
+ * @return {Promise} Returns a promise if no "callback" is given
709
710
  */
710
711
 
711
712
  Aggregate.prototype.explain = function(verbosity, callback) {
@@ -772,6 +773,7 @@ Aggregate.prototype.explain = function(verbosity, callback) {
772
773
  * await Model.aggregate([{ $match: { foo: 'bar' } }]).allowDiskUse(true);
773
774
  *
774
775
  * @param {Boolean} value Should tell server it can use hard drive to store data during aggregation.
776
+ * @return {Aggregate} this
775
777
  * @see mongodb https://docs.mongodb.org/manual/reference/command/aggregate/
776
778
  */
777
779
 
@@ -788,6 +790,7 @@ Aggregate.prototype.allowDiskUse = function(value) {
788
790
  * Model.aggregate(..).hint({ qty: 1, category: 1 }).exec(callback)
789
791
  *
790
792
  * @param {Object|String} value a hint object or the index name
793
+ * @return {Aggregate} this
791
794
  * @see mongodb https://docs.mongodb.org/manual/reference/command/aggregate/
792
795
  */
793
796
 
@@ -805,6 +808,7 @@ Aggregate.prototype.hint = function(value) {
805
808
  * await Model.aggregate(..).session(session);
806
809
  *
807
810
  * @param {ClientSession} session
811
+ * @return {Aggregate} this
808
812
  * @see mongodb https://docs.mongodb.org/manual/reference/command/aggregate/
809
813
  */
810
814
 
@@ -826,10 +830,10 @@ Aggregate.prototype.session = function(session) {
826
830
  * agg.options; // `{ allowDiskUse: true }`
827
831
  *
828
832
  * @param {Object} options keys to merge into current options
829
- * @param [options.maxTimeMS] number limits the time this aggregation will run, see [MongoDB docs on `maxTimeMS`](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/)
830
- * @param [options.allowDiskUse] boolean if true, the MongoDB server will use the hard drive to store data during this aggregation
831
- * @param [options.collation] object see [`Aggregate.prototype.collation()`](./docs/api.html#aggregate_Aggregate-collation)
832
- * @param [options.session] ClientSession see [`Aggregate.prototype.session()`](./docs/api.html#aggregate_Aggregate-session)
833
+ * @param {Number} [options.maxTimeMS] number limits the time this aggregation will run, see [MongoDB docs on `maxTimeMS`](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/)
834
+ * @param {Boolean} [options.allowDiskUse] boolean if true, the MongoDB server will use the hard drive to store data during this aggregation
835
+ * @param {Object} [options.collation] object see [`Aggregate.prototype.collation()`](./docs/api.html#aggregate_Aggregate-collation)
836
+ * @param {ClientSession} [options.session] ClientSession see [`Aggregate.prototype.session()`](./docs/api.html#aggregate_Aggregate-session)
833
837
  * @see mongodb https://docs.mongodb.org/manual/reference/command/aggregate/
834
838
  * @return {Aggregate} this
835
839
  * @api public
@@ -855,7 +859,7 @@ Aggregate.prototype.option = function(value) {
855
859
  * });
856
860
  *
857
861
  * @param {Object} options
858
- * @param {Number} options.batchSize set the cursor batch size
862
+ * @param {Number} [options.batchSize] set the cursor batch size
859
863
  * @param {Boolean} [options.useMongooseAggCursor] use experimental mongoose-specific aggregation cursor (for `eachAsync()` and other query cursor semantics)
860
864
  * @return {AggregationCursor} cursor representing this aggregation
861
865
  * @api public
@@ -940,11 +944,10 @@ Aggregate.prototype.search = function(options) {
940
944
  *
941
945
  * MyModel.aggregate().match({ test: 1 }).pipeline(); // [{ $match: { test: 1 } }]
942
946
  *
943
- * @return {Array}
947
+ * @return {Array} The current pipeline similar to the operation that will be executed
944
948
  * @api public
945
949
  */
946
950
 
947
-
948
951
  Aggregate.prototype.pipeline = function() {
949
952
  return this._pipeline;
950
953
  };
@@ -957,12 +960,10 @@ Aggregate.prototype.pipeline = function() {
957
960
  * aggregate.exec(callback);
958
961
  *
959
962
  * // Because a promise is returned, the `callback` is optional.
960
- * const promise = aggregate.exec();
961
- * promise.then(..);
963
+ * const result = await aggregate.exec();
962
964
  *
963
- * @see Promise #promise_Promise
964
965
  * @param {Function} [callback]
965
- * @return {Promise}
966
+ * @return {Promise} Returns a Promise if no "callback" is given.
966
967
  * @api public
967
968
  */
968
969
 
@@ -1018,13 +1019,13 @@ Aggregate.prototype.exec = function(callback) {
1018
1019
  };
1019
1020
 
1020
1021
  /**
1021
- * Provides promise for aggregate.
1022
+ * Provides a Promise-like `then` function, which will call `.exec` without a callback
1023
+ * Compatible with `await`.
1022
1024
  *
1023
1025
  * #### Example:
1024
1026
  *
1025
1027
  * Model.aggregate(..).then(successCallback, errorCallback);
1026
1028
  *
1027
- * @see Promise #promise_Promise
1028
1029
  * @param {Function} [resolve] successCallback
1029
1030
  * @param {Function} [reject] errorCallback
1030
1031
  * @return {Promise}
@@ -1037,6 +1038,7 @@ Aggregate.prototype.then = function(resolve, reject) {
1037
1038
  * Executes the query returning a `Promise` which will be
1038
1039
  * resolved with either the doc(s) or rejected with the error.
1039
1040
  * Like [`.then()`](#query_Query-then), but only takes a rejection handler.
1041
+ * Compatible with `await`.
1040
1042
  *
1041
1043
  * @param {Function} [reject]
1042
1044
  * @return {Promise}
package/lib/browser.js CHANGED
@@ -103,7 +103,7 @@ exports.VirtualType = require('./virtualtype');
103
103
  * _Alias of mongoose.Schema.Types for backwards compatibility._
104
104
  *
105
105
  * @property SchemaTypes
106
- * @see Schema.SchemaTypes #schema_Schema.Types
106
+ * @see Schema.SchemaTypes #schema_Schema-Types
107
107
  * @api public
108
108
  */
109
109
 
package/lib/cast.js CHANGED
@@ -24,8 +24,8 @@ const ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
24
24
  *
25
25
  * @param {Schema} schema
26
26
  * @param {Object} obj Object to cast
27
- * @param {Object} options the query options
28
- * @param {Query} context passed to setters
27
+ * @param {Object} [options] the query options
28
+ * @param {Query} [context] passed to setters
29
29
  * @api private
30
30
  */
31
31
  module.exports = function cast(schema, obj, options, context) {
package/lib/collection.js CHANGED
@@ -15,7 +15,7 @@ const immediate = require('./helpers/immediate');
15
15
  *
16
16
  * @param {String} name name of the collection
17
17
  * @param {Connection} conn A MongooseConnection instance
18
- * @param {Object} opts optional collection options
18
+ * @param {Object} [opts] optional collection options
19
19
  * @api public
20
20
  */
21
21
 
package/lib/connection.js CHANGED
@@ -393,7 +393,7 @@ Connection.prototype.config;
393
393
  * @param {string} collection The collection to create
394
394
  * @param {Object} [options] see [MongoDB driver docs](https://mongodb.github.io/node-mongodb-native/2.2/api/Db.html#createCollection)
395
395
  * @param {Function} [callback]
396
- * @return {Promise}
396
+ * @return {Promise} Returns a Promise if no `callback` is given.
397
397
  * @api public
398
398
  */
399
399
 
@@ -491,6 +491,9 @@ Connection.prototype.transaction = function transaction(fn, options) {
491
491
  doc.set(doc.schema.options.versionKey, state.versionKey);
492
492
  }
493
493
 
494
+ if (state.modifiedPaths.length > 0 && doc.$__.activePaths.states.modify == null) {
495
+ doc.$__.activePaths.states.modify = {};
496
+ }
494
497
  for (const path of state.modifiedPaths) {
495
498
  doc.$__.activePaths.paths[path] = 'modify';
496
499
  doc.$__.activePaths.states.modify[path] = true;
@@ -521,7 +524,7 @@ Connection.prototype.transaction = function transaction(fn, options) {
521
524
  * @method dropCollection
522
525
  * @param {string} collection The collection to delete
523
526
  * @param {Function} [callback]
524
- * @return {Promise}
527
+ * @return {Promise} Returns a Promise if no `callback` is given.
525
528
  * @api public
526
529
  */
527
530
 
@@ -541,7 +544,7 @@ Connection.prototype.dropCollection = _wrapConnHelper(function dropCollection(co
541
544
  *
542
545
  * @method dropDatabase
543
546
  * @param {Function} [callback]
544
- * @return {Promise}
547
+ * @return {Promise} Returns a Promise if no `callback` is given.
545
548
  * @api public
546
549
  */
547
550
 
@@ -550,8 +553,8 @@ Connection.prototype.dropDatabase = _wrapConnHelper(function dropDatabase(cb) {
550
553
  // init-ed. It is sufficiently common to call `dropDatabase()` after
551
554
  // `mongoose.connect()` but before creating models that we want to
552
555
  // support this. See gh-6796
553
- for (const name of Object.keys(this.models)) {
554
- delete this.models[name].$init;
556
+ for (const model of Object.values(this.models)) {
557
+ delete model.$init;
555
558
  }
556
559
  this.db.dropDatabase(cb);
557
560
  });
@@ -608,6 +611,8 @@ Connection.prototype._shouldBufferCommands = function _shouldBufferCommands() {
608
611
  *
609
612
  * @param {Error} err
610
613
  * @param {Function} callback optional
614
+ * @emits "error" Emits the `error` event with the given `err`, unless a callback is specified
615
+ * @returns {Promise|null} Returns a rejected Promise if no `callback` is given.
611
616
  * @api private
612
617
  */
613
618
 
@@ -668,7 +673,7 @@ Connection.prototype.onOpen = function() {
668
673
  * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
669
674
  * @param {Boolean} [options.autoCreate=false] Set to `true` to make Mongoose automatically call `createCollection()` on every model created on this connection.
670
675
  * @param {Function} [callback]
671
- * @returns {Connection} this
676
+ * @returns {Promise<Connection>} Returns a Promise if no `callback` is given.
672
677
  * @api public
673
678
  */
674
679
 
@@ -838,6 +843,11 @@ Connection.prototype.openUri = function(uri, options, callback) {
838
843
  );
839
844
  }
840
845
 
846
+ for (const model of Object.values(this.models)) {
847
+ // Errors handled internally, so safe to ignore error
848
+ model.init(function $modelInitNoop() {});
849
+ }
850
+
841
851
  return this.$initialConnection;
842
852
  };
843
853
 
@@ -913,6 +923,14 @@ function _setClient(conn, client, options, dbName) {
913
923
  }
914
924
  }
915
925
 
926
+ /**
927
+ * Destory the connection (not just a alias of [`.close`](#connection_Connection-close))
928
+ *
929
+ * @param {Boolean} [force]
930
+ * @param {Function} [callback]
931
+ * @returns {Promise} Returns a Promise if no `callback` is given.
932
+ */
933
+
916
934
  Connection.prototype.destroy = function(force, callback) {
917
935
  if (typeof force === 'function') {
918
936
  callback = force;
@@ -935,7 +953,7 @@ Connection.prototype.destroy = function(force, callback) {
935
953
  *
936
954
  * @param {Boolean} [force] optional
937
955
  * @param {Function} [callback] optional
938
- * @return {Promise}
956
+ * @return {Promise} Returns a Promise if no `callback` is given.
939
957
  * @api public
940
958
  */
941
959
 
@@ -951,6 +969,13 @@ Connection.prototype.close = function(force, callback) {
951
969
  this.$wasForceClosed = !!force;
952
970
  }
953
971
 
972
+ for (const model of Object.values(this.models)) {
973
+ // If manually disconnecting, make sure to clear each model's `$init`
974
+ // promise, so Mongoose knows to re-run `init()` in case the
975
+ // connection is re-opened. See gh-12047.
976
+ delete model.$init;
977
+ }
978
+
954
979
  return promiseOrCallback(callback, cb => {
955
980
  this._close(force, false, cb);
956
981
  });
@@ -961,7 +986,8 @@ Connection.prototype.close = function(force, callback) {
961
986
  *
962
987
  * @param {Boolean} force
963
988
  * @param {Boolean} destroy
964
- * @param {Function} callback
989
+ * @param {Function} [callback]
990
+ * @returns {Connection} this
965
991
  * @api private
966
992
  */
967
993
  Connection.prototype._close = function(force, destroy, callback) {
@@ -1081,6 +1107,7 @@ Connection.prototype.collection = function(name, options) {
1081
1107
  * Equivalent to calling `.plugin(fn)` on each schema you create.
1082
1108
  *
1083
1109
  * #### Example:
1110
+ *
1084
1111
  * const db = mongoose.createConnection('mongodb://localhost:27017/mydb');
1085
1112
  * db.plugin(() => console.log('Applied'));
1086
1113
  * db.plugins.length; // 1
@@ -1108,7 +1135,7 @@ Connection.prototype.plugin = function(fn, opts) {
1108
1135
  * const Ticket = db.model('Ticket', new Schema(..));
1109
1136
  * const Venue = db.model('Venue');
1110
1137
  *
1111
- * _When no `collection` argument is passed, Mongoose produces a collection name by passing the model `name` to the [utils.toCollectionName](#utils_exports.toCollectionName) method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option._
1138
+ * _When no `collection` argument is passed, Mongoose produces a collection name by passing the model `name` to the [utils.toCollectionName](#utils_exports-toCollectionName) method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option._
1112
1139
  *
1113
1140
  * #### Example:
1114
1141
  *
@@ -1274,7 +1301,7 @@ Connection.prototype.deleteModel = function(name) {
1274
1301
 
1275
1302
  /**
1276
1303
  * Watches the entire underlying database for changes. Similar to
1277
- * [`Model.watch()`](/docs/api/model.html#model_Model.watch).
1304
+ * [`Model.watch()`](/docs/api/model.html#model_Model-watch).
1278
1305
  *
1279
1306
  * This function does **not** trigger any middleware. In particular, it
1280
1307
  * does **not** trigger aggregate middleware.
@@ -1330,6 +1357,7 @@ Connection.prototype.watch = function(pipeline, options) {
1330
1357
  * to connect.
1331
1358
  *
1332
1359
  * #### Example:
1360
+ *
1333
1361
  * const conn = await mongoose.createConnection('mongodb://localhost:27017/test').
1334
1362
  * asPromise();
1335
1363
  * conn.readyState; // 1, means Mongoose is connected
@@ -1345,7 +1373,7 @@ Connection.prototype.asPromise = function asPromise() {
1345
1373
  /**
1346
1374
  * Returns an array of model names created on this connection.
1347
1375
  * @api public
1348
- * @return {Array}
1376
+ * @return {String[]}
1349
1377
  */
1350
1378
 
1351
1379
  Connection.prototype.modelNames = function() {
@@ -1353,9 +1381,10 @@ Connection.prototype.modelNames = function() {
1353
1381
  };
1354
1382
 
1355
1383
  /**
1356
- * @brief Returns if the connection requires authentication after it is opened. Generally if a
1384
+ * Returns if the connection requires authentication after it is opened. Generally if a
1357
1385
  * username and password are both provided than authentication is needed, but in some cases a
1358
1386
  * password is not required.
1387
+ *
1359
1388
  * @api private
1360
1389
  * @return {Boolean} true if the connection should be authenticated after it is opened, otherwise false.
1361
1390
  */
@@ -1365,8 +1394,9 @@ Connection.prototype.shouldAuthenticate = function() {
1365
1394
  };
1366
1395
 
1367
1396
  /**
1368
- * @brief Returns a boolean value that specifies if the current authentication mechanism needs a
1397
+ * Returns a boolean value that specifies if the current authentication mechanism needs a
1369
1398
  * password to authenticate according to the auth objects passed into the openUri methods.
1399
+ *
1370
1400
  * @api private
1371
1401
  * @return {Boolean} true if the authentication mechanism specified in the options object requires
1372
1402
  * a password, otherwise false.
@@ -1379,10 +1409,11 @@ Connection.prototype.authMechanismDoesNotRequirePassword = function() {
1379
1409
  };
1380
1410
 
1381
1411
  /**
1382
- * @brief Returns a boolean value that specifies if the provided objects object provides enough
1412
+ * Returns a boolean value that specifies if the provided objects object provides enough
1383
1413
  * data to authenticate with. Generally this is true if the username and password are both specified
1384
1414
  * but in some authentication methods, a password is not required for authentication so only a username
1385
1415
  * is required.
1416
+ *
1386
1417
  * @param {Object} [options] the options object passed into the openUri methods.
1387
1418
  * @api private
1388
1419
  * @return {Boolean} true if the provided options object provides enough data to authenticate with,
@@ -1399,6 +1430,7 @@ Connection.prototype.optionsProvideAuthenticationData = function(options) {
1399
1430
  * that this connection uses to talk to MongoDB.
1400
1431
  *
1401
1432
  * #### Example:
1433
+ *
1402
1434
  * const conn = await mongoose.createConnection('mongodb://localhost:27017/test').
1403
1435
  * asPromise();
1404
1436
  *
@@ -1418,6 +1450,7 @@ Connection.prototype.getClient = function getClient() {
1418
1450
  * reuse it.
1419
1451
  *
1420
1452
  * #### Example:
1453
+ *
1421
1454
  * const client = await mongodb.MongoClient.connect('mongodb://localhost:27017/test');
1422
1455
  *
1423
1456
  * const conn = mongoose.createConnection().setClient(client);
@@ -1426,6 +1459,7 @@ Connection.prototype.getClient = function getClient() {
1426
1459
  * conn.readyState; // 1, means 'CONNECTED'
1427
1460
  *
1428
1461
  * @api public
1462
+ * @param {MongClient} client The Client to set to be used.
1429
1463
  * @return {Connection} this
1430
1464
  */
1431
1465
 
@@ -1443,16 +1477,20 @@ Connection.prototype.setClient = function setClient(client) {
1443
1477
  this._connectionString = client.s.url;
1444
1478
  _setClient(this, client, {}, client.s.options.dbName);
1445
1479
 
1480
+ for (const model of Object.values(this.models)) {
1481
+ // Errors handled internally, so safe to ignore error
1482
+ model.init(function $modelInitNoop() {});
1483
+ }
1484
+
1446
1485
  return this;
1447
1486
  };
1448
1487
 
1449
1488
  /**
1450
- *
1451
1489
  * Syncs all the indexes for the models registered with this connection.
1452
1490
  *
1453
- * @param {Object} options
1454
- * @param {Boolean} options.continueOnError `false` by default. If set to `true`, mongoose will not throw an error if one model syncing failed, and will return an object where the keys are the names of the models, and the values are the results/errors for each model.
1455
- * @return {Promise} Returns a Promise, when the Promise resolves the value is a list of the dropped indexes.
1491
+ * @param {Object} [options]
1492
+ * @param {Boolean} [options.continueOnError] `false` by default. If set to `true`, mongoose will not throw an error if one model syncing failed, and will return an object where the keys are the names of the models, and the values are the results/errors for each model.
1493
+ * @return {Promise<Object>} Returns a Promise, when the Promise resolves the value is a list of the dropped indexes.
1456
1494
  */
1457
1495
  Connection.prototype.syncIndexes = async function syncIndexes(options = {}) {
1458
1496
  const result = {};
@@ -20,6 +20,13 @@ class ChangeStream extends EventEmitter {
20
20
  this.pipeline = pipeline;
21
21
  this.options = options;
22
22
 
23
+ if (options && options.hydrate && !options.model) {
24
+ throw new Error(
25
+ 'Cannot create change stream with `hydrate: true` ' +
26
+ 'unless calling `Model.watch()`'
27
+ );
28
+ }
29
+
23
30
  // This wrapper is necessary because of buffering.
24
31
  changeStreamThunk((err, driverChangeStream) => {
25
32
  if (err != null) {
@@ -46,7 +53,12 @@ class ChangeStream extends EventEmitter {
46
53
  });
47
54
 
48
55
  ['close', 'change', 'end', 'error'].forEach(ev => {
49
- this.driverChangeStream.on(ev, data => this.emit(ev, data));
56
+ this.driverChangeStream.on(ev, data => {
57
+ if (data.fullDocument != null && this.options && this.options.hydrate) {
58
+ data.fullDocument = this.options.model.hydrate(data.fullDocument);
59
+ }
60
+ this.emit(ev, data);
61
+ });
50
62
  });
51
63
  });
52
64
 
@@ -69,6 +81,32 @@ class ChangeStream extends EventEmitter {
69
81
  }
70
82
 
71
83
  next(cb) {
84
+ if (this.options && this.options.hydrate) {
85
+ if (cb != null) {
86
+ const originalCb = cb;
87
+ cb = (err, data) => {
88
+ if (err != null) {
89
+ return originalCb(err);
90
+ }
91
+ if (data.fullDocument != null) {
92
+ data.fullDocument = this.options.model.hydrate(data.fullDocument);
93
+ }
94
+ return originalCb(null, data);
95
+ };
96
+ }
97
+
98
+ let maybePromise = this.driverChangeStream.next(cb);
99
+ if (maybePromise && typeof maybePromise.then === 'function') {
100
+ maybePromise = maybePromise.then(data => {
101
+ if (data.fullDocument != null) {
102
+ data.fullDocument = this.options.model.hydrate(data.fullDocument);
103
+ }
104
+ return data;
105
+ });
106
+ }
107
+ return maybePromise;
108
+ }
109
+
72
110
  return this.driverChangeStream.next(cb);
73
111
  }
74
112