mongoose 7.2.4 → 7.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
@@ -1094,7 +1094,7 @@ Aggregate.prototype.then = function(resolve, reject) {
1094
1094
  };
1095
1095
 
1096
1096
  /**
1097
- * Executes the query returning a `Promise` which will be
1097
+ * Executes the aggregation returning a `Promise` which will be
1098
1098
  * resolved with either the doc(s) or rejected with the error.
1099
1099
  * Like [`.then()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.then), but only takes a rejection handler.
1100
1100
  * Compatible with `await`.
@@ -1108,6 +1108,21 @@ Aggregate.prototype.catch = function(reject) {
1108
1108
  return this.exec().then(null, reject);
1109
1109
  };
1110
1110
 
1111
+ /**
1112
+ * Executes the aggregate returning a `Promise` which will be
1113
+ * resolved with `.finally()` chained.
1114
+ *
1115
+ * More about [Promise `finally()` in JavaScript](https://thecodebarbarian.com/using-promise-finally-in-node-js.html).
1116
+ *
1117
+ * @param {Function} [onFinally]
1118
+ * @return {Promise}
1119
+ * @api public
1120
+ */
1121
+
1122
+ Aggregate.prototype.finally = function(onFinally) {
1123
+ return this.exec().finally(onFinally);
1124
+ };
1125
+
1111
1126
  /**
1112
1127
  * Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js)
1113
1128
  * You do not need to call this function explicitly, the JavaScript runtime
@@ -436,13 +436,13 @@ function _virtualPopulate(model, docs, options, _virtualRes) {
436
436
  data.justOne = justOne;
437
437
 
438
438
  // `match`
439
- let match = get(options, 'match', null) ||
440
- get(data, 'virtual.options.match', null) ||
439
+ const baseMatch = get(data, 'virtual.options.match', null) ||
441
440
  get(data, 'virtual.options.options.match', null);
441
+ let match = get(options, 'match', null) || baseMatch;
442
442
 
443
443
  let hasMatchFunction = typeof match === 'function';
444
444
  if (hasMatchFunction) {
445
- match = match.call(doc, doc);
445
+ match = match.call(doc, doc, data.virtual);
446
446
  }
447
447
 
448
448
  if (Array.isArray(localField) && Array.isArray(foreignField) && localField.length === foreignField.length) {
package/lib/model.js CHANGED
@@ -356,7 +356,7 @@ Model.prototype.$__handleSave = function(options, callback) {
356
356
  const optionsWithCustomValues = Object.assign({}, options, saveOptions);
357
357
  const where = this.$__where();
358
358
  const optimisticConcurrency = this.$__schema.options.optimisticConcurrency;
359
- if (optimisticConcurrency) {
359
+ if (optimisticConcurrency && !Array.isArray(optimisticConcurrency)) {
360
360
  const key = this.$__schema.options.versionKey;
361
361
  const val = this.$__getValue(key);
362
362
  if (val != null) {
@@ -479,6 +479,7 @@ function generateVersionError(doc, modifiedPaths) {
479
479
  * newProduct === product; // true
480
480
  *
481
481
  * @param {Object} [options] options optional options
482
+ * @param {Boolean} [options.ordered] saves the docs in series rather than parallel.
482
483
  * @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()).
483
484
  * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](https://mongoosejs.com/docs/guide.html#safe). Use the `w` option instead.
484
485
  * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
@@ -717,7 +718,15 @@ Model.prototype.$__delta = function() {
717
718
 
718
719
  const optimisticConcurrency = this.$__schema.options.optimisticConcurrency;
719
720
  if (optimisticConcurrency) {
720
- this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE;
721
+ if (Array.isArray(optimisticConcurrency)) {
722
+ const optCon = new Set(optimisticConcurrency);
723
+ const modPaths = this.modifiedPaths();
724
+ if (modPaths.find(path => optCon.has(path))) {
725
+ this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE;
726
+ }
727
+ } else {
728
+ this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE;
729
+ }
721
730
  }
722
731
 
723
732
  if (!dirty.length && VERSION_ALL !== this.$__.version) {
@@ -2846,25 +2855,47 @@ Model.create = async function create(doc, options) {
2846
2855
  if (args.length === 0) {
2847
2856
  return Array.isArray(doc) ? [] : null;
2848
2857
  }
2858
+ let res = [];
2859
+ if (options.ordered) {
2860
+ for (let i = 0; i < args.length; i++) {
2861
+ const doc = args[i];
2862
+ const Model = this.discriminators && doc[discriminatorKey] != null ?
2863
+ this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) :
2864
+ this;
2865
+ if (Model == null) {
2866
+ throw new MongooseError(`Discriminator "${doc[discriminatorKey]}" not ` +
2867
+ `found for model "${this.modelName}"`);
2868
+ }
2869
+ let toSave = doc;
2870
+ if (!(toSave instanceof Model)) {
2871
+ toSave = new Model(toSave);
2872
+ }
2849
2873
 
2850
- const res = await Promise.all(args.map(async doc => {
2851
- const Model = this.discriminators && doc[discriminatorKey] != null ?
2852
- this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) :
2853
- this;
2854
- if (Model == null) {
2855
- throw new MongooseError(`Discriminator "${doc[discriminatorKey]}" not ` +
2856
- `found for model "${this.modelName}"`);
2874
+ await toSave.$save(options);
2875
+ res.push(toSave);
2857
2876
  }
2858
- let toSave = doc;
2877
+ return res;
2878
+ } else {
2879
+ res = await Promise.all(args.map(async doc => {
2880
+ const Model = this.discriminators && doc[discriminatorKey] != null ?
2881
+ this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) :
2882
+ this;
2883
+ if (Model == null) {
2884
+ throw new MongooseError(`Discriminator "${doc[discriminatorKey]}" not ` +
2885
+ `found for model "${this.modelName}"`);
2886
+ }
2887
+ let toSave = doc;
2859
2888
 
2860
- if (!(toSave instanceof Model)) {
2861
- toSave = new Model(toSave);
2862
- }
2889
+ if (!(toSave instanceof Model)) {
2890
+ toSave = new Model(toSave);
2891
+ }
2892
+
2893
+ await toSave.$save(options);
2863
2894
 
2864
- await toSave.$save(options);
2895
+ return toSave;
2896
+ }));
2897
+ }
2865
2898
 
2866
- return toSave;
2867
- }));
2868
2899
 
2869
2900
  if (!Array.isArray(doc) && args.length === 1) {
2870
2901
  return res[0];
@@ -347,6 +347,18 @@ SubdocumentPath.defaultOptions = {};
347
347
 
348
348
  SubdocumentPath.set = SchemaType.set;
349
349
 
350
+ /**
351
+ * Attaches a getter for all SubdocumentPath instances
352
+ *
353
+ * @param {Function} getter
354
+ * @return {this}
355
+ * @function get
356
+ * @static
357
+ * @api public
358
+ */
359
+
360
+ SubdocumentPath.get = SchemaType.get;
361
+
350
362
  /*!
351
363
  * ignore
352
364
  */
@@ -170,6 +170,18 @@ SchemaArray.defaultOptions = {};
170
170
  */
171
171
  SchemaArray.set = SchemaType.set;
172
172
 
173
+ /**
174
+ * Attaches a getter for all Array instances
175
+ *
176
+ * @param {Function} getter
177
+ * @return {this}
178
+ * @function get
179
+ * @static
180
+ * @api public
181
+ */
182
+
183
+ SchemaArray.get = SchemaType.get;
184
+
173
185
  /*!
174
186
  * Inherits from SchemaType.
175
187
  */
@@ -62,6 +62,23 @@ SchemaBigInt._cast = castBigInt;
62
62
 
63
63
  SchemaBigInt.set = SchemaType.set;
64
64
 
65
+ /**
66
+ * Attaches a getter for all BigInt instances
67
+ *
68
+ * #### Example:
69
+ *
70
+ * // Convert bigints to numbers
71
+ * mongoose.Schema.BigInt.get(v => v == null ? v : Number(v));
72
+ *
73
+ * @param {Function} getter
74
+ * @return {this}
75
+ * @function get
76
+ * @static
77
+ * @api public
78
+ */
79
+
80
+ SchemaBigInt.get = SchemaType.get;
81
+
65
82
  /**
66
83
  * Get/set the function used to cast arbitrary values to booleans.
67
84
  *
@@ -65,6 +65,25 @@ SchemaBoolean._cast = castBoolean;
65
65
 
66
66
  SchemaBoolean.set = SchemaType.set;
67
67
 
68
+ /**
69
+ * Attaches a getter for all Boolean instances
70
+ *
71
+ * #### Example:
72
+ *
73
+ * mongoose.Schema.Boolean.get(v => v === true ? 'yes' : 'no');
74
+ *
75
+ * const Order = mongoose.model('Order', new Schema({ isPaid: Boolean }));
76
+ * new Order({ isPaid: false }).isPaid; // 'no'
77
+ *
78
+ * @param {Function} getter
79
+ * @return {this}
80
+ * @function get
81
+ * @static
82
+ * @api public
83
+ */
84
+
85
+ SchemaBoolean.get = SchemaType.get;
86
+
68
87
  /**
69
88
  * Get/set the function used to cast arbitrary values to booleans.
70
89
  *
@@ -70,6 +70,26 @@ SchemaBuffer._checkRequired = v => !!(v && v.length);
70
70
 
71
71
  SchemaBuffer.set = SchemaType.set;
72
72
 
73
+ /**
74
+ * Attaches a getter for all Buffer instances
75
+ *
76
+ * #### Example:
77
+ *
78
+ * // Always convert to string when getting an ObjectId
79
+ * mongoose.Schema.Types.Buffer.get(v => v.toString('hex'));
80
+ *
81
+ * const Model = mongoose.model('Test', new Schema({ buf: Buffer } }));
82
+ * typeof (new Model({ buf: Buffer.fromString('hello') }).buf); // 'string'
83
+ *
84
+ * @param {Function} getter
85
+ * @return {this}
86
+ * @function get
87
+ * @static
88
+ * @api public
89
+ */
90
+
91
+ SchemaBuffer.get = SchemaType.get;
92
+
73
93
  /**
74
94
  * Override the function the required validator uses to check whether a string
75
95
  * passes the `required` check.
@@ -70,6 +70,26 @@ SchemaDate._cast = castDate;
70
70
 
71
71
  SchemaDate.set = SchemaType.set;
72
72
 
73
+ /**
74
+ * Attaches a getter for all Date instances
75
+ *
76
+ * #### Example:
77
+ *
78
+ * // Always convert Dates to string
79
+ * mongoose.Date.get(v => v.toString());
80
+ *
81
+ * const Model = mongoose.model('Test', new Schema({ date: { type: Date, default: () => new Date() } }));
82
+ * typeof (new Model({}).date); // 'string'
83
+ *
84
+ * @param {Function} getter
85
+ * @return {this}
86
+ * @function get
87
+ * @static
88
+ * @api public
89
+ */
90
+
91
+ SchemaDate.get = SchemaType.get;
92
+
73
93
  /**
74
94
  * Get/set the function used to cast arbitrary values to dates.
75
95
  *
@@ -66,6 +66,23 @@ Decimal128._cast = castDecimal128;
66
66
 
67
67
  Decimal128.set = SchemaType.set;
68
68
 
69
+ /**
70
+ * Attaches a getter for all Decimal128 instances
71
+ *
72
+ * #### Example:
73
+ *
74
+ * // Automatically convert Decimal128s to Numbers
75
+ * mongoose.Schema.Decimal128.get(v => v == null ? v : Number(v));
76
+ *
77
+ * @param {Function} getter
78
+ * @return {this}
79
+ * @function get
80
+ * @static
81
+ * @api public
82
+ */
83
+
84
+ Decimal128.get = SchemaType.get;
85
+
69
86
  /**
70
87
  * Get/set the function used to cast arbitrary values to decimals.
71
88
  *
@@ -37,7 +37,7 @@ let Subdocument;
37
37
  */
