mongoose 5.11.13 → 5.11.17

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/index.d.ts CHANGED
@@ -546,7 +546,7 @@ declare module 'mongoose' {
546
546
  populated(path: string): any;
547
547
 
548
548
  /** Removes this document from the db. */
549
- remove(options?: QueryOptions): Query<any, this>;
549
+ remove(options?: QueryOptions): Promise<this>;
550
550
  remove(options?: QueryOptions, cb?: (err: CallbackError, res: any) => void): void;
551
551
 
552
552
  /** Sends a replaceOne command with this document `_id` as the query selector. */
@@ -630,11 +630,11 @@ declare module 'mongoose' {
630
630
  countDocuments(filter: FilterQuery<T>, callback?: (err: any, count: number) => void): Query<number, T>;
631
631
 
632
632
  /** Creates a new document or documents */
633
- create<DocContents = T | DocumentDefinition<T>>(doc: DocContents): Promise<T>;
634
633
  create<DocContents = T | DocumentDefinition<T>>(docs: DocContents[], options?: SaveOptions): Promise<T[]>;
634
+ create<DocContents = T | DocumentDefinition<T>>(doc: DocContents): Promise<T>;
635
635
  create<DocContents = T | DocumentDefinition<T>>(...docs: DocContents[]): Promise<T[]>;
636
- create<DocContents = T | DocumentDefinition<T>>(doc: DocContents, callback: (err: CallbackError, doc: T) => void): void;
637
636
  create<DocContents = T | DocumentDefinition<T>>(docs: DocContents[], callback: (err: CallbackError, docs: T[]) => void): void;
637
+ create<DocContents = T | DocumentDefinition<T>>(doc: DocContents, callback: (err: CallbackError, doc: T) => void): void;
638
638
 
639
639
  /**
640
640
  * Create the collection for this model. By default, if no indexes are specified,
@@ -753,6 +753,7 @@ declare module 'mongoose' {
753
753
  /** Casts and validates the given object against this model's schema, passing the given `context` to custom validators. */
754
754
  validate(callback?: (err: any) => void): Promise<void>;
755
755
  validate(optional: any, callback?: (err: any) => void): Promise<void>;
756
+ validate(optional: any, pathsToValidate: string[], callback?: (err: any) => void): Promise<void>;
756
757
 
757
758
  /** Watches the underlying collection for changes using [MongoDB change streams](https://docs.mongodb.com/manual/changeStreams/). */
758
759
  watch(pipeline?: Array<Record<string, unknown>>, options?: mongodb.ChangeStreamOptions): mongodb.ChangeStream;
@@ -844,6 +845,8 @@ declare module 'mongoose' {
844
845
 
845
846
  /** Creates a Query, applies the passed conditions, and returns the Query. */
846
847
  where(path: string, val?: any): Query<Array<T>, T>;
848
+ where(obj: object): Query<Array<T>, T>;
849
+ where(): Query<Array<T>, T>;
847
850
  }
848
851
 
849
852
  interface QueryOptions {
@@ -1037,16 +1040,20 @@ declare module 'mongoose' {
1037
1040
  useProjection?: boolean;
1038
1041
  }
1039
1042
 
1043
+ type MongooseDocumentMiddleware = 'validate' | 'save' | 'remove' | 'updateOne' | 'deleteOne' | 'init';
1040
1044
  type MongooseQueryMiddleware = 'count' | 'deleteMany' | 'deleteOne' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndRemove' | 'findOneAndUpdate' | 'remove' | 'update' | 'updateOne' | 'updateMany';
1041
1045
 
1042
- class Schema<DocType extends Document = Document, M extends Model<DocType> = Model<DocType>> extends events.EventEmitter {
1046
+ type SchemaPreOptions = { document?: boolean, query?: boolean };
1047
+ type SchemaPostOptions = { document?: boolean, query?: boolean };
1048
+
1049
+ class Schema<DocType extends Document = Document, M extends Model<DocType> = Model<DocType>, SchemaDefinitionType = undefined> extends events.EventEmitter {
1043
1050
  /**
1044
1051
  * Create a new schema
1045
1052
  */
1046
- constructor(definition?: SchemaDefinition, options?: SchemaOptions);
1053
+ constructor(definition?: SchemaDefinition<DocumentDefinition<SchemaDefinitionType>>, options?: SchemaOptions);
1047
1054
 
1048
1055
  /** Adds key path / schema type pairs to this schema. */
1049
- add(obj: SchemaDefinition | Schema, prefix?: string): this;
1056
+ add(obj: SchemaDefinition<DocumentDefinition<SchemaDefinitionType>> | Schema, prefix?: string): this;
1050
1057
 
1051
1058
  /**
1052
1059
  * Array of child schemas (from document arrays and single nested subdocs)
@@ -1113,21 +1120,33 @@ declare module 'mongoose' {
1113
1120
  plugin(fn: (schema: Schema, opts?: any) => void, opts?: any): this;
1114
1121
 
1115
1122
  /** Defines a post hook for the model. */
1116
- post<T extends Document = DocType>(method: 'validate' | 'save' | 'remove' | 'updateOne' | 'deleteOne' | 'init' | RegExp, fn: (this: T, res: any, next: (err?: CallbackError) => void) => void): this;
1117
- post<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | string | RegExp, fn: (this: T, res: any, next: (err: CallbackError) => void) => void): this;
1123
+ post<T extends Document = DocType>(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, fn: (this: T, res: any, next: (err?: CallbackError) => void) => void): this;
1124
+ post<T extends Document = DocType>(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, options: SchemaPostOptions, fn: (this: T, res: any, next: (err?: CallbackError) => void) => void): this;
1125
+ post<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | string | RegExp, fn: (this: T, res: any, next: (err: CallbackError) => void) => void): this;
1126
+ post<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | string | RegExp, options: SchemaPostOptions, fn: (this: T, res: any, next: (err: CallbackError) => void) => void): this;
1118
1127
  post<T extends Aggregate<any> = Aggregate<any>>(method: 'aggregate' | RegExp, fn: (this: T, res: Array<any>, next: (err: CallbackError) => void) => void): this;
1128
+ post<T extends Aggregate<any> = Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPostOptions, fn: (this: T, res: Array<any>, next: (err: CallbackError) => void) => void): this;
1119
1129
  post<T extends Model<DocType> = M>(method: 'insertMany' | RegExp, fn: (this: T, res: any, next: (err: CallbackError) => void) => void): this;
1130
+ post<T extends Model<DocType> = M>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: (this: T, res: any, next: (err: CallbackError) => void) => void): this;
1120
1131
 
