mongoose 9.2.4 → 9.3.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.
package/lib/aggregate.js CHANGED
@@ -46,7 +46,7 @@ const validRedactStringValues = new Set(['$$DESCEND', '$$PRUNE', '$$KEEP']);
46
46
  * new Aggregate([{ $match: { _id: new mongoose.Types.ObjectId('00000000000000000000000a') } }]);
47
47
  *
48
48
  * @see MongoDB https://www.mongodb.com/docs/manual/applications/aggregation/
49
- * @see driver https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#aggregate
49
+ * @see driver https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#aggregate
50
50
  * @param {Array} [pipeline] aggregation pipeline as an array of objects
51
51
  * @param {Model|Connection} [modelOrConn] the model or connection to use with this aggregate.
52
52
  * @api public
@@ -933,7 +933,7 @@ Aggregate.prototype.option = function(value) {
933
933
  * @param {boolean} [options.useMongooseAggCursor] use experimental mongoose-specific aggregation cursor (for `eachAsync()` and other query cursor semantics)
934
934
  * @return {AggregationCursor} cursor representing this aggregation
935
935
  * @api public
936
- * @see mongodb https://mongodb.github.io/node-mongodb-native/4.9/classes/AggregationCursor.html
936
+ * @see mongodb https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html
937
937
  */
938
938
 
939
939
  Aggregate.prototype.cursor = function(options) {
@@ -952,7 +952,7 @@ Aggregate.prototype.cursor = function(options) {
952
952
  * @param {object} collation options
953
953
  * @return {Aggregate} this
954
954
  * @api public
955
- * @see mongodb https://mongodb.github.io/node-mongodb-native/4.9/interfaces/CollationOptions.html
955
+ * @see mongodb https://mongodb.github.io/node-mongodb-native/7.0/interfaces/CollationOptions.html
956
956
  */
957
957
 
958
958
  Aggregate.prototype.collation = function(collation) {
@@ -1015,7 +1015,7 @@ Aggregate.prototype.search = function(options) {
1015
1015
  *
1016
1016
  * MyModel.aggregate().match({ test: 1 }).pipeline(); // [{ $match: { test: 1 } }]
1017
1017
  *
1018
- * @return {Array} The current pipeline similar to the operation that will be executed
1018
+ * @return {PipelineStage[]} The current pipeline similar to the operation that will be executed
1019
1019
  * @api public
1020
1020
  */
1021
1021
 
@@ -1023,6 +1023,29 @@ Aggregate.prototype.pipeline = function() {
1023
1023
  return this._pipeline;
1024
1024
  };
1025
1025
 
1026
+ /**
1027
+ * Returns the current pipeline as a `$unionWith`-safe pipeline.
1028
+ * Throws if this pipeline contains `$out` or `$merge`.
1029
+ *
1030
+ * #### Example:
1031
+ *
1032
+ * const base = MyModel.aggregate().match({ test: 1 });
1033
+ * base.pipelineForUnionWith(); // [{ $match: { test: 1 } }]
1034
+ *
1035
+ * @return {Array} The current pipeline with `$unionWith` stage restrictions
1036
+ * @api public
1037
+ */
1038
+
1039
+ Aggregate.prototype.pipelineForUnionWith = function pipelineForUnionWith() {
1040
+ for (const stage of this._pipeline) {
1041
+ if (stage?.$out != null || stage?.$merge != null) {
1042
+ throw new MongooseError('Aggregate pipeline for $unionWith cannot include `$out` or `$merge` stages');
1043
+ }
1044
+ }
1045
+
1046
+ return this._pipeline;
1047
+ };
1048
+
1026
1049
  /**
1027
1050
  * Executes the aggregate pipeline on the currently bound Model.
1028
1051
  *
package/lib/connection.js CHANGED
@@ -410,11 +410,11 @@ Connection.prototype.config;
410
410
  * with specified options. Used to create [capped collections](https://www.mongodb.com/docs/manual/core/capped-collections/)
411
411
  * and [views](https://www.mongodb.com/docs/manual/core/views/) from mongoose.
412
412
  *
413
- * Options are passed down without modification to the [MongoDB driver's `createCollection()` function](https://mongodb.github.io/node-mongodb-native/4.9/classes/Db.html#createCollection)
413
+ * Options are passed down without modification to the [MongoDB driver's `createCollection()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#createCollection)
414
414
  *
415
415
  * @method createCollection
416
416
  * @param {string} collection The collection to create
417
- * @param {object} [options] see [MongoDB driver docs](https://mongodb.github.io/node-mongodb-native/4.9/classes/Db.html#createCollection)
417
+ * @param {object} [options] see [MongoDB driver docs](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#createCollection)
418
418
  * @return {Promise}
419
419
  * @api public
420
420
  */
@@ -678,7 +678,7 @@ Connection.prototype.withSession = async function withSession(executor) {
678
678
  *
679
679
  *
680
680
  * @method startSession
681
- * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/4.9/classes/MongoClient.html#startSession)
681
+ * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html#startSession)
682
682
  * @param {boolean} [options.causalConsistency=true] set to false to disable causal consistency
683
683
  * @return {Promise<ClientSession>} promise that resolves to a MongoDB driver `ClientSession`
684
684
  * @api public
@@ -701,7 +701,7 @@ Connection.prototype.startSession = async function startSession(options) {
701
701
  * async function executes successfully and attempt to retry if
702
702
  * there was a retriable error.
703
703
  *
704
- * Calls the MongoDB driver's [`session.withTransaction()`](https://mongodb.github.io/node-mongodb-native/4.9/classes/ClientSession.html#withTransaction),
704
+ * Calls the MongoDB driver's [`session.withTransaction()`](https://mongodb.github.io/node-mongodb-native/7.0/classes/ClientSession.html#withTransaction),
705
705
  * but also handles resetting Mongoose document state as shown below.
706
706
  *
707
707
  * #### Example:
@@ -1025,7 +1025,7 @@ Connection.prototype.onOpen = function() {
1025
1025
  * Opens the connection with a URI using `MongoClient.connect()`.
1026
1026
  *
1027
1027
  * @param {string} uri The URI to connect with.
1028
- * @param {object} [options] Passed on to [`MongoClient.connect`](https://mongodb.github.io/node-mongodb-native/4.9/classes/MongoClient.html#connect-1)
1028
+ * @param {object} [options] Passed on to [`MongoClient.connect`](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html#connect)
1029
1029
  * @param {boolean} [options.bufferCommands=true] Mongoose specific option. Set to false to [disable buffering](https://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection.
1030
1030
  * @param {number} [options.bufferTimeoutMS=10000] Mongoose specific option. If `bufferCommands` is true, Mongoose will throw an error after `bufferTimeoutMS` if the operation is still buffered.
1031
1031
  * @param {string} [options.dbName] The name of the database we want to use. If not provided, use database name from connection string.
@@ -1036,7 +1036,6 @@ Connection.prototype.onOpen = function() {
1036
1036
  * @param {number} [options.serverSelectionTimeoutMS] If `useUnifiedTopology = true`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds).
1037
1037
  * @param {number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation.
1038
1038
  * @param {boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
1039
- * @param {Class} [options.promiseLibrary] Sets the [underlying driver's promise library](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/MongoClientOptions.html#promiseLibrary).
1040
1039
  * @param {number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. `socketTimeoutMS` defaults to 0, which means Node.js will not time out the socket due to inactivity. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
1041
1040
  * @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.
1042
1041
  * @param {boolean} [options.autoCreate=false] Set to `true` to make Mongoose automatically call `createCollection()` on every model created on this connection.
@@ -1599,27 +1598,14 @@ Connection.prototype.deleteModel = function deleteModel(name) {
1599
1598
  *
1600
1599
  * @api public
1601
1600
  * @param {Array} [pipeline]
1602
- * @param {object} [options] passed without changes to [the MongoDB driver's `Db#watch()` function](https://mongodb.github.io/node-mongodb-native/4.9/classes/Db.html#watch)
1601
+ * @param {object} [options] passed without changes to [the MongoDB driver's `Db#watch()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#watch)
1603
1602
  * @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter
1604
1603
  */
1605
1604
 
1606
1605
  Connection.prototype.watch = function watch(pipeline, options) {
1607
- const changeStreamThunk = cb => {
1608
- immediate(() => {
1609
- if (this.readyState === STATES.connecting) {
1610
- this.once('open', function() {
1611
- const driverChangeStream = this.db.watch(pipeline, options);
1612
- cb(null, driverChangeStream);
1613
- });
1614
- } else {
1615
- const driverChangeStream = this.db.watch(pipeline, options);
1616
- cb(null, driverChangeStream);
1617
- }
1618
- });
1619
- };
1606
+ const changeStreamPromise = this._waitForConnect().then(() => this.db.watch(pipeline, options));
1620
1607
 
1621
- const changeStream = new ChangeStream(changeStreamThunk, pipeline, options);
1622
- return changeStream;
1608
+ return new ChangeStream(changeStreamPromise, pipeline, options);
1623
1609
  };
1624
1610
 
1625
1611
  /**
@@ -1702,7 +1688,7 @@ Connection.prototype.optionsProvideAuthenticationData = function optionsProvideA
1702
1688
  };
1703
1689
 
1704
1690
  /**
1705
- * Returns the [MongoDB driver `MongoClient`](https://mongodb.github.io/node-mongodb-native/4.9/classes/MongoClient.html) instance
1691
+ * Returns the [MongoDB driver `MongoClient`](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html) instance
1706
1692
  * that this connection uses to talk to MongoDB.
1707
1693
  *
1708
1694
  * #### Example:
@@ -1721,7 +1707,7 @@ Connection.prototype.getClient = function getClient() {
1721
1707
  };
1722
1708
 
1723
1709
  /**
1724
- * Set the [MongoDB driver `MongoClient`](https://mongodb.github.io/node-mongodb-native/4.9/classes/MongoClient.html) instance
1710
+ * Set the [MongoDB driver `MongoClient`](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html) instance
1725
1711
  * that this connection uses to talk to MongoDB. This is useful if you already have a MongoClient instance, and want to
1726
1712
  * reuse it.
1727
1713
  *
@@ -217,7 +217,7 @@ AggregationCursor.prototype._markError = function(error) {
217
217
  * @api public
218
218
  * @method close
219
219
  * @emits "close"
220
- * @see AggregationCursor.close https://mongodb.github.io/node-mongodb-native/4.9/classes/AggregationCursor.html#close
220
+ * @see AggregationCursor.close https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#close
221
221
  */
222
222
 
223
223
  AggregationCursor.prototype.close = async function close() {
@@ -386,7 +386,7 @@ function _transformForAsyncIterator(doc) {
386
386
  }
387
387
 
388
388
  /**
389
- * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/4.9/classes/AggregationCursor.html#addCursorFlag).
389
+ * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#addCursorFlag).
390
390
  * Useful for setting the `noCursorTimeout` and `tailable` flags.
391
391
  *
392
392
  * @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag
@@ -18,7 +18,7 @@ const driverChangeStreamEvents = ['close', 'change', 'end', 'error', 'resumeToke
18
18
  */
19
19
 
20
20
  class ChangeStream extends EventEmitter {
21
- constructor(changeStreamThunk, pipeline, options) {
21
+ constructor(changeStreamPromise, pipeline, options) {
22
22
  super();
23
23
 
24
24
  this.driverChangeStream = null;
@@ -35,36 +35,22 @@ class ChangeStream extends EventEmitter {
35
35
  );
36
36
  }
37
37
 
38
- let syncError = null;
39
- this.$driverChangeStreamPromise = new Promise((resolve, reject) => {
40
- // This wrapper is necessary because of buffering.
41
- try {
42
- changeStreamThunk((err, driverChangeStream) => {
43
- if (err != null) {
44
- this.errored = true;
45
- this.emit('error', err);
46
- return reject(err);
47
- }
48
-
49
- this.driverChangeStream = driverChangeStream;
50
- this.emit('ready');
51
- resolve();
52
- });
53
- } catch (err) {
54
- syncError = err;
38
+ this.$driverChangeStreamPromise = changeStreamPromise.then(
39
+ driverChangeStream => {
40
+ this.driverChangeStream = driverChangeStream;
41
+ // Use setImmediate so the stream pump (_read) has a chance to run and
42
+ // the driver cursor initializes before 'ready' resolves. Without this,
43
+ // changes emitted immediately after 'ready' can be missed because the
44
+ // underlying cursor hasn't sent its initial aggregate to MongoDB yet.
45
+ setImmediate(() => this.emit('ready'));
46
+ return this;
47
+ },
48
+ err => {
55
49
  this.errored = true;
56
50
  this.emit('error', err);
57
- reject(err);
51
+ throw err;
58
52
  }
59
- });
60
-
61
- // Because a ChangeStream is an event emitter, there's no way to register an 'error' handler
62
- // that catches errors which occur in the constructor, unless we force sync errors into async
63
- // errors with setImmediate(). For cleaner stack trace, we just immediately throw any synchronous
64
- // errors that occurred with changeStreamThunk().
65
- if (syncError != null) {
66
- throw syncError;
67
- }
53
+ );
68
54
  }
69
55
 
70
56
  _bindEvents() {
@@ -114,7 +100,20 @@ class ChangeStream extends EventEmitter {
114
100
  if (this.errored) {
115
101
  throw new MongooseError('Cannot call hasNext() on errored ChangeStream');
116
102
  }
117
- return this.driverChangeStream.hasNext(cb);
103
+
104
+ if (this.driverChangeStream != null) {
105
+ return this.driverChangeStream.hasNext(cb);
106
+ }
107
+
108
+ return this.$driverChangeStreamPromise.then(
109
+ () => this.driverChangeStream.hasNext(cb),
110
+ err => {
111
+ if (cb != null) {
112
+ return cb(err);
113
+ }
114
+ throw err;
115
+ }
116
+ );
118
117
  }
119
118
 
120
119
  next(cb) {
@@ -135,7 +134,20 @@ class ChangeStream extends EventEmitter {
135
134
  };
136
135
  }
137
136
 
138
- let maybePromise = this.driverChangeStream.next(cb);
137
+ let maybePromise;
138
+ if (this.driverChangeStream != null) {
139
+ maybePromise = this.driverChangeStream.next(cb);
140
+ } else {
141
+ maybePromise = this.$driverChangeStreamPromise.then(
142
+ () => this.driverChangeStream.next(cb),
143
+ err => {
144
+ if (cb != null) {
145
+ return cb(err);
146
+ }
147
+ throw err;
148
+ }
149
+ );
150
+ }
139
151
  if (typeof maybePromise?.then === 'function') {
140
152
  maybePromise = maybePromise.then(data => {
141
153
  if (data.fullDocument != null) {
@@ -147,7 +159,19 @@ class ChangeStream extends EventEmitter {
147
159
  return maybePromise;
148
160
  }
149
161
 
150
- return this.driverChangeStream.next(cb);
162
+ if (this.driverChangeStream != null) {
163
+ return this.driverChangeStream.next(cb);
164
+ }
165
+
166
+ return this.$driverChangeStreamPromise.then(
167
+ () => this.driverChangeStream.next(cb),
168
+ err => {
169
+ if (cb != null) {
170
+ return cb(err);
171
+ }
172
+ throw err;
173
+ }
174
+ );
151
175
  }
152
176
 
153
177
  addListener(event, handler) {
@@ -174,10 +198,6 @@ class ChangeStream extends EventEmitter {
174
198
  return super.once(event, handler);
175
199
  }
176
200
 
177
- _queue(cb) {
178
- this.once('ready', () => cb());
179
- }
180
-
181
201
  close() {
182
202
  this.closed = true;
183
203
  if (this.driverChangeStream) {
@@ -227,7 +227,7 @@ QueryCursor.prototype._markError = function(error) {
227
227
  * @api public
228
228
  * @method close
229
229
  * @emits close
230
- * @see AggregationCursor.close https://mongodb.github.io/node-mongodb-native/4.9/classes/AggregationCursor.html#close
230
+ * @see AggregationCursor.close https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#close
231
231
  */
232
232
 
233
233
  QueryCursor.prototype.close = async function close() {
@@ -368,7 +368,7 @@ QueryCursor.prototype.eachAsync = function(fn, opts) {
368
368
  QueryCursor.prototype.options;
369
369
 
370
370
  /**
371
- * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/4.9/classes/FindCursor.html#addCursorFlag).
371
+ * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/7.0/classes/FindCursor.html#addCursorFlag).
372
372
  * Useful for setting the `noCursorTimeout` and `tailable` flags.
373
373
  *
374
374
  * @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag