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]