mongoose 8.16.0 → 8.16.2

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.
@@ -506,14 +506,15 @@ function _next(ctx, cb) {
506
506
  return _nextDoc(ctx, doc, null, callback);
507
507
  }
508
508
 
509
- ctx.query.model.populate(doc, ctx._pop).then(
510
- doc => {
511
- _nextDoc(ctx, doc, ctx._pop, callback);
512
- },
513
- err => {
514
- callback(err);
509
+ _nextDoc(ctx, doc, ctx._pop, (err, doc) => {
510
+ if (err != null) {
511
+ return callback(err);
515
512
  }
516
- );
513
+ ctx.query.model.populate(doc, ctx._pop).then(
514
+ doc => callback(null, doc),
515
+ err => callback(err)
516
+ );
517
+ });
517
518
  },
518
519
  error => {
519
520
  callback(error);
package/lib/document.js CHANGED
@@ -1114,7 +1114,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1114
1114
  }
1115
1115
 
1116
1116
  if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') {
1117
- this.$set(pathName, valForKey, constructing, Object.assign({}, options, { _skipMarkModified: true }));
1117
+ this.$set(pathName, valForKey, constructing, options);
1118
1118
  $applyDefaultsToNested(this.$get(pathName), pathName, this);
1119
1119
  continue;
1120
1120
  } else if (strict) {
package/lib/model.js CHANGED
@@ -532,7 +532,7 @@ Model.prototype.$__save = function(options, callback) {
532
532
  this.$__undoReset();
533
533
  const err = this.$__.$versionError ||
534
534
  new VersionError(this, version, this.$__.modifiedPaths);
535
- return callback(err);
535
+ return callback(err, this);
536
536
  }
537
537
 
538
538
  // increment version if was successful
@@ -3454,35 +3454,36 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3454
3454
  });
3455
3455
  }
