mongoose 4.11.8 → 4.11.12

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/History.md CHANGED
@@ -1,3 +1,41 @@
1
+ 4.11.12 / 2017-09-18
2
+ ====================
3
+ * docs(model): asterisk should not render as markdown bullet #5644 [timkinnane](https://github.com/timkinnane)
4
+ * docs: use useMongoClient in connection example #5627 [GabrielNicolasAvellaneda](https://github.com/GabrielNicolasAvellaneda)
5
+ * fix(connection): call callback when initial connection failed #5626
6
+ * fix(query): apply select correctly if a given nested schema is used for 2 different paths #5603
7
+ * fix(document): add graceful fallback for setting a doc array value and `pull()`-ing a doc #3511
8
+
9
+ 4.11.11 / 2017-09-10
10
+ ====================
11
+ * fix(connection): properly set readyState in response to driver 'close' and 'reconnect' events #5604
12
+ * fix(document): ensure single embedded doc setters only get called once, with correct value #5601
13
+ * fix(timestamps): allow enabling updatedAt without createdAt #5598
14
+ * test: improve unique validator test by making create run before ensureIndex #5595 #5562
15
+ * fix(query): ensure find callback only gets called once when post init hook throws error #5592
16
+
17
+ 4.11.10 / 2017-09-03
18
+ ====================
19
+ * docs: add KeenIO tracking #5612
20
+ * fix(schema): ensure validators declared with `.validate()` get copied with clone() #5607
21
+ * fix: remove unnecessary jest warning #5480
22
+ * fix(discriminator): prevent implicit discriminator schema id from clobbering base schema custom id #5591
23
+ * fix(schema): hide schema objectid warning for non-hex strings of length 24 #5587
24
+ * docs(populate): use story schema defined key author instead of creator #5578 [dmric](https://github.com/dmric)
25
+ * docs(document): describe usage of `.set()` #5576
26
+ * fix(document): ensure correct scope in single nested validators #5569
27
+ * fix(populate): don't mark path as populated until populate() is done #5564
28
+ * fix(document): make push()-ing a doc onto an empty array act as manual population #5504
29
+ * fix(connection): emit timeout event on socket timeout #4513
30
+
31
+ 4.11.9 / 2017-08-27
32
+ ===================
33
+ * fix(error): avoid using arguments.callee because that breaks strict mode #5572
34
+ * docs(schematypes): fix spacing #5567
35
+ * fix(query): enforce binary subtype always propagates to mongodb #5551
36
+ * fix(query): only skip castForQuery for mongoose arrays #5536
37
+ * fix(browser): rely on browser entrypoint to decide whether to use BrowserDocument or NodeDocument #5480
38
+
1
39
  4.11.8 / 2017-08-23
2
40
  ===================
3
41
  * feat: add warning about using schema ObjectId as type ObjectId #5571 [efkan](https://github.com/efkan)
package/lib/browser.js CHANGED
@@ -1,7 +1,10 @@
1
1
  /* eslint-env browser */
2
2
 
3
+ var DocumentProvider = require('./document_provider.js');
3
4
  var PromiseProvider = require('./promise_provider');
4
5
 
6
+ DocumentProvider.setBrowser(true);
7
+
5
8
  /**
6
9
  * The Mongoose [Promise](#promise_Promise) constructor.
7
10
  *
@@ -114,7 +117,7 @@ exports.utils = require('./utils.js');
114
117
  * @method Document
115
118
  * @api public
116
119
  */
117
- exports.Document = require('./document_provider.js')();
120
+ exports.Document = DocumentProvider();
118
121
 
