This project aims to provide an object-mapping layer on top of Mongo to ease common activities such as:

Marshalling from Mongo to Groovy/Java types and back again

Support for GORM dynamic finders, criteria and named queries

Session-managed transactions

Validating domain instances backed by the Mongo datastore

Compatibility with GORM for Hibernate

This implementation tries to be as compatible as possible with GORM for Hibernate. In general you can refer to the GORM documentation and the "Domain Classes" section of the reference guide (see the right nav) for usage information.

The following key features are supported by GORM for Mongo:

Simple persistence methods

Dynamic finders

Criteria queries

Named queries

Inheritance

Embedded types

Query by example

However, some features are not supported:

HQL queries

Composite primary keys

Many-to-many associations (these can be modelled with a mapping class)

Any direct interaction with the Hibernate API

Custom Hibernate user types (custom types are allowed with a different API)

There may be other limitations not mentioned here so in general it shouldn’t be expected that an application based on GORM for Hibernate will "just work" without some tweaking involved. Having said that, the large majority of common GORM functionality is supported.

Configuration Properties

In previous versions of the plugin configuration was specified using the grails.mongo prefix. This is now grails.mongodb instead. For example, the following configuration in application.groovy or Config.groovy:

grails {
mongo {
databaseName = 'foo'
}
}

Should now be expressed as:

grails {
mongodb {
databaseName = 'foo'
}
}

Persistence Engine

Since GORM for MongoDB 5.0 there is now a choice of persistence engine. The original GORM for MongoDB converted MongoDB documents to and from Groovy objects. This engine is still present and can be activated with the following configuration in grails-app/conf/application.yml:

grails:
mongodb:
engine: mapping

However, 5.0 and above uses a new engine based on MongoDB 3.x driver codecs which means objects are no longer converted first to MongoDB Document objects and then to Groovy objects, instead the driver reads Groovy objects directly from the JSON stream at the driver level, which is far more efficient, however may not exhibit exactly the same behaviour as the previous version. To active this new engine use the following codec (or leave it blank as the codec engine is now the default):

grails:
mongodb:
engine: codec

When switching from mapping to codec with an existing database, there are several conditions that may require manual changes.

The version property of all documents, including embedded, may need to have it’s type changed.

The codec engine is more strict about data types than the mapping engine. For example, data stored in mongo as an INT32 could be converted to a BigDecimal. This is no longer possible and the data will need to be changed. Other examples include strings like "1470924116786" could be converted to Date objects in the mapping engine, but will not work in the codec engine. While testing your application, you can identify these types of issues by the error message that will occur. The error will occur at AbstractBsonReader.verifyBSONType. The easiest way to determine which property is the culprit, is to debug your application and put a breakpoint on that method. The variable currentName will contain the property causing the error.

An example script to update the version property of top level documents is:

Getting Started

Basic Setup

To get started with GORM for MongoDB within Grails you need configure it as a dependency in build.gradle:

dependencies {
compile 'org.grails.plugins:mongodb:6.1.7'
}

If you are using a version of Grails 3 earlier than 3.3 then you may need to enforce the GORM version. If you are using Grails 3.2.7 or above this can be done by modifying the gormVersion setting in gradle.properties:

gormVersion=6.1.7.RELEASE

Otherwise if you are using an earlier version of Grails you can force the GORM version by adding the following block directly above the dependencies block:

Common Errors

If you receive an error that indicates a failure to resolve the grails-datastore-simple dependency you may need to add the following to build.gradle directly above the dependencies block:

build.gradle

configurations.all {
exclude module:'grails-datastore-simple'
}

If you receive an error at runtime such as:

Caused by: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.bson.BsonDecimal128. at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ChildCodecRegistry.get(ChildCodecRegistry.java:51)
at org.bson.codecs.BsonTypeCodecMap.<init>(BsonTypeCodecMap.java:44)
at org.bson.codecs.BsonDocumentCodec.<init>(BsonDocumentCodec.java:65)

It means you have an older version of the MongoDB Java driver on your classpath and you should add the following declaration to build.gradle to ensure the dependency is correct:

build.gradle

