@@ -18,13 +18,29 @@ const populateModelSymbol = require('../helpers/symbols').populateModelSymbol;
1818 */
1919
2020class MongooseMap extends Map {
21- constructor ( v , path , doc , schemaType ) {
21+ constructor ( v , path , doc , schemaType , options ) {
2222 if ( getConstructorName ( v ) === 'Object' ) {
2323 v = Object . keys ( v ) . reduce ( ( arr , key ) => arr . concat ( [ [ key , v [ key ] ] ] ) , [ ] ) ;
2424 }
2525 super ( v ) ;
2626 this . $__parent = doc != null && doc . $__ != null ? doc : null ;
27- this . $__path = path ;
27+
28+ // Calculate the full path from the root document
29+ // Priority: parent.$basePath (from subdoc) > options.path (from parent map/structure) > path (schema path)
30+ // Subdocuments have the most up-to-date path info, so prefer that over options.path
31+ if ( this . $__parent ?. $isSingleNested && this . $__parent . $basePath ) {
32+ this . $__path = this . $__parent . $basePath + '.' + path ;
33+ // Performance optimization: store path relative to parent subdocument
34+ // to avoid string operations in set() hot path
35+ this . $__pathRelativeToParent = path ;
36+ } else if ( options ?. path ) {
37+ this . $__path = options . path ;
38+ this . $__pathRelativeToParent = null ;
39+ } else {
40+ this . $__path = path ;
41+ this . $__pathRelativeToParent = null ;
42+ }
43+
2844 this . $__schemaType = schemaType == null ? new Mixed ( path ) : schemaType ;
2945
3046 this . $__runDeferred ( ) ;
@@ -37,6 +53,14 @@ class MongooseMap extends Map {
3753
3854 if ( value != null && value . $isSingleNested ) {
3955 value . $basePath = this . $__path + '.' + key ;
56+ // Store the path relative to parent subdoc for efficient markModified()
57+ if ( this . $__pathRelativeToParent != null ) {
58+ // Map's parent is a subdocument, store path relative to that subdoc
59+ value . $pathRelativeToParent = this . $__pathRelativeToParent + '.' + key ;
60+ } else {
61+ // Map's parent is root document, store the full path
62+ value . $pathRelativeToParent = this . $__path + '.' + key ;
63+ }
4064 }
4165 }
4266
@@ -136,9 +160,16 @@ class MongooseMap extends Map {
136160 }
137161 } else {
138162 try {
139- const options = this . $__schemaType . $isMongooseDocumentArray || this . $__schemaType . $isSingleNested || this . $__schemaType . $isMongooseArray || this . $__schemaType . $isSchemaMap ?
140- { path : fullPath . call ( this ) } :
141- null ;
163+ let options = null ;
164+ if ( this . $__schemaType . $isMongooseDocumentArray || this . $__schemaType . $isSingleNested || this . $__schemaType . $isMongooseArray || this . $__schemaType . $isSchemaMap ) {
165+ options = { path : fullPath . call ( this ) } ;
166+ // For subdocuments, also pass the relative path to avoid string operations
167+ if ( this . $__schemaType . $isSingleNested ) {
168+ options . pathRelativeToParent = this . $__pathRelativeToParent != null ?
169+ this . $__pathRelativeToParent + '.' + key :
170+ this . $__path + '.' + key ;
171+ }
172+ }
142173 value = this . $__schemaType . applySetters (
143174 value ,
144175 this . $__parent ,
@@ -157,13 +188,34 @@ class MongooseMap extends Map {
157188
158189 super . set ( key , value ) ;
159190
191+ // Set relative path on subdocuments to avoid string operations in markModified()
192+ // The path should be relative to the parent subdocument (if any), not just the key
193+ if ( value != null && value . $isSingleNested ) {
194+ if ( this . $__pathRelativeToParent != null ) {
195+ // Map's parent is a subdocument, store path relative to that subdoc (e.g., 'items.i2')
196+ value . $pathRelativeToParent = this . $__pathRelativeToParent + '.' + key ;
197+ } else {
198+ // Map's parent is root document, store just the full path
199+ value . $pathRelativeToParent = this . $__path + '.' + key ;
200+ }
201+ }
202+
160203 if ( parent != null && parent . $__ != null && ! deepEqual ( value , priorVal ) ) {
161- const path = fullPath . call ( this ) ;
162- parent . markModified ( path ) ;
204+ // Optimization: if parent is a subdocument, use precalculated relative path
205+ // to avoid building a full path just to strip the parent's prefix
206+ let pathToMark ;
207+ if ( this . $__pathRelativeToParent != null ) {
208+ // Parent is a subdocument - use precalculated relative path (e.g., 'items.i1')
209+ pathToMark = this . $__pathRelativeToParent + '.' + key ;
210+ } else {
211+ // Parent is root document or map - use full path
212+ pathToMark = fullPath . call ( this ) ;
213+ }
214+ parent . markModified ( pathToMark ) ;
163215 // If overwriting the full document array or subdoc, make sure to clean up any paths that were modified
164216 // before re: #15108
165217 if ( this . $__schemaType . $isMongooseDocumentArray || this . $__schemaType . $isSingleNested ) {
166- cleanModifiedSubpaths ( parent , path ) ;
218+ cleanModifiedSubpaths ( parent , pathToMark ) ;
167219 }
168220 }
169221
0 commit comments