mongoose 5.4.23 → 5.5.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/History.md +50 -0
- package/lib/aggregate.js +16 -4
- package/lib/connection.js +58 -1
- package/lib/document.js +195 -36
- package/lib/drivers/node-mongodb-native/collection.js +16 -4
- package/lib/drivers/node-mongodb-native/index.js +1 -1
- package/lib/helpers/document/cleanModifiedSubpaths.js +3 -0
- package/lib/helpers/document/compile.js +16 -1
- package/lib/helpers/document/getEmbeddedDiscriminatorPath.js +5 -5
- package/lib/helpers/model/applyHooks.js +4 -1
- package/lib/helpers/model/applyStaticHooks.js +61 -0
- package/lib/helpers/populate/assignVals.js +11 -1
- package/lib/helpers/query/applyQueryMiddleware.js +6 -2
- package/lib/helpers/query/castUpdate.js +0 -2
- package/lib/helpers/schema/applyPlugins.js +43 -0
- package/lib/helpers/symbols.js +4 -8
- package/lib/index.js +22 -36
- package/lib/internal.js +1 -0
- package/lib/model.js +155 -33
- package/lib/plugins/validateBeforeSave.js +7 -1
- package/lib/query.js +135 -29
- package/lib/schema/array.js +50 -0
- package/lib/schema/documentarray.js +39 -25
- package/lib/schema.js +6 -5
- package/lib/schematype.js +51 -6
- package/lib/types/array.js +20 -21
- package/lib/types/buffer.js +8 -34
- package/lib/types/documentarray.js +4 -3
- package/lib/types/embedded.js +1 -1
- package/lib/types/subdocument.js +27 -1
- package/package.json +6 -5
|
@@ -26,7 +26,13 @@ module.exports = function(schema) {
|
|
|
26
26
|
|
|
27
27
|
// Validate
|
|
28
28
|
if (shouldValidate) {
|
|
29
|
-
|
|
29
|
+
const hasValidateModifiedOnlyOption = options &&
|
|
30
|
+
(typeof options === 'object') &&
|
|
31
|
+
('validateModifiedOnly' in options);
|
|
32
|
+
const validateOptions = hasValidateModifiedOnlyOption ?
|
|
33
|
+
{validateModifiedOnly: options.validateModifiedOnly} :
|
|
34
|
+
null;
|
|
35
|
+
this.validate(validateOptions, function(error) {
|
|
30
36
|
return _this.schema.s.hooks.execPost('save:error', _this, [ _this], { error: error }, function(error) {
|
|
31
37
|
next(error);
|
|
32
38
|
});
|
package/lib/query.js
CHANGED
|
@@ -845,6 +845,47 @@ Query.prototype.mod = function() {
|
|
|
845
845
|
* @api public
|
|
846
846
|
*/
|
|
847
847
|
|
|
848
|
+
/**
|
|
849
|
+
* Get/set the current projection (AKA fields). Pass `null` to remove the
|
|
850
|
+
* current projection.
|
|
851
|
+
*
|
|
852
|
+
* Unlike `projection()`, the `select()` function modifies the current
|
|
853
|
+
* projection in place. This function overwrites the existing projection.
|
|
854
|
+
*
|
|
855
|
+
* ####Example:
|
|
856
|
+
*
|
|
857
|
+
* const q = Model.find();
|
|
858
|
+
* q.projection(); // null
|
|
859
|
+
*
|
|
860
|
+
* q.select('a b');
|
|
861
|
+
* q.projection(); // { a: 1, b: 1 }
|
|
862
|
+
*
|
|
863
|
+
* q.projection({ c: 1 });
|
|
864
|
+
* q.projection(); // { c: 1 }
|
|
865
|
+
*
|
|
866
|
+
* q.projection(null);
|
|
867
|
+
* q.projection(); // null
|
|
868
|
+
*
|
|
869
|
+
*
|
|
870
|
+
* @method projection
|
|
871
|
+
* @memberOf Query
|
|
872
|
+
* @instance
|
|
873
|
+
* @param {Object|null} arg
|
|
874
|
+
* @return {Object} the current projection
|
|
875
|
+
* @api public
|
|
876
|
+
*/
|
|
877
|
+
|
|
878
|
+
Query.prototype.projection = function(arg) {
|
|
879
|
+
if (arguments.length === 0) {
|
|
880
|
+
return this._fields;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
this._fields = {};
|
|
884
|
+
this._userProvidedFields = {};
|
|
885
|
+
this.select(arg);
|
|
886
|
+
return this._fields;
|
|
887
|
+
};
|
|
888
|
+
|
|
848
889
|
/**
|
|
849
890
|
* Specifies which document fields to include or exclude (also known as the query "projection")
|
|
850
891
|
*
|
|
@@ -2305,6 +2346,27 @@ Query.prototype.countDocuments = function(conditions, callback) {
|
|
|
2305
2346
|
return this;
|
|
2306
2347
|
};
|
|
2307
2348
|
|
|
2349
|
+
/**
|
|
2350
|
+
* Thunk around findOne()
|
|
2351
|
+
*
|
|
2352
|
+
* @param {Function} [callback]
|
|
2353
|
+
* @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
|
|
2354
|
+
* @api private
|
|
2355
|
+
*/
|
|
2356
|
+
|
|
2357
|
+
Query.prototype.__distinct = wrapThunk(function __distinct(callback) {
|
|
2358
|
+
this._castConditions();
|
|
2359
|
+
|
|
2360
|
+
if (this.error()) {
|
|
2361
|
+
callback(this.error());
|
|
2362
|
+
return null;
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
// don't pass in the conditions because we already merged them in
|
|
2366
|
+
this._collection.collection.
|
|
2367
|
+
distinct(this._distinct, this._conditions, callback);
|
|
2368
|
+
});
|
|
2369
|
+
|
|
2308
2370
|
/**
|
|
2309
2371
|
* Declares or executes a distict() operation.
|
|
2310
2372
|
*
|
|
@@ -2351,16 +2413,16 @@ Query.prototype.distinct = function(field, conditions, callback) {
|
|
|
2351
2413
|
this.error(new ObjectParameterError(conditions, 'filter', 'distinct'));
|
|
2352
2414
|
}
|
|
2353
2415
|
|
|
2354
|
-
if (
|
|
2355
|
-
this.
|
|
2416
|
+
if (field != null) {
|
|
2417
|
+
this._distinct = field;
|
|
2418
|
+
}
|
|
2419
|
+
this.op = 'distinct';
|
|
2356
2420
|
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
return this;
|
|
2360
|
-
}
|
|
2421
|
+
if (callback != null) {
|
|
2422
|
+
this.__distinct(callback);
|
|
2361
2423
|
}
|
|
2362
2424
|
|
|
2363
|
-
return
|
|
2425
|
+
return this;
|
|
2364
2426
|
};
|
|
2365
2427
|
|
|
2366
2428
|
/**
|
|
@@ -3357,12 +3419,7 @@ Query.prototype._findAndModify = function(type, callback) {
|
|
|
3357
3419
|
if (error) {
|
|
3358
3420
|
return callback(error);
|
|
3359
3421
|
}
|
|
3360
|
-
|
|
3361
|
-
castedDoc = castedDoc.toBSON();
|
|
3362
|
-
}
|
|
3363
|
-
_this._collection.findAndModify(castedQuery, castedDoc, opts, _wrapThunkCallback(_this, function(error, res) {
|
|
3364
|
-
return cb(error, res ? res.value : res, res);
|
|
3365
|
-
}));
|
|
3422
|
+
_legacyFindAndModify.call(_this, castedQuery, castedDoc, opts, callback);
|
|
3366
3423
|
};
|
|
3367
3424
|
|
|
3368
3425
|
try {
|
|
@@ -3371,12 +3428,7 @@ Query.prototype._findAndModify = function(type, callback) {
|
|
|
3371
3428
|
callback(error);
|
|
3372
3429
|
}
|
|
3373
3430
|
} else {
|
|
3374
|
-
|
|
3375
|
-
castedDoc = castedDoc.toBSON();
|
|
3376
|
-
}
|
|
3377
|
-
this._collection.findAndModify(castedQuery, castedDoc, opts, _wrapThunkCallback(_this, function(error, res) {
|
|
3378
|
-
return cb(error, res ? res.value : res, res);
|
|
3379
|
-
}));
|
|
3431
|
+
_legacyFindAndModify.call(_this, castedQuery, castedDoc, opts, callback);
|
|
3380
3432
|
}
|
|
3381
3433
|
|
|
3382
3434
|
return this;
|
|
@@ -3393,6 +3445,23 @@ function _completeOneLean(doc, res, opts, callback) {
|
|
|
3393
3445
|
return callback(null, doc);
|
|
3394
3446
|
}
|
|
3395
3447
|
|
|
3448
|
+
|
|
3449
|
+
/*!
|
|
3450
|
+
* ignore
|
|
3451
|
+
*/
|
|
3452
|
+
|
|
3453
|
+
const _legacyFindAndModify = util.deprecate(function(filter, update, opts, cb) {
|
|
3454
|
+
if (update && update.toBSON) {
|
|
3455
|
+
update = update.toBSON();
|
|
3456
|
+
}
|
|
3457
|
+
const collection = this._collection;
|
|
3458
|
+
collection.findAndModify(filter, update, opts, _wrapThunkCallback(this, function(error, res) {
|
|
3459
|
+
return cb(error, res ? res.value : res, res);
|
|
3460
|
+
}));
|
|
3461
|
+
}, 'Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the ' +
|
|
3462
|
+
'`useFindAndModify` option set to false are deprecated. See: ' +
|
|
3463
|
+
'https://mongoosejs.com/docs/deprecations.html#-findandmodify-');
|
|
3464
|
+
|
|
3396
3465
|
/*!
|
|
3397
3466
|
* Override mquery.prototype._mergeUpdate to handle mongoose objects in
|
|
3398
3467
|
* updates.
|
|
@@ -3654,9 +3723,16 @@ Query.prototype._replaceOne = wrapThunk(function(callback) {
|
|
|
3654
3723
|
* @param {Object} [doc] the update command
|
|
3655
3724
|
* @param {Object} [options]
|
|
3656
3725
|
* @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.
|
|
3657
|
-
* @param {
|
|
3726
|
+
* @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
|
|
3727
|
+
* @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
|
|
3728
|
+
* @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
|
|
3729
|
+
* @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
|
|
3730
|
+
* @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
|
|
3731
|
+
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
|
|
3732
|
+
* @param {Function} [callback] params are (error, writeOpResult)
|
|
3658
3733
|
* @return {Query} this
|
|
3659
3734
|
* @see Model.update #model_Model.update
|
|
3735
|
+
* @see Query docs https://mongoosejs.com/docs/queries.html
|
|
3660
3736
|
* @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
|
|
3661
3737
|
* @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
|
|
3662
3738
|
* @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
|
|
@@ -3714,9 +3790,15 @@ Query.prototype.update = function(conditions, doc, options, callback) {
|
|
|
3714
3790
|
* @param {Object} [options]
|
|
3715
3791
|
* @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.
|
|
3716
3792
|
* @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
|
|
3717
|
-
* @param {
|
|
3793
|
+
* @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
|
|
3794
|
+
* @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
|
|
3795
|
+
* @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
|
|
3796
|
+
* @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
|
|
3797
|
+
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
|
|
3798
|
+
* @param {Function} [callback] params are (error, writeOpResult)
|
|
3718
3799
|
* @return {Query} this
|
|
3719
3800
|
* @see Model.update #model_Model.update
|
|
3801
|
+
* @see Query docs https://mongoosejs.com/docs/queries.html
|
|
3720
3802
|
* @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
|
|
3721
3803
|
* @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
|
|
3722
3804
|
* @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
|
|
@@ -3775,9 +3857,15 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) {
|
|
|
3775
3857
|
* @param {Object} [options]
|
|
3776
3858
|
* @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.
|
|
3777
3859
|
* @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
|
|
3860
|
+
* @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
|
|
3861
|
+
* @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
|
|
3862
|
+
* @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
|
|
3863
|
+
* @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
|
|
3864
|
+
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
|
|
3778
3865
|
* @param {Function} [callback] params are (error, writeOpResult)
|
|
3779
3866
|
* @return {Query} this
|
|
3780
3867
|
* @see Model.update #model_Model.update
|
|
3868
|
+
* @see Query docs https://mongoosejs.com/docs/queries.html
|
|
3781
3869
|
* @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
|
|
3782
3870
|
* @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
|
|
3783
3871
|
* @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
|
|
@@ -3832,10 +3920,17 @@ Query.prototype.updateOne = function(conditions, doc, options, callback) {
|
|
|
3832
3920
|
* @param {Object} [criteria]
|
|
3833
3921
|
* @param {Object} [doc] the update command
|
|
3834
3922
|
* @param {Object} [options]
|
|
3923
|
+
* @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.
|
|
3835
3924
|
* @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
|
|
3836
|
-
* @param {
|
|
3925
|
+
* @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
|
|
3926
|
+
* @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
|
|
3927
|
+
* @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
|
|
3928
|
+
* @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
|
|
3929
|
+
* @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
|
|
3930
|
+
* @param {Function} [callback] params are (error, writeOpResult)
|
|
3837
3931
|
* @return {Query} this
|
|
3838
3932
|
* @see Model.update #model_Model.update
|
|
3933
|
+
* @see Query docs https://mongoosejs.com/docs/queries.html
|
|
3839
3934
|
* @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
|
|
3840
3935
|
* @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
|
|
3841
3936
|
* @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
|
|
@@ -4241,10 +4336,10 @@ function castDoc(query, overwrite) {
|
|
|
4241
4336
|
* })
|
|
4242
4337
|
*
|
|
4243
4338
|
* Kitten.find().populate({
|
|
4244
|
-
*
|
|
4245
|
-
*
|
|
4246
|
-
*
|
|
4247
|
-
*
|
|
4339
|
+
* path: 'owner',
|
|
4340
|
+
* select: 'name',
|
|
4341
|
+
* match: { color: 'black' },
|
|
4342
|
+
* options: { sort: { name: -1 } }
|
|
4248
4343
|
* }).exec(function (err, kittens) {
|
|
4249
4344
|
* console.log(kittens[0].owner.name) // Zoopa
|
|
4250
4345
|
* })
|
|
@@ -4254,13 +4349,21 @@ function castDoc(query, overwrite) {
|
|
|
4254
4349
|
* console.log(kittens[0].owner.name) // Zoopa
|
|
4255
4350
|
* })
|
|
4256
4351
|
*
|
|
4257
|
-
* Paths are populated after the query executes and a response is received. A
|
|
4352
|
+
* Paths are populated after the query executes and a response is received. A
|
|
4353
|
+
* separate query is then executed for each path specified for population. After
|
|
4354
|
+
* a response for each query has also been returned, the results are passed to
|
|
4355
|
+
* the callback.
|
|
4258
4356
|
*
|
|
4259
4357
|
* @param {Object|String} path either the path to populate or an object specifying all parameters
|
|
4260
4358
|
* @param {Object|String} [select] Field selection for the population query
|
|
4261
4359
|
* @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field.
|
|
4262
4360
|
* @param {Object} [match] Conditions for the population query
|
|
4263
4361
|
* @param {Object} [options] Options for the population query (sort, etc)
|
|
4362
|
+
* @param {String} [options.path=null] The path to populate.
|
|
4363
|
+
* @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries.
|
|
4364
|
+
* @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](/docs/schematypes.html#schematype-options).
|
|
4365
|
+
* @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them.
|
|
4366
|
+
* @param {Object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://docs.mongodb.com/manual/tutorial/query-documents/), or a function that returns a filter object.
|
|
4264
4367
|
* @see population ./populate.html
|
|
4265
4368
|
* @see Query#select #query_Query-select
|
|
4266
4369
|
* @see Model.populate #model_Model.populate
|
|
@@ -4504,13 +4607,16 @@ Query.prototype.cursor = function cursor(opts) {
|
|
|
4504
4607
|
this.setOptions(opts);
|
|
4505
4608
|
}
|
|
4506
4609
|
|
|
4610
|
+
const options = Object.assign({}, this.options, {
|
|
4611
|
+
projection: this.projection()
|
|
4612
|
+
});
|
|
4507
4613
|
try {
|
|
4508
4614
|
this.cast(this.model);
|
|
4509
4615
|
} catch (err) {
|
|
4510
|
-
return (new QueryCursor(this,
|
|
4616
|
+
return (new QueryCursor(this, options))._markError(err);
|
|
4511
4617
|
}
|
|
4512
4618
|
|
|
4513
|
-
return new QueryCursor(this,
|
|
4619
|
+
return new QueryCursor(this, options);
|
|
4514
4620
|
};
|
|
4515
4621
|
|
|
4516
4622
|
// the rest of these are basically to support older Mongoose syntax with mquery
|
package/lib/schema/array.js
CHANGED
|
@@ -146,6 +146,56 @@ SchemaArray.options = { castNonArrays: true };
|
|
|
146
146
|
SchemaArray.prototype = Object.create(SchemaType.prototype);
|
|
147
147
|
SchemaArray.prototype.constructor = SchemaArray;
|
|
148
148
|
|
|
149
|
+
/*!
|
|
150
|
+
* ignore
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
SchemaArray._checkRequired = SchemaType.prototype.checkRequired;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Override the function the required validator uses to check whether an array
|
|
157
|
+
* passes the `required` check.
|
|
158
|
+
*
|
|
159
|
+
* ####Example:
|
|
160
|
+
*
|
|
161
|
+
* // Require non-empty array to pass `required` check
|
|
162
|
+
* mongoose.Schema.Types.Array.checkRequired(v => Array.isArray(v) && v.length);
|
|
163
|
+
*
|
|
164
|
+
* const M = mongoose.model({ arr: { type: Array, required: true } });
|
|
165
|
+
* new M({ arr: [] }).validateSync(); // `null`, validation fails!
|
|
166
|
+
*
|
|
167
|
+
* @param {Function} fn
|
|
168
|
+
* @return {Function}
|
|
169
|
+
* @function checkRequired
|
|
170
|
+
* @static
|
|
171
|
+
* @api public
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
SchemaArray.checkRequired = SchemaType.checkRequired;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check if the given value satisfies the `required` validator.
|
|
178
|
+
*
|
|
179
|
+
* @param {Any} value
|
|
180
|
+
* @param {Document} doc
|
|
181
|
+
* @return {Boolean}
|
|
182
|
+
* @api public
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
SchemaArray.prototype.checkRequired = function checkRequired(value, doc) {
|
|
186
|
+
if (SchemaType._isRef(this, value, doc, true)) {
|
|
187
|
+
return !!value;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// `require('util').inherits()` does **not** copy static properties, and
|
|
191
|
+
// plugins like mongoose-float use `inherits()` for pre-ES6.
|
|
192
|
+
const _checkRequired = typeof this.constructor.checkRequired == 'function' ?
|
|
193
|
+
this.constructor.checkRequired() :
|
|
194
|
+
SchemaArray.checkRequired();
|
|
195
|
+
|
|
196
|
+
return _checkRequired(value);
|
|
197
|
+
};
|
|
198
|
+
|
|
149
199
|
/**
|
|
150
200
|
* Adds an enum validator if this is an array of strings. Equivalent to
|
|
151
201
|
* `SchemaString.prototype.enum()`
|
|
@@ -13,6 +13,8 @@ const util = require('util');
|
|
|
13
13
|
const utils = require('../utils');
|
|
14
14
|
const getDiscriminatorByValue = require('../queryhelpers').getDiscriminatorByValue;
|
|
15
15
|
|
|
16
|
+
const arrayParentSymbol = require('../helpers/symbols').arrayParentSymbol;
|
|
17
|
+
|
|
16
18
|
let MongooseDocumentArray;
|
|
17
19
|
let Subdocument;
|
|
18
20
|
|
|
@@ -207,8 +209,8 @@ DocumentArray.prototype.doValidate = function(array, fn, scope, options) {
|
|
|
207
209
|
// If you set the array index directly, the doc might not yet be
|
|
208
210
|
// a full fledged mongoose subdoc, so make it into one.
|
|
209
211
|
if (!(doc instanceof Subdocument)) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
const Constructor = getConstructor(_this, array[i]);
|
|
213
|
+
doc = array[i] = new Constructor(doc, array, undefined, undefined, i);
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
doc.$__validate(callback);
|
|
@@ -255,8 +257,8 @@ DocumentArray.prototype.doValidateSync = function(array, scope) {
|
|
|
255
257
|
// If you set the array index directly, the doc might not yet be
|
|
256
258
|
// a full fledged mongoose subdoc, so make it into one.
|
|
257
259
|
if (!(doc instanceof Subdocument)) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
const Constructor = getConstructor(this, array[i]);
|
|
261
|
+
doc = array[i] = new Constructor(doc, array, undefined, undefined, i);
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
const subdocValidateError = doc.validateSync();
|
|
@@ -290,15 +292,16 @@ DocumentArray.prototype.getDefault = function(scope) {
|
|
|
290
292
|
}
|
|
291
293
|
|
|
292
294
|
ret = new MongooseDocumentArray(ret, this.path, scope);
|
|
293
|
-
const _parent = ret
|
|
294
|
-
ret
|
|
295
|
+
const _parent = ret[arrayParentSymbol];
|
|
296
|
+
ret[arrayParentSymbol] = null;
|
|
295
297
|
|
|
296
298
|
for (let i = 0; i < ret.length; ++i) {
|
|
297
|
-
|
|
299
|
+
const Constructor = getConstructor(this, ret[i]);
|
|
300
|
+
ret[i] = new Constructor(ret[i], ret, undefined,
|
|
298
301
|
undefined, i);
|
|
299
302
|
}
|
|
300
303
|
|
|
301
|
-
ret
|
|
304
|
+
ret[arrayParentSymbol] = _parent;
|
|
302
305
|
|
|
303
306
|
return ret;
|
|
304
307
|
};
|
|
@@ -349,24 +352,11 @@ DocumentArray.prototype.cast = function(value, doc, init, prev, options) {
|
|
|
349
352
|
continue;
|
|
350
353
|
}
|
|
351
354
|
|
|
352
|
-
|
|
353
|
-
if (Constructor.discriminators &&
|
|
354
|
-
Constructor.schema &&
|
|
355
|
-
Constructor.schema.options &&
|
|
356
|
-
typeof value[i][Constructor.schema.options.discriminatorKey] === 'string') {
|
|
357
|
-
if (Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]]) {
|
|
358
|
-
Constructor = Constructor.discriminators[value[i][Constructor.schema.options.discriminatorKey]];
|
|
359
|
-
} else {
|
|
360
|
-
const constructorByValue = getDiscriminatorByValue(Constructor, value[i][Constructor.schema.options.discriminatorKey]);
|
|
361
|
-
if (constructorByValue) {
|
|
362
|
-
Constructor = constructorByValue;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
355
|
+
const Constructor = getConstructor(this, value[i]);
|
|
366
356
|
|
|
367
357
|
// Check if the document has a different schema (re gh-3701)
|
|
368
358
|
if ((value[i].$__) &&
|
|
369
|
-
value[i]
|
|
359
|
+
!(value[i] instanceof Constructor)) {
|
|
370
360
|
value[i] = value[i].toObject({ transform: false, virtuals: false });
|
|
371
361
|
}
|
|
372
362
|
|
|
@@ -417,6 +407,30 @@ DocumentArray.prototype.cast = function(value, doc, init, prev, options) {
|
|
|
417
407
|
return value;
|
|
418
408
|
};
|
|
419
409
|
|
|
410
|
+
/*!
|
|
411
|
+
* Find the correct subdoc constructor, taking into account discriminators
|
|
412
|
+
*/
|
|
413
|
+
|
|
414
|
+
function getConstructor(docArray, subdoc) {
|
|
415
|
+
let Constructor = docArray.casterConstructor;
|
|
416
|
+
const schema = Constructor.schema;
|
|
417
|
+
const discriminatorKey = schema.options.discriminatorKey;
|
|
418
|
+
const discriminatorValue = subdoc[discriminatorKey];
|
|
419
|
+
if (Constructor.discriminators && typeof discriminatorValue === 'string') {
|
|
420
|
+
if (Constructor.discriminators[discriminatorValue]) {
|
|
421
|
+
Constructor = Constructor.discriminators[discriminatorValue];
|
|
422
|
+
} else {
|
|
423
|
+
const constructorByValue = getDiscriminatorByValue(Constructor,
|
|
424
|
+
discriminatorValue);
|
|
425
|
+
if (constructorByValue) {
|
|
426
|
+
Constructor = constructorByValue;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return Constructor;
|
|
432
|
+
}
|
|
433
|
+
|
|
420
434
|
/*!
|
|
421
435
|
* ignore
|
|
422
436
|
*/
|
|
@@ -432,12 +446,12 @@ DocumentArray.prototype.clone = function() {
|
|
|
432
446
|
*/
|
|
433
447
|
|
|
434
448
|
function _clearListeners(arr) {
|
|
435
|
-
if (arr == null || arr
|
|
449
|
+
if (arr == null || arr[arrayParentSymbol] == null) {
|
|
436
450
|
return;
|
|
437
451
|
}
|
|
438
452
|
|
|
439
453
|
for (const key in arr._handlers) {
|
|
440
|
-
arr.
|
|
454
|
+
arr[arrayParentSymbol].removeListener(key, arr._handlers[key]);
|
|
441
455
|
}
|
|
442
456
|
}
|
|
443
457
|
|
package/lib/schema.js
CHANGED
|
@@ -1049,11 +1049,12 @@ Schema.prototype.setupTimestamp = function(timestamps) {
|
|
|
1049
1049
|
|
|
1050
1050
|
_setTimestampsOnUpdate[symbols.builtInMiddleware] = true;
|
|
1051
1051
|
|
|
1052
|
-
|
|
1053
|
-
this.pre('
|
|
1054
|
-
this.pre('
|
|
1055
|
-
this.pre('
|
|
1056
|
-
this.pre('
|
|
1052
|
+
const opts = { query: true, model: false };
|
|
1053
|
+
this.pre('findOneAndUpdate', opts, _setTimestampsOnUpdate);
|
|
1054
|
+
this.pre('replaceOne', opts, _setTimestampsOnUpdate);
|
|
1055
|
+
this.pre('update', opts, _setTimestampsOnUpdate);
|
|
1056
|
+
this.pre('updateOne', opts, _setTimestampsOnUpdate);
|
|
1057
|
+
this.pre('updateMany', opts, _setTimestampsOnUpdate);
|
|
1057
1058
|
|
|
1058
1059
|
function _setTimestampsOnUpdate(next) {
|
|
1059
1060
|
const now = this.model.base.now();
|
package/lib/schematype.js
CHANGED
|
@@ -10,6 +10,7 @@ const $type = require('./schema/operators/type');
|
|
|
10
10
|
const get = require('./helpers/get');
|
|
11
11
|
const immediate = require('./helpers/immediate');
|
|
12
12
|
const schemaTypeSymbol = require('./helpers/symbols').schemaTypeSymbol;
|
|
13
|
+
const util = require('util');
|
|
13
14
|
const utils = require('./utils');
|
|
14
15
|
const validatorErrorSymbol = require('./helpers/symbols').validatorErrorSymbol;
|
|
15
16
|
|
|
@@ -50,6 +51,21 @@ function SchemaType(path, options, instance) {
|
|
|
50
51
|
if (this[prop] && typeof this[prop] === 'function') {
|
|
51
52
|
// { unique: true, index: true }
|
|
52
53
|
if (prop === 'index' && this._index) {
|
|
54
|
+
if (options.index === false) {
|
|
55
|
+
const index = this._index;
|
|
56
|
+
if (typeof index === 'object' && index != null) {
|
|
57
|
+
if (index.unique) {
|
|
58
|
+
throw new Error('Path "' + this.path + '" may not have `index` ' +
|
|
59
|
+
'set to false and `unique` set to true');
|
|
60
|
+
}
|
|
61
|
+
if (index.sparse) {
|
|
62
|
+
throw new Error('Path "' + this.path + '" may not have `index` ' +
|
|
63
|
+
'set to false and `sparse` set to true');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this._index = false;
|
|
68
|
+
}
|
|
53
69
|
continue;
|
|
54
70
|
}
|
|
55
71
|
|
|
@@ -262,6 +278,14 @@ SchemaType.prototype.unique = function(bool) {
|
|
|
262
278
|
*/
|
|
263
279
|
|
|
264
280
|
SchemaType.prototype.text = function(bool) {
|
|
281
|
+
if (this._index === false) {
|
|
282
|
+
if (!bool) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
throw new Error('Path "' + this.path + '" may not have `index` set to ' +
|
|
286
|
+
'false and `text` set to true');
|
|
287
|
+
}
|
|
288
|
+
|
|
265
289
|
if (this._index === null || this._index === undefined ||
|
|
266
290
|
typeof this._index === 'boolean') {
|
|
267
291
|
this._index = {};
|
|
@@ -287,8 +311,15 @@ SchemaType.prototype.text = function(bool) {
|
|
|
287
311
|
*/
|
|
288
312
|
|
|
289
313
|
SchemaType.prototype.sparse = function(bool) {
|
|
290
|
-
if (this._index ===
|
|
291
|
-
|
|
314
|
+
if (this._index === false) {
|
|
315
|
+
if (!bool) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
throw new Error('Path "' + this.path + '" may not have `index` set to ' +
|
|
319
|
+
'false and `sparse` set to true');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (this._index == null || typeof this._index === 'boolean') {
|
|
292
323
|
this._index = {};
|
|
293
324
|
} else if (typeof this._index === 'string') {
|
|
294
325
|
this._index = {type: this._index};
|
|
@@ -597,6 +628,11 @@ SchemaType.prototype.validate = function(obj, message, type) {
|
|
|
597
628
|
}
|
|
598
629
|
properties = {message: message, type: type, validator: obj};
|
|
599
630
|
}
|
|
631
|
+
|
|
632
|
+
if (properties.isAsync) {
|
|
633
|
+
handleIsAsync();
|
|
634
|
+
}
|
|
635
|
+
|
|
600
636
|
this.validators.push(properties);
|
|
601
637
|
return this;
|
|
602
638
|
}
|
|
@@ -620,6 +656,15 @@ SchemaType.prototype.validate = function(obj, message, type) {
|
|
|
620
656
|
return this;
|
|
621
657
|
};
|
|
622
658
|
|
|
659
|
+
/*!
|
|
660
|
+
* ignore
|
|
661
|
+
*/
|
|
662
|
+
|
|
663
|
+
const handleIsAsync = util.deprecate(function handleIsAsync() {},
|
|
664
|
+
'Mongoose: the `isAsync` option for custom validators is deprecated. Make ' +
|
|
665
|
+
'your async validators return a promise instead: ' +
|
|
666
|
+
'https://mongoosejs.com/docs/validation.html#async-custom-validators');
|
|
667
|
+
|
|
623
668
|
/**
|
|
624
669
|
* Adds a required validator to this SchemaType. The validator gets added
|
|
625
670
|
* to the front of this SchemaType's validators array using `unshift()`.
|
|
@@ -906,7 +951,7 @@ SchemaType.prototype.select = function select(val) {
|
|
|
906
951
|
* @api private
|
|
907
952
|
*/
|
|
908
953
|
|
|
909
|
-
SchemaType.prototype.doValidate = function(value, fn, scope) {
|
|
954
|
+
SchemaType.prototype.doValidate = function(value, fn, scope, options) {
|
|
910
955
|
let err = false;
|
|
911
956
|
const path = this.path;
|
|
912
957
|
let count = this.validators.length;
|
|
@@ -945,7 +990,7 @@ SchemaType.prototype.doValidate = function(value, fn, scope) {
|
|
|
945
990
|
let ok;
|
|
946
991
|
|
|
947
992
|
const validatorProperties = utils.clone(v);
|
|
948
|
-
validatorProperties.path = path;
|
|
993
|
+
validatorProperties.path = options && options.path ? options.path : path;
|
|
949
994
|
validatorProperties.value = value;
|
|
950
995
|
|
|
951
996
|
if (validator instanceof RegExp) {
|
|
@@ -1041,7 +1086,7 @@ function asyncValidate(validator, scope, value, props, cb) {
|
|
|
1041
1086
|
* @api private
|
|
1042
1087
|
*/
|
|
1043
1088
|
|
|
1044
|
-
SchemaType.prototype.doValidateSync = function(value, scope) {
|
|
1089
|
+
SchemaType.prototype.doValidateSync = function(value, scope, options) {
|
|
1045
1090
|
let err = null;
|
|
1046
1091
|
const path = this.path;
|
|
1047
1092
|
const count = this.validators.length;
|
|
@@ -1077,7 +1122,7 @@ SchemaType.prototype.doValidateSync = function(value, scope) {
|
|
|
1077
1122
|
|
|
1078
1123
|
const validator = v.validator;
|
|
1079
1124
|
const validatorProperties = utils.clone(v);
|
|
1080
|
-
validatorProperties.path = path;
|
|
1125
|
+
validatorProperties.path = options && options.path ? options.path : path;
|
|
1081
1126
|
validatorProperties.value = value;
|
|
1082
1127
|
let ok;
|
|
1083
1128
|
|