11'use strict' ;
2- const modifiedPaths = require ( './common' ) . modifiedPaths ;
32const get = require ( './get' ) ;
43
54/**
@@ -29,21 +28,16 @@ module.exports = function(filter, schema, castedDoc, options) {
2928 const updatedKeys = { } ;
3029 const updatedValues = { } ;
3130 const numKeys = keys . length ;
32- const modified = { } ;
3331
3432 let hasDollarUpdate = false ;
3533
3634 for ( let i = 0 ; i < numKeys ; ++ i ) {
37- if ( keys [ i ] . startsWith ( '$' ) ) {
38- modifiedPaths ( castedDoc [ keys [ i ] ] , '' , modified ) ;
35+ if ( keys [ i ] . charAt ( 0 ) === '$' ) {
3936 hasDollarUpdate = true ;
37+ break ;
4038 }
4139 }
4240
43- if ( ! hasDollarUpdate ) {
44- modifiedPaths ( castedDoc , '' , modified ) ;
45- }
46-
4741 const paths = Object . keys ( filter ) ;
4842 const numPaths = paths . length ;
4943 for ( let i = 0 ; i < numPaths ; ++ i ) {
@@ -54,7 +48,7 @@ module.exports = function(filter, schema, castedDoc, options) {
5448 const numConditionKeys = conditionKeys . length ;
5549 let hasDollarKey = false ;
5650 for ( let j = 0 ; j < numConditionKeys ; ++ j ) {
57- if ( conditionKeys [ j ] . startsWith ( '$' ) ) {
51+ if ( conditionKeys [ j ] . charAt ( 0 ) === '$' ) {
5852 hasDollarKey = true ;
5953 break ;
6054 }
@@ -64,7 +58,6 @@ module.exports = function(filter, schema, castedDoc, options) {
6458 }
6559 }
6660 updatedKeys [ path ] = true ;
67- modified [ path ] = true ;
6861 }
6962
7063 if ( options && options . overwrite && ! hasDollarUpdate ) {
@@ -79,16 +72,17 @@ module.exports = function(filter, schema, castedDoc, options) {
7972 return ;
8073 }
8174 const def = schemaType . getDefault ( null , true ) ;
82- if ( isModified ( modified , path ) ) {
83- return ;
84- }
8575 if ( typeof def === 'undefined' ) {
8676 return ;
8777 }
88- if ( schemaType . splitPath ( ) . includes ( '$*' ) ) {
78+ const pathPieces = schemaType . splitPath ( ) ;
79+ if ( pathPieces . includes ( '$*' ) ) {
8980 // Skip defaults underneath maps. We should never do `$setOnInsert` on a path with `$*`
9081 return ;
9182 }
83+ if ( isModified ( castedDoc , updatedKeys , path , pathPieces , hasDollarUpdate ) ) {
84+ return ;
85+ }
9286
9387 castedDoc = castedDoc || { } ;
9488 castedDoc . $setOnInsert = castedDoc . $setOnInsert || { } ;
@@ -101,31 +95,66 @@ module.exports = function(filter, schema, castedDoc, options) {
10195 return castedDoc ;
10296} ;
10397
104- function isModified ( modified , path ) {
105- if ( modified [ path ] ) {
98+ function isModified ( castedDoc , updatedKeys , path , pathPieces , hasDollarUpdate ) {
99+ // Check if path is in filter (updatedKeys)
100+ if ( updatedKeys [ path ] ) {
106101 return true ;
107102 }
108103
109- // Is any parent path of `path` modified?
110- const sp = path . split ( '.' ) ;
111- let cur = sp [ 0 ] ;
112- for ( let i = 1 ; i < sp . length ; ++ i ) {
113- if ( modified [ cur ] ) {
104+ // Check if any parent path is in filter
105+ let cur = pathPieces [ 0 ] ;
106+ for ( let i = 1 ; i < pathPieces . length ; ++ i ) {
107+ if ( updatedKeys [ cur ] ) {
114108 return true ;
115109 }
116- cur += '.' + sp [ i ] ;
110+ cur += '.' + pathPieces [ i ] ;
117111 }
118112
119- // Is any child of ` path` modified?
120- const modifiedKeys = Object . keys ( modified ) ;
121- if ( modifiedKeys . length ) {
122- const parentPath = path + '.' ;
123-
124- for ( const modifiedPath of modifiedKeys ) {
125- if ( modifiedPath . slice ( 0 , path . length + 1 ) === parentPath ) {
126- return true ;
113+ // Check if path is modified in the update
114+ if ( hasDollarUpdate ) {
115+ // Check each update operator
116+ for ( const key in castedDoc ) {
117+ if ( key . charAt ( 0 ) === '$' ) {
118+ if ( pathExistsInUpdate ( castedDoc [ key ] , path , pathPieces ) ) {
119+ return true ;
120+ }
127121 }
128122 }
123+ } else {
124+ // No dollar operators, check the castedDoc directly
125+ if ( pathExistsInUpdate ( castedDoc , path , pathPieces ) ) {
126+ return true ;
127+ }
128+ }
129+
130+ return false ;
131+ }
132+
133+ function pathExistsInUpdate ( update , targetPath , pathPieces ) {
134+ if ( update == null || typeof update !== 'object' ) {
135+ return false ;
136+ }
137+
138+ // Check exact match
139+ if ( update . hasOwnProperty ( targetPath ) ) {
140+ return true ;
141+ }
142+
143+ // Check if any parent path exists
144+ let cur = pathPieces [ 0 ] ;
145+ for ( let i = 1 ; i < pathPieces . length ; ++ i ) {
146+ if ( update . hasOwnProperty ( cur ) ) {
147+ return true ;
148+ }
149+ cur += '.' + pathPieces [ i ] ;
150+ }
151+
152+ // Check if any child path exists (e.g., path is "a" and update has "a.b")
153+ const prefix = targetPath + '.' ;
154+ for ( const key in update ) {
155+ if ( key . startsWith ( prefix ) ) {
156+ return true ;
157+ }
129158 }
130159
131160 return false ;
0 commit comments