mongoose 5.12.13 → 5.12.14

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
@@ -368,7 +368,7 @@ declare module 'mongoose' {
368
368
  }
369
369
 
370
370
  class Document<T = any, TQueryHelpers = any> {
371
- constructor(doc?: T | any);
371
+ constructor(doc?: any);
372
372
 
373
373
  /** This documents _id. */
374
374
  _id?: T;
@@ -1225,7 +1225,7 @@ declare module 'mongoose' {
1225
1225
  statics: { [name: string]: (this: M, ...args: any[]) => any };
1226
1226
 
1227
1227
  /** Creates a virtual type with the given name. */
1228
- virtual(name: string, options?: any): VirtualType;
1228
+ virtual(name: string, options?: VirtualTypeOptions): VirtualType;
1229
1229
 
1230
1230
  /** Object of currently defined virtuals on this schema */
1231
1231
  virtuals: any;
@@ -1618,17 +1618,64 @@ declare module 'mongoose' {
1618
1618
  validator: ValidateFn<T> | LegacyAsyncValidateFn<T> | AsyncValidateFn<T>;
1619
1619
  }
1620
1620
 
1621
+ interface VirtualTypeOptions {
1622
+ /** If `ref` is not nullish, this becomes a populated virtual. */
1623
+ ref?: string | Function;
1624
+
1625
+ /** The local field to populate on if this is a populated virtual. */
1626
+ localField?: string | Function;
1627
+
1628
+ /** The foreign field to populate on if this is a populated virtual. */
1629
+ foreignField?: string | Function;
1630
+
1631
+ /**
1632
+ * By default, a populated virtual is an array. If you set `justOne`,
1633
+ * the populated virtual will be a single doc or `null`.
1634
+ */
1635
+ justOne?: boolean;
1636
+
1637
+ /** If you set this to `true`, Mongoose will call any custom getters you defined on this virtual. */
1638
+ getters?: boolean;
1639
+
1640
+ /**
1641
+ * If you set this to `true`, `populate()` will set this virtual to the number of populated
1642
+ * documents, as opposed to the documents themselves, using `Query#countDocuments()`.
1643
+ */
1644
+ count?: boolean;
1645
+
1646
+ /** Add an extra match condition to `populate()`. */
1647
+ match?: FilterQuery<any> | Function;
1648
+
1649
+ /** Add a default `limit` to the `populate()` query. */
1650
+ limit?: number;
1651
+
1652
+ /** Add a default `skip` to the `populate()` query. */
1653
+ skip?: number;
1654
+
1655
+ /**
1656
+ * For legacy reasons, `limit` with `populate()` may give incorrect results because it only
1657
+ * executes a single query for every document being populated. If you set `perDocumentLimit`,
1658
+ * Mongoose will ensure correct `limit` per document by executing a separate query for each
1659
+ * document to `populate()`. For example, `.find().populate({ path: 'test', perDocumentLimit: 2 })`
1660
+ * will execute 2 additional queries if `.find()` returns 2 documents.
1661
+ */
1662
+ perDocumentLimit?: number;
1663
+
1664
+ /** Additional options like `limit` and `lean`. */
1665
+ options?: QueryOptions;
1666
+ }
1667
+
1621
1668
  class VirtualType {
1622
1669
  /** Applies getters to `value`. */
1623
1670
  applyGetters(value: any, doc: Document): any;
1671
+
1624
1672
  /** Applies setters to `value`. */
1625
1673
  applySetters(value: any, doc: Document): any;
1626
1674
 
1627
1675
  /** Adds a custom getter to this virtual. */
1628
- // eslint-disable-next-line @typescript-eslint/ban-types
1629
1676
  get(fn: Function): this;
1677
+
1630
1678
  /** Adds a custom setter to this virtual. */
1631
- // eslint-disable-next-line @typescript-eslint/ban-types
1632
1679
  set(fn: Function): this;
1633
1680
  }
1634
1681
 
@@ -2760,7 +2807,7 @@ declare module 'mongoose' {
2760
2807
  export class ValidationError extends Error {
2761
2808
  name: 'ValidationError';
2762
2809
 
2763
- errors: { [path: string]: ValidatorError | CastError };
2810
+ errors: { [path: string]: ValidatorError | CastError | ValidationError };
2764
2811
  }
2765
2812
 
2766
2813
  export class ValidatorError extends Error {
package/lib/document.js CHANGED
@@ -2928,8 +2928,8 @@ Document.prototype.$__reset = function reset() {
2928
2928
  doc.$__reset();
2929
2929
  if (doc.$__parent === _this) {
2930
2930
  _this.$__.activePaths.init(doc.$basePath);
2931
- } else if (doc.$__parent != null) {
2932
- // If map path underneath nested path, may end up with a case where
2931
+ } else if (doc.$__parent != null && doc.$__parent.ownerDocument) {
2932
+ // If map path underneath subdocument, may end up with a case where
2933
2933
  // map path is modified but parent still needs to be reset. See gh-10295
2934
2934
  doc.$__parent.$__reset();
2935
2935
  }
@@ -7,6 +7,7 @@
7
7
  const MongooseCollection = require('../../collection');
8
8
  const MongooseError = require('../../error/mongooseError');
9
9
  const Collection = require('mongodb').Collection;
10
+ const ObjectId = require('./objectid');
10
11
  const get = require('../../helpers/get');
11
12
  const sliced = require('sliced');
12
13
  const stream = require('stream');
@@ -21,9 +22,11 @@ const util = require('util');
21
22
  * @api private
22
23
  */
23
24
 
24
- function NativeCollection(name, options) {
25
+ function NativeCollection(name, conn, options) {
25
26
  this.collection = null;
26
27
  this.Promise = options.Promise || Promise;
28
+ this.modelName = options.modelName;
29
+ delete options.modelName;
27
30
  this._closed = false;
28
31
  MongooseCollection.apply(this, arguments);
29
32
  }
@@ -128,6 +131,7 @@ function iter(i) {
128
131
  const _this = this;
129
132
  const debug = get(_this, 'conn.base.options.debug');
130
133
  const lastArg = arguments[arguments.length - 1];
134
+ const opId = new ObjectId();
131
135
 
132
136
  // If user force closed, queueing will hang forever. See #5664
133
137
  if (this.conn.$wasForceClosed) {
@@ -141,12 +145,20 @@ function iter(i) {
141
145
  }
142
146
  }
143
147
 
148
+ let _args = args;
149
+ let callback = null;
144
150
  if (this._shouldBufferCommands() && this.buffer) {
145
151
  if (syncCollectionMethods[i]) {
146
152
  throw new Error('Collection method ' + i + ' is synchronous');
147
153
  }
148
154
 
149
- this.conn.emit('buffer', { method: i, args: args });
155
+ this.conn.emit('buffer', {
156
+ _id: opId,
157
+ modelName: _this.modelName,
158
+ collectionName: _this.name,
159
+ method: i,
160
+ args: args
161
+ });
150
162
 
151
163
  let callback;
152
164
  let _args;
@@ -182,7 +194,9 @@ function iter(i) {
182
194
  if (removed) {
183
195
  const message = 'Operation `' + this.name + '.' + i + '()` buffering timed out after ' +
184
196
  bufferTimeoutMS + 'ms';
185
- callback(new MongooseError(message));
197
+ const err = new MongooseError(message);
198
+ this.conn.emit('buffer-end', { _id: opId, modelName: _this.modelName, collectionName: _this.name, method: i, error: err });
199
+ callback(err);
186
200
  }
187
201
  }, bufferTimeoutMS);
188
202
 
@@ -192,6 +206,16 @@ function iter(i) {
192
206
  }
193
207
 
194
208
  return promise;
209
+ } else if (!syncCollectionMethods[i] && typeof lastArg === 'function') {
210
+ callback = function collectionOperationCallback(err, res) {
211
+ if (err != null) {
212
+ _this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: _this.name, method: i, error: err });
213
+ } else {
214
+ _this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: _this.name, method: i, result: res });
215
+ }
216
+ return lastArg.apply(this, arguments);
217
+ };
218
+ _args = args.slice(0, args.length - 1).concat([callback]);
195
219
  }
196
220
 
197
221
  if (debug) {
@@ -207,6 +231,8 @@ function iter(i) {
207
231
  }
208
232
  }
209
233
 
234
+ this.conn.emit('operation-start', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, params: _args });
235
+
210
236
  try {
211
237
  if (collection == null) {
212
238
  const message = 'Cannot call `' + this.name + '.' + i + '()` before initial connection ' +
@@ -214,13 +240,30 @@ function iter(i) {
214
240
  'you have `bufferCommands = false`.';
215
241
  throw new MongooseError(message);
216
242
  }
217
- return collection[i].apply(collection, args);
243
+ const ret = collection[i].apply(collection, _args);
244
+ if (ret != null && typeof ret.then === 'function') {
245
+ return ret.then(
246
+ res => {
247
+ this.conn.emit('operation-end', { _id: opId, modelName: this.modelName, collectionName: this.name, method: i, result: res });
248
+ return res;
249
+ },
250
+ err => {
251
+ this.conn.emit('operation-end', { _id: opId, modelName: this.modelName, collectionName: this.name, method: i, error: err });
252
+ throw err;
253
+ }
254
+ );
255
+ }
256
+ return ret;
218
257
  } catch (error) {
219
258
  // Collection operation may throw because of max bson size, catch it here
220
259
  // See gh-3906
221
- if (args.length > 0 &&
222
- typeof args[args.length - 1] === 'function') {
223
- args[args.length - 1](error);
260
+ if (typeof callback === 'function') {
261
+ callback(error);
262
+ } else {
263
+ this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error: error });
264
+ }
265
+ if (typeof lastArg === 'function') {
266
+ lastArg(error);
224
267
  } else {
225
268
  throw error;
226
269
  }
@@ -9,6 +9,9 @@ function arrayDepth(arr) {
9
9
  if (arr.length === 0) {
10
10
  return { min: 1, max: 1, containsNonArrayItem: false };
11
11
  }
12
+ if (arr.length === 1 && !Array.isArray(arr[0])) {
13
+ return { min: 1, max: 1, containsNonArrayItem: false };
14
+ }
12
15
 
13
16
  const res = arrayDepth(arr[0]);
14
17
 
@@ -53,7 +53,7 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
53
53
  }
54
54
  // Populating a nested path should always be a no-op re: #9073.
55
55
  // People shouldn't do this, but apparently they do.
56
- if (modelSchema.nested[options.path]) {
56
+ if (options._localModel != null && options._localModel.schema.nested[options.path]) {
57
57
  continue;
58
58
  }
59
59
  const isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
package/lib/model.js CHANGED
@@ -1073,6 +1073,7 @@ Model.prototype.model = function model(name) {
1073
1073
  * - `findOne()`
1074
1074
  *
1075
1075
  * @param {Object} filter
1076
+ * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
1076
1077
  * @param {Function} [callback] callback
1077
1078
  * @return {Promise}
1078
1079
  */
@@ -4744,7 +4745,8 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4744
4745
  const collectionOptions = {
4745
4746
  schemaUserProvidedOptions: _userProvidedOptions,
4746
4747
  capped: schema.options.capped,
4747
- Promise: model.base.Promise
4748
+ Promise: model.base.Promise,
4749
+ modelName: name
4748
4750
  };
4749
4751
  if (schema.options.autoCreate !== void 0) {
4750
4752
  collectionOptions.autoCreate = schema.options.autoCreate;
@@ -31,6 +31,10 @@ exports.preparePopulationOptions = function preparePopulationOptions(query, opti
31
31
  forEach(makeLean(options.lean));
32
32
  }
33
33
 
34
+ pop.forEach(opts => {
35
+ opts._localModel = query.model;
36
+ });
37
+
34
38
  return pop;
35
39
  };
36
40
 
@@ -71,6 +75,9 @@ exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query,
71
75
  pop.forEach(p => {
72
76
  p._queryProjection = projection;
73
77
  });
78
+ pop.forEach(opts => {
79
+ opts._localModel = query.model;
80
+ });
74
81
 
75
82
  return pop;
76
83
  };
@@ -282,14 +282,14 @@ SchemaArray.prototype.applyGetters = function(value, scope) {
282
282
  };
283
283
 
284
284
  SchemaArray.prototype._applySetters = function(value, scope, init, priorVal) {
285
- if (this.casterConstructor instanceof SchemaArray &&
285
+ if (this.casterConstructor.$isMongooseArray &&
286
286
  SchemaArray.options.castNonArrays &&
287
287
  !this[isNestedArraySymbol]) {
288
288
  // Check nesting levels and wrap in array if necessary
289
289
  let depth = 0;
290
290
  let arr = this;
291
291
  while (arr != null &&
292
- arr instanceof SchemaArray &&
292
+ arr.$isMongooseArray &&
293
293
  !arr.$isMongooseDocumentArray) {
294
294
  ++depth;
295
295
  arr = arr.casterConstructor;
@@ -361,20 +361,22 @@ SchemaArray.prototype.cast = function(value, doc, init, prev, options) {
361
361
  }
362
362
 
363
363
  const caster = this.caster;
364
+ const isMongooseArray = caster.$isMongooseArray;
365
+ const isArrayOfNumbers = caster.instance === 'Number';
364
366
  if (caster && this.casterConstructor !== Mixed) {
365
367
  try {
366
368
  for (i = 0; i < len; i++) {
367
369
  // Special case: number arrays disallow undefined.
368
370
  // Re: gh-840
369
371
  // See commit 1298fe92d2c790a90594bd08199e45a4a09162a6
370
- if (caster.instance === 'Number' && value[i] === void 0) {
372
+ if (isArrayOfNumbers && value[i] === void 0) {
371
373
  throw new MongooseError('Mongoose number arrays disallow storing undefined');
372
374
  }
373
375
  const opts = {};
374
376
  // Perf: creating `arrayPath` is expensive for large arrays.
375
377
  // We only need `arrayPath` if this is a nested array, so
376
378
  // skip if possible.
377
- if (caster.$isMongooseArray) {
379
+ if (isMongooseArray) {
378
380
  if (options.arrayPath != null) {
379
381
  opts.arrayPathIndex = i;
380
382
  } else if (caster._arrayParentPath != null) {
package/lib/schema.js CHANGED
@@ -694,6 +694,10 @@ Schema.prototype.path = function(path, obj) {
694
694
  !utils.hasUserDefinedProperty(obj.of, this.options.typeKey);
695
695
  _mapType = isInlineSchema ? new Schema(obj.of) : obj.of;
696
696
  }
697
+ if (utils.hasUserDefinedProperty(obj, 'ref')) {
698
+ _mapType = { type: _mapType, ref: obj.ref };
699
+ }
700
+
697
701
  this.paths[mapPath] = this.interpretAsType(mapPath,
698
702
  _mapType, this.options);
699
703
  this.mapPaths.push(this.paths[mapPath]);
@@ -773,12 +777,18 @@ Schema.prototype.path = function(path, obj) {
773
777
 
774
778
  if (schemaType.$isMongooseDocumentArray) {
775
779
  for (const key of Object.keys(schemaType.schema.paths)) {
776
- this.subpaths[path + '.' + key] = schemaType.schema.paths[key];
777
- schemaType.schema.paths[key].$isUnderneathDocArray = true;
780
+ const _schemaType = schemaType.schema.paths[key];
781
+ this.subpaths[path + '.' + key] = _schemaType;
782
+ if (typeof _schemaType === 'object' && _schemaType != null) {
783
+ _schemaType.$isUnderneathDocArray = true;
784
+ }
778
785
  }
779
786
  for (const key of Object.keys(schemaType.schema.subpaths)) {
780
- this.subpaths[path + '.' + key] = schemaType.schema.subpaths[key];
781
- schemaType.schema.subpaths[key].$isUnderneathDocArray = true;
787
+ const _schemaType = schemaType.schema.subpaths[key];
788
+ this.subpaths[path + '.' + key] = _schemaType;
789
+ if (typeof _schemaType === 'object' && _schemaType != null) {
790
+ _schemaType.$isUnderneathDocArray = true;
791
+ }
782
792
  }
783
793
  for (const key of Object.keys(schemaType.schema.singleNestedPaths)) {
784
794
  const _schemaType = schemaType.schema.singleNestedPaths[key];
@@ -5,7 +5,6 @@
5
5
  'use strict';
6
6
 
7
7
  const CoreMongooseArray = require('./core_array');
8
- const Document = require('../document');
9
8
 
10
9
  const arrayAtomicsSymbol = require('../helpers/symbols').arrayAtomicsSymbol;
11
10
  const arrayParentSymbol = require('../helpers/symbols').arrayParentSymbol;
@@ -56,22 +55,18 @@ function MongooseArray(values, path, doc, schematype) {
56
55
 
57
56
  if (values[arrayAtomicsSymbol] != null) {
58
57
  arr[arrayAtomicsSymbol] = values[arrayAtomicsSymbol];
59
- } else {
60
- arr[arrayAtomicsSymbol] = {};
61
58
  }
62
59
  } else {
63
60
  arr = new CoreMongooseArray();
64
- arr[arrayAtomicsSymbol] = {};
65
61
  }
66
62
 
67
63
  arr[arrayPathSymbol] = path;
68
- arr[arraySchemaSymbol] = void 0;
69
64
 
70
65
  // Because doc comes from the context of another function, doc === global
71
66
  // can happen if there was a null somewhere up the chain (see #3020)
72
67
  // RB Jun 17, 2015 updated to check for presence of expected paths instead
73
68
  // to make more proof against unusual node environments
74
- if (doc && doc instanceof Document) {
69
+ if (doc != null && doc.$__ != null) {
75
70
  arr[arrayParentSymbol] = doc;
76
71
  arr[arraySchemaSymbol] = schematype || doc.schema.path(path);
77
72
  }
@@ -94,7 +94,7 @@ class CoreMongooseArray extends Array {
94
94
  */
95
95
 
96
96
  $atomics() {
97
- return this[arrayAtomicsSymbol];
97
+ return this[arrayAtomicsSymbol] || {};
98
98
  }
99
99
 
100
100
  /*!
@@ -328,6 +328,8 @@ class CoreMongooseArray extends Array {
328
328
  return this;
329
329
  }
330
330
 
331
+ this[arrayAtomicsSymbol] || (this[arrayAtomicsSymbol] = {});
332
+
331
333
  const atomics = this[arrayAtomicsSymbol];
332
334
 
333
335
  // reset pop/shift after save
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "5.12.13",
4
+ "version": "5.12.14",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -40,7 +40,6 @@
40
40
  "acquit": "1.x",
41
41
  "acquit-ignore": "0.1.x",
42
42
  "acquit-require": "0.1.x",
43
- "async": "2.6.2",
44
43
  "babel-loader": "8.1.0",
45
44
  "benchmark": "2.1.4",
46
45
  "bluebird": "3.5.5",