////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
////

=== Identity Generation
By default in GORM entities are supplied with an integer-based identifier. So for example the following entity:

[source,groovy]
----
class Person {}
----

Has a property called `id` of type `java.lang.Long`. In this case GORM for Mongo will generate a sequence based identifier using the technique https://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/[described in the Mongo documentation] on Atomic operations.

However, sequence based integer identifiers are not ideal for environments that require https://docs.mongodb.org/manual/sharding/[sharding] (one of the nicer features of Mongo). Hence it is generally advised to use either String based ids:

[source,groovy]
----
class Person {
    String id
}
----

Or a native BSON https://api.mongodb.org/java/current/org/bson/types/ObjectId.html[ObjectId]:

[source,groovy]
----
import org.bson.types.ObjectId

class Person {
    ObjectId id
}
----

BSON `ObjectId` instances are generated in a similar fashion to `UUIDs`.


==== Assigned Identifiers


Note that if you manually assign an identifier, then you will need to use the `insert` method instead of the `save` method, otherwise GORM can't work out whether you are trying to achieve an insert or an update. Example:

[source,groovy]
----
class Person {
    String id
}
...
Person p = new Person()
p.id = "Fred"
// to insert
p.insert()
// to update
p.save()
----


==== Decoupling the Declared Type from the Storage Type

When a domain declares `String id`, GORM by default writes `_id` as a BSON `String`. This is convenient in application code — HTTP controllers return clean JSON, URLs embed ids directly, and you never need `new ObjectId(...)` at call sites — but it gives up the native `ObjectId` benefits at the storage layer (12-byte index entries vs. 24-byte hex strings, embedded creation timestamp, etc.).

You can get both using the `storedAs` mapping option. The domain sees a `String id` (hex form), but `_id` on disk is a BSON `ObjectId`:

[source,groovy]
----
import org.bson.types.ObjectId

class Person {
    String id

    static mapping = {
        id storedAs: ObjectId
    }
}
----

With `storedAs: ObjectId`:

* `_id` is written as BSON `ObjectId` (native index size, sort order, creation timestamp)
* `person.id` in application code is the 24-char hex string (clean JSON, no conversion at boundaries)
* `Person.get(hexString)`, `findAllByIdInList([hexA, hexB])`, updates, and deletes all coerce the declared-type value to the storage type automatically
* Existing documents that already have BSON `ObjectId` `_id` values continue to load without a data migration

This is especially useful when migrating a legacy `ObjectId id` domain to `String id` for ergonomic reasons, because neither a data migration nor any change in application call sites is required.

===== Global Default

You can opt in globally with an `application.yml` setting, which applies to every domain declaring `String id` that does not specify its own `storedAs`:

[source,yaml]
----
grails:
  mongodb:
    stringIds:
      defaultStoredAs: objectid
----

Valid values are `string` (current default, BSON String) and `objectid` (BSON ObjectId). Unrecognized values fall back to the default with a warning logged at startup.

Per-domain `storedAs` always wins over the global default. Domains using a natural string key (slug, email, UUID) should opt out explicitly:

[source,groovy]
----
class UserProfile {
    String id   // e.g. "jsmith@example.com"

    static mapping = {
        id generator: 'assigned', storedAs: String
    }
}
----

===== Caveats

* `storedAs` is currently honored only for converting between `String` and `ObjectId`. Other combinations are accepted but behave as if `storedAs` were unset.
* Combining `generator: 'assigned'` with `storedAs: ObjectId` requires that the assigned value be a valid 24-char ObjectId hex string. Invalid values fall back to writing BSON `String` on the assumption that the domain is using a natural key despite the `storedAs` setting — consider declaring `storedAs: String` (or omitting it) in that case.