mongoose 8.3.0 → 8.3.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.
package/lib/document.js CHANGED
@@ -1053,7 +1053,11 @@ Document.prototype.$set = function $set(path, val, type, options) {
1053
1053
  if (path.$__isNested) {
1054
1054
  path = path.toObject();
1055
1055
  } else {
1056
- path = path._doc;
1056
+ // This ternary is to support gh-7898 (copying virtuals if same schema)
1057
+ // while not breaking gh-10819, which for some reason breaks if we use toObject()
1058
+ path = path.$__schema === this.$__schema
1059
+ ? applyVirtuals(path, { ...path._doc })
1060
+ : path._doc;
1057
1061
  }
1058
1062
  }
1059
1063
  if (path == null) {
@@ -4087,6 +4091,7 @@ function applyVirtuals(self, json, options, toObjectOptions) {
4087
4091
  ? toObjectOptions.aliases
4088
4092
  : true;
4089
4093
 
4094
+ options = options || {};
4090
4095
  let virtualsToApply = null;
4091
4096
  if (Array.isArray(options.virtuals)) {
4092
4097
  virtualsToApply = new Set(options.virtuals);
@@ -4103,7 +4108,6 @@ function applyVirtuals(self, json, options, toObjectOptions) {
4103
4108
  return json;
4104
4109
  }
4105
4110
 
4106
- options = options || {};
4107
4111
  for (i = 0; i < numPaths; ++i) {
4108
4112
  path = paths[i];
4109
4113
 
@@ -4184,7 +4188,12 @@ function applyGetters(self, json, options) {
4184
4188
  for (let ii = 0; ii < plen; ++ii) {
4185
4189
  part = parts[ii];
4186
4190
  v = cur[part];
4187
- if (ii === last) {
4191
+ // If we've reached a non-object part of the branch, continuing would
4192
+ // cause "Cannot create property 'foo' on string 'bar'" error.
4193
+ // Necessary for mongoose-intl plugin re: gh-14446
4194
+ if (branch != null && typeof branch !== 'object') {
4195
+ break;
4196
+ } else if (ii === last) {
4188
4197
  const val = self.$get(path);
4189
4198
  branch[part] = clone(val, options);
4190
4199
  if (Array.isArray(branch[part]) && schema.paths[path].$embeddedSchemaType) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  module.exports = applyEmbeddedDiscriminators;
4
4
 
5
- function applyEmbeddedDiscriminators(schema, seen = new WeakSet()) {
5
+ function applyEmbeddedDiscriminators(schema, seen = new WeakSet(), overwriteExisting = false) {
6
6
  if (seen.has(schema)) {
7
7
  return;
8
8
  }
@@ -16,13 +16,20 @@ function applyEmbeddedDiscriminators(schema, seen = new WeakSet()) {
16
16
  if (!schemaType.schema._applyDiscriminators) {
17
17
  continue;
18
18
  }
19
- if (schemaType._appliedDiscriminators) {
19
+ if (schemaType._appliedDiscriminators && !overwriteExisting) {
20
20
  continue;
21
21
  }
22
22
  for (const discriminatorKey of schemaType.schema._applyDiscriminators.keys()) {
23
- const discriminatorSchema = schemaType.schema._applyDiscriminators.get(discriminatorKey);
23
+ const {
24
+ schema: discriminatorSchema,
25
+ options
26
+ } = schemaType.schema._applyDiscriminators.get(discriminatorKey);
24
27
  applyEmbeddedDiscriminators(discriminatorSchema, seen);
25
- schemaType.discriminator(discriminatorKey, discriminatorSchema);
28
+ schemaType.discriminator(
29
+ discriminatorKey,
30
+ discriminatorSchema,
31
+ overwriteExisting ? { ...options, overwriteExisting: true } : options
32
+ );
26
33
  }
27
34
  schemaType._appliedDiscriminators = true;
28
35
  }
@@ -21,7 +21,7 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
21
21
  * ignore
22
22
  */
23
23
 
24
- module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks) {
24
+ module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks, overwriteExisting) {
25
25
  if (!(schema && schema.instanceOfSchema)) {
26
26
  throw new Error('You must pass a valid discriminator Schema');
27
27
  }
@@ -205,7 +205,7 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
205
205
 
206
206
  model.schema.discriminators[name] = schema;
207
207
 
208
- if (model.discriminators[name] && !schema.options.overwriteModels) {
208
+ if (model.discriminators[name] && !schema.options.overwriteModels && !overwriteExisting) {
209
209
  throw new Error('Discriminator with name "' + name + '" already exists');
210
210
  }
211
211
 
@@ -101,8 +101,8 @@ module.exports = function assignVals(o) {
101
101
  valueToSet = numDocs(rawIds[i]);
102
102
  } else if (Array.isArray(o.match)) {
103
103
  valueToSet = Array.isArray(rawIds[i]) ?
104
- rawIds[i].filter(sift(o.match[i])) :
105
- [rawIds[i]].filter(sift(o.match[i]))[0];
104
+ rawIds[i].filter(v => v == null || sift(o.match[i])(v)) :
105
+ [rawIds[i]].filter(v => v == null || sift(o.match[i])(v))[0];
106
106
  } else {
107
107
  valueToSet = rawIds[i];
108
108
  }
package/lib/model.js CHANGED
@@ -23,6 +23,7 @@ const VersionError = require('./error/version');
23
23
  const ParallelSaveError = require('./error/parallelSave');
24
24
  const applyDefaultsHelper = require('./helpers/document/applyDefaults');
25
25
  const applyDefaultsToPOJO = require('./helpers/model/applyDefaultsToPOJO');
26
+ const applyEmbeddedDiscriminators = require('./helpers/discriminator/applyEmbeddedDiscriminators');
26
27
  const applyHooks = require('./helpers/model/applyHooks');
27
28
  const applyMethods = require('./helpers/model/applyMethods');
28
29
  const applyProjection = require('./helpers/projection/applyProjection');
@@ -375,7 +376,9 @@ Model.prototype.$__handleSave = function(options, callback) {
375
376
  }
376
377
  minimize(updateOp[key]);
377
378
  if (Object.keys(updateOp[key]).length === 0) {
378
- updateOp[key] = null;
379
+ delete updateOp[key];
380
+ update.$unset = update.$unset || {};
381
+ update.$unset[key] = 1;
379
382
  }
380
383
  }
381
384
  }
@@ -4746,7 +4749,7 @@ function _assign(model, vals, mod, assignmentOpts) {
4746
4749
  }
4747
4750
  // flag each as result of population
4748
4751
  if (!lean) {
4749
- val.$__.wasPopulated = val.$__.wasPopulated || true;
4752
+ val.$__.wasPopulated = val.$__.wasPopulated || { value: _val };
4750
4753
  }
4751
4754
  }
4752
4755
  }
@@ -4893,6 +4896,34 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4893
4896
  return model;
4894
4897
  };
4895
4898
 
4899
+ /**
4900
+ * Update this model to use the new connection, including updating all internal
4901
+ * references and creating a new `Collection` instance using the new connection.
4902
+ * Not for external use, only used by `setDriver()` to ensure that you can still
4903
+ * call `setDriver()` after creating a model using `mongoose.model()`.
4904
+ *
4905
+ * @param {Connection} newConnection the new connection to use
4906
+ * @api private
4907
+ */
4908
+
4909
+ Model.$__updateConnection = function $__updateConnection(newConnection) {
4910
+ this.db = newConnection;
4911
+ this.prototype.db = newConnection;
4912
+ this.prototype[modelDbSymbol] = newConnection;
4913
+
4914
+ const collection = newConnection.collection(
4915
+ this.collection.collectionName,
4916
+ this.collection.opts
4917
+ );
4918
+
4919
+ this.prototype.collection = collection;
4920
+ this.prototype.$collection = collection;
4921
+ this.prototype[modelCollectionSymbol] = collection;
4922
+
4923
+ this.collection = collection;
4924
+ this.$__collection = collection;
4925
+ };
4926
+
4896
4927
  /**
4897
4928
  * Register custom query methods for this model
4898
4929
  *
@@ -5001,6 +5032,14 @@ Model.__subclass = function subclass(conn, schema, collection) {
5001
5032
 
5002
5033
  Model.recompileSchema = function recompileSchema() {
5003
5034
  this.prototype.$__setSchema(this.schema);
5035
+
5036
+ if (this.schema._applyDiscriminators != null) {
5037
+ for (const disc of this.schema._applyDiscriminators.keys()) {
5038
+ this.discriminator(disc, this.schema._applyDiscriminators.get(disc));
5039
+ }
5040
+ }
5041
+
5042
+ applyEmbeddedDiscriminators(this.schema, new WeakSet(), true);
5004
5043
  };
5005
5044
 
5006
5045
  /**
package/lib/mongoose.js CHANGED
@@ -166,9 +166,19 @@ Mongoose.prototype.setDriver = function setDriver(driver) {
166
166
  _mongoose.__driver = driver;
167
167
 
168
168
  const Connection = driver.Connection;
169
+ const oldDefaultConnection = _mongoose.connections[0];
169
170
  _mongoose.connections = [new Connection(_mongoose)];
170
171
  _mongoose.connections[0].models = _mongoose.models;
171
172
 
173
+ // Update all models that pointed to the old default connection to
174
+ // the new default connection, including collections
175
+ for (const model of Object.values(_mongoose.models)) {
176
+ if (model.db !== oldDefaultConnection) {
177
+ continue;
178
+ }
179
+ model.$__updateConnection(_mongoose.connections[0]);
180
+ }
181
+
172
182
  return _mongoose;
173
183
  };
174
184
 
@@ -627,7 +637,11 @@ Mongoose.prototype._model = function(name, schema, collection, options) {
627
637
 
628
638
  if (schema._applyDiscriminators != null) {
629
639
  for (const disc of schema._applyDiscriminators.keys()) {
630
- model.discriminator(disc, schema._applyDiscriminators.get(disc));
640
+ const {
641
+ schema: discriminatorSchema,
642
+ options
643
+ } = schema._applyDiscriminators.get(disc);
644
+ model.discriminator(disc, discriminatorSchema, options);
631
645
  }
632
646
  }
633
647
 
@@ -447,19 +447,9 @@ SchemaDocumentArray.prototype.cast = function(value, doc, init, prev, options) {
447
447
 
448
448
  const Constructor = getConstructor(this.casterConstructor, rawArray[i]);
449
449
 
450
- // Check if the document has a different schema (re gh-3701)
451
- if (rawArray[i].$__ != null && !(rawArray[i] instanceof Constructor)) {
452
- const spreadDoc = handleSpreadDoc(rawArray[i], true);
453
- if (rawArray[i] !== spreadDoc) {
454
- rawArray[i] = spreadDoc;
455
- } else {
456
- rawArray[i] = rawArray[i].toObject({
457
- transform: false,
458
- // Special case: if different model, but same schema, apply virtuals
459
- // re: gh-7898
460
- virtuals: rawArray[i].schema === Constructor.schema
461
- });
462
- }
450
+ const spreadDoc = handleSpreadDoc(rawArray[i], true);
451
+ if (rawArray[i] !== spreadDoc) {
452
+ rawArray[i] = spreadDoc;
463
453
  }
464
454
 
465
455
  if (rawArray[i] instanceof Subdocument) {
@@ -325,7 +325,7 @@ SchemaSubdocument.prototype.discriminator = function(name, schema, options) {
325
325
  schema = schema.clone();
326
326
  }
327
327
 
328
- schema = discriminator(this.caster, name, schema, value);
328
+ schema = discriminator(this.caster, name, schema, value, null, null, options.overwriteExisting);
329
329
 
330
330
  this.caster.discriminators[name] = _createConstructor(schema, this.caster);
331
331
 
package/lib/schema.js CHANGED
@@ -626,12 +626,18 @@ Schema.prototype.defaultOptions = function(options) {
626
626
  *
627
627
  * @param {String} name the name of the discriminator
628
628
  * @param {Schema} schema the discriminated Schema
629
+ * @param {Object} [options] discriminator options
630
+ * @param {String} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
631
+ * @param {Boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning.
632
+ * @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.
633
+ * @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.
634
+ * @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.
629
635
  * @return {Schema} the Schema instance
630
636
  * @api public
631
637
  */
632
- Schema.prototype.discriminator = function(name, schema) {
638
+ Schema.prototype.discriminator = function(name, schema, options) {
633
639
  this._applyDiscriminators = this._applyDiscriminators || new Map();
634
- this._applyDiscriminators.set(name, schema);
640
+ this._applyDiscriminators.set(name, { schema, options });
635
641
 
636
642
  return this;
637
643
  };
@@ -1952,6 +1958,7 @@ Schema.prototype.plugin = function(fn, opts) {
1952
1958
  'got "' + (typeof fn) + '"');
1953
1959
  }
1954
1960
 
1961
+
1955
1962
  if (opts && opts.deduplicate) {
1956
1963
  for (const plugin of this.plugins) {
1957
1964
  if (plugin.fn === fn) {
@@ -2755,7 +2762,7 @@ function isArrayFilter(piece) {
2755
2762
  */
2756
2763
 
2757
2764
  Schema.prototype._preCompile = function _preCompile() {
2758
- idGetter(this);
2765
+ this.plugin(idGetter, { deduplicate: true });
2759
2766
  };
2760
2767
 
2761
2768
  /*!
package/lib/schemaType.js CHANGED
@@ -878,7 +878,7 @@ SchemaType.prototype.validateAll = function(validators) {
878
878
  *
879
879
  * schema.path('name').validate({
880
880
  * validator: function() { throw new Error('Oops!'); },
881
- * // `errors['name']` will be "Oops!"
881
+ * // `errors['name'].message` will be "Oops!"
882
882
  * message: function(props) { return props.reason.message; }
883
883
  * });
884
884
  *
@@ -1542,7 +1542,7 @@ SchemaType.prototype._castRef = function _castRef(value, doc, init) {
1542
1542
  }
1543
1543
 
1544
1544
  if (value.$__ != null) {
1545
- value.$__.wasPopulated = value.$__.wasPopulated || true;
1545
+ value.$__.wasPopulated = value.$__.wasPopulated || { value: value._id };
1546
1546
  return value;
1547
1547
  }
1548
1548
 
@@ -1568,7 +1568,7 @@ SchemaType.prototype._castRef = function _castRef(value, doc, init) {
1568
1568
  !doc.$__.populated[path].options.options ||
1569
1569
  !doc.$__.populated[path].options.options.lean) {
1570
1570
  ret = new pop.options[populateModelSymbol](value);
1571
- ret.$__.wasPopulated = true;
1571
+ ret.$__.wasPopulated = { value: ret._id };
1572
1572
  }
1573
1573
 
1574
1574
  return ret;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.3.0",
4
+ "version": "8.3.2",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -20,7 +20,7 @@
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
22
  "bson": "^6.5.0",
23
- "kareem": "2.6.0",
23
+ "kareem": "2.6.3",
24
24
  "mongodb": "6.5.0",
25
25
  "mpath": "0.9.0",
26
26
  "mquery": "5.0.0",
package/types/index.d.ts CHANGED
@@ -431,7 +431,7 @@ declare module 'mongoose' {
431
431
  fn: (
432
432
  this: T,
433
433
  next: (err?: CallbackError) => void,
434
- ops: Array<mongodb.AnyBulkWriteOperation<any> & MongooseBulkWritePerWriteOptions>,
434
+ ops: Array<AnyBulkWriteOperation<any>>,
435
435
  options?: mongodb.BulkWriteOptions & MongooseBulkWriteOptions
436
436
  ) => void | Promise<void>
437
437
  ): this;
package/types/models.d.ts CHANGED
@@ -23,13 +23,21 @@ declare module 'mongoose' {
23
23
  ): U;
24
24
  }
25
25
 
26
- interface MongooseBulkWriteOptions {
26
+ interface MongooseBulkWriteOptions extends mongodb.BulkWriteOptions {
27
+ session?: ClientSession;
27
28
  skipValidation?: boolean;
28
29
  throwOnValidationError?: boolean;
29
- timestamps?: boolean;
30
30
  strict?: boolean | 'throw';
31
31
  }
32
32
 
33
+ interface MongooseBulkSaveOptions extends mongodb.BulkWriteOptions {
34
+ timestamps?: boolean;
35
+ session?: ClientSession;
36
+ }
37
+
38
+ /**
39
+ * @deprecated use AnyBulkWriteOperation instead
40
+ */
33
41
  interface MongooseBulkWritePerWriteOptions {
34
42
  timestamps?: boolean;
35
43
  strict?: boolean | 'throw';
@@ -200,6 +208,8 @@ declare module 'mongoose' {
200
208
  hint?: mongodb.Hint;
201
209
  /** When true, creates a new document if no document matches the query. */
202
210
  upsert?: boolean;
211
+ /** When false, do not add timestamps. */
212
+ timestamps?: boolean;
203
213
  }
204
214
 
205
215
  export interface UpdateManyModel<TSchema = AnyObject> {
@@ -215,6 +225,8 @@ declare module 'mongoose' {
215
225
  hint?: mongodb.Hint;
216
226
  /** When true, creates a new document if no document matches the query. */
217
227
  upsert?: boolean;
228
+ /** When false, do not add timestamps. */
229
+ timestamps?: boolean;
218
230
  }
219
231
 
220
232
  export interface DeleteOneModel<TSchema = AnyObject> {
@@ -281,11 +293,11 @@ declare module 'mongoose' {
281
293
  */
282
294
  bulkWrite<DocContents = TRawDocType>(
283
295
  writes: Array<AnyBulkWriteOperation<DocContents extends Document ? any : (DocContents extends {} ? DocContents : any)>>,
284
- options: mongodb.BulkWriteOptions & MongooseBulkWriteOptions & { ordered: false }
296
+ options: MongooseBulkWriteOptions & { ordered: false }
285
297
  ): Promise<mongodb.BulkWriteResult & { mongoose?: { validationErrors: Error[] } }>;
286
298
  bulkWrite<DocContents = TRawDocType>(
287
299
  writes: Array<AnyBulkWriteOperation<DocContents extends Document ? any : (DocContents extends {} ? DocContents : any)>>,
288
- options?: mongodb.BulkWriteOptions & MongooseBulkWriteOptions
300
+ options?: MongooseBulkWriteOptions
289
301
  ): Promise<mongodb.BulkWriteResult>;
290
302
 
291
303
  /**
@@ -293,7 +305,7 @@ declare module 'mongoose' {
293
305
  * sending multiple `save()` calls because with `bulkSave()` there is only one
294
306
  * network round trip to the MongoDB server.
295
307
  */
296
- bulkSave(documents: Array<Document>, options?: mongodb.BulkWriteOptions & { timestamps?: boolean }): Promise<mongodb.BulkWriteResult>;
308
+ bulkSave(documents: Array<Document>, options?: MongooseBulkSaveOptions): Promise<mongodb.BulkWriteResult>;
297
309
 
298
310
  /** Collection the model uses. */
299
311
  collection: Collection;
package/types/query.d.ts CHANGED
@@ -1,24 +1,7 @@
1
1
  declare module 'mongoose' {
2
2
  import mongodb = require('mongodb');
3
3
 
4
- type StringQueryTypeCasting = string | RegExp;
5
- type ObjectIdQueryTypeCasting = Types.ObjectId | string;
6
- type UUIDQueryTypeCasting = Types.UUID | string;
7
-
8
- type QueryTypeCasting<T> = T extends string
9
- ? StringQueryTypeCasting
10
- : T extends Types.ObjectId
11
- ? ObjectIdQueryTypeCasting
12
- : T extends Types.UUID
13
- ? UUIDQueryTypeCasting
14
- : T | any;
15
-
16
- export type ApplyBasicQueryCasting<T> = T | T[] | (T extends (infer U)[] ? QueryTypeCasting<U> : T);
17
- export type Condition<T> = ApplyBasicQueryCasting<QueryTypeCasting<T>> | QuerySelector<ApplyBasicQueryCasting<QueryTypeCasting<T>>>;
18
-
19
- type _FilterQuery<T> = {
20
- [P in keyof T]?: Condition<T[P]>;
21
- } & RootQuerySelector<T>;
4
+ export type Condition<T> = T | QuerySelector<T | any> | any;
22
5
 
23
6
  /**
24
7
  * Filter query to select the documents that match the query
@@ -27,7 +10,9 @@ declare module 'mongoose' {
27
10
  * { age: { $gte: 30 } }
28
11
  * ```
29
12
  */
30
- type FilterQuery<T> = _FilterQuery<T>;
13
+ type FilterQuery<T> = {
14
+ [P in keyof T]?: Condition<T[P]>;
15
+ } & RootQuerySelector<T>;
31
16
 
32
17
  type MongooseBaseQueryOptionKeys =
33
18
  | 'context'
@@ -193,9 +193,10 @@ declare module 'mongoose' {
193
193
  }
194
194
 
195
195
  interface Validator<DocType = any> {
196
- message?: string;
196
+ message?: string | ((props: ValidatorProps) => string);
197
197
  type?: string;
198
198
  validator?: ValidatorFunction<DocType>;
199
+ reason?: Error;
199
200
  }
200
201
 
201
202
  type ValidatorFunction<DocType = any> = (this: DocType, value: any, validatorProperties?: Validator) => any;
package/types/types.d.ts CHANGED
@@ -60,7 +60,7 @@ declare module 'mongoose' {
60
60
 
61
61
  class Decimal128 extends mongodb.Decimal128 { }
62
62
 
63
- class DocumentArray<T> extends Types.Array<T extends Types.Subdocument ? T : Types.Subdocument<InferId<T>> & T> {
63
+ class DocumentArray<T> extends Types.Array<T extends Types.Subdocument ? T : Types.Subdocument<InferId<T>, any, T> & T> {
64
64
  /** DocumentArray constructor */
65
65
  constructor(values: AnyObject[]);
66
66
 
@@ -83,7 +83,7 @@ declare module 'mongoose' {
83
83
  class ObjectId extends mongodb.ObjectId {
84
84
  }
85
85
 
86
- class Subdocument<IdType = any> extends Document<IdType> {
86
+ class Subdocument<IdType = any, TQueryHelpers = any, DocType = any> extends Document<IdType, TQueryHelpers, DocType> {
87
87
  $isSingleNested: true;
88
88
 
89
89
  /** Returns the top level document of this sub-document. */
@@ -6,6 +6,7 @@ declare module 'mongoose' {
6
6
  path: string;
7
7
  fullPath: string;
8
8
  value: any;
9
+ reason?: Error;
9
10
  }
10
11
 
11
12
  interface ValidatorMessageFn {