1121
- post<T extends Document = DocType>(method: 'validate' | 'save' | 'remove' | 'updateOne' | 'deleteOne' | 'init' | RegExp, fn: (this: T, err: NativeError, res: any, next: (err?: CallbackError) => void) => void): this;
1122
- post<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | string | RegExp, fn: (this: T, err: NativeError, res: any, next: (err: CallbackError) => void) => void): this;
1132
+ post<T extends Document = DocType>(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, fn: (this: T, err: NativeError, res: any, next: (err?: CallbackError) => void) => void): this;
1133
+ post<T extends Document = DocType>(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, options: SchemaPostOptions, fn: (this: T, err: NativeError, res: any, next: (err?: CallbackError) => void) => void): this;
1134
+ post<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | string | RegExp, fn: (this: T, err: NativeError, res: any, next: (err: CallbackError) => void) => void): this;
1135
+ post<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | string | RegExp, options: SchemaPostOptions, fn: (this: T, err: NativeError, res: any, next: (err: CallbackError) => void) => void): this;
1123
1136
  post<T extends Aggregate<any> = Aggregate<any>>(method: 'aggregate' | RegExp, fn: (this: T, err: NativeError, res: Array<any>, next: (err: CallbackError) => void) => void): this;
1137
+ post<T extends Aggregate<any> = Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPostOptions, fn: (this: T, err: NativeError, res: Array<any>, next: (err: CallbackError) => void) => void): this;
1124
1138
  post<T extends Model<DocType> = M>(method: 'insertMany' | RegExp, fn: (this: T, err: NativeError, res: any, next: (err: CallbackError) => void) => void): this;
1139
+ post<T extends Model<DocType> = M>(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: (this: T, err: NativeError, res: any, next: (err: CallbackError) => void) => void): this;
1125
1140
 
1126
1141
  /** Defines a pre hook for the model. */
1127
- pre<T extends Document = DocType>(method: 'validate' | 'save' | 'remove' | 'updateOne' | 'deleteOne' | 'init' | RegExp, fn: (this: T, next: (err?: CallbackError) => void) => void): this;
1128
- pre<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | string | RegExp, fn: (this: T, next: (err: CallbackError) => void) => void): this;
1142
+ pre<T extends Document = DocType>(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, fn: (this: T, next: (err?: CallbackError) => void) => void): this;
1143
+ pre<T extends Document = DocType>(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err?: CallbackError) => void) => void): this;
1144
+ pre<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | string | RegExp, fn: (this: T, next: (err: CallbackError) => void) => void): this;
1145
+ pre<T extends Query<any, any> = Query<any, any>>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | string | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err: CallbackError) => void) => void): this;
1129
1146
  pre<T extends Aggregate<any> = Aggregate<any>>(method: 'aggregate' | RegExp, fn: (this: T, next: (err: CallbackError) => void) => void): this;
