codeconsole opened a new pull request, #16297:
URL: https://github.com/apache/grails-core/pull/16297

   Makes `grails.mongodb.stringIds.defaultStoredAs` default to `objectid`. A 
domain declaring `String id` now persists `_id` as a BSON ObjectId while 
application code still sees the hex `String`. This is breaking for existing 
data, so it wants to land before 8.0.0.
   
   ```yaml
   grails:
     mongodb:
       stringIds:
         defaultStoredAs: string   # opt out; per-domain: id storedAs: String
   ```
   
   The default is applied in the field initializer rather than only in the 
config-reading constructors — `MongoMappingContext` has four constructors and 
only two read configuration, so `new MongoMappingContext("db")` was registering 
entities with a different default.
   
   ## Latent bugs this surfaced
   
   All five reproduce on 8.0.x today by setting `defaultStoredAs: objectid` 
explicitly; they are not caused by the default change.
   
   **References written with the declared type.** 
`ToOneEncoder`/`OneToManyEncoder` wrote the id as declared, so a reference 
pointed at an ObjectId `_id` with a BSON String:
   
   ```groovy
   // Captain._id is ObjectId, Boat.captain is String -> matches nothing
   Captain.collection.find(new Document("_id", boatDbo.captain)).first()   // 
null
   ```
   
   **Decoder read by predicted type.** A non-hex assigned id falls back to BSON 
String even under `storedAs: ObjectId`, so predicting the type threw:
   
   ```
   BsonInvalidOperationException: readObjectId can only be called when
   CurrentBSONType is OBJECT_ID, not when CurrentBSONType is STRING
   ```
   
   It now switches on `bsonReader.currentBsonType` and converts back to the 
declared type, which also handles collections written before a `storedAs` 
change.
   
   **Queries coerced only identity criteria.** A filter on a to-one association 
carries the associated entity's id but got no coercion, so bidirectional and 
`hasOne` lookups sent a hex String against an ObjectId foreign key:
   
   ```groovy
   captain.shipmates.size()   // 0
   face.nose                  // null
   ```
   
   **`findAllById` bypassed id coercion**, because a dynamic finder builds 
`Equals('id', ..)` rather than `IdEquals`:
   
   ```groovy
   GetItem.findAllById(hex)              // [] — sent BSON String against 
ObjectId _id
   GetItem.findAllByIdInList([hex])      // worked; the In handler already 
coerced
   ```
   
   ## Changes
   
   - `MongoMappingContext` — `DEFAULT_STRING_ID_STORED_AS`, applied via field 
initializer; unrecognized values fall back to the default rather than to a 
third silent behavior
   - `MongoIdCoercion` — adds `coerceIdToDeclaredType`, the inverse of 
`coerceIdToStoredType`
   - `PersistentEntityCodec` — coercion in `ToOneEncoder`, `OneToManyEncoder`, 
`ToOneDecoder`, `OneToManyDecoder`
   - `MongoQuery` — coerces to-one association and identity-property filters
   - Docs — `idGeneration.adoc`, `advancedConfig.adoc`, and an upgrade note 
covering the data-compatibility break
   
   ## Tests
   
   `grails-data-mongodb-core` is at parity with `8.0.x` on this machine (two 
pre-existing `MongoDatastoreLifecycleSpec` failures, unrelated). `spring-data`, 
`spring-boot`, `embedded` and `grails-data-mongodb` all pass.
   
   Adds coverage for the `defaultStoredAs: string` opt-out, which had none. 
`LegacyVideo` is pinned to `storedAs: String` so the legacy-breakage cases 
still reproduce, and two specs asserting raw BSON reference types were updated.
   


-- 
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