119
122
  /*!
120
123
  * Module exports.
package/lib/connection.js CHANGED
@@ -66,6 +66,7 @@ function Connection(base) {
66
66
  this.name = null;
67
67
  this.options = null;
68
68
  this.otherDbs = [];
69
+ this.states = STATES;
69
70
  this._readyState = STATES.disconnected;
70
71
  this._closeCalled = false;
71
72
  this._hasOpened = false;
@@ -760,14 +761,20 @@ Connection.prototype.openUri = function(uri, options, callback) {
760
761
  if (_this.listeners('error').length) {
761
762
  _this.emit('error', error);
762
763
  }
764
+ callback && callback(error);
763
765
  return reject(error);
764
766
  }
765
767
  // Backwards compat for mongoose 4.x
766
768
  db.on('reconnect', function() {
769
+ _this.readyState = STATES.connected;
767
770
  _this.emit('reconnected');
768
771
  });
769
772
  db.s.topology.on('close', function() {
770
- _this.emit('disconnected');
773
+ // Implicitly emits 'disconnected'
774
+ _this.readyState = STATES.disconnected;
775
+ });
776
+ db.on('timeout', function() {
777
+ _this.emit('timeout');
771
778
  });
772
779
 
773
780
  _this.db = db;
package/lib/document.js CHANGED
@@ -16,7 +16,10 @@ var isMongooseObject = utils.isMongooseObject;
16
16
  var inspect = require('util').inspect;
17
17
  var ValidationError = MongooseError.ValidationError;
18
18
  var InternalCache = require('./internal');
19
+ var cleanModifiedSubpaths = require('./services/document/cleanModifiedSubpaths');
20
+ var compile = require('./services/document/compile').compile;
19
21
  var deepEqual = utils.deepEqual;
22
+ var defineKey = require('./services/document/compile').defineKey;
20
23
  var hooks = require('hooks-fixed');
21
24
  var PromiseProvider = require('./promise_provider');
22
25
  var DocumentArray;
@@ -498,12 +501,13 @@ Document.prototype.set = function(path, val, type, options) {
498
501
  type = undefined;
499
502
  }
500
503
 
501
- var merge = options && options.merge,
502
- adhoc = type && type !== true,
503
- constructing = type === true,
504
- adhocs;
504
+ options = options || {};
505
+ var merge = options.merge;
506
+ var adhoc = type && type !== true;
507
+ var constructing = type === true;
508
+ var adhocs;
505
509
 
506
- var strict = options && 'strict' in options
510
+ var strict = 'strict' in options
507
511
  ? options.strict
508
512
  : this.$__.strictMode;
509
513
 
@@ -774,20 +778,6 @@ Document.prototype.set = function(path, val, type, options) {
774
778
  return this;
775
779
  };
776
780
 
777
- /*!
778
- * ignore
779
- */
780
-
781
- function cleanModifiedSubpaths(doc, path) {
782
- var _modifiedPaths = Object.keys(doc.$__.activePaths.states.modify);
783
- var _numModifiedPaths = _modifiedPaths.length;
784
- for (var j = 0; j < _numModifiedPaths; ++j) {
785
- if (_modifiedPaths[j].indexOf(path + '.') === 0) {
786
- delete doc.$__.activePaths.states.modify[_modifiedPaths[j]];
787
- }
788
- }
789
- }
790
-
791
781
  /**
792
782
  * Determine if we should mark this change as modified.
793
783
  *
@@ -1833,158 +1823,6 @@ Document.prototype.$__dirty = function() {
1833
1823
  return minimal;
1834
1824
  };
1835
1825
 
1836
- /*!
1837
- * Compiles schemas.
1838
- */
1839
-
1840
- function compile(tree, proto, prefix, options) {
1841
- var keys = Object.keys(tree);
1842
- var i = keys.length;
1843
- var len = keys.length;
1844
- var limb;
1845
- var key;
1846
-
1847
- if (options.retainKeyOrder) {
1848
- for (i = 0; i < len; ++i) {
1849
- key = keys[i];
1850
- limb = tree[key];
1851
-
1852
- defineKey(key,
1853
- ((utils.getFunctionName(limb.constructor) === 'Object'
1854
- && Object.keys(limb).length)
1855
- && (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type))
1856
- ? limb
1857
- : null)
1858
- , proto
1859
- , prefix
1860
- , keys
1861
- , options);
1862
- }
1863
- } else {
1864
- while (i--) {
1865
- key = keys[i];
1866
- limb = tree[key];
1867
-
1868
- defineKey(key,
1869
- ((utils.getFunctionName(limb.constructor) === 'Object'
1870
- && Object.keys(limb).length)
1871
- && (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type))
1872
- ? limb
1873
- : null)
1874
- , proto
1875
- , prefix
1876
- , keys
1877
- , options);
1878
- }
1879
- }
1880
- }
1881
-
1882
- // gets descriptors for all properties of `object`
1883
- // makes all properties non-enumerable to match previous behavior to #2211
1884
- function getOwnPropertyDescriptors(object) {
1885
- var result = {};
1886
-
1887
- Object.getOwnPropertyNames(object).forEach(function(key) {
1888
- result[key] = Object.getOwnPropertyDescriptor(object, key);
1889
- result[key].enumerable = ['isNew', '$__', 'errors', '_doc'].indexOf(key) === -1;
1890
- });
1891
-
1892
- return result;
1893
- }
1894
-
1895
- /*!
1896
- * Defines the accessor named prop on the incoming prototype.
1897
- */
1898
-
1899
- function defineKey(prop, subprops, prototype, prefix, keys, options) {
1900
- var path = (prefix ? prefix + '.' : '') + prop;
1901
- prefix = prefix || '';
1902
-
1903
- if (subprops) {
1904
- Object.defineProperty(prototype, prop, {
1905
- enumerable: true,
1906
- configurable: true,
1907
- get: function() {
1908
- var _this = this;
1909
- if (!this.$__.getters) {
1910
- this.$__.getters = {};
1911
- }
1912
-
1913
- if (!this.$__.getters[path]) {
1914
- var nested = Object.create(Object.getPrototypeOf(this), getOwnPropertyDescriptors(this));
1915
-
1916
- // save scope for nested getters/setters
1917
- if (!prefix) {
1918
- nested.$__.scope = this;
1919
- }
1920
-
1921
- // shadow inherited getters from sub-objects so
1922
- // thing.nested.nested.nested... doesn't occur (gh-366)
1923
- var i = 0,
1924
- len = keys.length;
1925
-
1926
- for (; i < len; ++i) {
1927
- // over-write the parents getter without triggering it
1928
- Object.defineProperty(nested, keys[i], {
1929
- enumerable: false, // It doesn't show up.
1930
- writable: true, // We can set it later.
1931
- configurable: true, // We can Object.defineProperty again.
1932
- value: undefined // It shadows its parent.
1933
- });
1934
- }
1935
-
1936
- Object.defineProperty(nested, 'toObject', {
1937
- enumerable: false,
1938
- configurable: true,
1939
- writable: false,
1940
- value: function() {
1941
- return clone(_this.get(path), { retainKeyOrder: true });
1942
- }
1943
- });
1944
-
1945
- Object.defineProperty(nested, 'toJSON', {
1946
- enumerable: false,
1947
- configurable: true,
1948
- writable: false,
1949
- value: function() {
1950
- return _this.get(path);
1951
- }
1952
- });
1953
-
1954
- Object.defineProperty(nested, '$__isNested', {
1955
- enumerable: false,
1956
- configurable: true,
1957
- writable: false,
1958
- value: true
1959
- });
1960
-
1961
- compile(subprops, nested, path, options);
1962
- this.$__.getters[path] = nested;
1963
- }
1964
-
1965
- return this.$__.getters[path];
1966
- },
1967
- set: function(v) {
1968
- if (v instanceof Document) {
1969
- v = v.toObject({ transform: false });
1970
- }
1971
- return (this.$__.scope || this).set(path, v);
1972
- }
1973
- });
1974
- } else {
1975
- Object.defineProperty(prototype, prop, {
1976
- enumerable: true,
1977
- configurable: true,
1978
- get: function() {
1979
- return this.get.call(this.$__.scope || this, path);
1980
- },
1981
- set: function(v) {
1982
- return this.set.call(this.$__.scope || this, path, v);
1983
- }
1984
- });
1985
- }
1986
- }
1987
-
1988
1826
  /**
1989
1827
  * Assigns/compiles `schema` into this documents prototype.
1990
1828
  *
@@ -8,14 +8,23 @@
8
8
  var Document = require('./document.js');
9
9
  var BrowserDocument = require('./browserDocument.js');
10
10
 
11
+ var isBrowser = false;
12
+
11
13
  /**
12
14
  * Returns the Document constructor for the current context
13
15
  *
14
16
  * @api private
15
17
  */