1147
+ pre<T extends Aggregate<any> = Aggregate<any>>(method: 'aggregate' | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err: CallbackError) => void) => void): this;
1130
1148
  pre<T extends Model<DocType> = M>(method: 'insertMany' | RegExp, fn: (this: T, next: (err: CallbackError) => void) => void): this;
1149
+ pre<T extends Model<DocType> = M>(method: 'insertMany' | RegExp, options: SchemaPreOptions, fn: (this: T, next: (err: CallbackError) => void) => void): this;
1131
1150
 
1132
1151
  /** Object of currently defined query helpers on this schema. */
1133
1152
  query: any;
@@ -1163,11 +1182,17 @@ declare module 'mongoose' {
1163
1182
  virtualpath(name: string): VirtualType | null;
1164
1183
  }
1165
1184
 
1185
+ type SchemaDefinitionWithBuiltInClass<T> = T extends number
1186
+ ? (typeof Number | 'number' | 'Number')
1187
+ : T extends string
1188
+ ? (typeof String | 'string' | 'String')
1189
+ : (Function | string);
1190
+
1166
1191
  type SchemaDefinitionProperty<T = undefined> = SchemaTypeOptions<T extends undefined ? any : T> |
1167
- Function |
1168
- string |
1169
- Schema |
1170
- Schema[] |
1192
+ SchemaDefinitionWithBuiltInClass<T> |
1193
+ typeof SchemaType |
1194
+ Schema<T extends Document ? T : Document<any>> |
1195
+ Schema<T extends Document ? T : Document<any>>[] |
1171
1196
  SchemaTypeOptions<T extends undefined ? any : T>[] |
1172
1197
  Function[] |
1173
1198
  SchemaDefinition<T> |
