@@ -2689,7 +2689,7 @@ function _evaluateRequiredFunctions(doc) {
26892689 * ignore
26902690 */
26912691
2692- function _getPathsToValidate ( doc , pathsToValidate , pathsToSkip ) {
2692+ function _getPathsToValidate ( doc , pathsToValidate , pathsToSkip , isNestedValidate ) {
26932693 const doValidateOptions = { } ;
26942694
26952695 _evaluateRequiredFunctions ( doc ) ;
@@ -2709,35 +2709,40 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
27092709 Object . keys ( doc . $__ . activePaths . getStatePaths ( 'default' ) ) . forEach ( addToPaths ) ;
27102710 function addToPaths ( p ) { paths . add ( p ) ; }
27112711
2712- const subdocs = doc . $getAllSubdocs ( ) ;
2713- const modifiedPaths = doc . modifiedPaths ( ) ;
2714- for ( const subdoc of subdocs ) {
2715- if ( subdoc . $basePath ) {
2716- const fullPathToSubdoc = subdoc . $isSingleNested ? subdoc . $__pathRelativeToParent ( ) : subdoc . $__fullPathWithIndexes ( ) ;
2717-
2718- // Remove child paths for now, because we'll be validating the whole
2719- // subdoc.
2720- // The following is a faster take on looping through every path in `paths`
2721- // and checking if the path starts with `fullPathToSubdoc` re: gh-13191
2722- for ( const modifiedPath of subdoc . modifiedPaths ( ) ) {
2723- paths . delete ( fullPathToSubdoc + '.' + modifiedPath ) ;
2724- }
2712+ if ( ! isNestedValidate ) {
2713+ // If we're validating a subdocument, all this logic will run anyway on the top-level document, so skip for subdocuments
2714+ const subdocs = doc . $getAllSubdocs ( ) ;
2715+ const modifiedPaths = doc . modifiedPaths ( ) ;
2716+ for ( const subdoc of subdocs ) {
2717+ if ( subdoc . $basePath ) {
2718+ const fullPathToSubdoc = subdoc . $isSingleNested ? subdoc . $__pathRelativeToParent ( ) : subdoc . $__fullPathWithIndexes ( ) ;
2719+
2720+ // Remove child paths for now, because we'll be validating the whole
2721+ // subdoc.
2722+ // The following is a faster take on looping through every path in `paths`
2723+ // and checking if the path starts with `fullPathToSubdoc` re: gh-13191
2724+ for ( const modifiedPath of subdoc . modifiedPaths ( ) ) {
2725+ paths . delete ( fullPathToSubdoc + '.' + modifiedPath ) ;
2726+ }
27252727
2726- if ( doc . $isModified ( fullPathToSubdoc , null , modifiedPaths ) &&
2727- ! doc . isDirectModified ( fullPathToSubdoc ) &&
2728- ! doc . $isDefault ( fullPathToSubdoc ) ) {
2729- paths . add ( fullPathToSubdoc ) ;
2728+ if ( doc . $isModified ( fullPathToSubdoc , null , modifiedPaths ) &&
2729+ // Avoid using isDirectModified() here because that does additional checks on whether the parent path
2730+ // is direct modified, which can cause performance issues re: gh-14897
2731+ ! doc . $__ . activePaths . getStatePaths ( 'modify' ) . hasOwnProperty ( fullPathToSubdoc ) &&
2732+ ! doc . $isDefault ( fullPathToSubdoc ) ) {
2733+ paths . add ( fullPathToSubdoc ) ;
27302734
2731- if ( doc . $__ . pathsToScopes == null ) {
2732- doc . $__ . pathsToScopes = { } ;
2733- }
2734- doc . $__ . pathsToScopes [ fullPathToSubdoc ] = subdoc . $isDocumentArrayElement ?
2735- subdoc . __parentArray :
2736- subdoc . $parent ( ) ;
2735+ if ( doc . $__ . pathsToScopes == null ) {
2736+ doc . $__ . pathsToScopes = { } ;
2737+ }
2738+ doc . $__ . pathsToScopes [ fullPathToSubdoc ] = subdoc . $isDocumentArrayElement ?
2739+ subdoc . __parentArray :
2740+ subdoc . $parent ( ) ;
27372741
2738- doValidateOptions [ fullPathToSubdoc ] = { skipSchemaValidators : true } ;
2739- if ( subdoc . $isDocumentArrayElement && subdoc . __index != null ) {
2740- doValidateOptions [ fullPathToSubdoc ] . index = subdoc . __index ;
2742+ doValidateOptions [ fullPathToSubdoc ] = { skipSchemaValidators : true } ;
2743+ if ( subdoc . $isDocumentArrayElement && subdoc . __index != null ) {
2744+ doValidateOptions [ fullPathToSubdoc ] . index = subdoc . __index ;
2745+ }
27412746 }
27422747 }
27432748 }
@@ -2972,7 +2977,7 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
29722977 paths = [ ...paths ] ;
29732978 doValidateOptionsByPath = { } ;
29742979 } else {
2975- const pathDetails = _getPathsToValidate ( this , pathsToValidate , pathsToSkip ) ;
2980+ const pathDetails = _getPathsToValidate ( this , pathsToValidate , pathsToSkip , options && options . _nestedValidate ) ;
29762981 paths = shouldValidateModifiedOnly ?
29772982 pathDetails [ 0 ] . filter ( ( path ) => this . $isModified ( path ) ) :
29782983 pathDetails [ 0 ] ;
@@ -3059,7 +3064,8 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
30593064 const doValidateOptions = {
30603065 ...doValidateOptionsByPath [ path ] ,
30613066 path : path ,
3062- validateAllPaths
3067+ validateAllPaths,
3068+ _nestedValidate : true
30633069 } ;
30643070
30653071 schemaType . doValidate ( val , function ( err ) {
@@ -3478,44 +3484,9 @@ Document.prototype.$__reset = function reset() {
34783484 // Skip for subdocuments
34793485 const subdocs = ! this . $isSubdocument ? this . $getAllSubdocs ( ) : null ;
34803486 if ( subdocs && subdocs . length > 0 ) {
3481- const resetArrays = new Set ( ) ;
34823487 for ( const subdoc of subdocs ) {
3483- const fullPathWithIndexes = subdoc . $__fullPathWithIndexes ( ) ;
34843488 subdoc . $__reset ( ) ;
3485- if ( this . isModified ( fullPathWithIndexes ) || isParentInit ( fullPathWithIndexes ) ) {
3486- if ( subdoc . $isDocumentArrayElement ) {
3487- resetArrays . add ( subdoc . parentArray ( ) ) ;
3488- } else {
3489- const parent = subdoc . $parent ( ) ;
3490- if ( parent === this ) {
3491- this . $__ . activePaths . clearPath ( subdoc . $basePath ) ;
3492- } else if ( parent != null && parent . $isSubdocument ) {
3493- // If map path underneath subdocument, may end up with a case where
3494- // map path is modified but parent still needs to be reset. See gh-10295
3495- parent . $__reset ( ) ;
3496- }
3497- }
3498- }
3499- }
3500-
3501- for ( const array of resetArrays ) {
3502- this . $__ . activePaths . clearPath ( array . $path ( ) ) ;
3503- array [ arrayAtomicsBackupSymbol ] = array [ arrayAtomicsSymbol ] ;
3504- array [ arrayAtomicsSymbol ] = { } ;
3505- }
3506- }
3507-
3508- function isParentInit ( path ) {
3509- path = path . indexOf ( '.' ) === - 1 ? [ path ] : path . split ( '.' ) ;
3510- let cur = '' ;
3511- for ( let i = 0 ; i < path . length ; ++ i ) {
3512- cur += ( cur . length ? '.' : '' ) + path [ i ] ;
3513- if ( _this . $__ . activePaths [ cur ] === 'init' ) {
3514- return true ;
3515- }
35163489 }
3517-
3518- return false ;
35193490 }
35203491
35213492 // clear atomics
0 commit comments