16
18
  module.exports = function() {
17
- if (typeof window !== 'undefined' && typeof document !== 'undefined' && document === window.document) {
19
+ if (isBrowser) {
18
20
  return BrowserDocument;
19
21
  }
20
22
  return Document;
21
23
  };
24
+
25
+ /*!
26
+ * ignore
27
+ */
28
+ module.exports.setBrowser = function(flag) {
29
+ isBrowser = flag;
30
+ };
@@ -15,7 +15,11 @@ function MissingSchemaError() {
15
15
  + 'Use mongoose.Document(name, schema)';
16
16
  MongooseError.call(this, msg);
17
17
  this.name = 'MissingSchemaError';
18
- Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
18
+ if (Error.captureStackTrace) {
19
+ Error.captureStackTrace(this);
20
+ } else {
21
+ this.stack = new Error().stack;
22
+ }
19
23
  }
20
24
 
21
25
  /*!
@@ -24,7 +24,11 @@ function DivergentArrayError(paths) {
24
24
 
25
25
  MongooseError.call(this, msg);
26
26
  this.name = 'DivergentArrayError';
27
- Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
27
+ if (Error.captureStackTrace) {
28
+ Error.captureStackTrace(this);
29
+ } else {
30
+ this.stack = new Error().stack;
31
+ }
28
32
  }
29
33
 
30
34
  /*!
@@ -16,7 +16,11 @@ function MissingSchemaError(name) {
16
16
  + 'Use mongoose.model(name, schema)';
17
17
  MongooseError.call(this, msg);
18
18
  this.name = 'MissingSchemaError';
19
- Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
19
+ if (Error.captureStackTrace) {
20
+ Error.captureStackTrace(this);
21
+ } else {
22
+ this.stack = new Error().stack;
23
+ }
20
24
  }
21
25
 
22
26
  /*!
@@ -14,7 +14,11 @@ var MongooseError = require('../error.js');
14
14
  function OverwriteModelError(name) {
15
15
  MongooseError.call(this, 'Cannot overwrite `' + name + '` model once compiled.');
16
16
  this.name = 'OverwriteModelError';
17
- Error.captureStackTrace && Error.captureStackTrace(this, arguments.callee);
17
+ if (Error.captureStackTrace) {
18
+ Error.captureStackTrace(this);
19
+ } else {
20
+ this.stack = new Error().stack;
21
+ }
18
22
  }
19
23
 
20
24
  /*!
package/lib/index.js CHANGED
@@ -858,13 +858,3 @@ MongooseThenable.prototype.catch = function(onRejected) {
858
858
  */
859
859
 
860
860
  var mongoose = module.exports = exports = new Mongoose;
861
-
862
- /*!
863
- * ignore
864
- */
865
-
866
- if (typeof jest !== 'undefined' && typeof window !== 'undefined') {
867
- console.warn('You are running mongoose with jest in the jsdom environment. ' +
868
- 'You probably do not want to do this. Switch to the node environment: ' +
869
- 'https://facebook.github.io/jest/docs/configuration.html#testenvironment-string');
870
- }
package/lib/model.js CHANGED
@@ -865,7 +865,10 @@ for (var i in EventEmitter.prototype) {
865
865
  Model.init = function init() {
866
866
  if ((this.schema.options.autoIndex) ||
867
867
  (this.schema.options.autoIndex == null && this.db.config.autoIndex)) {
868
- this.ensureIndexes({ __noPromise: true, _automatic: true });
868
+ var _this = this;
869
+ setImmediate(function() {
870
+ _this.ensureIndexes({ __noPromise: true, _automatic: true });
871
+ });
869
872
  }
870
873
 
871
874
  this.schema.emit('init', this);
@@ -1283,7 +1286,7 @@ Model.find = function find(conditions, projection, options, callback) {
1283
1286
  *
1284
1287
  * Note: `findById()` triggers `findOne` hooks.
1285
1288
  *
1286
- * * Except for how it treats `undefined`. If you use `findOne()`, you'll see
1289
+ * \* Except for how it treats `undefined`. If you use `findOne()`, you'll see
1287
1290
  * that `findOne(undefined)` and `findOne({ _id: undefined })` are equivalent
1288
1291
  * to `findOne({})` and return arbitrary documents. However, mongoose
1289
1292
  * translates `findById(undefined)` into `findOne({ _id: null })`.
@@ -1562,12 +1565,12 @@ Model.$where = function $where() {
1562
1565
  * ####Example:
1563
1566
  *
1564
1567
  * var query = { name: 'borne' };
1565
- * Model.findOneAndUpdate(query, { name: 'jason borne' }, options, callback)
1568
+ * Model.findOneAndUpdate(query, { name: 'jason bourne' }, options, callback)
1566
1569
  *
1567
1570
  * // is sent as
1568
- * Model.findOneAndUpdate(query, { $set: { name: 'jason borne' }}, options, callback)
1571
+ * Model.findOneAndUpdate(query, { $set: { name: 'jason bourne' }}, options, callback)
1569
1572
  *
1570
- * This helps prevent accidentally overwriting your document with `{ name: 'jason borne' }`.
1573
+ * This helps prevent accidentally overwriting your document with `{ name: 'jason bourne' }`.
1571
1574
  *
1572
1575
  * ####Note:
1573
1576
  *
@@ -1586,7 +1589,7 @@ Model.$where = function $where() {
1586
1589
  *
1587
1590
  * Model.findById(id, function (err, doc) {
1588
1591
  * if (err) ..
1589
- * doc.name = 'jason borne';
1592
+ * doc.name = 'jason bourne';
1590
1593
  * doc.save(callback);
1591
1594
  * });
1592
1595
  *
@@ -1676,12 +1679,12 @@ Model.findOneAndUpdate = function(conditions, update, options, callback) {
1676
1679
  *
1677
1680
  * ####Example:
1678
1681
  *
1679
- * Model.findByIdAndUpdate(id, { name: 'jason borne' }, options, callback)
1682
+ * Model.findByIdAndUpdate(id, { name: 'jason bourne' }, options, callback)
1680
1683
  *
1681
1684
  * // is sent as
1682
- * Model.findByIdAndUpdate(id, { $set: { name: 'jason borne' }}, options, callback)
1685
+ * Model.findByIdAndUpdate(id, { $set: { name: 'jason bourne' }}, options, callback)
1683
1686
  *
1684
- * This helps prevent accidentally overwriting your document with `{ name: 'jason borne' }`.
1687
+ * This helps prevent accidentally overwriting your document with `{ name: 'jason bourne' }`.
1685
1688
  *
1686
1689
  * ####Note:
1687
1690
  *
@@ -1700,7 +1703,7 @@ Model.findOneAndUpdate = function(conditions, update, options, callback) {
1700
1703
  *
1701
1704
  * Model.findById(id, function (err, doc) {
1702
1705
  * if (err) ..
1703
- * doc.name = 'jason borne';
1706
+ * doc.name = 'jason bourne';
1704
1707
  * doc.save(callback);
1705
1708
  * });
1706
1709
  *
@@ -1775,7 +1778,7 @@ Model.findByIdAndUpdate = function(id, update, options, callback) {
1775
1778
  *
1776
1779
  * Model.findById(id, function (err, doc) {
1777
1780
  * if (err) ..
1778
- * doc.name = 'jason borne';
1781
+ * doc.name = 'jason bourne';
1779
1782
  * doc.save(callback);
1780
1783
  * });
1781
1784
  *
@@ -2325,13 +2328,13 @@ Model.hydrate = function(obj) {
2325
2328
  * ####Example:
2326
2329
  *
2327
2330
  * var query = { name: 'borne' };
2328
- * Model.update(query, { name: 'jason borne' }, options, callback)
2331
+ * Model.update(query, { name: 'jason bourne' }, options, callback)
2329
2332
  *
2330
2333
  * // is sent as
2331
- * Model.update(query, { $set: { name: 'jason borne' }}, options, callback)
2334
+ * Model.update(query, { $set: { name: 'jason bourne' }}, options, callback)
2332
2335
  * // if overwrite option is false. If overwrite is true, sent without the $set wrapper.
2333
2336
  *
2334
- * This helps prevent accidentally overwriting all documents in your collection with `{ name: 'jason borne' }`.
2337
+ * This helps prevent accidentally overwriting all documents in your collection with `{ name: 'jason bourne' }`.
2335
2338
  *
2336
2339
  * ####Note:
2337
2340
  *
@@ -2356,7 +2359,7 @@ Model.hydrate = function(obj) {
2356
2359
  *
2357
2360
  * Model.findOne({ name: 'borne' }, function (err, doc) {
2358
2361
  * if (err) ..
2359
- * doc.name = 'jason borne';
2362
+ * doc.name = 'jason bourne';
2360
2363
  * doc.save(callback);
2361
2364
  * })
2362
2365
  *
@@ -3136,6 +3139,14 @@ function populate(model, docs, options, callback) {
3136
3139
  var key;
3137
3140
  var val;
3138
3141
 
3142
+ // Clone because `assignRawDocsToIdStructure` will mutate the array
3143
+ var allIds = [].concat(mod.allIds.map(function(v) {
3144
+ if (Array.isArray(v)) {
3145
+ return [].concat(v);
3146
+ }
3147
+ return v;
3148
+ }));
3149
+
3139
3150
  // optimization:
3140
3151
  // record the document positions as returned by
3141
3152
  // the query result.
@@ -3197,6 +3208,7 @@ function populate(model, docs, options, callback) {
3197
3208
  assignVals({
3198
3209
  originalModel: model,
3199
3210
  rawIds: mod.allIds,
3211
+ allIds: allIds,
3200
3212
  localField: mod.localField,
3201
3213
  foreignField: mod.foreignField,
3202
3214
  rawDocs: rawDocs,
@@ -3205,7 +3217,8 @@ function populate(model, docs, options, callback) {
3205
3217
  path: options.path,
3206
3218
  options: assignmentOpts,
3207
3219
  justOne: mod.justOne,
3208
- isVirtual: mod.isVirtual
3220
+ isVirtual: mod.isVirtual,
3221
+ allOptions: mod
3209
3222
  });
3210
3223
  }
3211
3224
  }
@@ -3259,6 +3272,9 @@ function assignVals(o) {
3259
3272
  }
3260
3273
  cur = cur[parts[j]];
3261
3274
  }
3275
+ if (docs[i].$__) {
3276
+ docs[i].populated(o.path, o.allIds[i], o.allOptions);
3277
+ }
3262
3278
  utils.setValue(o.path, rawIds[i], docs[i], setValue);
3263
3279
  }
3264
3280
  }
@@ -3454,9 +3470,6 @@ function getModelsMapForPopulate(model, docs, options) {
3454
3470
  var ret = convertTo_id(utils.getValue(localField, doc));
3455
3471
  var id = String(utils.getValue(foreignField, doc));
3456
3472
  options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
3457
- if (doc.$__) {
3458
- doc.populated(options.path, options._docs[id], options);
3459
- }
3460
3473
 
3461
3474
  var k = modelNames.length;
3462
3475
  while (k--) {
package/lib/query.js CHANGED
@@ -1305,11 +1305,16 @@ function completeMany(model, docs, fields, userProvidedFields, pop, callback) {
1305
1305
  var arr = [];
1306
1306
  var count = docs.length;
1307
1307
  var len = count;
1308
- var opts = pop ?
1309
- {populated: pop}
1310
- : undefined;
1311
- function init(err) {
1312
- if (err) return callback(err);
1308
+ var opts = pop ? { populated: pop } : undefined;
1309
+ var error = null;
1310
+ function init(_error) {
1311
+ if (error != null) {
1312
+ return;
1313
+ }
1314
+ if (_error != null) {
1315
+ error = _error;
1316
+ return callback(error);
1317
+ }
1313
1318
  --count || callback(null, arr);
1314
1319
  }
1315
1320
  for (var i = 0; i < len; ++i) {
@@ -101,9 +101,9 @@ exports.applyPaths = function applyPaths(fields, schema) {
101
101
  // if selecting, apply default schematype select:true fields
102
102
  // if excluding, apply schematype select:false fields
103
103
 
104
- var selected = [],
105
- excluded = [],
106
- seen = [];
104
+ var selected = [];
105
+ var excluded = [];
106
+ var stack = [];
107
107
 
108
108
  var analyzePath = function(path, type) {
109
109
  if (typeof type.selected !== 'boolean') return;
@@ -133,8 +133,10 @@ exports.applyPaths = function applyPaths(fields, schema) {
133
133
  prefix || (prefix = '');
134
134
 
135
135
  // avoid recursion
136
- if (~seen.indexOf(schema)) return;
137
- seen.push(schema);
136
+ if (stack.indexOf(schema) !== -1) {
137
+ return;
138
+ }
139
+ stack.push(schema);
138
140
 
139
141
  schema.eachPath(function(path, type) {
140
142
  if (prefix) path = prefix + '.' + path;
@@ -146,6 +148,8 @@ exports.applyPaths = function applyPaths(fields, schema) {
146
148
  analyzeSchema(type.schema, path);
147
149
  }
148
150
  });
151
+
152
+ stack.pop();
149
153
  };
150
154
 
151
155
  analyzeSchema(schema);
@@ -133,7 +133,13 @@ Embedded.prototype.cast = function(val, doc, init, priorVal) {
133
133
  }
134
134
 
135
135
  if (priorVal && priorVal.$isSingleNested) {
136
- subdoc = new this.caster(priorVal.toObject(), doc ? doc.$__.selected : void 0, doc);
136
+ var _priorVal = priorVal.toObject({
137
+ minimize: true,
138
+ transform: false,
139
+ virtuals: false,
140
+ getters: false
141
+ });
142
+ subdoc = new this.caster(_priorVal, doc ? doc.$__.selected : void 0, doc);
137
143
  } else {
138
144
  subdoc = new this.caster(void 0, doc ? doc.$__.selected : void 0, doc);
139
145
  }
@@ -20,10 +20,14 @@ var SchemaType = require('../schematype'),
20
20
  */
21
21
 
22
22
  function ObjectId(key, options) {
23
- if ((typeof key === 'string' && key.length === 24) || typeof key === 'undefined') {
24
- console.warn('To create a new ObjectId please try ' +
23
+ var isKeyHexStr = typeof key === 'string' && /^a-f0-9$/i.test(key);
24
+ var suppressWarning = options && options.suppressWarning;
25
+ if ((isKeyHexStr || typeof key === 'undefined') && !suppressWarning) {
26
+ console.warn('mongoose: To create a new ObjectId please try ' +
25
27
  '`Mongoose.Types.ObjectId` instead of using ' +
26
- '`Mongoose.Schema.ObjectId`.');
28
+ '`Mongoose.Schema.ObjectId`. Set the `suppressWarning` option if ' +
29
+ 'you\'re trying to create a hex char path in your schema.');
30
+ console.trace();
27
31
  }
28
32
  SchemaType.call(this, key, options, 'ObjectID');
29
33
  }
package/lib/schema.js CHANGED
@@ -277,7 +277,7 @@ Schema.prototype.tree;
277
277
  */
278
278
 
279
279
  Schema.prototype.clone = function() {
280
- var s = new Schema(this.obj, this.options);
280
+ var s = new Schema(this.paths, this.options);
281
281
  // Clone the call queue
282
282
  s.callQueue = this.callQueue.map(function(f) { return f; });
283
283
  s.methods = utils.clone(this.methods);
@@ -531,6 +531,10 @@ Schema.prototype.path = function(path, obj) {
531
531
  */
532
532
 
533
533
  Schema.interpretAsType = function(path, obj, options) {
534
+ if (obj instanceof SchemaType) {
535
+ return obj;
536
+ }
537
+
534
538
  if (obj.constructor) {
535
539
  var constructorName = utils.getFunctionName(obj.constructor);
536
540
  if (constructorName !== 'Object') {
@@ -775,28 +779,35 @@ Schema.prototype.hasMixedParent = function(path) {
775
779
  */
776
780
  Schema.prototype.setupTimestamp = function(timestamps) {
777
781
  if (timestamps) {
778
- var createdAt = timestamps.createdAt || 'createdAt';
779
- var updatedAt = timestamps.updatedAt || 'updatedAt';
782
+ var createdAt = handleTimestampOption(timestamps, 'createdAt');
783
+ var updatedAt = handleTimestampOption(timestamps, 'updatedAt');
780
784
  var schemaAdditions = {};
781
785
 
782
- var parts = createdAt.split('.');
786
+ var parts;
783
787
  var i;
784
- var cur = schemaAdditions;
785
- if (this.pathType(createdAt) === 'adhocOrUndefined') {
786
- for (i = 0; i < parts.length; ++i) {
787
- cur[parts[i]] = (i < parts.length - 1 ?
788
- cur[parts[i]] || {} :
789
- Date);
788
+ var cur;
789
+
790
+ if (createdAt != null) {
791
+ cur = schemaAdditions;
792
+ parts = createdAt.split('.');
793
+ if (this.pathType(createdAt) === 'adhocOrUndefined') {
794
+ for (i = 0; i < parts.length; ++i) {
795
+ cur[parts[i]] = (i < parts.length - 1 ?
796
+ cur[parts[i]] || {} :
797
+ Date);
798
+ }
790
799
  }
791
800
  }
792
801
 
793
- parts = updatedAt.split('.');
794
- cur = schemaAdditions;
795
- if (this.pathType(createdAt) === 'adhocOrUndefined') {
796
- for (i = 0; i < parts.length; ++i) {
797
- cur[parts[i]] = (i < parts.length - 1 ?
798
- cur[parts[i]] || {} :
799
- Date);
802
+ if (updatedAt != null) {
803
+ parts = updatedAt.split('.');
804
+ cur = schemaAdditions;
805
+ if (this.pathType(createdAt) === 'adhocOrUndefined') {
806
+ for (i = 0; i < parts.length; ++i) {
807
+ cur[parts[i]] = (i < parts.length - 1 ?
808
+ cur[parts[i]] || {} :
809
+ Date);
810
+ }
800
811
  }
801
812
  }
802
813
 
@@ -806,12 +817,20 @@ Schema.prototype.setupTimestamp = function(timestamps) {
806
817
  var defaultTimestamp = new Date();
807
818
  var auto_id = this._id && this._id.auto;
808
819
 
809
- if (!this.get(createdAt) && this.isSelected(createdAt)) {
820
+ if (createdAt != null && !this.get(createdAt) && this.isSelected(createdAt)) {
810
821
  this.set(createdAt, auto_id ? this._id.getTimestamp() : defaultTimestamp);
811
822
  }
812
823
 
813
- if (this.isNew || this.isModified()) {
814
- this.set(updatedAt, this.isNew ? this.get(createdAt) : defaultTimestamp);
824
+ if (updatedAt != null && (this.isNew || this.isModified())) {
825
+ var ts = defaultTimestamp;
826
+ if (this.isNew) {
827
+ if (createdAt != null) {
828
+ ts = this.get(createdAt);
829
+ } else if (auto_id) {
830
+ ts = this._id.getTimestamp();
831
+ }
832
+ }
833
+ this.set(updatedAt, ts);
815
834
  }
816
835
 
817
836
  next();
@@ -827,10 +846,10 @@ Schema.prototype.setupTimestamp = function(timestamps) {
827
846
  updates.$set = {};
828
847
  _updates = updates.$set;
829
848
  }
830
- if (!currentUpdate[updatedAt]) {
849
+ if (updatedAt != null && !currentUpdate[updatedAt]) {
831
850
  _updates[updatedAt] = now;
832
851
  }
833
- if (!currentUpdate[createdAt]) {
852
+ if (createdAt != null && !currentUpdate[createdAt]) {
834
853
  _updates[createdAt] = now;
835
854
  }
836
855
  return updates;
@@ -838,19 +857,22 @@ Schema.prototype.setupTimestamp = function(timestamps) {
838
857
  updates = { $set: {} };
839
858
  currentUpdate = currentUpdate || {};
840
859
 
841
- if (!currentUpdate.$currentDate || !currentUpdate.$currentDate[updatedAt]) {
860
+ if (updatedAt != null &&
861
+ (!currentUpdate.$currentDate || !currentUpdate.$currentDate[updatedAt])) {
842
862
  updates.$set[updatedAt] = now;
843
863
  }
844
864
 
845
- if (currentUpdate[createdAt]) {
846
- delete currentUpdate[createdAt];
847
- }
848
- if (currentUpdate.$set && currentUpdate.$set[createdAt]) {
849
- delete currentUpdate.$set[createdAt];
850
- }
865
+ if (createdAt != null) {
866
+ if (currentUpdate[createdAt]) {
867
+ delete currentUpdate[createdAt];
868
+ }
869
+ if (currentUpdate.$set && currentUpdate.$set[createdAt]) {
870
+ delete currentUpdate.$set[createdAt];
871
+ }
851
872
 
852
- updates.$setOnInsert = {};
853
- updates.$setOnInsert[createdAt] = now;
873
+ updates.$setOnInsert = {};
874
+ updates.$setOnInsert[createdAt] = now;
875
+ }
854
876
 
855
877
  return updates;
856
878
  };
@@ -885,6 +907,23 @@ Schema.prototype.setupTimestamp = function(timestamps) {
885
907
  }
886
908
  };
887
909
 
910
+ /*!
911
+ * ignore
912
+ */
913
+
914
+ function handleTimestampOption(arg, prop) {
915
+ if (typeof arg === 'boolean') {
916
+ return prop;
917
+ }
918
+ if (typeof arg[prop] === 'boolean') {
919
+ return arg[prop] ? prop : null;
920
+ }
921
+ if (!(prop in arg)) {
922
+ return prop;
923
+ }
924
+ return arg[prop];
925
+ }
926
+
888
927
  /*!
889
928
  * ignore
890
929
  */
@@ -912,16 +951,24 @@ function applyTimestampsToChildren(query) {
912
951
  $path.$isMongooseDocumentArray &&
913
952
  $path.schema.options.timestamps) {
914
953
  timestamps = $path.schema.options.timestamps;
915
- createdAt = timestamps.createdAt || 'createdAt';
916
- updatedAt = timestamps.updatedAt || 'updatedAt';
954
+ createdAt = handleTimestampOption(timestamps, 'createdAt');
955
+ updatedAt = handleTimestampOption(timestamps, 'updatedAt');
917
956
  if (update.$push[key].$each) {
918
957
  update.$push[key].$each.forEach(function(subdoc) {
919
- subdoc[updatedAt] = now;
920
- subdoc[createdAt] = now;
958
+ if (updatedAt != null) {
959
+ subdoc[updatedAt] = now;
960
+ }
961
+ if (createdAt != null) {
962
+ subdoc[createdAt] = now;
963
+ }
921
964
  });
922
965
  } else {
923
- update.$push[key][updatedAt] = now;
924
- update.$push[key][createdAt] = now;
966
+ if (updatedAt != null) {
967
+ update.$push[key][updatedAt] = now;
968
+ }
969
+ if (createdAt != null) {
970
+ update.$push[key][createdAt] = now;
971
+ }
925
972
  }
926
973
  }
927
974
  }
@@ -936,20 +983,28 @@ function applyTimestampsToChildren(query) {
936
983
  len = update.$set[key].length;
937
984
  timestamps = schema.path(key).schema.options.timestamps;
938
985
  if (timestamps) {
939
- createdAt = timestamps.createdAt || 'createdAt';
940
- updatedAt = timestamps.updatedAt || 'updatedAt';
986
+ createdAt = handleTimestampOption(timestamps, 'createdAt');
987
+ updatedAt = handleTimestampOption(timestamps, 'updatedAt');
941
988
  for (var i = 0; i < len; ++i) {
942
- update.$set[key][i][updatedAt] = now;
943
- update.$set[key][i][createdAt] = now;
989
+ if (updatedAt != null) {
990
+ update.$set[key][i][updatedAt] = now;
991
+ }
992
+ if (createdAt != null) {
993
+ update.$set[key][i][createdAt] = now;
994
+ }
944
995
  }
945
996
  }
946
997
  } else if (update.$set[key] && path.$isSingleNested) {
947
998
  timestamps = schema.path(key).schema.options.timestamps;
948
999
  if (timestamps) {
949
- createdAt = timestamps.createdAt || 'createdAt';
950
- updatedAt = timestamps.updatedAt || 'updatedAt';
951
- update.$set[key][updatedAt] = now;
952
- update.$set[key][createdAt] = now;
1000
+ createdAt = handleTimestampOption(timestamps, 'createdAt');
1001
+ updatedAt = handleTimestampOption(timestamps, 'updatedAt');
1002
+ if (updatedAt != null) {
1003
+ update.$set[key][updatedAt] = now;
1004
+ }
1005
+ if (createdAt != null) {
1006
+ update.$set[key][createdAt] = now;
1007
+ }
953
1008
  }
954
1009
  }
955
1010
  }
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ /*!
4
+ * ignore
5
+ */
6
+
7
+ module.exports = function cleanModifiedSubpaths(doc, path) {
8
+ var _modifiedPaths = Object.keys(doc.$__.activePaths.states.modify);
9
+ var _numModifiedPaths = _modifiedPaths.length;
10
+ var deleted = 0;
11
+ for (var j = 0; j < _numModifiedPaths; ++j) {
12
+ if (_modifiedPaths[j].indexOf(path + '.') === 0) {
13
+ delete doc.$__.activePaths.states.modify[_modifiedPaths[j]];
14
+ ++deleted;
15
+ }
16
+ }
17
+ return deleted;
18
+ };
@@ -0,0 +1,165 @@
1
+ 'use strict';
2
+
3
+ var Document;
4
+ var utils = require('../../utils');
5
+
6
+ /*!
7
+ * exports
8
+ */
9
+
10
+ exports.compile = compile;
11
+ exports.defineKey = defineKey;
12
+
13
+ /*!
14
+ * Compiles schemas.
15
+ */
16
+
17
+ function compile(tree, proto, prefix, options) {
18
+ Document = Document || require('../../document');
19
+ var keys = Object.keys(tree);
20
+ var i = keys.length;
21
+ var len = keys.length;
22
+ var limb;
23
+ var key;
24
+
25
+ if (options.retainKeyOrder) {
26
+ for (i = 0; i < len; ++i) {
27
+ key = keys[i];
28
+ limb = tree[key];
29
+
30
+ defineKey(key,
31
+ ((utils.getFunctionName(limb.constructor) === 'Object'
32
+ && Object.keys(limb).length)
33
+ && (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type))
34
+ ? limb
35
+ : null)
36
+ , proto
37
+ , prefix
38
+ , keys
39
+ , options);
40
+ }
41
+ } else {
42
+ while (i--) {
43
+ key = keys[i];
44
+ limb = tree[key];
45
+
46
+ defineKey(key,
47
+ ((utils.getFunctionName(limb.constructor) === 'Object'
48
+ && Object.keys(limb).length)
49
+ && (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type))
50
+ ? limb
51
+ : null)
52
+ , proto
53
+ , prefix
54
+ , keys
55
+ , options);
56
+ }
57
+ }
58
+ }
59
+
60
+ /*!
61
+ * Defines the accessor named prop on the incoming prototype.
62
+ */
63
+
64
+ function defineKey(prop, subprops, prototype, prefix, keys, options) {
65
+ Document = Document || require('../../document');
66
+ var path = (prefix ? prefix + '.' : '') + prop;
67
+ prefix = prefix || '';
68
+
69
+ if (subprops) {
70
+ Object.defineProperty(prototype, prop, {
71
+ enumerable: true,
72
+ configurable: true,
73
+ get: function() {
74
+ var _this = this;
75
+ if (!this.$__.getters) {
76
+ this.$__.getters = {};
77
+ }
78
+
79
+ if (!this.$__.getters[path]) {
80
+ var nested = Object.create(Object.getPrototypeOf(this), getOwnPropertyDescriptors(this));
81
+
82
+ // save scope for nested getters/setters
83
+ if (!prefix) {
84
+ nested.$__.scope = this;
85
+ }
86
+
87
+ // shadow inherited getters from sub-objects so
88
+ // thing.nested.nested.nested... doesn't occur (gh-366)
89
+ var i = 0,
90
+ len = keys.length;
91
+
92
+ for (; i < len; ++i) {
93
+ // over-write the parents getter without triggering it
94
+ Object.defineProperty(nested, keys[i], {
95
+ enumerable: false, // It doesn't show up.
96
+ writable: true, // We can set it later.
97
+ configurable: true, // We can Object.defineProperty again.
98
+ value: undefined // It shadows its parent.
99
+ });
100
+ }
101
+
102
+ Object.defineProperty(nested, 'toObject', {
103
+ enumerable: false,
104
+ configurable: true,
105
+ writable: false,
106
+ value: function() {
107
+ return utils.clone(_this.get(path), { retainKeyOrder: true });
108
+ }
109
+ });
110
+
111
+ Object.defineProperty(nested, 'toJSON', {
112
+ enumerable: false,
113
+ configurable: true,
114
+ writable: false,
115
+ value: function() {
116
+ return _this.get(path);
117
+ }
118
+ });
119
+
120
+ Object.defineProperty(nested, '$__isNested', {
121
+ enumerable: false,
122
+ configurable: true,
123
+ writable: false,
124
+ value: true
125
+ });
126
+
127
+ compile(subprops, nested, path, options);
128
+ this.$__.getters[path] = nested;
129
+ }
130
+
131
+ return this.$__.getters[path];
132
+ },
133
+ set: function(v) {
134
+ if (v instanceof Document) {
135
+ v = v.toObject({ transform: false });
136
+ }
137
+ return (this.$__.scope || this).set(path, v);
138
+ }
139
+ });
140
+ } else {
141
+ Object.defineProperty(prototype, prop, {
142
+ enumerable: true,
143
+ configurable: true,
144
+ get: function() {
145
+ return this.get.call(this.$__.scope || this, path);
146
+ },
147
+ set: function(v) {
148
+ return this.set.call(this.$__.scope || this, path, v);
149
+ }
150
+ });
151
+ }
152
+ }
153
+
154
+ // gets descriptors for all properties of `object`
155
+ // makes all properties non-enumerable to match previous behavior to #2211
156
+ function getOwnPropertyDescriptors(object) {
157
+ var result = {};
158
+
159
+ Object.getOwnPropertyNames(object).forEach(function(key) {
160
+ result[key] = Object.getOwnPropertyDescriptor(object, key);
161
+ result[key].enumerable = ['isNew', '$__', 'errors', '_doc'].indexOf(key) === -1;
162
+ });
163
+
164
+ return result;
165
+ }
@@ -35,6 +35,14 @@ module.exports = function discriminator(model, name, schema) {
35
35
  }
36
36
 
37
37
  function merge(schema, baseSchema) {
38
+ if (baseSchema.paths._id &&
39
+ baseSchema.paths._id.options &&
40
+ !baseSchema.paths._id.options.auto) {
41
+ var originalSchema = schema;
42
+ utils.merge(schema, originalSchema, { retainKeyOrder: true });
43
+ delete schema.paths._id;
44
+ delete schema.tree._id;
45
+ }
38
46
  utils.merge(schema, baseSchema, { retainKeyOrder: true });
39
47
 
40
48
  var obj = {};
@@ -308,7 +308,7 @@ function castUpdateVal(schema, val, op, $conditional, context) {
308
308
  return schema.castForQueryWrapper({
309
309
  val: val,
310
310
  context: context,
311
- $skipQueryCastForUpdate: val != null
311
+ $skipQueryCastForUpdate: val != null && schema.$isMongooseArray
312
312
  });
313
313
  }
314
314
 
@@ -5,7 +5,9 @@
5
5
  var EmbeddedDocument = require('./embedded');
6
6
  var Document = require('../document');
7
7
  var ObjectId = require('./objectid');
8
+ var cleanModifiedSubpaths = require('../services/document/cleanModifiedSubpaths');
8
9
  var utils = require('../utils');
10
+
9
11
  var isMongooseObject = utils.isMongooseObject;
10
12
 
11
13
  /**
@@ -103,7 +105,7 @@ MongooseArray.mixin = {
103
105
 
104
106
  if (populated && value !== null && value !== undefined) {
105
107
  // cast to the populated Models schema
106
- Model = populated.options.model;
108
+ Model = populated.options.model || populated.options.Model;
107
109
 
108
110
  // only objects are permitted so we can safely assume that
109
111
  // non-objects are to be interpreted as _id
@@ -154,7 +156,7 @@ MongooseArray.mixin = {
154
156
  }
155
157
  }
156
158
 
157
- parent.markModified(dirtyPath, elem);
159
+ parent.markModified(dirtyPath, arguments.length > 0 ? elem : parent);
158
160
  }
159
161
 
160
162
  return this;
@@ -306,9 +308,28 @@ MongooseArray.mixin = {
306
308
  */
307
309
 
308
310
  push: function() {
311
+ var ref = this._schema.caster.options && this._schema.caster.options.ref;
312
+ if (this.length === 0 &&
313
+ arguments.length) {
314
+ var allSubdocs = true;
315
+ for (var i = 0; i < arguments.length; ++i) {
316
+ var arg = arguments[i];
317
+ var model = arg.constructor;
318
+ allSubdocs = arg instanceof Document &&
319
+ ref &&
320
+ (model.modelName === ref || model.baseModelName === ref);
321
+ if (!allSubdocs) {
322
+ break;
323
+ }
324
+ }
325
+
326
+ if (allSubdocs) {
327
+ this._parent.populated(this._path, [], { model: arguments[0].constructor });
328
+ }
329
+ }
309
330
  var values = [].map.call(arguments, this._mapCast, this);
310
331
  values = this._schema.applySetters(values, this._parent, undefined,
311
- undefined, {skipDocumentArrayCast: true});
332
+ undefined, { skipDocumentArrayCast: true });
312
333
  var ret = [].push.apply(this, values);
313
334
 
314
335
  // $pushAll might be fibbed (could be $push). But it makes it easier to
@@ -539,6 +560,15 @@ MongooseArray.mixin = {
539
560
  }
540
561
 
541
562
  this._markModified();
563
+
564
+ // Might have modified child paths and then pulled, like
565
+ // `doc.children[1].name = 'test';` followed by
566
+ // `doc.children.remove(doc.children[0]);`. In this case we fall back
567
+ // to a `$set` on the whole array. See #3511
568
+ if (cleanModifiedSubpaths(this._parent, this._path) > 0) {
569
+ this._registerAtomic('$set', this);
570
+ }
571
+
542
572
  return this;
543
573
  },
544
574
 
@@ -203,6 +203,19 @@ MongooseBuffer.mixin.toObject = function(options) {
203
203
  return new Binary(this, subtype);
204
204
  };
205
205
 
206
+ /**
207
+ * Converts this buffer for storage in MongoDB, including subtype
208
+ *
209
+ * @return {Binary}
210
+ * @api public
211
+ * @method toBSON
212
+ * @receiver MongooseBuffer
213
+ */
214
+
215
+ MongooseBuffer.mixin.toBSON = function() {
216
+ return new Binary(this, this._subtype || 0);
217
+ };
218
+
206
219
  /**
207
220
  * Determines if this buffer is equals to `other` buffer
208
221
  *
@@ -60,7 +60,7 @@ Subdocument.prototype.markModified = function(path) {
60
60
  if (this.$parent.isDirectModified(this.$basePath)) {
61
61
  return;
62
62
  }
63
- this.$parent.markModified([this.$basePath, path].join('.'));
63
+ this.$parent.markModified([this.$basePath, path].join('.'), this);
64
64
  }
65
65
  };
66
66
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "4.11.8",
4
+ "version": "4.11.12",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -55,6 +55,7 @@
55
55
  "tbd": "0.6.4",
56
56
  "uglify-js": "2.7.0",
57
57
  "uuid": "2.0.3",
58
+ "uuid-parse": "1.0.0",
58
59
  "validator": "5.4.0"
59
60
  },
60
61
  "browserDependencies": {