compile "org.mongodb:mongodb-driver:3.5.0"

Setup within Grails 2.x

For Grails 2.x you need to modify BuildConfig.groovy:

plugins {
compile ':mongodb:6.1.7'
}

Configuring MongoDB

With that done you need to set up a running MongoDB server. Refer to the MongoDB Documentation for an explanation on how to startup a MongoDB instance. Once installed, starting MongoDB is typically a matter of executing the following command:

MONGO_HOME/bin/mongod

With the above command executed in a terminal window you should see output like the following appear:

2015-11-18T19:38:50.073+0100 I JOURNAL <<initandlisten>> journal dir=/data/db/journal
2015-11-18T19:38:50.073+0100 I JOURNAL <<initandlisten>> recover : no journal files present, no recovery needed
2015-11-18T19:38:50.090+0100 I JOURNAL <<durability>> Durability thread started
2015-11-18T19:38:50.090+0100 I JOURNAL <<journal writer>> Journal writer thread started
2015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>> MongoDB starting : pid=52540 port=27017 dbpath=/data/db 64-bit host=Graemes-iMac.local
2015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>> ** WARNING: You are running this process as the root user, which is not recommended.
2015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>>
2015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>>
2015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>> ** WARNING: soft rlimits too low. Number of files is 256, should be at least 10002015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>> db version v3.0.42015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>> git version: 0481c958daeb2969800511e7475dc66986fa9ed5
2015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>> build info: Darwin mci-osx108-11.build.10gen.cc 12.5.0 Darwin Kernel Version 12.5.0: Sun Sep 2913:33:47 PDT 2013; root:xnu-2050.48.12~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49
2015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>> allocator: system
2015-11-18T19:38:50.090+0100 I CONTROL <<initandlisten>> options: {}
2015-11-18T19:38:50.176+0100 I NETWORK <<initandlisten>> waiting for connections on port 27017

As you can see the server is running on port 27017, but don’t worry the MongoDB plugin for Grails will automatically configure itself to look for MongoDB on that port by default.

If you want to configure how Grails connects to MongoDB then you can do so using the following settings in grails-app/conf/application.yml:

Using MongoDB Standalone

If you plan to use MongoDB as your primary datastore then you need to remove the Hibernate plugin from the build.gradle file by commenting out the hibernate line in the plugins block

compile 'org.grails.plugins:hibernate'

With this done all domain classes in grails-app/domain will be persisted via MongoDB and not Hibernate. You can create a domain class by running the regular create-domain-class command:

grails create-domain-classPerson

The Person domain class will automatically be a persistent entity that can be stored in MongoDB.

Combining MongoDB and Hibernate

If you have both the Hibernate and Mongo plugins installed then by default all classes in the grails-app/domain directory will be persisted by Hibernate and not Mongo. If you want to persist a particular domain class with Mongo then you must use the mapWith property in the domain class:

static mapWith = "mongo"

Advanced Configuration

Mongo Database Connection Configuration

As mentioned the GORM for MongoDB plugin will configure all the defaults for you, but if you wish to customize those defaults you can do so in the grails-app/conf/application.groovy file:

MongoDB Connection Strings

Using MongoDB connection strings is currently the most flexible and recommended way to configure MongoDB connections.

Configuration Options Guide

Below is a complete example showing all configuration options:

grails {
mongodb {
databaseName = "myDb"// the default database name
host = "localhost"// the host to connect to
port = 27017// the port to connect to
username = ".."// the username to connect with
password = ".."// the password to connect with
stateless = false// whether to use stateless sessions by default// Alternatively, using 'url'// url = "mongodb://localhost/mydb"
options {
connectionsPerHost = 10// The maximum number of connections allowed per host
threadsAllowedToBlockForConnectionMultiplier = 5
maxWaitTime = 120000// Max wait time of a blocking thread for a connection.
connectTimeout = 0// The connect timeout in milliseconds. 0 == infinite
socketTimeout = 0// The socket timeout. 0 == infinite
socketKeepAlive = false// Whether or not to have socket keep alive turned on
writeConcern = new com.mongodb.WriteConcern(0, 0, false) // Specifies the number of servers to wait for on the write operation, and exception raising behavior
sslEnabled = false// Specifies if the driver should use an SSL connection to Mongo
socketFactory = ... // Specifies the SocketFactory to use for creating connections
}
}
}