@@ -1175,7 +1200,7 @@ declare module 'mongoose' {
1175
1200
 
1176
1201
  type SchemaDefinition<T = undefined> = T extends undefined
1177
1202
  ? { [path: string]: SchemaDefinitionProperty; }
1178
- : { [path in keyof T]-?: SchemaDefinitionProperty<T[path]>; };
1203
+ : { [path in keyof T]?: SchemaDefinitionProperty<T[path]>; };
1179
1204
 
1180
1205
  interface SchemaOptions {
1181
1206
  /**
@@ -1340,7 +1365,7 @@ declare module 'mongoose' {
1340
1365
  }
1341
1366
 
1342
1367
  interface SchemaTypeOptions<T> {
1343
- type?: T;
1368
+ type?: T | SchemaDefinitionWithBuiltInClass<T>;
1344
1369
 
1345
1370
  /** Defines a virtual with the given name that gets/sets this path. */
1346
1371
  alias?: string;
@@ -1357,7 +1382,7 @@ declare module 'mongoose' {
1357
1382
  * path cannot be set to a nullish value. If a function, Mongoose calls the
1358
1383
  * function and only checks for nullish values if the function returns a truthy value.
1359
1384
  */
1360
- required?: boolean | (() => boolean) | [boolean, string];
1385
+ required?: boolean | (() => boolean) | [boolean, string] | [() => boolean, string];
1361
1386
 
1362
1387
  /**
1363
1388
  * The default value for this path. If a function, Mongoose executes the function
@@ -1421,7 +1446,7 @@ declare module 'mongoose' {
1421
1446
  set?: (value: T, schematype?: this) => any;
1422
1447
 
1423
1448
  /** array of allowed values for this path. Allowed for strings, numbers, and arrays of strings */
1424
- enum?: Array<string | number | null>
1449
+ enum?: Array<string | number | null> | { [path: string]: string | number | null };
1425
1450
 
1426
1451
  /** The default [subtype](http://bsonspec.org/spec.html) associated with this buffer when it is stored in MongoDB. Only allowed for buffer paths */
1427
1452
  subtype?: number
@@ -1769,7 +1794,7 @@ declare module 'mongoose' {
1769
1794
 
1770
1795
  type ReturnsNewDoc = { new: true } | { returnOriginal: false };
1771
1796
 
1772
- interface Query<ResultType, DocType extends Document> {
1797
+ class Query<ResultType, DocType extends Document> {
1773
1798
  _mongooseOptions: MongooseQueryOptions;
1774
1799
 
1775
1800
  /** Executes the query */
@@ -2147,6 +2172,8 @@ declare module 'mongoose' {
2147
2172
 
2148
2173
  /** Specifies a path for use with chaining. */
2149
2174
  where(path: string, val?: any): this;
2175
+ where(obj: object): this;
2176
+ where(): this;
2150
2177
 
2151
2178
  /** Defines a `$within` or `$geoWithin` argument for geo-spatial queries. */
2152
2179
  within(val?: any): this;
package/lib/aggregate.js CHANGED
@@ -8,7 +8,7 @@ const AggregationCursor = require('./cursor/AggregationCursor');
8
8
  const Query = require('./query');
9
9
  const applyGlobalMaxTimeMS = require('./helpers/query/applyGlobalMaxTimeMS');
10
10
  const promiseOrCallback = require('./helpers/promiseOrCallback');
11
- const stringifyAccumulatorOptions = require('./helpers/aggregate/stringifyAccumulatorOptions');
11
+ const stringifyFunctionOperators = require('./helpers/aggregate/stringifyFunctionOperators');
12
12
  const util = require('util');
13
13
  const utils = require('./utils');
14
14
  const read = Query.prototype.read;
@@ -983,7 +983,7 @@ Aggregate.prototype.exec = function(callback) {
983
983
 
984
984
  return promiseOrCallback(callback, cb => {
985
985
  prepareDiscriminatorPipeline(this);
986
- stringifyAccumulatorOptions(this._pipeline);
986
+ stringifyFunctionOperators(this._pipeline);
987
987
 
988
988
  model.hooks.execPre('aggregate', this, error => {
989
989
  if (error) {
package/lib/connection.js CHANGED
@@ -469,7 +469,7 @@ Connection.prototype.startSession = _wrapConnHelper(function startSession(option
469
469
  * @method transaction
470
470
  * @param {Function} fn Function to execute in a transaction
471
471
  * @param {mongodb.TransactionOptions} [options] Optional settings for the transaction
472
- * @return {Promise<Any>} promise that resolves to the returned value of `fn`
472
+ * @return {Promise<Any>} promise that is fulfilled if Mongoose successfully committed the transaction, or rejects if the transaction was aborted or if Mongoose failed to commit the transaction. If fulfilled, the promise resolves to a MongoDB command result.
473
473
  * @api public
474
474
  */
475
475
 
package/lib/document.js CHANGED
@@ -41,7 +41,6 @@ const isMongooseObject = utils.isMongooseObject;
41
41
  const arrayAtomicsBackupSymbol = Symbol('mongoose.Array#atomicsBackup');
42
42
  const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol;
43
43
  const documentArrayParent = require('./helpers/symbols').documentArrayParent;
44
- const documentIsSelected = require('./helpers/symbols').documentIsSelected;
45
44
  const documentIsModified = require('./helpers/symbols').documentIsModified;
46
45
  const documentModifiedPaths = require('./helpers/symbols').documentModifiedPaths;
47
46
  const documentSchemaSymbol = require('./helpers/symbols').documentSchemaSymbol;
@@ -141,7 +140,6 @@ function Document(obj, fields, skipId, options) {
141
140
  });
142
141
  }
143
142
  }
144
-
145
143
  if (obj) {
146
144
  // Skip set hooks
147
145
  if (this.$__original_set) {
@@ -665,7 +663,7 @@ function init(self, obj, doc, opts, prefix) {
665
663
  // Should still work if not a model-level discriminator, but should not be
666
664
  // necessary. This is *only* to catch the case where we queried using the
667
665
  // base model and the discriminated model has a projection
668
- if (self.schema.$isRootDiscriminator && !self.isSelected(path)) {
666
+ if (self.schema.$isRootDiscriminator && !self.$__isSelected(path)) {
669
667
  return;
670
668
  }
671
669
 
@@ -900,6 +898,7 @@ Document.prototype.overwrite = function overwrite(obj) {
900
898
  */
901
899
 
902
900
  Document.prototype.$set = function $set(path, val, type, options) {
901
+
903
902
  if (utils.isPOJO(type)) {
904
903
  options = type;
905
904
  type = undefined;
@@ -938,9 +937,13 @@ Document.prototype.$set = function $set(path, val, type, options) {
938
937
  path = path._doc;
939
938
  }
940
939
  }
940
+ if (path == null) {
941
+ const _ = path;
942
+ path = val;
943
+ val = _;
944
+ }
941
945
 
942
946
  prefix = val ? val + '.' : '';
943
-
944
947
  keys = Object.keys(path);
945
948
  const len = keys.length;
946
949
 
@@ -1140,7 +1143,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1140
1143
  // traverse the element ({nested: null})` is not likely. If user gets
1141
1144
  // that error, its their fault for now. We should reconsider disallowing
1142
1145
  // modifying not selected paths for 6.x
1143
- if (!this.isSelected(curPath)) {
1146
+ if (!this.$__isSelected(curPath)) {
1144
1147
  this.unmarkModified(curPath);
1145
1148
  }
1146
1149
  cur = this.$__getValue(curPath);
@@ -1421,7 +1424,7 @@ Document.prototype.$__shouldModify = function(pathToMark, path, constructing, pa
1421
1424
  return false;
1422
1425
  }
1423
1426
 
1424
- if (val === void 0 && !this.isSelected(path)) {
1427
+ if (val === void 0 && !this.$__isSelected(path)) {
1425
1428
  // when a path is not selected in a query, its initial
1426
1429
  // value will be undefined.
1427
1430
  return true;
@@ -2062,7 +2065,7 @@ Document.prototype.isSelected = function isSelected(path) {
2062
2065
  path = path.split(' ');
2063
2066
  }
2064
2067
  if (Array.isArray(path)) {
2065
- return path.some(p => this.isSelected(p));
2068
+ return path.some(p => this.$__isSelected(p));
2066
2069
  }
2067
2070
 
2068
2071
  const paths = Object.keys(this.$__.selected);
@@ -2111,7 +2114,7 @@ Document.prototype.isSelected = function isSelected(path) {
2111
2114
  return !inclusive;
2112
2115
  };
2113
2116
 
2114
- Document.prototype[documentIsSelected] = Document.prototype.isSelected;
2117
+ Document.prototype.$__isSelected = Document.prototype.isSelected;
2115
2118
 
2116
2119
  /**
2117
2120
  * Checks if `path` was explicitly selected. If no projection, always returns
@@ -2257,10 +2260,9 @@ function _getPathsToValidate(doc) {
2257
2260
  const skipSchemaValidators = {};
2258
2261
 
2259
2262
  _evaluateRequiredFunctions(doc);
2260
-
2261
2263
  // only validate required fields when necessary
2262
2264
  let paths = new Set(Object.keys(doc.$__.activePaths.states.require).filter(function(path) {
2263
- if (!doc.isSelected(path) && !doc.isModified(path)) {
2265
+ if (!doc.$__isSelected(path) && !doc.isModified(path)) {
2264
2266
  return false;
2265
2267
  }
2266
2268
  if (path in doc.$__.cachedRequired) {
@@ -2341,7 +2343,6 @@ function _getPathsToValidate(doc) {
2341
2343
  }
2342
2344
  }
2343
2345
 
2344
-
2345
2346
  for (const path of paths) {
2346
2347
  // Single nested paths (paths embedded under single nested subdocs) will
2347
2348
  // be validated on their own when we call `validate()` on the subdoc itself.
@@ -2435,11 +2436,9 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2435
2436
  pathDetails[0].filter((path) => this.isModified(path)) :
2436
2437
  pathDetails[0];
2437
2438
  const skipSchemaValidators = pathDetails[1];
2438
-
2439
2439
  if (Array.isArray(pathsToValidate)) {
2440
2440
  paths = _handlePathsToValidate(paths, pathsToValidate);
2441
2441
  }
2442
-
2443
2442
  if (paths.length === 0) {
2444
2443
  return process.nextTick(function() {
2445
2444
  const error = _complete();
@@ -2607,7 +2606,6 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
2607
2606
  if (Array.isArray(pathsToValidate)) {
2608
2607
  paths = _handlePathsToValidate(paths, pathsToValidate);
2609
2608
  }
2610
-
2611
2609
  const validating = {};
2612
2610
 
2613
2611
  paths.forEach(function(path) {
@@ -3569,7 +3567,7 @@ function applyGetters(self, json, options) {
3569
3567
  let part;
3570
3568
  cur = self._doc;
3571
3569
 
3572
- if (!self.isSelected(path)) {
3570
+ if (!self.$__isSelected(path)) {
3573
3571
  continue;
3574
3572
  }
3575
3573
 
@@ -4135,7 +4133,6 @@ Document.prototype.$__fullPath = function(path) {
4135
4133
 
4136
4134
  Document.prototype.getChanges = function() {
4137
4135
  const delta = this.$__delta();
4138
-
4139
4136
  const changes = delta ? delta[1] : {};
4140
4137
  return changes;
4141
4138
  };
@@ -67,13 +67,14 @@ if (util.inspect.custom) {
67
67
 
68
68
  /*!
69
69
  * Helper for JSON.stringify
70
+ * Ensure `name` and `message` show up in toJSON output re: gh-9847
70
71
  */
71
72
  Object.defineProperty(ValidationError.prototype, 'toJSON', {
72
73
  enumerable: false,
73
74
  writable: false,
74
75
  configurable: true,
75
76
  value: function() {
76
- return Object.assign({}, this, { message: this.message });
77
+ return Object.assign({}, this, { name: this.name, message: this.message });
77
78
  }
78
79
  });
79
80
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- module.exports = function stringifyAccumulatorOptions(pipeline) {
3
+ module.exports = function stringifyFunctionOperators(pipeline) {
4
4
  if (!Array.isArray(pipeline)) {
5
5
  return;
6
6
  }
@@ -17,9 +17,21 @@ module.exports = function stringifyAccumulatorOptions(pipeline) {
17
17
  }
18
18
  }
19
19
 
20
+ const stageType = Object.keys(stage)[0];
21
+ if (stageType && typeof stage[stageType] === 'object') {
22
+ const stageOptions = stage[stageType];
23
+ for (const key of Object.keys(stageOptions)) {
24
+ if (stageOptions[key] != null &&
25
+ stageOptions[key].$function != null &&
26
+ typeof stageOptions[key].$function.body === 'function') {
27
+ stageOptions[key].$function.body = stageOptions[key].$function.body.toString();
28
+ }
29
+ }
30
+ }
31
+
20
32
  if (stage.$facet != null) {
21
33
  for (const key of Object.keys(stage.$facet)) {
22
- stringifyAccumulatorOptions(stage.$facet[key]);
34
+ stringifyFunctionOperators(stage.$facet[key]);
23
35
  }
24
36
  }
25
37
  }
@@ -111,7 +111,7 @@ function cloneObject(obj, options, isArrayChild) {
111
111
  const ret = {};
112
112
  let hasKeys;
113
113
 
114
- for (const k in obj) {
114
+ for (const k of Object.keys(obj)) {
115
115
  if (specialProperties.has(k)) {
116
116
  continue;
117
117
  }
@@ -240,7 +240,7 @@ function valueFilter(val, assignmentOpts, populateOptions) {
240
240
  if (populateOptions.justOne === false) {
241
241
  return [];
242
242
  }
243
- return val;
243
+ return val == null ? val : null;
244
244
  }
245
245
 
246
246
  /*!
@@ -43,7 +43,6 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
43
43
  for (i = 0; i < len; i++) {
44
44
  doc = docs[i];
45
45
  let justOne = null;
46
-
47
46
  schema = getSchemaTypes(modelSchema, doc, options.path);
48
47
  // Special case: populating a path that's a DocumentArray unless
49
48
  // there's an explicit `ref` or `refPath` re: gh-8946
@@ -174,9 +173,11 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
174
173
  }
175
174
  } else if (schema && !schema[schemaMixedSymbol]) {
176
175
  // Skip Mixed types because we explicitly don't do casting on those.
177
- justOne = Array.isArray(schema) ?
178
- schema.every(schema => !schema.$isMongooseArray) :
179
- !schema.$isMongooseArray;
176
+ if (options.path.endsWith('.' + schema.path)) {
177
+ justOne = Array.isArray(schema) ?
178
+ schema.every(schema => !schema.$isMongooseArray) :
179
+ !schema.$isMongooseArray;
180
+ }
180
181
  }
181
182
 
182
183
  if (!modelNames) {
@@ -358,7 +359,6 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
358
359
  }
359
360
  }
360
361
  }
361
-
362
362
  return map;
363
363
 
364
364
  function _getModelNames(doc, schema) {
@@ -20,7 +20,6 @@ const populateModelSymbol = require('../symbols').populateModelSymbol;
20
20
  module.exports = function getSchemaTypes(schema, doc, path) {
21
21
  const pathschema = schema.path(path);
22
22
  const topLevelDoc = doc;
23
-
24
23
  if (pathschema) {
25
24
  return pathschema;
26
25
  }
@@ -33,7 +32,6 @@ module.exports = function getSchemaTypes(schema, doc, path) {
33
32
  while (p--) {
34
33
  trypath = parts.slice(0, p).join('.');
35
34
  foundschema = schema.path(trypath);
36
-
37
35
  if (foundschema == null) {
38
36
  continue;
39
37
  }
@@ -117,7 +115,6 @@ module.exports = function getSchemaTypes(schema, doc, path) {
117
115
  ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
118
116
  !foundschema.schema.$isSingleNested;
119
117
  }
120
-
121
118
  return ret;
122
119
  }
123
120
  } else if (p !== parts.length &&
@@ -153,7 +150,6 @@ module.exports = function getSchemaTypes(schema, doc, path) {
153
150
  ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
154
151
  !model.schema.$isSingleNested;
155
152
  }
156
-
157
153
  return ret;
158
154
  }
159
155
  }
@@ -177,15 +173,12 @@ module.exports = function getSchemaTypes(schema, doc, path) {
177
173
  ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
178
174
  !schema.$isSingleNested;
179
175
  }
180
-
181
176
  return ret;
182
177
  }
183
178
  }
184
-
185
179
  return foundschema;
186
180
  }
187
181
  }
188
-
189
182
  // look for arrays
190
183
  const parts = path.split('.');
191
184
  for (let i = 0; i < parts.length; ++i) {
@@ -25,7 +25,7 @@ module.exports = function castFilterPath(query, schematype, val) {
25
25
  if (nested && schematype && !schematype.caster) {
26
26
  const _keys = Object.keys(nested);
27
27
  if (_keys.length && isOperator(_keys[0])) {
28
- for (const key in nested) {
28
+ for (const key of Object.keys(nested)) {
29
29
  nested[key] = schematype.castForQueryWrapper({
30
30
  $conditional: key,
31
31
  val: nested[key],
@@ -51,7 +51,7 @@ module.exports = function setupTimestamps(schema, timestamps) {
51
51
  (this.ownerDocument ? this.ownerDocument() : this).constructor.base.now();
52
52
  const auto_id = this._id && this._id.auto;
53
53
 
54
- if (!skipCreatedAt && createdAt && !this.get(createdAt) && this.isSelected(createdAt)) {
54
+ if (!skipCreatedAt && createdAt && !this.get(createdAt) && this.$__isSelected(createdAt)) {
55
55
  this.$set(createdAt, auto_id ? this._id.getTimestamp() : defaultTimestamp);
56
56
  }
57
57
 
@@ -38,8 +38,7 @@ module.exports = function castArrayFilters(query) {
38
38
  if (filter == null) {
39
39
  throw new Error(`Got null array filter in ${arrayFilters}`);
40
40
  }
41
- for (const key in filter) {
42
-
41
+ for (const key of Object.keys(filter)) {
43
42
  if (filter[key] == null) {
44
43
  continue;
45
44
  }
package/lib/model.js CHANGED
@@ -538,7 +538,6 @@ function operand(self, where, delta, data, val, op) {
538
538
  op || (op = '$set');
539
539
  if (!delta[op]) delta[op] = {};
540
540
  delta[op][data.path] = val;
541
-
542
541
  // disabled versioning?
543
542
  if (self.schema.options.versionKey === false) return;
544
543
 
@@ -748,7 +747,6 @@ Model.prototype.$__delta = function() {
748
747
  if (this.$__.version) {
749
748
  this.$__version(where, delta);
750
749
  }
751
-
752
750
  return [where, delta];
753
751
  };
754
752
 
@@ -825,7 +823,7 @@ Model.prototype.$__version = function(where, delta) {
825
823
  // there is no way to select the correct version. we could fail
826
824
  // fast here and force them to include the versionKey but
827
825
  // thats a bit intrusive. can we do this automatically?
828
- if (!this.isSelected(key)) {
826
+ if (!this.$__isSelected(key)) {
829
827
  return;
830
828
  }
831
829
 
@@ -1652,6 +1650,10 @@ function _ensureIndexes(model, options, callback) {
1652
1650
  if ('background' in options) {
1653
1651
  indexOptions.background = options.background;
1654
1652
  }
1653
+ if (model.schema.options.hasOwnProperty('collation') &&
1654
+ !indexOptions.hasOwnProperty('collation')) {
1655
+ indexOptions.collation = model.schema.options.collation;
1656
+ }
1655
1657
 
1656
1658
  const methodName = useCreateIndex ? 'createIndex' : 'ensureIndex';
1657
1659
  model.collection[methodName](indexFields, indexOptions, utils.tick(function(err, name) {
@@ -4338,7 +4340,6 @@ function populate(model, docs, options, callback) {
4338
4340
  }
4339
4341
 
4340
4342
  const modelsMap = getModelsMapForPopulate(model, docs, options);
4341
-
4342
4343
  if (modelsMap instanceof MongooseError) {
4343
4344
  return immediate(function() {
4344
4345
  callback(modelsMap);
@@ -4422,7 +4423,6 @@ function populate(model, docs, options, callback) {
4422
4423
  } else if (mod.options.limit != null) {
4423
4424
  assignmentOpts.originalLimit = mod.options.limit;
4424
4425
  }
4425
-
4426
4426
  params.push([mod, match, select, assignmentOpts, _next]);
4427
4427
  }
4428
4428
 
@@ -4441,7 +4441,6 @@ function populate(model, docs, options, callback) {
4441
4441
  for (const arr of params) {
4442
4442
  _execPopulateQuery.apply(null, arr);
4443
4443
  }
4444
-
4445
4444
  function _next(err, valsFromDb) {
4446
4445
  if (err != null) {
4447
4446
  return callback(err, null);
@@ -4475,7 +4474,6 @@ function populate(model, docs, options, callback) {
4475
4474
 
4476
4475
  function _execPopulateQuery(mod, match, select, assignmentOpts, callback) {
4477
4476
  const subPopulate = utils.clone(mod.options.populate);
4478
-
4479
4477
  const queryOptions = Object.assign({
4480
4478
  skip: mod.options.skip,
4481
4479
  limit: mod.options.limit,
@@ -4594,7 +4592,12 @@ function _assign(model, vals, mod, assignmentOpts) {
4594
4592
  if (Array.isArray(rawDocs[key])) {
4595
4593
  rawDocs[key].push(val);
4596
4594
  rawOrder[key].push(i);
4597
- } else {
4595
+ } else if (isVirtual ||
4596
+ rawDocs[key].constructor !== val.constructor ||
4597
+ String(rawDocs[key]._id) !== String(val._id)) {
4598
+ // May need to store multiple docs with the same id if there's multiple models
4599
+ // if we have discriminators or a ref function. But avoid converting to an array
4600
+ // if we have multiple queries on the same model because of `perDocumentLimit` re: gh-9906
4598
4601
  rawDocs[key] = [rawDocs[key], val];
4599
4602
  rawOrder[key] = [rawOrder[key], i];
4600
4603
  }
package/lib/query.js CHANGED
@@ -1397,9 +1397,11 @@ Query.prototype.setOptions = function(options, overwrite) {
1397
1397
  Query.prototype.explain = function(verbose) {
1398
1398
  if (arguments.length === 0) {
1399
1399
  this.options.explain = true;
1400
- return this;
1400
+ } else if (verbose === false) {
1401
+ delete this.options.explain;
1402
+ } else {
1403
+ this.options.explain = verbose;
1401
1404
  }
1402
- this.options.explain = verbose;
1403
1405
  return this;
1404
1406
  };
1405
1407
 
@@ -4964,7 +4966,7 @@ Query.prototype.tailable = function(val, opts) {
4964
4966
  }
4965
4967
 
4966
4968
  if (opts && typeof opts === 'object') {
4967
- for (const key in opts) {
4969
+ for (const key of Object.keys(opts)) {
4968
4970
  if (key === 'awaitdata') {
4969
4971
  // For backwards compatibility
4970
4972
  this.options[key] = !!opts[key];
@@ -147,7 +147,7 @@ SingleNestedPath.prototype.$conditionalHandlers.$exists = $exists;
147
147
  * @api private
148
148
  */
149
149
 
150
- SingleNestedPath.prototype.cast = function(val, doc, init, priorVal) {
150
+ SingleNestedPath.prototype.cast = function(val, doc, init, priorVal, options) {
151
151
  if (val && val.$isSingleNested && val.parent === doc) {
152
152
  return val;
153
153
  }
@@ -169,16 +169,16 @@ SingleNestedPath.prototype.cast = function(val, doc, init, priorVal) {
169
169
  }
170
170
  return obj;
171
171
  }, {});
172
-
172
+ options = Object.assign({}, options, { priorDoc: priorVal });
173
173
  if (init) {
174
174
  subdoc = new Constructor(void 0, selected, doc);
175
175
  subdoc.init(val);
176
176
  } else {
177
177
  if (Object.keys(val).length === 0) {
178
- return new Constructor({}, selected, doc, undefined, { priorDoc: priorVal });
178
+ return new Constructor({}, selected, doc, undefined, options);
179
179
  }
180
180
 
181
- return new Constructor(val, selected, doc, undefined, { priorDoc: priorVal });
181
+ return new Constructor(val, selected, doc, undefined, options);
182
182
  }
183
183
 
184
184
  return subdoc;