38
38
 
39
39
  function DocumentArrayPath(key, schema, options, schemaOptions) {
40
- if (schema.options.timeseries) {
40
+ if (schema.options && schema.options.timeseries) {
41
41
  throw new InvalidSchemaOptionError(key, 'timeseries');
42
42
  }
43
43
  const schemaTypeIdOption = DocumentArrayPath.defaultOptions &&
@@ -605,6 +605,18 @@ DocumentArrayPath.defaultOptions = {};
605
605
 
606
606
  DocumentArrayPath.set = SchemaType.set;
607
607
 
608
+ /**
609
+ * Attaches a getter for all DocumentArrayPath instances
610
+ *
611
+ * @param {Function} getter
612
+ * @return {this}
613
+ * @function get
614
+ * @static
615
+ * @api public
616
+ */
617
+
618
+ DocumentArrayPath.get = SchemaType.get;
619
+
608
620
  /*!
609
621
  * Module exports.
610
622
  */
@@ -153,6 +153,26 @@ SchemaUUID._cast = function(value) {
153
153
  throw new CastError(SchemaUUID.schemaName, value, this.path);
154
154
  };
155
155
 
156
+ /**
157
+ * Attaches a getter for all UUID instances.
158
+ *
159
+ * #### Example:
160
+ *
161
+ * // Note that `v` is a string by default
162
+ * mongoose.Schema.UUID.get(v => v.toUpperCase());
163
+ *
164
+ * const Model = mongoose.model('Test', new Schema({ test: 'UUID' }));
165
+ * new Model({ test: uuid.v4() }).test; // UUID with all uppercase
166
+ *
167
+ * @param {Function} getter
168
+ * @return {this}
169
+ * @function get
170
+ * @static
171
+ * @api public
172
+ */
173
+
174
+ SchemaUUID.get = SchemaType.get;
175
+
156
176
  /**
157
177
  * Sets a default option for all UUID instances.
158
178
  *
package/lib/schema.js CHANGED
@@ -70,6 +70,7 @@ let id = 0;
70
70
  * - [toObject](https://mongoosejs.com/docs/guide.html#toObject) - object - no default
71
71
  * - [typeKey](https://mongoosejs.com/docs/guide.html#typeKey) - string - defaults to 'type'
72
72
  * - [validateBeforeSave](https://mongoosejs.com/docs/guide.html#validateBeforeSave) - bool - defaults to `true`
73
+ * - [validateModifiedOnly](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()) - bool - defaults to `false`
73
74
  * - [versionKey](https://mongoosejs.com/docs/guide.html#versionKey): string or object - defaults to "__v"
74
75
  * - [optimisticConcurrency](https://mongoosejs.com/docs/guide.html#optimisticConcurrency): bool - defaults to false. Set to true to enable [optimistic concurrency](https://thecodebarbarian.com/whats-new-in-mongoose-5-10-optimistic-concurrency.html).
75
76
  * - [collation](https://mongoosejs.com/docs/guide.html#collation): object - defaults to null (which means use no collation)
@@ -549,6 +550,7 @@ Schema.prototype.defaultOptions = function(options) {
549
550
  shardKey: null,
550
551
  read: null,
551
552
  validateBeforeSave: true,
553
+ validateModifiedOnly: false,
552
554
  // the following are only applied at construction time
553
555
  _id: true,
554
556
  id: id,
@@ -2192,6 +2194,7 @@ Schema.prototype.indexes = function() {
2192
2194
  * @param {Boolean|Function} [options.justOne=false] Only works with populate virtuals. If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), will be a single doc or `null`. Otherwise, the populate virtual will be an array.
2193
2195
  * @param {Boolean} [options.count=false] Only works with populate virtuals. If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), this populate virtual will contain the number of documents rather than the documents themselves when you `populate()`.
2194
2196
  * @param {Function|null} [options.get=null] Adds a [getter](https://mongoosejs.com/docs/tutorials/getters-setters.html) to this virtual to transform the populated doc.
2197
+ * @param {Object|Function} [options.match=null] Apply a default [`match` option to populate](https://mongoosejs.com/docs/populate.html#match), adding an additional filter to the populate query.
2195
2198
  * @return {VirtualType}
2196
2199
  */
2197
2200
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "7.2.4",
4
+ "version": "7.3.0",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "bson": "^5.3.0",
23
23
  "kareem": "2.5.1",
24
- "mongodb": "5.5.0",
24
+ "mongodb": "5.6.0",
25
25
  "mpath": "0.9.0",
26
26
  "mquery": "5.0.0",
27
27
  "ms": "2.1.3",
@@ -16,6 +16,9 @@ declare module 'mongoose' {
16
16
  */
17
17
  [Symbol.asyncIterator](): AsyncIterableIterator<Unpacked<ResultType>>;
18
18
 
19
+ // Returns a string representation of this aggregation.
20
+ [Symbol.toStringTag]: string;
21
+
19
22
  options: AggregateOptions;
20
23
 
21
24
  /**
@@ -73,6 +76,12 @@ declare module 'mongoose' {
73
76
  /** Appends a new $fill operator to this aggregate pipeline */
74
77
  fill(arg: PipelineStage.Fill['$fill']): this;
75
78
 
79
+ /**
80
+ * Executes the aggregation returning a `Promise` which will be
81
+ * resolved with `.finally()` chained.
82
+ */
83
+ finally: Promise<ResultType>['finally'];
84
+
76
85
  /** Appends new custom $graphLookup operator(s) to this aggregate pipeline, performing a recursive search on a collection. */
77
86
  graphLookup(options: PipelineStage.GraphLookup['$graphLookup']): this;
78
87
 
package/types/index.d.ts CHANGED
@@ -512,7 +512,7 @@ declare module 'mongoose' {
512
512
  count?: boolean;
513
513
 
514
514
  /** Add an extra match condition to `populate()`. */
515
- match?: FilterQuery<any> | Function;
515
+ match?: FilterQuery<any> | ((doc: Record<string, any>, virtual?: this) => Record<string, any> | null);
516
516
 
517
517
  /** Add a default `limit` to the `populate()` query. */
518
518
  limit?: number;
package/types/models.d.ts CHANGED
@@ -123,6 +123,7 @@ declare module 'mongoose' {
123
123
  SessionOption {
124
124
  checkKeys?: boolean;
125
125
  j?: boolean;
126
+ ordered?: boolean;
126
127
  safe?: boolean | WriteConcern;
127
128
  timestamps?: boolean | QueryTimestampsConfig;
128
129
  validateBeforeSave?: boolean;
package/types/query.d.ts CHANGED
@@ -622,7 +622,31 @@ declare module 'mongoose' {
622
622
  ): QueryWithHelpers<any, DocType, THelpers, RawDocType, 'replaceOne'>;
623
623
 
624
624
  /** Specifies which document fields to include or exclude (also known as the query "projection") */
625
- select(arg: string | string[] | Record<string, number | boolean | object>): this;
625
+ select<RawDocTypeOverride extends { [P in keyof RawDocType]?: any } = {}>(
626
+ arg: string | string[] | Record<string, number | boolean | object>
627
+ ): QueryWithHelpers<
628
+ IfEquals<
629
+ RawDocTypeOverride,
630
+ {},
631
+ ResultType,
632
+ ResultType extends any[] ?
633
+ ResultType extends HydratedDocument<any>[] ?
634
+ HydratedDocument<RawDocTypeOverride>[] :
635
+ RawDocTypeOverride[] :
636
+ ResultType extends HydratedDocument<any> ?
637
+ HydratedDocument<RawDocTypeOverride> :
638
+ RawDocTypeOverride
639
+ >,
640
+ DocType,
641
+ THelpers,
642
+ IfEquals<
643
+ RawDocTypeOverride,
644
+ {},
645
+ RawDocType,
646
+ RawDocTypeOverride
647
+ >,
648
+ QueryOp
649
+ >;
626
650
 
627
651
  /** Determines if field selection has been made. */
628
652
  selected(): boolean;
@@ -154,6 +154,11 @@ declare module 'mongoose' {
154
154
  * objects which don't pass validation, you can set validateBeforeSave to false.
155
155
  */
156
156
  validateBeforeSave?: boolean;
157
+ /**
158
+ * By default, validation will run on modified and required paths before saving to the database.
159
+ * You can choose to have Mongoose only validate modified paths by setting validateModifiedOnly to true.
160
+ */
161
+ validateModifiedOnly?: boolean;
157
162
  /**
158
163
  * The versionKey is a property set on each document when first created by Mongoose. This keys value
159
164
  * contains the internal revision of the document. The versionKey option is a string that represents