3456
3456
  } else {
3457
- let remaining = validations.length;
3458
- let validOps = [];
3457
+ let validOpIndexes = [];
3459
3458
  let validationErrors = [];
3460
3459
  const results = [];
3461
- await new Promise((resolve) => {
3462
- if (validations.length === 0) {
3463
- return resolve();
3464
- }
3465
- for (let i = 0; i < validations.length; ++i) {
3466
- validations[i]((err) => {
3467
- if (err == null) {
3468
- validOps.push(i);
3469
- } else {
3470
- validationErrors.push({ index: i, error: err });
3471
- results[i] = err;
3472
- }
3473
- if (--remaining <= 0) {
3460
+ if (validations.length > 0) {
3461
+ validOpIndexes = await Promise.all(ops.map((op, i) => {
3462
+ if (i >= validations.length) {
3463
+ return i;
3464
+ }
3465
+ return new Promise((resolve) => {
3466
+ validations[i]((err) => {
3467
+ if (err == null) {
3468
+ resolve(i);
3469
+ } else {
3470
+ validationErrors.push({ index: i, error: err });
3471
+ results[i] = err;
3472
+ }
3474
3473
  resolve();
3475
- }
3474
+ });
3476
3475
  });
3477
- }
3478
- });
3476
+ }));
3477
+ validOpIndexes = validOpIndexes.filter(index => index != null);
3478
+ } else {
3479
+ validOpIndexes = ops.map((op, i) => i);
3480
+ }
3479
3481
 
3480
3482
  validationErrors = validationErrors.
3481
3483
  sort((v1, v2) => v1.index - v2.index).
3482
3484
  map(v => v.error);
3483
3485
 
3484
- const validOpIndexes = validOps;
3485
- validOps = validOps.sort().map(index => ops[index]);
3486
+ const validOps = validOpIndexes.sort().map(index => ops[index]);
3486
3487
 
3487
3488
  if (validOps.length === 0) {
3488
3489
  if (options.throwOnValidationError && validationErrors.length) {
@@ -45,7 +45,7 @@ Object.defineProperty(SchemaStringOptions.prototype, 'enum', opts);
45
45
  Object.defineProperty(SchemaStringOptions.prototype, 'match', opts);
46
46
 
47
47
  /**
48
- * If truthy, Mongoose will add a custom setter that lowercases this string
48
+ * If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that lowercases this string
49
49
  * using JavaScript's built-in `String#toLowerCase()`.
50
50
  *
51
51
  * @api public
@@ -58,7 +58,7 @@ Object.defineProperty(SchemaStringOptions.prototype, 'match', opts);
58
58
  Object.defineProperty(SchemaStringOptions.prototype, 'lowercase', opts);
59
59
 
60
60
  /**
61
- * If truthy, Mongoose will add a custom setter that removes leading and trailing
61
+ * If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that removes leading and trailing
62
62
  * whitespace using [JavaScript's built-in `String#trim()`](https://masteringjs.io/tutorials/fundamentals/trim-string).
63
63
  *
64
64
  * @api public
@@ -177,7 +177,7 @@ SchemaBigInt.prototype.cast = function(value) {
177
177
  * ignore
178
178
  */
179
179
 
180
- SchemaBigInt.$conditionalHandlers = {
180
+ const $conditionalHandlers = {
181
181
  ...SchemaType.prototype.$conditionalHandlers,
182
182
  $gt: handleSingle,
183
183
  $gte: handleSingle,
@@ -185,6 +185,11 @@ SchemaBigInt.$conditionalHandlers = {
185
185
  $lte: handleSingle
186
186
  };
187
187
 
188
+ Object.defineProperty(SchemaBigInt.prototype, '$conditionalHandlers', {
189
+ enumerable: false,
190
+ value: $conditionalHandlers
191
+ });
192
+
188
193
  /*!
189
194
  * ignore
190
195
  */
@@ -204,7 +209,7 @@ function handleSingle(val, context) {
204
209
  SchemaBigInt.prototype.castForQuery = function($conditional, val, context) {
205
210
  let handler;
206
211
  if ($conditional != null) {
207
- handler = SchemaBigInt.$conditionalHandlers[$conditional];
212
+ handler = this.$conditionalHandlers[$conditional];
208
213
 
209
214
  if (handler) {
210
215
  return handler.call(this, val);
@@ -235,7 +235,12 @@ SchemaBoolean.prototype.cast = function(value) {
235
235
  }
236
236
  };
237
237
 
238
- SchemaBoolean.$conditionalHandlers = { ...SchemaType.prototype.$conditionalHandlers };
238
+ const $conditionalHandlers = { ...SchemaType.prototype.$conditionalHandlers };
239
+
240
+ Object.defineProperty(SchemaBoolean.prototype, '$conditionalHandlers', {
241
+ enumerable: false,
242
+ value: $conditionalHandlers
243
+ });
239
244
 
240
245
  /**
241
246
  * Casts contents for queries.
@@ -248,7 +253,7 @@ SchemaBoolean.$conditionalHandlers = { ...SchemaType.prototype.$conditionalHandl
248
253
  SchemaBoolean.prototype.castForQuery = function($conditional, val, context) {
249
254
  let handler;
250
255
  if ($conditional != null) {
251
- handler = SchemaBoolean.$conditionalHandlers[$conditional];
256
+ handler = this.$conditionalHandlers[$conditional];
252
257
 
253
258
  if (handler) {
254
259
  return handler.call(this, val);
@@ -259,7 +259,7 @@ function handleSingle(val, context) {
259
259
  return this.castForQuery(null, val, context);
260
260
  }
261
261
 
262
- SchemaBuffer.prototype.$conditionalHandlers = {
262
+ const $conditionalHandlers = {
263
263
  ...SchemaType.prototype.$conditionalHandlers,
264
264
  $bitsAllClear: handleBitwiseOperator,
265
265
  $bitsAnyClear: handleBitwiseOperator,
@@ -271,6 +271,12 @@ SchemaBuffer.prototype.$conditionalHandlers = {
271
271
  $lte: handleSingle
272
272
  };
273
273
 
274
+ Object.defineProperty(SchemaBuffer.prototype, '$conditionalHandlers', {
275
+ enumerable: false,
276
+ value: $conditionalHandlers
277
+ });
278
+
279
+
274
280
  /**
275
281
  * Casts contents for queries.
276
282
  *
@@ -389,7 +389,7 @@ function handleSingle(val) {
389
389
  return this.cast(val);
390
390
  }
391
391
 
392
- SchemaDate.prototype.$conditionalHandlers = {
392
+ const $conditionalHandlers = {
393
393
  ...SchemaType.prototype.$conditionalHandlers,
394
394
  $gt: handleSingle,
395
395
  $gte: handleSingle,
@@ -397,6 +397,10 @@ SchemaDate.prototype.$conditionalHandlers = {
397
397
  $lte: handleSingle
398
398
  };
399
399
 
400
+ Object.defineProperty(SchemaDate.prototype, '$conditionalHandlers', {
401
+ enumerable: false,
402
+ value: $conditionalHandlers
403
+ });
400
404
 
401
405
  /**
402
406
  * Casts contents for queries.
@@ -214,7 +214,7 @@ function handleSingle(val) {
214
214
  return this.cast(val);
215
215
  }
216
216
 
217
- SchemaDecimal128.prototype.$conditionalHandlers = {
217
+ const $conditionalHandlers = {
218
218
  ...SchemaType.prototype.$conditionalHandlers,
219
219
  $gt: handleSingle,
220
220
  $gte: handleSingle,
@@ -222,6 +222,11 @@ SchemaDecimal128.prototype.$conditionalHandlers = {
222
222
  $lte: handleSingle
223
223
  };
224
224
 
225
+ Object.defineProperty(SchemaDecimal128.prototype, '$conditionalHandlers', {
226
+ enumerable: false,
227
+ value: $conditionalHandlers
228
+ });
229
+
225
230
  /**
226
231
  * Returns this schema type's representation in a JSON schema.
227
232
  *
@@ -197,7 +197,7 @@ function handleSingle(val) {
197
197
  return this.cast(val);
198
198
  }
199
199
 
200
- SchemaDouble.prototype.$conditionalHandlers = {
200
+ const $conditionalHandlers = {
201
201
  ...SchemaType.prototype.$conditionalHandlers,
202
202
  $gt: handleSingle,
203
203
  $gte: handleSingle,
@@ -205,6 +205,11 @@ SchemaDouble.prototype.$conditionalHandlers = {
205
205
  $lte: handleSingle
206
206
  };
207
207
 
208
+ Object.defineProperty(SchemaDouble.prototype, '$conditionalHandlers', {
209
+ enumerable: false,
210
+ value: $conditionalHandlers
211
+ });
212
+
208
213
  /**
209
214
  * Returns this schema type's representation in a JSON schema.
210
215
  *
@@ -197,7 +197,7 @@ SchemaInt32.prototype.cast = function(value) {
197
197
  * ignore
198
198
  */
199
199
 
200
- SchemaInt32.$conditionalHandlers = {
200
+ const $conditionalHandlers = {
201
201
  ...SchemaType.prototype.$conditionalHandlers,
202
202
  $gt: handleSingle,
203
203
  $gte: handleSingle,
@@ -209,6 +209,11 @@ SchemaInt32.$conditionalHandlers = {
209
209
  $bitsAnySet: handleBitwiseOperator
210
210
  };
211
211
 
212
+ Object.defineProperty(SchemaInt32.prototype, '$conditionalHandlers', {
213
+ enumerable: false,
214
+ value: $conditionalHandlers
215
+ });
216
+
212
217
  /*!
213
218
  * ignore
214
219
  */
@@ -228,7 +233,7 @@ function handleSingle(val, context) {
228
233
  SchemaInt32.prototype.castForQuery = function($conditional, val, context) {
229
234
  let handler;
230
235
  if ($conditional != null) {
231
- handler = SchemaInt32.$conditionalHandlers[$conditional];
236
+ handler = this.$conditionalHandlers[$conditional];
232
237
 
233
238
  if (handler) {
234
239
  return handler.call(this, val);
@@ -124,6 +124,19 @@ SchemaMixed.prototype.castForQuery = function($cond, val) {
124
124
  return val;
125
125
  };
126
126
 
127
+ /**
128
+ * Returns this schema type's representation in a JSON schema.
129
+ *
130
+ * @param [options]
131
+ * @param [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`.
132
+ * @returns {Object} JSON schema properties
133
+ */
134
+
135
+ // eslint-disable-next-line no-unused-vars
136
+ SchemaMixed.prototype.toJSONSchema = function toJSONSchema(_options) {
137
+ return {};
138
+ };
139
+
127
140
  /*!
128
141
  * Module exports.
129
142
  */
@@ -400,7 +400,7 @@ function handleArray(val) {
400
400
  });
401
401
  }
402
402
 
403
- SchemaNumber.prototype.$conditionalHandlers = {
403
+ const $conditionalHandlers = {
404
404
  ...SchemaType.prototype.$conditionalHandlers,
405
405
  $bitsAllClear: handleBitwiseOperator,
406
406
  $bitsAnyClear: handleBitwiseOperator,
@@ -413,6 +413,11 @@ SchemaNumber.prototype.$conditionalHandlers = {
413
413
  $mod: handleArray
414
414
  };
415
415
 
416
+ Object.defineProperty(SchemaNumber.prototype, '$conditionalHandlers', {
417
+ enumerable: false,
418
+ value: $conditionalHandlers
419
+ });
420
+
416
421
  /**
417
422
  * Casts contents for queries.
418
423
  *
@@ -260,7 +260,7 @@ function handleSingle(val) {
260
260
  return this.cast(val);
261
261
  }
262
262
 
263
- SchemaObjectId.prototype.$conditionalHandlers = {
263
+ const $conditionalHandlers = {
264
264
  ...SchemaType.prototype.$conditionalHandlers,
265
265
  $gt: handleSingle,
266
266
  $gte: handleSingle,
@@ -268,6 +268,11 @@ SchemaObjectId.prototype.$conditionalHandlers = {
268
268
  $lte: handleSingle
269
269
  };
270
270
 
271
+ Object.defineProperty(SchemaObjectId.prototype, '$conditionalHandlers', {
272
+ enumerable: false,
273
+ value: $conditionalHandlers
274
+ });
275
+
271
276
  /*!
272
277
  * ignore
273
278
  */
@@ -661,10 +661,8 @@ const $conditionalHandlers = {
661
661
  };
662
662
 
663
663
  Object.defineProperty(SchemaString.prototype, '$conditionalHandlers', {
664
- configurable: false,
665
664
  enumerable: false,
666
- writable: false,
667
- value: Object.freeze($conditionalHandlers)
665
+ value: $conditionalHandlers
668
666
  });
669
667
 
670
668
  /**
@@ -242,7 +242,7 @@ function handleArray(val) {
242
242
  });
243
243
  }
244
244
 
245
- SchemaUUID.prototype.$conditionalHandlers = {
245
+ const $conditionalHandlers = {
246
246
  ...SchemaType.prototype.$conditionalHandlers,
247
247
  $bitsAllClear: handleBitwiseOperator,
248
248
  $bitsAnyClear: handleBitwiseOperator,
@@ -258,6 +258,11 @@ SchemaUUID.prototype.$conditionalHandlers = {
258
258
  $nin: handleArray
259
259
  };
260
260
 
261
+ Object.defineProperty(SchemaUUID.prototype, '$conditionalHandlers', {
262
+ enumerable: false,
263
+ value: $conditionalHandlers
264
+ });
265
+
261
266
  /**
262
267
  * Casts contents for queries.
263
268
  *
package/lib/schemaType.js CHANGED
@@ -695,10 +695,13 @@ SchemaType.prototype.transform = function(fn) {
695
695
  * console.log(user.email); // 'avenue@q.com'
696
696
  * User.updateOne({ _id: _id }, { $set: { email: 'AVENUE@Q.COM' } }); // update to 'avenue@q.com'
697
697
  *
698
+ * // Setters also transform query filters
699
+ * const user = await User.find({ email: 'AVENUE@Q.COM' }); // query for 'avenue@q.com'
700
+ *
698
701
  * As you can see above, setters allow you to transform the data before it
699
702
  * stored in MongoDB, or before executing a query.
700
703
  *
701
- * _NOTE: we could have also just used the built-in `lowercase: true` SchemaType option instead of defining our own function._
704
+ * _NOTE: we could have also just used the built-in [`lowercase: true` SchemaType option](https://mongoosejs.com/docs/api/schemastringoptions.html#SchemaStringOptions.prototype.lowercase) instead of defining our own function._
702
705
  *
703
706
  * new Schema({ email: { type: String, lowercase: true }})
704
707
  *
@@ -1779,8 +1782,8 @@ SchemaType.prototype._duplicateKeyErrorMessage = null;
1779
1782
  * @returns {Object} JSON schema properties
1780
1783
  */
1781
1784
 
1782
- SchemaType.prototype.toJSONSchema = function toJSONSchema() {
1783
- throw new Error('Converting unsupported SchemaType to JSON Schema: ' + this.instance);
1785
+ SchemaType.prototype.toJSONSchema = function toJSONSchema(_options) { // eslint-disable-line no-unused-vars
1786
+ throw new Error(`Converting unsupported SchemaType to JSON Schema: ${this.instance} at path "${this.path}"`);
1784
1787
  };
1785
1788
 
1786
1789
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.16.0",
4
+ "version": "8.16.2",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -29,9 +29,9 @@
29
29
  "sift": "17.1.3"
30
30
  },
31
31
  "devDependencies": {
32
- "@babel/core": "7.27.4",
32
+ "@babel/core": "7.27.7",
33
33
  "@babel/preset-env": "7.27.2",
34
- "@mongodb-js/mongodb-downloader": "^0.3.9",
34
+ "@mongodb-js/mongodb-downloader": "^0.4.2",
35
35
  "@typescript-eslint/eslint-plugin": "^8.19.1",
36
36
  "@typescript-eslint/parser": "^8.19.1",
37
37
  "acquit": "1.4.0",
@@ -42,7 +42,7 @@
42
42
  "babel-loader": "8.2.5",
43
43
  "broken-link-checker": "^0.7.8",
44
44
  "buffer": "^5.6.0",
45
- "cheerio": "1.0.0",
45
+ "cheerio": "1.1.0",
46
46
  "crypto-browserify": "3.12.1",
47
47
  "dox": "1.0.0",
48
48
  "eslint": "8.57.1",
@@ -56,7 +56,7 @@
56
56
  "markdownlint-cli2": "^0.18.1",
57
57
  "marked": "15.0.12",
58
58
  "mkdirp": "^3.0.1",
59
- "mocha": "11.5.0",
59
+ "mocha": "11.7.1",
60
60
  "moment": "2.30.1",
61
61
  "mongodb-memory-server": "10.1.4",
62
62
  "mongodb-runner": "^5.8.2",
@@ -64,7 +64,7 @@
64
64
  "nyc": "15.1.0",
65
65
  "pug": "3.0.3",
66
66
  "q": "1.5.1",
67
- "sinon": "20.0.0",
67
+ "sinon": "21.0.0",
68
68
  "stream-browserify": "3.0.0",
69
69
  "tsd": "0.32.0",
70
70
  "typescript": "5.8.3",
package/types/index.d.ts CHANGED
@@ -206,7 +206,7 @@ declare module 'mongoose' {
206
206
  [k: string]: string;
207
207
  }
208
208
 
209
- export interface ToObjectOptions<THydratedDocumentType = HydratedDocument<unknown>> {
209
+ export interface ToObjectOptions<RawDocType = unknown, THydratedDocumentType = HydratedDocument<RawDocType>> {
210
210
  /** if `options.virtuals = true`, you can set `options.aliases = false` to skip applying aliases. This option is a no-op if `options.virtuals = false`. */
211
211
  aliases?: boolean;
212
212
  /** if true, replace any conventionally populated paths with the original id in the output. Has no affect on virtual populated paths. */
@@ -224,7 +224,7 @@ declare module 'mongoose' {
224
224
  /** if set, mongoose will call this function to allow you to transform the returned object */
225
225
  transform?: boolean | ((
226
226
  doc: THydratedDocumentType,
227
- ret: Record<string, any>,
227
+ ret: Default__v<Require_id<RawDocType>>,
228
228
  options: ToObjectOptions<THydratedDocumentType>
229
229
  ) => any);
230
230
  /** If true, omits fields that are excluded in this document's projection. Unless you specified a projection, this will omit any field that has `select: false` in the schema. */
@@ -505,7 +505,7 @@ declare module 'mongoose' {
505
505
  requiredPaths(invalidate?: boolean): string[];
506
506
 
507
507
  /** Sets a schema option. */
508
- set<K extends keyof SchemaOptions>(key: K, value: SchemaOptions[K], _tags?: any): this;
508
+ set<K extends keyof SchemaOptions>(key: K, value: SchemaOptions<DocType>[K], _tags?: any): this;
509
509
 
510
510
  /** Adds static "class" methods to Models compiled from this schema. */
511
511
  static<K extends keyof TStaticMethods>(name: K, fn: TStaticMethods[K]): this;
@@ -519,7 +519,7 @@ declare module 'mongoose' {
519
519
  toJSONSchema(options?: { useBsonType?: boolean }): Record<string, any>;
520
520
 
521
521
  /** Creates a virtual type with the given name. */
522
- virtual<T = HydratedDocument<DocType, TVirtuals & TInstanceMethods, TQueryHelpers>>(
522
+ virtual<T = THydratedDocumentType>(
523
523
  name: keyof TVirtuals | string,
524
524
  options?: VirtualTypeOptions<T, DocType>
525
525
  ): VirtualType<T>;
@@ -146,14 +146,14 @@ declare module 'mongoose' {
146
146
  */
147
147
  strictQuery?: boolean | 'throw';
148
148
  /** Exactly the same as the toObject option but only applies when the document's toJSON method is called. */
149
- toJSON?: ToObjectOptions<THydratedDocumentType>;
149
+ toJSON?: ToObjectOptions<DocType, THydratedDocumentType>;
150
150
  /**
151
151
  * Documents have a toObject method which converts the mongoose document into a plain JavaScript object.
152
152
  * This method accepts a few options. Instead of applying these options on a per-document basis, we may
153
153
  * declare the options at the schema level and have them applied to all of the schema's documents by
154
154
  * default.
155
155
  */
156
- toObject?: ToObjectOptions<THydratedDocumentType>;
156
+ toObject?: ToObjectOptions<DocType, THydratedDocumentType>;
157
157
  /**
158
158
  * By default, if you have an object with key 'type' in your schema, mongoose will interpret it as a
159
159
  * type declaration. However, for applications like geoJSON, the 'type' property is important. If you want to
@@ -196,13 +196,13 @@ declare module 'mongoose' {
196
196
  /** Attaches a validator that succeeds if the data string matches the given regular expression, and fails otherwise. */
197
197
  match?: RegExp | [RegExp, string] | readonly [RegExp, string];
198
198
 
199
- /** If truthy, Mongoose will add a custom setter that lowercases this string using JavaScript's built-in `String#toLowerCase()`. */
199
+ /** If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that lowercases this string using JavaScript's built-in `String#toLowerCase()`. */
200
200
  lowercase?: boolean;
201
201
 
202
- /** If truthy, Mongoose will add a custom setter that removes leading and trailing whitespace using JavaScript's built-in `String#trim()`. */
202
+ /** If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that removes leading and trailing whitespace using JavaScript's built-in `String#trim()`. */
203
203
  trim?: boolean;
204
204
 
205
- /** If truthy, Mongoose will add a custom setter that uppercases this string using JavaScript's built-in `String#toUpperCase()`. */
205
+ /** If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that uppercases this string using JavaScript's built-in `String#toUpperCase()`. */
206
206
  uppercase?: boolean;
207
207
 
208
208
  /** If set, Mongoose will add a custom validator that ensures the given string's `length` is at least the given number. */