Global Mapping Configuration

Using the grails.mongodb.default.mapping setting in grails-app/conf/application.groovy you can configure global mapping options across your domain classes. This is useful if, for example, you want to disable optimistic locking globally or you wish to use DBRefs in your association mappings. For example, the following configuration will disable optimistic locking globally and use DBRefs for all properties:

grails.mongodb.default.mapping = {
version false'*'(reference:true)
}

The * method is used to indicate that the setting applies to all properties.

Using GORM in Spring Boot

To use GORM for MongoDB in Spring Boot add the necessary dependencies to your Boot application:

Using ComponentScan without a value results in Boot scanning for classes in the same package or any package nested within the Application class package.
If your GORM entities are in a different package specify the package name as the value of the ComponentScan annotation.

Finally create your GORM entities and ensure they are annotated with grails.persistence.Entity:

If you are using Spring with an existing ApplicationContext you can instead call MongoDbDataStoreSpringInitializer.configureForBeanDefinitionRegistry prior to refreshing the context. You can pass the Spring Environment object to the constructor for configuration:

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:

Indexing using the index method

In the above example I define an index on an embedded attribtue of the document. In fact what arguments you pass to the index method get passed to the underlying MongoDB createIndex method.

Customizing the WriteConcern

A feature of MongoDB is its ability to customize how important a database write is to the user. The Java client models this as a WriteConcern and there are various options that indicate whether the client cares about server or network errors, or whether the data has been successfully written or not.

If you wish to customize the WriteConcern for a domain class you can do so in the mapping block:

For versioned entities, if a lower level of WriteConcern than WriteConcern.ACKNOWLEDGE is specified, WriteConcern.ACKNOWLEDGE will also be used for updates, to ensure that optimistic locking failures are reported.

Dynamic Attributes

Unlike a relational database, MongoDB allows for "schemaless" persistence where there are no limits to the number of attributes a particular document can have. A GORM domain class on the other hand has a schema in that there are a fixed number of properties. For example consider the following domain class:

classPlant {
boolean goesInPatch
String name
}

Here there are two fixed properties, name and goesInPatch, that will be persisted into the MongoDB document. Using GORM for MongoDB you can however use dynamic properties via the Groovy subscript operator. For example:

Using the subscript operator you can add additional attributes to the underlying Document instance that gets persisted to the MongoDB allowing for more dynamic domain models.

Custom User Types

GORM for MongoDB will persist all common known Java types like String, Integer, URL etc., however if you want to persist one of your own classes that is not a domain class you can implement a custom user type.

Custom Codecs

GORM for MongoDB is built ontop of MongoDB’s BSON encoding framework. This means it is possible to implement custom Codecs for encoding and decoding values to and from BSON.

For example consider the following simple Groovy class:

classBirthday {
Date date
}

By default the encoding engine does not know how to represent this type as a BSON value. To make the encoding engine understand this type you have to implement a custom codec:

With that done you then need to register the custom Codec. There are two ways to achieve this.

You can register a list of codecs in the grails.mongodb.codecs setting in application.yml:

grails:
mongodb:
codecs:
- my.company.BirthdayCodec

Or you can create a META-INF/services/org.bson.codecs.Codec file containing the fully qualified class name of the Codec. If there are multiple codec classes you would like to register, put each one on a separate line.

Custom Types with GORM

Another option is to define a GORM custom type. For example consider the following class:

The above BirthdayType class is a custom user type implementation for MongoDB for the Birthday class. It provides implementations for three methods: readInternal, writeInternal and the optional queryInternal. If you do not implement queryInternal your custom type can be persisted but not queried.

The writeInternal method gets passed the property, the key to store it under, the value and the native DBObject where the custom type is to be stored:

You can then read the values of the custom type and register them with the DBObject. The readInternal method gets passed the PersistentProperty, the key the user type info is stored under (although you may want to use multiple keys) and the DBObject:

