mongoose 4.12.6 → 4.13.3
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/.travis.yml +1 -0
- package/History.md +33 -0
- package/lib/aggregate.js +64 -15
- package/lib/cast.js +1 -1
- package/lib/connection.js +32 -2
- package/lib/cursor/AggregationCursor.js +11 -4
- package/lib/document.js +65 -38
- package/lib/model.js +100 -23
- package/lib/query.js +6 -22
- package/lib/queryhelpers.js +20 -3
- package/lib/schema/array.js +1 -1
- package/lib/schema/buffer.js +27 -0
- package/lib/schema/documentarray.js +0 -3
- package/lib/schema/embedded.js +0 -3
- package/lib/schema.js +9 -3
- package/lib/schematype.js +12 -7
- package/lib/services/document/compile.js +3 -2
- package/lib/services/model/applyHooks.js +10 -2
- package/lib/services/projection/isInclusive.js +28 -0
- package/lib/services/query/castUpdate.js +55 -17
- package/lib/utils.js +6 -0
- package/package.json +3 -2
- package/release-items.md +1 -1
package/.travis.yml
CHANGED
package/History.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
4.13.3 / 2017-11-15
|
|
2
|
+
===================
|
|
3
|
+
* chore: add node 8 to travis #5818 [superheri](https://github.com/superheri)
|
|
4
|
+
* fix(document): don't apply transforms to nested docs when updating already saved doc #5807
|
|
5
|
+
|
|
6
|
+
4.13.2 / 2017-11-11
|
|
7
|
+
===================
|
|
8
|
+
* feat(buffer): add support for subtype prop #5530
|
|
9
|
+
|
|
10
|
+
4.13.1 / 2017-11-08
|
|
11
|
+
===================
|
|
12
|
+
* fix: accept multiple paths or array of paths to depopulate #5798 #5797 [adamreisnz](https://github.com/adamreisnz)
|
|
13
|
+
* fix(document): pass default array as actual array rather than taking first element #5780
|
|
14
|
+
* fix(model): increment version when $set-ing it in a save() that requires a version bump #5779
|
|
15
|
+
* fix(query): don't explicitly project in discriminator key if user projected in parent path #5775 #5754
|
|
16
|
+
* fix(model): cast query option to geoNear() #5765
|
|
17
|
+
* fix(query): don't treat projection with just $slice as inclusive #5737
|
|
18
|
+
* fix(discriminator): defer applying embedded discriminator hooks until top-level model is compiled #5706
|
|
19
|
+
* docs(discriminator): add warning to always attach hooks before calling discriminator() #5706
|
|
20
|
+
|
|
21
|
+
4.13.0 / 2017-11-02
|
|
22
|
+
===================
|
|
23
|
+
* feat(aggregate): add $addFields helper #5740 [AyushG3112](https://github.com/AyushG3112)
|
|
24
|
+
* feat(connection): add connection-level bufferCommands #5720
|
|
25
|
+
* feat(connection): add createCollection() helper #5712
|
|
26
|
+
* feat(populate): support setting localField and foreignField to functions #5704 #5602
|
|
27
|
+
* feat(query): add multipleCastError option for aggregating cast errors when casting update #5609
|
|
28
|
+
* feat(populate): allow passing a function to virtual ref #5602
|
|
29
|
+
* feat(schema): add excludeIndexes option to optionally prevent collecting indexes from nested schemas #5575
|
|
30
|
+
* feat(model): report validation errors from `insertMany()` if using `ordered: false` and `rawResult: true` #5337
|
|
31
|
+
* feat(aggregate): add pre/post aggregate middleware #5251
|
|
32
|
+
* feat(schema): allow using `set` as a schema path #1939
|
|
33
|
+
|
|
1
34
|
4.12.6 / 2017-11-01
|
|
2
35
|
===================
|
|
3
36
|
* fix(schema): make clone() copy query helpers correctly #5752
|
package/lib/aggregate.js
CHANGED
|
@@ -106,6 +106,41 @@ Aggregate.prototype.append = function() {
|
|
|
106
106
|
return this;
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Appends a new $addFields operator to this aggregate pipeline.
|
|
111
|
+
* Requires MongoDB v3.4+ to work
|
|
112
|
+
*
|
|
113
|
+
* ####Examples:
|
|
114
|
+
*
|
|
115
|
+
* // adding new fields based on existing fields
|
|
116
|
+
* aggregate.addFields({
|
|
117
|
+
* newField: '$b.nested'
|
|
118
|
+
* , plusTen: { $add: ['$val', 10]}
|
|
119
|
+
* , sub: {
|
|
120
|
+
* name: '$a'
|
|
121
|
+
* }
|
|
122
|
+
* })
|
|
123
|
+
*
|
|
124
|
+
* // etc
|
|
125
|
+
* aggregate.addFields({ salary_k: { $divide: [ "$salary", 1000 ] } });
|
|
126
|
+
*
|
|
127
|
+
* @param {Object} arg field specification
|
|
128
|
+
* @see $addFields https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/
|
|
129
|
+
* @return {Aggregate}
|
|
130
|
+
* @api public
|
|
131
|
+
*/
|
|
132
|
+
Aggregate.prototype.addFields = function(arg) {
|
|
133
|
+
var fields = {};
|
|
134
|
+
if (typeof arg === 'object' && !util.isArray(arg)) {
|
|
135
|
+
Object.keys(arg).forEach(function(field) {
|
|
136
|
+
fields[field] = arg[field];
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
throw new Error('Invalid addFields() argument. Must be an object');
|
|
140
|
+
}
|
|
141
|
+
return this.append({$addFields: fields});
|
|
142
|
+
};
|
|
143
|
+
|
|
109
144
|
/**
|
|
110
145
|
* Appends a new $project operator to this aggregate pipeline.
|
|
111
146
|
*
|
|
@@ -625,26 +660,27 @@ Aggregate.prototype.exec = function(callback) {
|
|
|
625
660
|
throw new Error('Aggregate not bound to any Model');
|
|
626
661
|
}
|
|
627
662
|
var _this = this;
|
|
663
|
+
var model = this._model;
|
|
628
664
|
var Promise = PromiseProvider.get();
|
|
629
|
-
var options = utils.clone(this.options);
|
|
665
|
+
var options = utils.clone(this.options || {});
|
|
666
|
+
var pipeline = this._pipeline;
|
|
667
|
+
var collection = this._model.collection;
|
|
630
668
|
|
|
631
669
|
if (options && options.cursor) {
|
|
632
670
|
if (options.cursor.async) {
|
|
633
671
|
delete options.cursor.async;
|
|
634
672
|
return new Promise.ES6(function(resolve) {
|
|
635
|
-
if (!
|
|
673
|
+
if (!collection.buffer) {
|
|
636
674
|
process.nextTick(function() {
|
|
637
|
-
var cursor =
|
|
638
|
-
aggregate(_this._pipeline, options || {});
|
|
675
|
+
var cursor = collection.aggregate(pipeline, options);
|
|
639
676
|
decorateCursor(cursor);
|
|
640
677
|
resolve(cursor);
|
|
641
678
|
callback && callback(null, cursor);
|
|
642
679
|
});
|
|
643
680
|
return;
|
|
644
681
|
}
|
|
645
|
-
|
|
646
|
-
var cursor =
|
|
647
|
-
aggregate(_this._pipeline, options || {});
|
|
682
|
+
collection.emitter.once('queue', function() {
|
|
683
|
+
var cursor = collection.aggregate(pipeline, options);
|
|
648
684
|
decorateCursor(cursor);
|
|
649
685
|
resolve(cursor);
|
|
650
686
|
callback && callback(null, cursor);
|
|
@@ -654,14 +690,13 @@ Aggregate.prototype.exec = function(callback) {
|
|
|
654
690
|
delete options.cursor.useMongooseAggCursor;
|
|
655
691
|
return new AggregationCursor(this);
|
|
656
692
|
}
|
|
657
|
-
var cursor =
|
|
658
|
-
aggregate(this._pipeline, this.options || {});
|
|
693
|
+
var cursor = collection.aggregate(pipeline, options);
|
|
659
694
|
decorateCursor(cursor);
|
|
660
695
|
return cursor;
|
|
661
696
|
}
|
|
662
697
|
|
|
663
698
|
return new Promise.ES6(function(resolve, reject) {
|
|
664
|
-
if (!
|
|
699
|
+
if (!pipeline.length) {
|
|
665
700
|
var err = new Error('Aggregate has empty pipeline');
|
|
666
701
|
if (callback) {
|
|
667
702
|
callback(err);
|
|
@@ -672,9 +707,20 @@ Aggregate.prototype.exec = function(callback) {
|
|
|
672
707
|
|
|
673
708
|
prepareDiscriminatorPipeline(_this);
|
|
674
709
|
|
|
675
|
-
_this
|
|
676
|
-
|
|
677
|
-
|
|
710
|
+
model.hooks.execPre('aggregate', _this, function(error) {
|
|
711
|
+
if (error) {
|
|
712
|
+
var _opts = { error: error };
|
|
713
|
+
return model.hooks.execPost('aggregate', _this, [null], _opts, function(error) {
|
|
714
|
+
if (callback) {
|
|
715
|
+
callback(error);
|
|
716
|
+
}
|
|
717
|
+
reject(error);
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
collection.aggregate(pipeline, options, function(error, result) {
|
|
722
|
+
var _opts = { error: error };
|
|
723
|
+
model.hooks.execPost('aggregate', _this, [result], _opts, function(error, result) {
|
|
678
724
|
if (error) {
|
|
679
725
|
if (callback) {
|
|
680
726
|
callback(error);
|
|
@@ -688,6 +734,8 @@ Aggregate.prototype.exec = function(callback) {
|
|
|
688
734
|
}
|
|
689
735
|
resolve(result);
|
|
690
736
|
});
|
|
737
|
+
});
|
|
738
|
+
});
|
|
691
739
|
});
|
|
692
740
|
};
|
|
693
741
|
|
|
@@ -758,6 +806,8 @@ function isOperator(obj) {
|
|
|
758
806
|
* @param {Aggregate} aggregate Aggregate to prepare
|
|
759
807
|
*/
|
|
760
808
|
|
|
809
|
+
Aggregate._prepareDiscriminatorPipeline = prepareDiscriminatorPipeline;
|
|
810
|
+
|
|
761
811
|
function prepareDiscriminatorPipeline(aggregate) {
|
|
762
812
|
var schema = aggregate._model.schema,
|
|
763
813
|
discriminatorMapping = schema && schema.discriminatorMapping;
|
|
@@ -781,12 +831,11 @@ function prepareDiscriminatorPipeline(aggregate) {
|
|
|
781
831
|
} else {
|
|
782
832
|
var match = {};
|
|
783
833
|
match[discriminatorKey] = discriminatorValue;
|
|
784
|
-
aggregate._pipeline
|
|
834
|
+
aggregate._pipeline.unshift({ $match: match });
|
|
785
835
|
}
|
|
786
836
|
}
|
|
787
837
|
}
|
|
788
838
|
|
|
789
|
-
|
|
790
839
|
/*!
|
|
791
840
|
* Exports
|
|
792
841
|
*/
|
package/lib/cast.js
CHANGED
|
@@ -10,7 +10,7 @@ var utils = require('./utils');
|
|
|
10
10
|
var ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Handles internal casting for
|
|
13
|
+
* Handles internal casting for query filters.
|
|
14
14
|
*
|
|
15
15
|
* @param {Schema} schema
|
|
16
16
|
* @param {Object} obj Object to cast
|
package/lib/connection.js
CHANGED
|
@@ -368,6 +368,28 @@ Connection.prototype._openWithoutPromise = function() {
|
|
|
368
368
|
});
|
|
369
369
|
};
|
|
370
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Helper for `createCollection()`. Will explicitly create the given collection
|
|
373
|
+
* with specified options. Used to create [capped collections](https://docs.mongodb.com/manual/core/capped-collections/)
|
|
374
|
+
* and [views](https://docs.mongodb.com/manual/core/views/) from mongoose.
|
|
375
|
+
*
|
|
376
|
+
* Options are passed down without modification to the [MongoDB driver's `createCollection()` function](http://mongodb.github.io/node-mongodb-native/2.2/api/Db.html#createCollection)
|
|
377
|
+
*
|
|
378
|
+
* @param {string} collection The collection to delete
|
|
379
|
+
* @param {Object} [options] see [MongoDB driver docs](http://mongodb.github.io/node-mongodb-native/2.2/api/Db.html#createCollection)
|
|
380
|
+
* @param {Function} [callback]
|
|
381
|
+
* @return {Promise}
|
|
382
|
+
* @api public
|
|
383
|
+
*/
|
|
384
|
+
|
|
385
|
+
Connection.prototype.createCollection = _wrapConnHelper(function createCollection(collection, options, cb) {
|
|
386
|
+
if (typeof options === 'function') {
|
|
387
|
+
cb = options;
|
|
388
|
+
options = {};
|
|
389
|
+
}
|
|
390
|
+
this.db.createCollection(collection, options, cb);
|
|
391
|
+
});
|
|
392
|
+
|
|
371
393
|
/**
|
|
372
394
|
* Helper for `dropCollection()`. Will delete the given collection, including
|
|
373
395
|
* all documents and indexes.
|
|
@@ -403,8 +425,10 @@ function _wrapConnHelper(fn) {
|
|
|
403
425
|
return function() {
|
|
404
426
|
var _this = this;
|
|
405
427
|
var Promise = PromiseProvider.get();
|
|
406
|
-
var
|
|
407
|
-
var
|
|
428
|
+
var cb = arguments.length > 0 ? arguments[arguments.length - 1] : null;
|
|
429
|
+
var argsWithoutCb = typeof cb === 'function' ?
|
|
430
|
+
Array.prototype.slice.call(arguments, 0, arguments.length - 1) :
|
|
431
|
+
Array.prototype.slice.call(arguments);
|
|
408
432
|
var promise = new Promise.ES6(function(resolve, reject) {
|
|
409
433
|
if (_this.readyState !== STATES.connected) {
|
|
410
434
|
_this.on('open', function() {
|
|
@@ -782,6 +806,12 @@ Connection.prototype.openUri = function(uri, options, callback) {
|
|
|
782
806
|
this.user = options.auth.user;
|
|
783
807
|
this.pass = options.auth.password;
|
|
784
808
|
}
|
|
809
|
+
|
|
810
|
+
if (options.bufferCommands != null) {
|
|
811
|
+
options.bufferMaxEntries = 0;
|
|
812
|
+
this.config.bufferCommands = options.bufferCommands;
|
|
813
|
+
delete options.bufferCommands;
|
|
814
|
+
}
|
|
785
815
|
}
|
|
786
816
|
|
|
787
817
|
this._connectionOptions = options;
|
|
@@ -14,6 +14,9 @@ var util = require('util');
|
|
|
14
14
|
* in addition to several other mechanisms for loading documents from MongoDB
|
|
15
15
|
* one at a time.
|
|
16
16
|
*
|
|
17
|
+
* Creating an AggregationCursor executes the model's pre aggregate hooks,
|
|
18
|
+
* but **not** the model's post aggregate hooks.
|
|
19
|
+
*
|
|
17
20
|
* Unless you're an advanced user, do **not** instantiate this class directly.
|
|
18
21
|
* Use [`Aggregate#cursor()`](/docs/api.html#aggregate_Aggregate-cursor) instead.
|
|
19
22
|
*
|
|
@@ -47,13 +50,17 @@ util.inherits(AggregationCursor, Readable);
|
|
|
47
50
|
|
|
48
51
|
function _init(model, c, agg) {
|
|
49
52
|
if (!model.collection.buffer) {
|
|
50
|
-
|
|
51
|
-
c.emit('cursor', c.cursor);
|
|
52
|
-
} else {
|
|
53
|
-
model.collection.emitter.once('queue', function() {
|
|
53
|
+
model.hooks.execPre('aggregate', agg, function() {
|
|
54
54
|
c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
|
|
55
55
|
c.emit('cursor', c.cursor);
|
|
56
56
|
});
|
|
57
|
+
} else {
|
|
58
|
+
model.collection.emitter.once('queue', function() {
|
|
59
|
+
model.hooks.execPre('aggregate', agg, function() {
|
|
60
|
+
c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
|
|
61
|
+
c.emit('cursor', c.cursor);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
66
|
|
package/lib/document.js
CHANGED
|
@@ -74,7 +74,7 @@ function Document(obj, fields, skipId, options) {
|
|
|
74
74
|
if (this.$__original_set) {
|
|
75
75
|
this.$__original_set(obj, undefined, true);
|
|
76
76
|
} else {
|
|
77
|
-
this
|
|
77
|
+
this.$set(obj, undefined, true);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -435,8 +435,18 @@ function init(self, obj, doc, prefix) {
|
|
|
435
435
|
*/
|
|
436
436
|
|
|
437
437
|
for (var k in hooks) {
|
|
438
|
-
if (k === '
|
|
438
|
+
if (k === 'post') {
|
|
439
439
|
Document.prototype['$' + k] = Document['$' + k] = hooks[k];
|
|
440
|
+
} else if (k === 'pre') {
|
|
441
|
+
Document.prototype.$pre = Document.$pre = function mongoosePreWrapper() {
|
|
442
|
+
if (arguments[0] === 'set') {
|
|
443
|
+
// Make set hooks also work for `$set`
|
|
444
|
+
var $setArgs = Array.prototype.slice.call(arguments);
|
|
445
|
+
$setArgs[0] = '$set';
|
|
446
|
+
hooks.pre.apply(this, $setArgs);
|
|
447
|
+
}
|
|
448
|
+
return hooks.pre.apply(this, arguments);
|
|
449
|
+
};
|
|
440
450
|
} else {
|
|
441
451
|
Document.prototype[k] = Document[k] = hooks[k];
|
|
442
452
|
}
|
|
@@ -468,29 +478,7 @@ Document.prototype.update = function update() {
|
|
|
468
478
|
};
|
|
469
479
|
|
|
470
480
|
/**
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
* ####Example:
|
|
474
|
-
*
|
|
475
|
-
* // path, value
|
|
476
|
-
* doc.set(path, value)
|
|
477
|
-
*
|
|
478
|
-
* // object
|
|
479
|
-
* doc.set({
|
|
480
|
-
* path : value
|
|
481
|
-
* , path2 : {
|
|
482
|
-
* path : value
|
|
483
|
-
* }
|
|
484
|
-
* })
|
|
485
|
-
*
|
|
486
|
-
* // on-the-fly cast to number
|
|
487
|
-
* doc.set(path, value, Number)
|
|
488
|
-
*
|
|
489
|
-
* // on-the-fly cast to string
|
|
490
|
-
* doc.set(path, value, String)
|
|
491
|
-
*
|
|
492
|
-
* // changing strict mode behavior
|
|
493
|
-
* doc.set(path, value, { strict: false });
|
|
481
|
+
* Alias for `set()`, used internally to avoid conflicts
|
|
494
482
|
*
|
|
495
483
|
* @param {String|Object} path path or object of key/vals to set
|
|
496
484
|
* @param {Any} val the value to set
|
|
@@ -499,7 +487,7 @@ Document.prototype.update = function update() {
|
|
|
499
487
|
* @api public
|
|
500
488
|
*/
|
|
501
489
|
|
|
502
|
-
Document.prototype
|
|
490
|
+
Document.prototype.$set = function(path, val, type, options) {
|
|
503
491
|
if (type && utils.getFunctionName(type.constructor) === 'Object') {
|
|
504
492
|
options = type;
|
|
505
493
|
type = undefined;
|
|
@@ -548,7 +536,7 @@ Document.prototype.set = function(path, val, type, options) {
|
|
|
548
536
|
|
|
549
537
|
if (len === 0 && !this.schema.options.minimize) {
|
|
550
538
|
if (val) {
|
|
551
|
-
this
|
|
539
|
+
this.$set(val, {});
|
|
552
540
|
}
|
|
553
541
|
return this;
|
|
554
542
|
}
|
|
@@ -583,7 +571,7 @@ Document.prototype.set = function(path, val, type, options) {
|
|
|
583
571
|
&& !(this.schema.paths[pathName] &&
|
|
584
572
|
this.schema.paths[pathName].options &&
|
|
585
573
|
this.schema.paths[pathName].options.ref)) {
|
|
586
|
-
this
|
|
574
|
+
this.$set(path[key], prefix + key, constructing);
|
|
587
575
|
} else if (strict) {
|
|
588
576
|
// Don't overwrite defaults with undefined keys (gh-3981)
|
|
589
577
|
if (constructing && path[key] === void 0 &&
|
|
@@ -599,9 +587,9 @@ Document.prototype.set = function(path, val, type, options) {
|
|
|
599
587
|
path[key] instanceof Document) {
|
|
600
588
|
p = p.toObject({ virtuals: false, transform: false });
|
|
601
589
|
}
|
|
602
|
-
this
|
|
590
|
+
this.$set(prefix + key, p, constructing);
|
|
603
591
|
} else if (pathtype === 'nested' && path[key] instanceof Document) {
|
|
604
|
-
this
|
|
592
|
+
this.$set(prefix + key,
|
|
605
593
|
path[key].toObject({transform: false}), constructing);
|
|
606
594
|
} else if (strict === 'throw') {
|
|
607
595
|
if (pathtype === 'nested') {
|
|
@@ -611,7 +599,7 @@ Document.prototype.set = function(path, val, type, options) {
|
|
|
611
599
|
}
|
|
612
600
|
}
|
|
613
601
|
} else if (path[key] !== void 0) {
|
|
614
|
-
this
|
|
602
|
+
this.$set(prefix + key, path[key], constructing);
|
|
615
603
|
}
|
|
616
604
|
}
|
|
617
605
|
|
|
@@ -629,7 +617,7 @@ Document.prototype.set = function(path, val, type, options) {
|
|
|
629
617
|
this.markModified(path);
|
|
630
618
|
cleanModifiedSubpaths(this, path);
|
|
631
619
|
} else {
|
|
632
|
-
this
|
|
620
|
+
this.$set(val, path, constructing);
|
|
633
621
|
}
|
|
634
622
|
return this;
|
|
635
623
|
}
|
|
@@ -681,7 +669,7 @@ Document.prototype.set = function(path, val, type, options) {
|
|
|
681
669
|
cur = cur[parts[i]];
|
|
682
670
|
curPath += (curPath.length > 0 ? '.' : '') + parts[i];
|
|
683
671
|
if (!cur) {
|
|
684
|
-
this
|
|
672
|
+
this.$set(curPath, {});
|
|
685
673
|
cur = this.getValue(curPath);
|
|
686
674
|
}
|
|
687
675
|
}
|
|
@@ -785,6 +773,40 @@ Document.prototype.set = function(path, val, type, options) {
|
|
|
785
773
|
return this;
|
|
786
774
|
};
|
|
787
775
|
|
|
776
|
+
/**
|
|
777
|
+
* Sets the value of a path, or many paths.
|
|
778
|
+
*
|
|
779
|
+
* ####Example:
|
|
780
|
+
*
|
|
781
|
+
* // path, value
|
|
782
|
+
* doc.set(path, value)
|
|
783
|
+
*
|
|
784
|
+
* // object
|
|
785
|
+
* doc.set({
|
|
786
|
+
* path : value
|
|
787
|
+
* , path2 : {
|
|
788
|
+
* path : value
|
|
789
|
+
* }
|
|
790
|
+
* })
|
|
791
|
+
*
|
|
792
|
+
* // on-the-fly cast to number
|
|
793
|
+
* doc.set(path, value, Number)
|
|
794
|
+
*
|
|
795
|
+
* // on-the-fly cast to string
|
|
796
|
+
* doc.set(path, value, String)
|
|
797
|
+
*
|
|
798
|
+
* // changing strict mode behavior
|
|
799
|
+
* doc.set(path, value, { strict: false });
|
|
800
|
+
*
|
|
801
|
+
* @param {String|Object} path path or object of key/vals to set
|
|
802
|
+
* @param {Any} val the value to set
|
|
803
|
+
* @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for "on-the-fly" attributes
|
|
804
|
+
* @param {Object} [options] optionally specify options that modify the behavior of the set
|
|
805
|
+
* @api public
|
|
806
|
+
*/
|
|
807
|
+
|
|
808
|
+
Document.prototype.set = Document.prototype.$set;
|
|
809
|
+
|
|
788
810
|
/**
|
|
789
811
|
* Determine if we should mark this change as modified.
|
|
790
812
|
*
|
|
@@ -2570,12 +2592,17 @@ Document.prototype.populated = function(path, val, options) {
|
|
|
2570
2592
|
*/
|
|
2571
2593
|
|
|
2572
2594
|
Document.prototype.depopulate = function(path) {
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2595
|
+
if (typeof path === 'string') {
|
|
2596
|
+
path = path.split(' ');
|
|
2597
|
+
}
|
|
2598
|
+
for (var i = 0; i < path.length; i++) {
|
|
2599
|
+
var populatedIds = this.populated(path[i]);
|
|
2600
|
+
if (!populatedIds) {
|
|
2601
|
+
continue;
|
|
2602
|
+
}
|
|
2603
|
+
delete this.$__.populated[path[i]];
|
|
2604
|
+
this.$set(path[i], populatedIds);
|
|
2576
2605
|
}
|
|
2577
|
-
delete this.$__.populated[path];
|
|
2578
|
-
this.set(path, populatedIds);
|
|
2579
2606
|
return this;
|
|
2580
2607
|
};
|
|
2581
2608
|
|
package/lib/model.js
CHANGED
|
@@ -20,6 +20,7 @@ var cast = require('./cast');
|
|
|
20
20
|
var castUpdate = require('./services/query/castUpdate');
|
|
21
21
|
var discriminator = require('./services/model/discriminator');
|
|
22
22
|
var isPathSelectedInclusive = require('./services/projection/isPathSelectedInclusive');
|
|
23
|
+
var get = require('lodash.get');
|
|
23
24
|
var mpath = require('mpath');
|
|
24
25
|
var parallel = require('async/parallel');
|
|
25
26
|
var parallelLimit = require('async/parallelLimit');
|
|
@@ -552,7 +553,13 @@ Model.prototype.$__delta = function() {
|
|
|
552
553
|
value = value.toObject();
|
|
553
554
|
operand(this, where, delta, data, value);
|
|
554
555
|
} else {
|
|
555
|
-
value = utils.clone(value, {
|
|
556
|
+
value = utils.clone(value, {
|
|
557
|
+
depopulate: true,
|
|
558
|
+
transform: false,
|
|
559
|
+
virtuals: false,
|
|
560
|
+
retainKeyOrder: true,
|
|
561
|
+
_isNested: true
|
|
562
|
+
});
|
|
556
563
|
operand(this, where, delta, data, value);
|
|
557
564
|
}
|
|
558
565
|
}
|
|
@@ -651,8 +658,13 @@ Model.prototype.$__version = function(where, delta) {
|
|
|
651
658
|
}
|
|
652
659
|
|
|
653
660
|
if (VERSION_INC === (VERSION_INC & this.$__.version)) {
|
|
654
|
-
if (
|
|
655
|
-
|
|
661
|
+
if (get(delta.$set, key, null) != null) {
|
|
662
|
+
// Version key is getting set, means we'll increment the doc's version
|
|
663
|
+
// after a successful save, so we should set the incremented version so
|
|
664
|
+
// future saves don't fail (gh-5779)
|
|
665
|
+
++delta.$set[key];
|
|
666
|
+
} else {
|
|
667
|
+
delta.$inc = delta.$inc || {};
|
|
656
668
|
delta.$inc[key] = 1;
|
|
657
669
|
}
|
|
658
670
|
}
|
|
@@ -921,9 +933,9 @@ Model.init = function init(callback) {
|
|
|
921
933
|
callback && callback(null, _this);
|
|
922
934
|
resolve(_this);
|
|
923
935
|
});
|
|
936
|
+
} else {
|
|
937
|
+
resolve(_this);
|
|
924
938
|
}
|
|
925
|
-
|
|
926
|
-
return _this;
|
|
927
939
|
});
|
|
928
940
|
|
|
929
941
|
return this.$init;
|
|
@@ -2102,6 +2114,11 @@ var INSERT_MANY_CONVERT_OPTIONS = {
|
|
|
2102
2114
|
* because it only sends one operation to the server, rather than one for each
|
|
2103
2115
|
* document.
|
|
2104
2116
|
*
|
|
2117
|
+
* Mongoose always validates each document **before** sending `insertMany`
|
|
2118
|
+
* to MongoDB. So if one document has a validation error, no documents will
|
|
2119
|
+
* be saved, unless you set
|
|
2120
|
+
* [the `ordered` option to false](https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/#error-handling).
|
|
2121
|
+
*
|
|
2105
2122
|
* This function does **not** trigger save middleware.
|
|
2106
2123
|
*
|
|
2107
2124
|
* This function triggers the following middleware:
|
|
@@ -2114,6 +2131,8 @@ var INSERT_MANY_CONVERT_OPTIONS = {
|
|
|
2114
2131
|
*
|
|
2115
2132
|
* @param {Array|Object|*} doc(s)
|
|
2116
2133
|
* @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#insertMany)
|
|
2134
|
+
* @param {Boolean} [options.ordered = true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`.
|
|
2135
|
+
* @param {Boolean} [options.rawResult = false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `false`, will return the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~insertWriteOpCallback) with a `mongoose` property that contains `validationErrors` if this is an unordered `insertMany`.
|
|
2117
2136
|
* @param {Function} [callback] callback
|
|
2118
2137
|
* @return {Promise}
|
|
2119
2138
|
* @api public
|
|
@@ -2128,16 +2147,18 @@ Model.insertMany = function(arr, options, callback) {
|
|
|
2128
2147
|
if (callback) {
|
|
2129
2148
|
callback = this.$wrapCallback(callback);
|
|
2130
2149
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2150
|
+
callback = callback || utils.noop;
|
|
2151
|
+
options = options || {};
|
|
2152
|
+
var limit = get(options, 'limit', 1000);
|
|
2153
|
+
var rawResult = get(options, 'rawResult', false);
|
|
2154
|
+
var ordered = get(options, 'ordered', true);
|
|
2135
2155
|
|
|
2136
2156
|
if (!Array.isArray(arr)) {
|
|
2137
2157
|
arr = [arr];
|
|
2138
2158
|
}
|
|
2139
2159
|
|
|
2140
2160
|
var toExecute = [];
|
|
2161
|
+
var validationErrors = [];
|
|
2141
2162
|
arr.forEach(function(doc) {
|
|
2142
2163
|
toExecute.push(function(callback) {
|
|
2143
2164
|
doc = new _this(doc);
|
|
@@ -2146,7 +2167,8 @@ Model.insertMany = function(arr, options, callback) {
|
|
|
2146
2167
|
// Option `ordered` signals that insert should be continued after reaching
|
|
2147
2168
|
// a failing insert. Therefore we delegate "null", meaning the validation
|
|
2148
2169
|
// failed. It's up to the next function to filter out all failed models
|
|
2149
|
-
if (
|
|
2170
|
+
if (ordered === false) {
|
|
2171
|
+
validationErrors.push(error);
|
|
2150
2172
|
return callback(null, null);
|
|
2151
2173
|
}
|
|
2152
2174
|
return callback(error);
|
|
@@ -2167,7 +2189,7 @@ Model.insertMany = function(arr, options, callback) {
|
|
|
2167
2189
|
});
|
|
2168
2190
|
// Quickly escape while there aren't any valid docAttributes
|
|
2169
2191
|
if (docAttributes.length < 1) {
|
|
2170
|
-
callback
|
|
2192
|
+
callback(null, []);
|
|
2171
2193
|
return;
|
|
2172
2194
|
}
|
|
2173
2195
|
var docObjects = docAttributes.map(function(doc) {
|
|
@@ -2180,7 +2202,7 @@ Model.insertMany = function(arr, options, callback) {
|
|
|
2180
2202
|
return doc.toObject(INSERT_MANY_CONVERT_OPTIONS);
|
|
2181
2203
|
});
|
|
2182
2204
|
|
|
2183
|
-
_this.collection.insertMany(docObjects, options, function(error) {
|
|
2205
|
+
_this.collection.insertMany(docObjects, options, function(error, res) {
|
|
2184
2206
|
if (error) {
|
|
2185
2207
|
callback && callback(error);
|
|
2186
2208
|
return;
|
|
@@ -2190,7 +2212,17 @@ Model.insertMany = function(arr, options, callback) {
|
|
|
2190
2212
|
docAttributes[i].emit('isNew', false);
|
|
2191
2213
|
docAttributes[i].constructor.emit('isNew', false);
|
|
2192
2214
|
}
|
|
2193
|
-
|
|
2215
|
+
if (rawResult) {
|
|
2216
|
+
if (ordered === false) {
|
|
2217
|
+
// Decorate with mongoose validation errors in case of unordered,
|
|
2218
|
+
// because then still do `insertMany()`
|
|
2219
|
+
res.mongoose = {
|
|
2220
|
+
validationErrors: validationErrors
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
return callback(null, res);
|
|
2224
|
+
}
|
|
2225
|
+
callback(null, docAttributes);
|
|
2194
2226
|
});
|
|
2195
2227
|
});
|
|
2196
2228
|
};
|
|
@@ -2763,7 +2795,9 @@ Model.geoNear = function(near, options, callback) {
|
|
|
2763
2795
|
});
|
|
2764
2796
|
}
|
|
2765
2797
|
|
|
2766
|
-
var x
|
|
2798
|
+
var x;
|
|
2799
|
+
var y;
|
|
2800
|
+
var schema = this.schema;
|
|
2767
2801
|
|
|
2768
2802
|
return new Promise.ES6(function(resolve, reject) {
|
|
2769
2803
|
var handler = function(err, res) {
|
|
@@ -2808,6 +2842,11 @@ Model.geoNear = function(near, options, callback) {
|
|
|
2808
2842
|
}
|
|
2809
2843
|
};
|
|
2810
2844
|
|
|
2845
|
+
if (options.query != null) {
|
|
2846
|
+
options.query = utils.clone(options.query, { retainKeyOrder: 1 });
|
|
2847
|
+
cast(schema, options.query);
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2811
2850
|
if (Array.isArray(near)) {
|
|
2812
2851
|
if (near.length !== 2) {
|
|
2813
2852
|
var error = new Error('If using legacy coordinates, must be an array ' +
|
|
@@ -3552,10 +3591,14 @@ function getModelsMapForPopulate(model, docs, options) {
|
|
|
3552
3591
|
}
|
|
3553
3592
|
var virtual = modelForCurrentDoc.schema._getVirtual(options.path);
|
|
3554
3593
|
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3594
|
+
var ref;
|
|
3595
|
+
if ((ref = get(schemaForCurrentDoc, 'options.ref')) != null) {
|
|
3596
|
+
modelNames = [ref];
|
|
3597
|
+
} else if ((ref = get(virtual, 'options.ref')) != null) {
|
|
3598
|
+
if (typeof ref === 'function') {
|
|
3599
|
+
ref = ref.call(doc, doc);
|
|
3600
|
+
}
|
|
3601
|
+
modelNames = [ref];
|
|
3559
3602
|
isVirtual = true;
|
|
3560
3603
|
} else {
|
|
3561
3604
|
// We may have a discriminator, in which case we don't want to
|
|
@@ -3576,9 +3619,18 @@ function getModelsMapForPopulate(model, docs, options) {
|
|
|
3576
3619
|
}
|
|
3577
3620
|
|
|
3578
3621
|
virtual = model.schema._getVirtual(options.path);
|
|
3579
|
-
var localField
|
|
3580
|
-
|
|
3581
|
-
|
|
3622
|
+
var localField;
|
|
3623
|
+
if (virtual && virtual.options) {
|
|
3624
|
+
var virtualPrefix = virtual.$nestedSchemaPath ?
|
|
3625
|
+
virtual.$nestedSchemaPath + '.' : '';
|
|
3626
|
+
if (typeof virtual.options.localField === 'function') {
|
|
3627
|
+
localField = virtualPrefix + virtual.options.localField.call(doc, doc);
|
|
3628
|
+
} else {
|
|
3629
|
+
localField = virtualPrefix + virtual.options.localField;
|
|
3630
|
+
}
|
|
3631
|
+
} else {
|
|
3632
|
+
localField = options.path;
|
|
3633
|
+
}
|
|
3582
3634
|
var foreignField = virtual && virtual.options ?
|
|
3583
3635
|
virtual.options.foreignField :
|
|
3584
3636
|
'_id';
|
|
@@ -3593,6 +3645,12 @@ function getModelsMapForPopulate(model, docs, options) {
|
|
|
3593
3645
|
}
|
|
3594
3646
|
|
|
3595
3647
|
options.isVirtual = isVirtual;
|
|
3648
|
+
if (typeof localField === 'function') {
|
|
3649
|
+
localField = localField.call(doc, doc);
|
|
3650
|
+
}
|
|
3651
|
+
if (typeof foreignField === 'function') {
|
|
3652
|
+
foreignField = foreignField.call(doc);
|
|
3653
|
+
}
|
|
3596
3654
|
var ret = convertTo_id(utils.getValue(localField, doc));
|
|
3597
3655
|
var id = String(utils.getValue(foreignField, doc));
|
|
3598
3656
|
options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
|
|
@@ -3841,8 +3899,17 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
|
|
|
3841
3899
|
|
|
3842
3900
|
model.prototype.$__setSchema(schema);
|
|
3843
3901
|
|
|
3902
|
+
var _userProvidedOptions = schema._userProvidedOptions || {};
|
|
3903
|
+
var bufferCommands = true;
|
|
3904
|
+
if (connection.config.bufferCommands != null) {
|
|
3905
|
+
bufferCommands = connection.config.bufferCommands;
|
|
3906
|
+
}
|
|
3907
|
+
if (_userProvidedOptions.bufferCommands != null) {
|
|
3908
|
+
bufferCommands = _userProvidedOptions.bufferCommands;
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3844
3911
|
var collectionOptions = {
|
|
3845
|
-
bufferCommands:
|
|
3912
|
+
bufferCommands: bufferCommands,
|
|
3846
3913
|
capped: schema.options.capped
|
|
3847
3914
|
};
|
|
3848
3915
|
|
|
@@ -3937,14 +4004,24 @@ Model.__subclass = function subclass(conn, schema, collection) {
|
|
|
3937
4004
|
: _this.prototype.schema;
|
|
3938
4005
|
|
|
3939
4006
|
var options = s.options || {};
|
|
4007
|
+
var _userProvidedOptions = s._userProvidedOptions || {};
|
|
3940
4008
|
|
|
3941
4009
|
if (!collection) {
|
|
3942
4010
|
collection = _this.prototype.schema.get('collection')
|
|
3943
4011
|
|| utils.toCollectionName(_this.modelName, options);
|
|
3944
4012
|
}
|
|
3945
4013
|
|
|
4014
|
+
var bufferCommands = true;
|
|
4015
|
+
if (s) {
|
|
4016
|
+
if (conn.config.bufferCommands != null) {
|
|
4017
|
+
bufferCommands = conn.config.bufferCommands;
|
|
4018
|
+
}
|
|
4019
|
+
if (_userProvidedOptions.bufferCommands != null) {
|
|
4020
|
+
bufferCommands = _userProvidedOptions.bufferCommands;
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
3946
4023
|
var collectionOptions = {
|
|
3947
|
-
bufferCommands:
|
|
4024
|
+
bufferCommands: bufferCommands,
|
|
3948
4025
|
capped: s && options.capped
|
|
3949
4026
|
};
|
|
3950
4027
|
|
package/lib/query.js
CHANGED
|
@@ -11,6 +11,7 @@ var cast = require('./cast');
|
|
|
11
11
|
var castUpdate = require('./services/query/castUpdate');
|
|
12
12
|
var hasDollarKeys = require('./services/query/hasDollarKeys');
|
|
13
13
|
var helpers = require('./queryhelpers');
|
|
14
|
+
var isInclusive = require('./services/projection/isInclusive');
|
|
14
15
|
var mquery = require('mquery');
|
|
15
16
|
var readPref = require('./drivers').ReadPreference;
|
|
16
17
|
var selectPopulatedFields = require('./services/query/selectPopulatedFields');
|
|
@@ -2025,6 +2026,7 @@ function prepareDiscriminatorCriteria(query) {
|
|
|
2025
2026
|
* @param {Object} [options]
|
|
2026
2027
|
* @param {Boolean} [options.passRawResult] if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
|
|
2027
2028
|
* @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
|
|
2029
|
+
* @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
|
|
2028
2030
|
* @param {Function} [callback] optional params are (error, doc), _unless_ `passRawResult` is used, in which case params are (error, doc, writeOpResult)
|
|
2029
2031
|
* @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
|
|
2030
2032
|
* @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
|
|
@@ -2720,6 +2722,7 @@ Query.prototype._replaceOne = function(callback) {
|
|
|
2720
2722
|
* @param {Object} [criteria]
|
|
2721
2723
|
* @param {Object} [doc] the update command
|
|
2722
2724
|
* @param {Object} [options]
|
|
2725
|
+
* @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
|
|
2723
2726
|
* @param {Function} [callback] optional, params are (error, writeOpResult)
|
|
2724
2727
|
* @return {Query} this
|
|
2725
2728
|
* @see Model.update #model_Model.update
|
|
@@ -2771,6 +2774,7 @@ Query.prototype.update = function(conditions, doc, options, callback) {
|
|
|
2771
2774
|
* @param {Object} [criteria]
|
|
2772
2775
|
* @param {Object} [doc] the update command
|
|
2773
2776
|
* @param {Object} [options]
|
|
2777
|
+
@param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
|
|
2774
2778
|
* @param {Function} [callback] optional params are (error, writeOpResult)
|
|
2775
2779
|
* @return {Query} this
|
|
2776
2780
|
* @see Model.update #model_Model.update
|
|
@@ -2821,6 +2825,7 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) {
|
|
|
2821
2825
|
* @param {Object} [criteria]
|
|
2822
2826
|
* @param {Object} [doc] the update command
|
|
2823
2827
|
* @param {Object} [options]
|
|
2828
|
+
@param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
|
|
2824
2829
|
* @param {Function} [callback] params are (error, writeOpResult)
|
|
2825
2830
|
* @return {Query} this
|
|
2826
2831
|
* @see Model.update #model_Model.update
|
|
@@ -3824,28 +3829,7 @@ Query.prototype.centerSphere = function() {
|
|
|
3824
3829
|
*/
|
|
3825
3830
|
|
|
3826
3831
|
Query.prototype.selectedInclusively = function selectedInclusively() {
|
|
3827
|
-
|
|
3828
|
-
return false;
|
|
3829
|
-
}
|
|
3830
|
-
|
|
3831
|
-
var keys = Object.keys(this._fields);
|
|
3832
|
-
if (keys.length === 0) {
|
|
3833
|
-
return false;
|
|
3834
|
-
}
|
|
3835
|
-
|
|
3836
|
-
for (var i = 0; i < keys.length; ++i) {
|
|
3837
|
-
var key = keys[i];
|
|
3838
|
-
if (this._fields[key] === 0 || this._fields[key] === false) {
|
|
3839
|
-
return false;
|
|
3840
|
-
}
|
|
3841
|
-
if (this._fields[key] &&
|
|
3842
|
-
typeof this._fields[key] === 'object' &&
|
|
3843
|
-
this._fields[key].$meta) {
|
|
3844
|
-
return false;
|
|
3845
|
-
}
|
|
3846
|
-
}
|
|
3847
|
-
|
|
3848
|
-
return true;
|
|
3832
|
+
return isInclusive(this._fields);
|
|
3849
3833
|
};
|
|
3850
3834
|
|
|
3851
3835
|
/**
|
package/lib/queryhelpers.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Module dependencies
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
var get = require('lodash.get');
|
|
6
7
|
var utils = require('./utils');
|
|
7
8
|
|
|
8
9
|
/*!
|
|
@@ -75,7 +76,6 @@ exports.createModel = function createModel(model, doc, fields, userProvidedField
|
|
|
75
76
|
|
|
76
77
|
exports.applyPaths = function applyPaths(fields, schema) {
|
|
77
78
|
// determine if query is selecting or excluding fields
|
|
78
|
-
|
|
79
79
|
var exclude;
|
|
80
80
|
var keys;
|
|
81
81
|
var ki;
|
|
@@ -123,8 +123,25 @@ exports.applyPaths = function applyPaths(fields, schema) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// check for parent exclusions
|
|
126
|
-
var
|
|
127
|
-
|
|
126
|
+
var pieces = path.split('.');
|
|
127
|
+
var root = pieces[0];
|
|
128
|
+
if (~excluded.indexOf(root)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Special case: if user has included a parent path of a discriminator key,
|
|
133
|
+
// don't explicitly project in the discriminator key because that will
|
|
134
|
+
// project out everything else under the parent path
|
|
135
|
+
if (!exclude && get(type, 'options.$skipDiscriminatorCheck', false)) {
|
|
136
|
+
var cur = '';
|
|
137
|
+
for (var i = 0; i < pieces.length; ++i) {
|
|
138
|
+
cur += (cur.length === 0 ? '' : '.') + pieces[i];
|
|
139
|
+
var projection = get(fields, cur, false);
|
|
140
|
+
if (projection && typeof projection !== 'object') {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
128
145
|
|
|
129
146
|
(type.selected ? selected : excluded).push(path);
|
|
130
147
|
};
|
package/lib/schema/array.js
CHANGED
package/lib/schema/buffer.js
CHANGED
|
@@ -113,6 +113,9 @@ SchemaBuffer.prototype.cast = function(value, doc, init) {
|
|
|
113
113
|
if (Buffer.isBuffer(value)) {
|
|
114
114
|
if (!value || !value.isMongooseBuffer) {
|
|
115
115
|
value = new MongooseBuffer(value, [this.path, doc]);
|
|
116
|
+
if (this.options.subtype != null) {
|
|
117
|
+
value._subtype = this.options.subtype;
|
|
118
|
+
}
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
return value;
|
|
@@ -135,12 +138,36 @@ SchemaBuffer.prototype.cast = function(value, doc, init) {
|
|
|
135
138
|
value = [value];
|
|
136
139
|
}
|
|
137
140
|
ret = new MongooseBuffer(value, [this.path, doc]);
|
|
141
|
+
if (this.options.subtype != null) {
|
|
142
|
+
ret._subtype = this.options.subtype;
|
|
143
|
+
}
|
|
138
144
|
return ret;
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
throw new CastError('buffer', value, this.path);
|
|
142
148
|
};
|
|
143
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Sets the default [subtype](https://studio3t.com/whats-new/best-practices-uuid-mongodb/)
|
|
152
|
+
* for this buffer. You can find a [list of allowed subtypes here](http://api.mongodb.com/python/current/api/bson/binary.html).
|
|
153
|
+
*
|
|
154
|
+
* ####Example:
|
|
155
|
+
*
|
|
156
|
+
* var s = new Schema({ uuid: { type: Buffer, subtype: 4 });
|
|
157
|
+
* var M = db.model('M', s);
|
|
158
|
+
* var m = new M({ uuid: 'test string' });
|
|
159
|
+
* m.uuid._subtype; // 4
|
|
160
|
+
*
|
|
161
|
+
* @param {Number} subtype the default subtype
|
|
162
|
+
* @return {SchemaType} this
|
|
163
|
+
* @api public
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
SchemaBuffer.prototype.subtype = function(subtype) {
|
|
167
|
+
this.options.subtype = subtype;
|
|
168
|
+
return this;
|
|
169
|
+
};
|
|
170
|
+
|
|
144
171
|
/*!
|
|
145
172
|
* ignore
|
|
146
173
|
*/
|
|
@@ -11,7 +11,6 @@ var EventEmitter = require('events').EventEmitter;
|
|
|
11
11
|
var MongooseDocumentArray = require('../types/documentarray');
|
|
12
12
|
var SchemaType = require('../schematype');
|
|
13
13
|
var Subdocument = require('../types/embedded');
|
|
14
|
-
var applyHooks = require('../services/model/applyHooks');
|
|
15
14
|
var discriminator = require('../services/model/discriminator');
|
|
16
15
|
var util = require('util');
|
|
17
16
|
var utils = require('../utils');
|
|
@@ -120,8 +119,6 @@ DocumentArray.prototype.discriminator = function(name, schema) {
|
|
|
120
119
|
|
|
121
120
|
this.casterConstructor.discriminators[name] = EmbeddedDocument;
|
|
122
121
|
|
|
123
|
-
applyHooks(EmbeddedDocument, schema);
|
|
124
|
-
|
|
125
122
|
return this.casterConstructor.discriminators[name];
|
|
126
123
|
};
|
|
127
124
|
|
package/lib/schema/embedded.js
CHANGED
|
@@ -8,7 +8,6 @@ var $exists = require('./operators/exists');
|
|
|
8
8
|
var EventEmitter = require('events').EventEmitter;
|
|
9
9
|
var SchemaType = require('../schematype');
|
|
10
10
|
var Subdocument = require('../types/subdocument');
|
|
11
|
-
var applyHooks = require('../services/model/applyHooks');
|
|
12
11
|
var castToNumber = require('./operators/helpers').castToNumber;
|
|
13
12
|
var discriminator = require('../services/model/discriminator');
|
|
14
13
|
var geospatial = require('./operators/geospatial');
|
|
@@ -255,7 +254,5 @@ Embedded.prototype.discriminator = function(name, schema) {
|
|
|
255
254
|
|
|
256
255
|
this.caster.discriminators[name] = _createConstructor(schema);
|
|
257
256
|
|
|
258
|
-
applyHooks(this.caster.discriminators[name], schema);
|
|
259
|
-
|
|
260
257
|
return this.caster.discriminators[name];
|
|
261
258
|
};
|
package/lib/schema.js
CHANGED
|
@@ -13,6 +13,7 @@ var SchemaType = require('./schematype');
|
|
|
13
13
|
var mpath = require('mpath');
|
|
14
14
|
|
|
15
15
|
var IS_KAREEM_HOOK = {
|
|
16
|
+
aggregate: true,
|
|
16
17
|
count: true,
|
|
17
18
|
find: true,
|
|
18
19
|
findOne: true,
|
|
@@ -327,6 +328,10 @@ Schema.prototype.defaultOptions = function(options) {
|
|
|
327
328
|
options.versionKey = false;
|
|
328
329
|
}
|
|
329
330
|
|
|
331
|
+
this._userProvidedOptions = utils.clone(options, {
|
|
332
|
+
retainKeyOrder: true
|
|
333
|
+
});
|
|
334
|
+
|
|
330
335
|
options = utils.options({
|
|
331
336
|
strict: true,
|
|
332
337
|
bufferCommands: true,
|
|
@@ -438,7 +443,6 @@ reserved.get =
|
|
|
438
443
|
reserved.modelName =
|
|
439
444
|
reserved.save =
|
|
440
445
|
reserved.schema =
|
|
441
|
-
reserved.set =
|
|
442
446
|
reserved.toObject =
|
|
443
447
|
reserved.validate =
|
|
444
448
|
reserved.remove =
|
|
@@ -588,7 +592,7 @@ Schema.interpretAsType = function(path, obj, options) {
|
|
|
588
592
|
if (cast &&
|
|
589
593
|
cast[options.typeKey] &&
|
|
590
594
|
cast[options.typeKey].instanceOfSchema) {
|
|
591
|
-
return new MongooseTypes.DocumentArray(path, cast[options.typeKey],
|
|
595
|
+
return new MongooseTypes.DocumentArray(path, cast[options.typeKey], cast);
|
|
592
596
|
}
|
|
593
597
|
|
|
594
598
|
if (Array.isArray(cast)) {
|
|
@@ -1427,7 +1431,9 @@ Schema.prototype.indexes = function() {
|
|
|
1427
1431
|
path = schema.paths[key];
|
|
1428
1432
|
|
|
1429
1433
|
if ((path instanceof MongooseTypes.DocumentArray) || path.$isSingleNested) {
|
|
1430
|
-
|
|
1434
|
+
if (path.options.excludeIndexes !== true) {
|
|
1435
|
+
collectIndexes(path.schema, prefix + key + '.');
|
|
1436
|
+
}
|
|
1431
1437
|
} else {
|
|
1432
1438
|
index = path._index || (path.caster && path.caster._index);
|
|
1433
1439
|
|
package/lib/schematype.js
CHANGED
|
@@ -28,18 +28,23 @@ function SchemaType(path, options, instance) {
|
|
|
28
28
|
this._index = null;
|
|
29
29
|
this.selected;
|
|
30
30
|
|
|
31
|
-
for (var
|
|
32
|
-
if (this[
|
|
31
|
+
for (var prop in options) {
|
|
32
|
+
if (this[prop] && typeof this[prop] === 'function') {
|
|
33
33
|
// { unique: true, index: true }
|
|
34
|
-
if (
|
|
34
|
+
if (prop === 'index' && this._index) {
|
|
35
35
|
continue;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
var val = options[prop];
|
|
39
|
+
// Special case so we don't screw up array defaults, see gh-5780
|
|
40
|
+
if (prop === 'default') {
|
|
41
|
+
this.default(val);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var opts = Array.isArray(val) ? val : [val];
|
|
41
46
|
|
|
42
|
-
this[
|
|
47
|
+
this[prop].apply(this, opts);
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
|
|
@@ -126,7 +126,8 @@ function defineKey(prop, subprops, prototype, prefix, keys, options) {
|
|
|
126
126
|
if (v instanceof Document) {
|
|
127
127
|
v = v.toObject({ transform: false });
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
var doc = this.$__.scope || this;
|
|
130
|
+
return doc.$set(path, v);
|
|
130
131
|
}
|
|
131
132
|
});
|
|
132
133
|
} else {
|
|
@@ -137,7 +138,7 @@ function defineKey(prop, subprops, prototype, prefix, keys, options) {
|
|
|
137
138
|
return this.get.call(this.$__.scope || this, path);
|
|
138
139
|
},
|
|
139
140
|
set: function(v) {
|
|
140
|
-
return this
|
|
141
|
+
return this.$set.call(this.$__.scope || this, path, v);
|
|
141
142
|
}
|
|
142
143
|
});
|
|
143
144
|
}
|
|
@@ -24,10 +24,18 @@ function applyHooks(model, schema) {
|
|
|
24
24
|
|
|
25
25
|
model.$appliedHooks = true;
|
|
26
26
|
for (i = 0; i < schema.childSchemas.length; ++i) {
|
|
27
|
-
|
|
27
|
+
var childModel = schema.childSchemas[i].model;
|
|
28
|
+
if (childModel.$appliedHooks) {
|
|
28
29
|
continue;
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
+
if (childModel.discriminators != null) {
|
|
32
|
+
keys = Object.keys(childModel.discriminators);
|
|
33
|
+
for (j = 0; j < keys.length; ++j) {
|
|
34
|
+
applyHooks(childModel.discriminators[keys[j]],
|
|
35
|
+
childModel.discriminators[keys[j]].schema);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
applyHooks(childModel, schema.childSchemas[i].schema);
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
if (!q.length) {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*!
|
|
4
|
+
* ignore
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
module.exports = function isInclusive(projection) {
|
|
8
|
+
if (projection == null) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
var props = Object.keys(projection);
|
|
13
|
+
var numProps = props.length;
|
|
14
|
+
if (numProps === 0) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for (var i = 0; i < numProps; ++i) {
|
|
19
|
+
var prop = props[i];
|
|
20
|
+
// If field is truthy (1, true, etc.) and not an object, then this
|
|
21
|
+
// projection must be inclusive. If object, assume its $meta, $slice, etc.
|
|
22
|
+
if (typeof projection[prop] !== 'object' && !!projection[prop]) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var StrictModeError = require('../../error/strict');
|
|
4
|
+
var ValidationError = require('../../error/validation');
|
|
4
5
|
var utils = require('../../utils');
|
|
5
6
|
|
|
6
7
|
/*!
|
|
@@ -102,13 +103,16 @@ module.exports = function castUpdate(schema, obj, options, context) {
|
|
|
102
103
|
*/
|
|
103
104
|
|
|
104
105
|
function walkUpdatePath(schema, obj, op, strict, context, pref) {
|
|
105
|
-
var prefix = pref ? pref + '.' : ''
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
var prefix = pref ? pref + '.' : '';
|
|
107
|
+
var keys = Object.keys(obj);
|
|
108
|
+
var i = keys.length;
|
|
109
|
+
var hasKeys = false;
|
|
110
|
+
var schematype;
|
|
111
|
+
var key;
|
|
112
|
+
var val;
|
|
113
|
+
|
|
114
|
+
var hasError = false;
|
|
115
|
+
var aggregatedError = new ValidationError();
|
|
112
116
|
|
|
113
117
|
var useNestedStrict = schema.options.useNestedStrict;
|
|
114
118
|
|
|
@@ -124,9 +128,14 @@ function walkUpdatePath(schema, obj, op, strict, context, pref) {
|
|
|
124
128
|
hasKeys = true;
|
|
125
129
|
|
|
126
130
|
if ('$each' in val) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
try {
|
|
132
|
+
obj[key] = {
|
|
133
|
+
$each: castUpdateVal(schematype, val.$each, op, context)
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
hasError = true;
|
|
137
|
+
_handleCastError(error, context, key, aggregatedError);
|
|
138
|
+
}
|
|
130
139
|
|
|
131
140
|
if (val.$slice != null) {
|
|
132
141
|
obj[key].$slice = val.$slice | 0;
|
|
@@ -140,14 +149,22 @@ function walkUpdatePath(schema, obj, op, strict, context, pref) {
|
|
|
140
149
|
obj[key].$position = val.$position;
|
|
141
150
|
}
|
|
142
151
|
} else {
|
|
143
|
-
|
|
152
|
+
try {
|
|
153
|
+
obj[key] = castUpdateVal(schematype, val, op, context);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
hasError = true;
|
|
156
|
+
_handleCastError(error, context, key, aggregatedError);
|
|
157
|
+
}
|
|
144
158
|
}
|
|
145
|
-
} else if (op === '$currentDate') {
|
|
159
|
+
} else if ((op === '$currentDate') || (op in castOps && schematype)) {
|
|
146
160
|
// $currentDate can take an object
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
try {
|
|
162
|
+
obj[key] = castUpdateVal(schematype, val, op, context);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
hasError = true;
|
|
165
|
+
_handleCastError(error, context, key, aggregatedError);
|
|
166
|
+
}
|
|
167
|
+
|
|
151
168
|
hasKeys = true;
|
|
152
169
|
} else {
|
|
153
170
|
var pathToCheck = (prefix + key);
|
|
@@ -208,13 +225,34 @@ function walkUpdatePath(schema, obj, op, strict, context, pref) {
|
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
hasKeys = true;
|
|
211
|
-
|
|
228
|
+
try {
|
|
229
|
+
obj[key] = castUpdateVal(schematype, val, op, key, context);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
hasError = true;
|
|
232
|
+
_handleCastError(error, context, key, aggregatedError);
|
|
233
|
+
}
|
|
212
234
|
}
|
|
213
235
|
}
|
|
214
236
|
}
|
|
237
|
+
|
|
238
|
+
if (hasError) {
|
|
239
|
+
throw aggregatedError;
|
|
240
|
+
}
|
|
241
|
+
|
|
215
242
|
return hasKeys;
|
|
216
243
|
}
|
|
217
244
|
|
|
245
|
+
/*!
|
|
246
|
+
* ignore
|
|
247
|
+
*/
|
|
248
|
+
|
|
249
|
+
function _handleCastError(error, query, key, aggregatedError) {
|
|
250
|
+
if (!query.options.multipleCastError) {
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
aggregatedError.addError(key, error);
|
|
254
|
+
}
|
|
255
|
+
|
|
218
256
|
/*!
|
|
219
257
|
* These operators should be cast to numbers instead
|
|
220
258
|
* of their path schema type.
|
package/lib/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mongoose",
|
|
3
3
|
"description": "Mongoose MongoDB ODM",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.13.3",
|
|
5
5
|
"author": "Guillermo Rauch <guillermo@learnboost.com>",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mongodb",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"bson": "~1.0.4",
|
|
24
24
|
"hooks-fixed": "2.0.2",
|
|
25
25
|
"kareem": "1.5.0",
|
|
26
|
+
"lodash.get": "4.4.2",
|
|
26
27
|
"mongodb": "2.2.33",
|
|
27
28
|
"mpath": "0.3.0",
|
|
28
29
|
"mpromise": "0.5.5",
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"istanbul": "0.4.4",
|
|
45
46
|
"jade": "0.26.3",
|
|
46
47
|
"lodash": "4.16.6",
|
|
47
|
-
"markdown": "0.
|
|
48
|
+
"markdown": "0.5.0",
|
|
48
49
|
"marked": "0.3.6",
|
|
49
50
|
"mocha": "3.2.0",
|
|
50
51
|
"mongoose-long": "0.1.1",
|
package/release-items.md
CHANGED
|
@@ -18,7 +18,7 @@ For 4.x
|
|
|
18
18
|
|
|
19
19
|
0. Change to the master branch
|
|
20
20
|
1. execute `make docs` (when this process completes you'll be on the gh-pages branch)
|
|
21
|
-
2. `git commit -a -m '
|
|
21
|
+
2. `git commit -a -m 'chore: website 4.x.x'`
|
|
22
22
|
3. `git push origin gh-pages`
|
|
23
23
|
|
|
24
24
|
For 3.8.x:
|