@@ -2756,47 +2756,7 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2756
2756
2757
2757
// gh-661: if a whole array is modified, make sure to run validation on all
2758
2758
// the children as well
2759
- for ( const path of paths ) {
2760
- const _pathType = doc . $__schema . path ( path ) ;
2761
- if ( ! _pathType ) {
2762
- continue ;
2763
- }
2764
-
2765
- if ( ! _pathType . $isMongooseArray ||
2766
- // To avoid potential performance issues, skip doc arrays whose children
2767
- // are not required. `getPositionalPathType()` may be slow, so avoid
2768
- // it unless we have a case of #6364
2769
- ( ! Array . isArray ( _pathType ) &&
2770
- _pathType . $isMongooseDocumentArray &&
2771
- ! ( _pathType && _pathType . schemaOptions && _pathType . schemaOptions . required ) ) ) {
2772
- continue ;
2773
- }
2774
-
2775
- // gh-11380: optimization. If the array isn't a document array and there's no validators
2776
- // on the array type, there's no need to run validation on the individual array elements.
2777
- if ( _pathType . $isMongooseArray &&
2778
- ! _pathType . $isMongooseDocumentArray && // Skip document arrays...
2779
- ! _pathType . $embeddedSchemaType . $isMongooseArray && // and arrays of arrays
2780
- _pathType . $embeddedSchemaType . validators . length === 0 ) {
2781
- continue ;
2782
- }
2783
-
2784
- const val = doc . $__getValue ( path ) ;
2785
- _pushNestedArrayPaths ( val , paths , path ) ;
2786
- }
2787
-
2788
- function _pushNestedArrayPaths ( val , paths , path ) {
2789
- if ( val != null ) {
2790
- const numElements = val . length ;
2791
- for ( let j = 0 ; j < numElements ; ++ j ) {
2792
- if ( Array . isArray ( val [ j ] ) ) {
2793
- _pushNestedArrayPaths ( val [ j ] , paths , path + '.' + j ) ;
2794
- } else {
2795
- paths . add ( path + '.' + j ) ;
2796
- }
2797
- }
2798
- }
2799
- }
2759
+ _addArrayPathsToValidate ( doc , paths ) ;
2800
2760
2801
2761
const flattenOptions = { skipArrays : true } ;
2802
2762
for ( const pathToCheck of paths ) {
@@ -2841,12 +2801,58 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2841
2801
return [ paths , doValidateOptions ] ;
2842
2802
}
2843
2803
2804
+ function _addArrayPathsToValidate ( doc , paths ) {
2805
+ for ( const path of paths ) {
2806
+ const _pathType = doc . $__schema . path ( path ) ;
2807
+ if ( ! _pathType ) {
2808
+ continue ;
2809
+ }
2810
+
2811
+ if ( ! _pathType . $isMongooseArray ||
2812
+ // To avoid potential performance issues, skip doc arrays whose children
2813
+ // are not required. `getPositionalPathType()` may be slow, so avoid
2814
+ // it unless we have a case of #6364
2815
+ ( ! Array . isArray ( _pathType ) &&
2816
+ _pathType . $isMongooseDocumentArray &&
2817
+ ! ( _pathType && _pathType . schemaOptions && _pathType . schemaOptions . required ) ) ) {
2818
+ continue ;
2819
+ }
2820
+
2821
+ // gh-11380: optimization. If the array isn't a document array and there's no validators
2822
+ // on the array type, there's no need to run validation on the individual array elements.
2823
+ if ( _pathType . $isMongooseArray &&
2824
+ ! _pathType . $isMongooseDocumentArray && // Skip document arrays...
2825
+ ! _pathType . $embeddedSchemaType . $isMongooseArray && // and arrays of arrays
2826
+ _pathType . $embeddedSchemaType . validators . length === 0 ) {
2827
+ continue ;
2828
+ }
2829
+
2830
+ const val = doc . $__getValue ( path ) ;
2831
+ _pushNestedArrayPaths ( val , paths , path ) ;
2832
+ }
2833
+ }
2834
+
2835
+ function _pushNestedArrayPaths ( val , paths , path ) {
2836
+ if ( val != null ) {
2837
+ const numElements = val . length ;
2838
+ for ( let j = 0 ; j < numElements ; ++ j ) {
2839
+ if ( Array . isArray ( val [ j ] ) ) {
2840
+ _pushNestedArrayPaths ( val [ j ] , paths , path + '.' + j ) ;
2841
+ } else {
2842
+ paths . add ( path + '.' + j ) ;
2843
+ }
2844
+ }
2845
+ }
2846
+ }
2847
+
2844
2848
/*!
2845
2849
* ignore
2846
2850
*/
2847
2851
2848
2852
Document . prototype . $__validate = function ( pathsToValidate , options , callback ) {
2849
- if ( typeof pathsToValidate === 'function' ) {
2853
+ if ( this . $__ . saveOptions && this . $__ . saveOptions . pathsToSave && ! pathsToValidate ) {
2854
+ pathsToValidate = [ ...this . $__ . saveOptions . pathsToSave ] ;
2855
+ } else if ( typeof pathsToValidate === 'function' ) {
2850
2856
callback = pathsToValidate ;
2851
2857
options = null ;
2852
2858
pathsToValidate = null ;
@@ -2868,6 +2874,19 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2868
2874
shouldValidateModifiedOnly = this . $__schema . options . validateModifiedOnly ;
2869
2875
}
2870
2876
2877
+ const validateAllPaths = options && options . validateAllPaths ;
2878
+ if ( validateAllPaths ) {
2879
+ if ( pathsToSkip ) {
2880
+ throw new TypeError ( 'Cannot set both `validateAllPaths` and `pathsToSkip`' ) ;
2881
+ }
2882
+ if ( pathsToValidate ) {
2883
+ throw new TypeError ( 'Cannot set both `validateAllPaths` and `pathsToValidate`' ) ;
2884
+ }
2885
+ if ( hasValidateModifiedOnlyOption && shouldValidateModifiedOnly ) {
2886
+ throw new TypeError ( 'Cannot set both `validateAllPaths` and `validateModifiedOnly`' ) ;
2887
+ }
2888
+ }
2889
+
2871
2890
const _this = this ;
2872
2891
const _complete = ( ) => {
2873
2892
let validationError = this . $__ . validationError ;
@@ -2905,11 +2924,33 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2905
2924
} ;
2906
2925
2907
2926
// only validate required fields when necessary
2908
- const pathDetails = _getPathsToValidate ( this , pathsToValidate , pathsToSkip ) ;
2909
- const paths = shouldValidateModifiedOnly ?
2910
- pathDetails [ 0 ] . filter ( ( path ) => this . $isModified ( path ) ) :
2911
- pathDetails [ 0 ] ;
2912
- const doValidateOptionsByPath = pathDetails [ 1 ] ;
2927
+ let paths ;
2928
+ let doValidateOptionsByPath ;
2929
+ if ( validateAllPaths ) {
2930
+ paths = new Set ( Object . keys ( this . $__schema . paths ) ) ;
2931
+ // gh-661: if a whole array is modified, make sure to run validation on all
2932
+ // the children as well
2933
+ for ( const path of paths ) {
2934
+ const schemaType = this . $__schema . path ( path ) ;
2935
+ if ( ! schemaType || ! schemaType . $isMongooseArray ) {
2936
+ continue ;
2937
+ }
2938
+ const val = this . $__getValue ( path ) ;
2939
+ if ( ! val ) {
2940
+ continue ;
2941
+ }
2942
+ _pushNestedArrayPaths ( val , paths , path ) ;
2943
+ }
2944
+ paths = [ ...paths ] ;
2945
+ doValidateOptionsByPath = { } ;
2946
+ } else {
2947
+ const pathDetails = _getPathsToValidate ( this , pathsToValidate , pathsToSkip ) ;
2948
+ paths = shouldValidateModifiedOnly ?
2949
+ pathDetails [ 0 ] . filter ( ( path ) => this . $isModified ( path ) ) :
2950
+ pathDetails [ 0 ] ;
2951
+ doValidateOptionsByPath = pathDetails [ 1 ] ;
2952
+ }
2953
+
2913
2954
if ( typeof pathsToValidate === 'string' ) {
2914
2955
pathsToValidate = pathsToValidate . split ( ' ' ) ;
2915
2956
}
@@ -2929,8 +2970,19 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2929
2970
const validated = { } ;
2930
2971
let total = 0 ;
2931
2972
2932
- for ( const path of paths ) {
2933
- validatePath ( path ) ;
2973
+ let pathsToSave = this . $__ . saveOptions ?. pathsToSave ;
2974
+ if ( Array . isArray ( pathsToSave ) ) {
2975
+ pathsToSave = new Set ( pathsToSave ) ;
2976
+ for ( const path of paths ) {
2977
+ if ( ! pathsToSave . has ( path ) ) {
2978
+ continue ;
2979
+ }
2980
+ validatePath ( path ) ;
2981
+ }
2982
+ } else {
2983
+ for ( const path of paths ) {
2984
+ validatePath ( path ) ;
2985
+ }
2934
2986
}
2935
2987
2936
2988
function validatePath ( path ) {
@@ -2979,7 +3031,8 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2979
3031
const doValidateOptions = {
2980
3032
...doValidateOptionsByPath [ path ] ,
2981
3033
path : path ,
2982
- validateModifiedOnly : shouldValidateModifiedOnly
3034
+ validateModifiedOnly : shouldValidateModifiedOnly ,
3035
+ validateAllPaths
2983
3036
} ;
2984
3037
2985
3038
schemaType . doValidate ( val , function ( err ) {
@@ -3097,6 +3150,16 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
3097
3150
3098
3151
let pathsToSkip = options && options . pathsToSkip ;
3099
3152
3153
+ const validateAllPaths = options && options . validateAllPaths ;
3154
+ if ( validateAllPaths ) {
3155
+ if ( pathsToSkip ) {
3156
+ throw new TypeError ( 'Cannot set both `validateAllPaths` and `pathsToSkip`' ) ;
3157
+ }
3158
+ if ( pathsToValidate ) {
3159
+ throw new TypeError ( 'Cannot set both `validateAllPaths` and `pathsToValidate`' ) ;
3160
+ }
3161
+ }
3162
+
3100
3163
if ( typeof pathsToValidate === 'string' ) {
3101
3164
const isOnePathOnly = pathsToValidate . indexOf ( ' ' ) === - 1 ;
3102
3165
pathsToValidate = isOnePathOnly ? [ pathsToValidate ] : pathsToValidate . split ( ' ' ) ;
@@ -3105,11 +3168,32 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
3105
3168
}
3106
3169
3107
3170
// only validate required fields when necessary
3108
- const pathDetails = _getPathsToValidate ( this , pathsToValidate , pathsToSkip ) ;
3109
- const paths = shouldValidateModifiedOnly ?
3110
- pathDetails [ 0 ] . filter ( ( path ) => this . $isModified ( path ) ) :
3111
- pathDetails [ 0 ] ;
3112
- const skipSchemaValidators = pathDetails [ 1 ] ;
3171
+ let paths ;
3172
+ let skipSchemaValidators ;
3173
+ if ( validateAllPaths ) {
3174
+ paths = new Set ( Object . keys ( this . $__schema . paths ) ) ;
3175
+ // gh-661: if a whole array is modified, make sure to run validation on all
3176
+ // the children as well
3177
+ for ( const path of paths ) {
3178
+ const schemaType = this . $__schema . path ( path ) ;
3179
+ if ( ! schemaType || ! schemaType . $isMongooseArray ) {
3180
+ continue ;
3181
+ }
3182
+ const val = this . $__getValue ( path ) ;
3183
+ if ( ! val ) {
3184
+ continue ;
3185
+ }
3186
+ _pushNestedArrayPaths ( val , paths , path ) ;
3187
+ }
3188
+ paths = [ ...paths ] ;
3189
+ skipSchemaValidators = { } ;
3190
+ } else {
3191
+ const pathDetails = _getPathsToValidate ( this , pathsToValidate , pathsToSkip ) ;
3192
+ paths = shouldValidateModifiedOnly ?
3193
+ pathDetails [ 0 ] . filter ( ( path ) => this . $isModified ( path ) ) :
3194
+ pathDetails [ 0 ] ;
3195
+ skipSchemaValidators = pathDetails [ 1 ] ;
3196
+ }
3113
3197
3114
3198
const validating = { } ;
3115
3199
@@ -3134,7 +3218,8 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
3134
3218
const err = p . doValidateSync ( val , _this , {
3135
3219
skipSchemaValidators : skipSchemaValidators [ path ] ,
3136
3220
path : path ,
3137
- validateModifiedOnly : shouldValidateModifiedOnly
3221
+ validateModifiedOnly : shouldValidateModifiedOnly ,
3222
+ validateAllPaths
3138
3223
} ) ;
3139
3224
if ( err ) {
3140
3225
const isSubdoc = p . $isSingleNested ||
0 commit comments