The method gets passed a criterion which is the type of query and depending on the type of query you may handle the query differently. For example the above implementation supports between and equals style queries. So the following 2 queries will work:

Person.findByBirthday(new Birthday(newDate()-7)) // find someone who was born 7 days ago
Person.findByBirthdayBetween(new Birthday(newDate()-7), new Birthday(newDate())) // find someone who was born in the last 7 days

However "like" or other query types will not work.

To register a custom type in a grails application simply register it as Spring bean. For example, to register the above BirthdayType add the following to grails-app/conf/spring/resources.groovy:

Geospacial Querying

To store data in a flat surface you use a "2d" index, whilst a "2dsphere" index used for spherical data. GORM for MongoDB supports both and the following sections describe how to define and query Geospacial data.

Geospacial 2D Sphere Support

Using a 2dsphere Index

MongoDB’s 2dsphere indexes support queries that calculate geometries on an earth-like sphere.

Although you can use coordinate pairs in a 2dsphere index, they are considered legacy by the MongoDB documentation and it is recommended you store data using GeoJSON Point types.

You must specify whether the number of a floating point or double by adding a d or f at the end of the number eg. 40.739037d. Groovy’s default type for decimal numbers is BigDecimal which is not supported by MongoDB.

Once you have your data indexed you can use MongoDB specific dynamic finders to find hotels near a given a location:

def h = Hotel.findByLocationNear([50, 60])
assert h.name == 'Hilton'

You can also find a location within a box (bound queries). Boxes are defined by specifying the lower-left and upper-right corners:

The MongoConnectionSources class will read the mongo client configurations from a Mongo collection called mongo.connections by default. To configure it you must specify the connectionSourcesClass in application.yml:

All new connection sources will be stored within the specified connectionsCollection and if the application is restarted will read from the connectionsCollection.

GORM for MongoDB does not implement provisioning of new MongoDB instances at runtime. This is something that would need to be implemented by a cloud services provider for example.

Stateless Mode

GORM for MongoDB supports both stateless and stateful modes for mapping domain classes to MongoDB. In general stateful mapping is superior for write heavy applications and stateless mode better for read heavy applications (particularily when large amounts of data is involved).

Stateful mode

Domain classes are by default stateful, which means when they are read from a MongoDB document their state is stored in the user session (which is typically bound to the request in Grails). This has several advantages for write heavy applications:

GORM can automatically detect whether a call to save() is a an update or an insert and act appropriately

GORM stores the state of the read MongoDB document and therefore updates to schemaless properties don’t require an extra query

GORM can store the current version and therefore implement optimistic locking

Repeated reads of the same entity can be retrieved from the cache, thus optimizing reads as well

For an example of when a stateful domain class is better consider the following:

With a stateful entity the updates to the three properties can be batched up and executed in the save() call, when there is no state then 3 updates needs to be executed for each schemaless property (ouch!).

Stateless Domain classes

However, stateful domain classes can cause problems for read-heavy applications. Take for example the following code:

The above example will read 100,000 books and print the title of each. In stateful mode this will almost certainly run out of memory as each MongoDB document is stored in user memory as is each book. Rewriting the code as follows will solve the problem:

Alternatively you can map the domain class as stateless, in which case its state will never be stored in the session:

classBook {
...
static mapping = {
stateless true
}
}

Disadvantages of Stateless Mode

There are several disadvantages to using stateless domain classes as the default. One disadvantage is that if you are using assigned identifiers GORM cannot detect whether you want to do an insert or an update so you have to be explicit about which one you want:

Book b = newBook()
b.id = "The Book"
b.insert()

In the above case we use the explicit insert method to tell Grails this is an insert not an udpate. Another disadvantage is that reading of schemaless/dynamic properties is more costly. For example:

You can easily convert from a native MongoDB Document into an entity using a cast:

def fred = Person.collection.findOne(firstName:"Fred") as Person

Transactions

MongoDB doesn’t support transactions directly, however GORM for MongoDB does batch up inserts and updates until the session is flushed. This makes it possible to support some rollback options.

