matrei commented on PR #16297:
URL: https://github.com/apache/grails-core/pull/16297#issuecomment-5566587272

   Round 3:
   
   ## Review Findings
   
   The latest update fixes the previous mapping-engine ID, update/delete, 
association-reference, and immutable-map issues. The focused tests pass. 
However, several public `updateAll` and mapping-engine association paths remain 
incomplete.
   
   ## Merge Blockers
   
   ### High: `updateAll` is not proxy-safe
   
   References:
   
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/MongoCodecSession.groovy:355-374`
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/MongoSession.java:378-394`
   - 
`grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/proxy/JavassistProxyFactory.java:60-64`
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/engine/codecs/PersistentEntityCodec.groovy:663-676`
   
   Both `updateAll` implementations obtain association IDs through the target 
entity reflector:
   
   ```groovy
   associatedEntity.reflector.getIdentifier(value)
   ```
   
   and:
   
   ```java
   getMappingContext().getEntityReflector(associatedEntity).getIdentifier(value)
   ```
   
   A lazy GORM proxy stores its ID in the proxy handler, not in the reflected 
domain field. The normal codec path already handles this with 
`proxyFactory.getIdentifier(value)`, but `updateAll` does not.
   
   Example:
   
   ```groovy
   RefProject project = new RefProject(name: 'Target').save(flush: true)
   RefProject proxy = RefProject.load(project.id)
   
   RefTicket.where { title == 'Proxy update' }
            .updateAll(project: proxy)
   ```
   
   The update can extract `null` instead of the proxy key and write an invalid 
association value. This affects both codec and mapping engines.
   
   ### High: Mapping-engine unidirectional collection and many-to-many 
references remain uncoerced
   
   References:
   
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/engine/AbstractMongoObectEntityPersister.java:449-464`
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/engine/AbstractMongoObectEntityPersister.java:392-411`
   
   The mapping-engine association indexer now coerces embedded collection keys 
and ordinary to-one references, but these paths still store declared String IDs 
directly:
   
   ```java
   dbRefs.add(new DBRef(getCollectionName(association.getAssociatedEntity()), 
foreignKey));
   ```
   
   ```java
   dbRefs.add(foreignKey);
   ```
   
   The mapping-engine many-to-many path also stores:
   
   ```java
   ids.add(entityAccess.getIdentifier(o));
   ```
   
   For a target with a String ID stored as ObjectId, the target `_id` is BSON 
ObjectId while collection entries or DBRef `$id` values remain BSON Strings. 
Raw MongoDB joins, DBRef consumers, and external clients will not match those 
references.
   
   ### High: `updateAll` incorrectly treats embedded to-one associations as ID 
references
   
   References:
   
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/MongoCodecSession.groovy:351-374`
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/MongoSession.java:378-394`
   - 
`grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/EmbeddedPersistentEntity.java:31-39`
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/engine/codecs/PersistentEntityCodec.groovy:260-276`
   
   `Embedded` extends `ToOne`, so the new association normalization also 
catches embedded properties.
   
   For:
   
   ```groovy
   class Address {
       String city
   }
   
   class Person {
       String id
       Address address
       static embedded = ['address']
   }
   ```
   
   This call:
   
   ```groovy
   Person.where { ... }
        .updateAll(address: new Address(city: 'after'))
   ```
   
   tries to extract an ID from the embedded object and writes that scalar 
value, or `null`, instead of encoding a BSON subdocument. Normal persistence 
has separate embedded encoding paths and does not send embedded values through 
`ToOneEncoder`.
   
   Embedded associations should be excluded from ID-reference normalization and 
encoded using the normal embedded mapping logic.
   
   ### High: `updateAll` does not encode collection associations
   
   References:
   
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/MongoCodecSession.groovy:351-398`
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/MongoSession.java:378-410`
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/engine/codecs/PersistentEntityCodec.groovy:619-644`
   
   The new normalization handles only `ToOne` associations. `OneToMany`, 
`ManyToMany`, and `EmbeddedCollection` properties are sent through `$set` 
unchanged.
   
   Example:
   
   ```groovy
   Parent.where { ... }
         .updateAll(children: [child])
   ```
   
   Normal persistence stores association IDs, or DBRefs when configured. The 
bulk-update path instead sends domain objects or embedded representations 
directly. Depending on the engine and codec registry, this can fail or create a 
representation inconsistent with normal persistence.
   
   If collection properties are part of the public `updateAll(Map)` contract, 
they need the same storage-aware encoding as normal persistence.
   
   ### Medium: Engine documentation wording is contradictory
   
   References:
   
   - 
`grails-data-mongodb/docs/src/docs/asciidoc/gettingStarted/advancedConfig.adoc:128-142`
   - 
`grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/config/MongoSettings.groovy:96-104`
   
   The documentation says:
   
   > The default, and only supported value, is `codec`.
   
   It then documents selecting `mapping`, and the implementation continues to 
support it with a deprecation warning.
   
   The wording should say that `codec` is the default and recommended engine, 
while `mapping` remains available for compatibility but is deprecated.
   
   ## Resolved Findings
   
   - Mapping-engine insert and point-read `_id` representation.
   - Mapping-engine update and delete filter coercion.
   - Mapping-engine iterable delete coercion.
   - Mapping-engine ordinary to-one reference and DBRef ID coercion.
   - Codec and mapping-engine ordinary to-one `updateAll`.
   - Codec `updateAll` caller-map mutation and immutable-map handling.
   - Association `IN` coercion for ordinary to-one associations.
   - Nested negation and junction coercion.
   - Empty assigned String ID deletion.
   - Identity-generation documentation contradiction.
   - Non-codec engine deprecation warning and documentation addition.
   
   ## Verification
   
   The following focused tests passed:
   
   ```text
   ./gradlew :grails-data-mongodb-core:test \
     --tests 
'org.grails.datastore.gorm.mongo.bugs.MappingEngineStringIdStorageSpec' \
     --tests 
'org.grails.datastore.gorm.mongo.bugs.StringIdAssociationStorageSpec' \
     --tests 
'org.grails.datastore.gorm.mongo.bugs.StringIdWithObjectIdStorageSpec' \
     --tests 
'org.grails.datastore.gorm.mongo.bugs.StringIdDefaultStoredAsConfigSpec' \
     --no-daemon
   ```
   
   The complete `grails-data-mongodb-core` test task was run previously with 
the expanded changes and the affected tests passed, but the local process 
exceeded the timeout before a final Gradle completion result.
   
   The remaining proxy, embedded, collection-association, and mapping-engine 
collection-reference paths should be addressed and covered with tests before 
approval.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to