You can use either transactional services or the static withTransaction method. To mark a service as using the MongoDB transaction manager, use the static transactional property with the value 'mongo':

static transactional = 'mongo'

Alternately you can do ad-hoc transactions using the withTransaction method:

For example in this case neither Person object will be persisted to the database, because underneath the surface a persistence session is being used to batch up both insert operations into a single insert. When an exception is thrown neither insert is ever executed, hence we allow for some transactional semantics at the GORM-level.

Using the lower level API you can of course also take advantage of Mongo’s support for Atomic operations.

Unit Testing

To write unit tests with MongoDB and Spock you can simply extend from grails.test.mongodb.MongoSpec.

MongoSpec is an abstract class that will initialise GORM in the setup phase of the specification being executed. It
uses by default a MongoClient instance that connects to a MongoDB instance as defined in your configuration
(by default, localhost and port 27017, see Getting Started for more details):

Note that the default implementation is to scan your classpath searching for domain classes, from the package defined
in the configuration property grails.codegen.defaultPackage, and all the way down its subpackages. If your application
is large, classpath scanning may be slow, so it’s better to override the method getDomainClasses():

@OverrideprotectedList<Class> getDomainClasses() {
[Book]
}

Integration Testing

There is a plugin available that will execute an in memory Mongo database during your integration tests. Data will be cleared between test cases so they can work similarly to H2 with @Rollback.

Description

collectionName

Purpose

Returns the name of the MongoDB collection used for the current domain class

Examples

println Book.collectionName

Description

The collectionName property allows introspection of the name of the DBCollection object used by a given domain class. Can be used in conjunction with useCollection to switch to different collections and back again.

Description

The $geoWithin operator is a geospatial query operator that queries for a defined point, line or shape that exists entirely within another defined shape. When determining inclusion, MongoDB considers the border of a shape to be part of the shape, subject to the precision of floating point numbers.
See $geoWithin for more information.

Description

Specifies a point for which a geospatial query returns the closest documents first. The query sorts the documents from nearest to farthest. MongoDB calculates distances for $nearSphere using spherical geometry.

searchTop

Purpose

Examples

Description

The searchTop method uses MongoDB’s full text search support to perform full text search on a "text" index with the results sorted by the MongoDB score. The method by default returns the top 5 results, but the second argument can be used to customize the number of results (top 3, top 10 etc.)

useCollection

Purpose

Allows switching which collection to use to persist for the domain class for the scope of the current session (connection).

Examples

Book.useCollection("non-fiction")

Description

The useCollection method allows switching, at runtime, the collection used persist and retrieve domain classes. The collectionName property will return the current collection being used. Note that the method switches the collection used for the scope of the current session/connection (ie. it is not permanent). If you wish to permanently change the collection used then you need to configure the mapping of the domain class.

useDatabase

Purpose

Allows switching which database to use to persist for the domain class for the scope of the current session (connection).

Examples

Book.useDatabase("non-fiction")

Description

The useDatabase method allows switching, at runtime, the database used persist and retrieve domain classes. The DB property will return the current database being used. Note that the method switches the database used for the scope of the current session/connection (ie. it is not permanent). If you wish to permanently change the database used then you need to configure the mapping of the domain class.

withCollection

Purpose

Allows switching which collection to use to persist for the domain class for the scope of the given closure

Examples

Book.withCollection("non-fiction") {
// code here
}

Description

The useCollection method allows switching, at runtime, the collection used persist and retrieve domain classes. The collectionName property will return the current collection being used. Note that the method switches the collection used for the scope of given closure (ie. it is not permanent). If you wish to permanently change the collection used then you need to configure the mapping of the domain class.

withDatabase

Purpose

Allows switching which database to use to persist for the domain class for the scope of the given closure.

Examples

Book.withDatabase("non-fiction") {
// code here
}

Description

The withDatabase method allows switching, at runtime, the database used persist and retrieve domain classes. The DB property will return the current database being used. Note that the method switches the database used for the scope of the given closure (ie. it is not permanent). If you wish to permanently change the database used then you need to configure the mapping of the domain class.