Yet Another Flavour of GORM: MongoDB |
|

Our crusade to make GORM ubiquitous across NoSQL stores hit another milestones today as we are pleased to announce GORM for MongoDB.
MongoDB is a document database that bridges the gap between key-value stores (which are fast and highly scalable) and traditional RDBMS systems (which provide rich queries and deep functionality).
Like the Redis and Gemfire plugins, GORM for MongoDB has full support for CRUD operations:
def person = new Person(name:"Fred", age: 45) person.save() person = Person.get(person.id) assert person != null person.delete() assert Person.get(person.id) == null
Rich querying with dynamic finders:
def fred = Person.findByName("Fred")
def adults = Person.findAllByAgeGreaterThan(18)
def teenagers = Person.findAllByAgeBetween(13, 18)
def children = Person.findAllByAgeLessThan(13)
Complex query handling with criteria queries:
def results = Person.withCriteria {
like('name', 'F%')
gt('age', 18)
}
And reusable query support with named queries:
class Person {
String name
int age
static namedQueries = {
adults { gt 'age', 18 }
childrenStartingWithF {
lt 'age', 18
like 'name', 'F%'
}
}
}
Person.adults.list()
Person.adults.findByName("Fred")
Person.childrenStartingWithF.count()
All this whilst still allowing access to the lower-level Mongo driver:
def fred = Person.collection.findOne(name:"Fred")
We have prepared a short screencast demonstrating GORM for MongoDB in action:
You can checkout the full documentation on the GORM for MongoDB plugin for more information on installation, configuration and working with the APIs.
GORM for MongoDB is built on the same codebase as the Redis and Gemfire support, speaking of which we are extremely excited with upcoming community contributions to the code, including support for Java Content Repository (JCR) and Riak (a scalable key/value datastore with a nice REST API).
We continue to be keen to work with the community on building out support for GORM implementations on top of other datastores, if you're interested in helping out give us a shout on the Grails mailing list.
Enjoy!
Similar Posts
- Introducing GORM for Gemfire
- Announcing GORM for Redis
- GORM for MongoDB: New Milestone, Richer Experience
- Spring Data Arora SR1 released
- More Grails 1.3 features





Julio Arias says:
Added on November 15th, 2010 at 12:03 pmNice article, but really bad audio in the video.
Graeme Rocher says:
Added on November 16th, 2010 at 10:33 amGlad you enjoyed the article.
Sorry about the audio, I really need to get a better microphone
numan says:
Added on November 16th, 2010 at 12:11 pmGreat job! Will try it out today
sumo says:
Added on November 16th, 2010 at 11:59 pmGreat job!
will it be possible, to use MongoDB as well as hibernate(with other RDBMS) simultaneously?
how is different from "gorm-mongodb" plugin ?
Valkiry says:
Added on November 17th, 2010 at 8:42 amHi!
It's great to have it in place now, but I am experiencing problems using it with spring-security-core plugin…have any ideas?
Sagar Jadhav says:
Added on November 17th, 2010 at 9:00 pmYou are a rockstar Graeme and this is one of the greatest contributions towards simplicity. And the data access makes sense again !
Raffel Desuza says:
Added on November 18th, 2010 at 12:57 amGreat job done. Wow.
Acai Max Cleanse
Claus Hausberger says:
Added on November 18th, 2010 at 6:02 amVery cool stuff. This is much better than the MongoDB Java API!!!
Claus
Chris Smith says:
Added on November 26th, 2010 at 12:27 amNice! Thanks Graeme, this goes a long way to make MongoDB access simple yet flexible. Integrating it into the spring-security-core plugin (which uses hibernate's sessionFactory directly, need to implement a couple of spring security classes, should be simple), so we'll see how it goes.
Kristian Pedersen says:
Added on November 30th, 2010 at 2:31 pmNice!
Are there any plans to support Composition in GORM for Mongo?
It looks like each domain class is mapped to a different Mongo collection. But, it would be nice to be able to build larger documents that fully embed other GORM objects (and lists of objects), which should be supported by mongo since it's storing BSON.
Graeme Rocher says:
Added on November 30th, 2010 at 2:36 pm@Kristian Yes I plan to implement the GORM "embedded" option for the next version that supports this feature
Matthias Hryniszak says:
Added on December 3rd, 2010 at 7:05 pmThanks for fixing the plugin's dependencies
Matthias Hryniszak says:
Added on December 3rd, 2010 at 7:29 pmIs there any chance for withCriteria to work across relations, like in this example:
Person.withCriteria {
pets: {
like('name', 'Din%')
}
}
This works perfectly fine for a relational database but I imagine it'll be hard to figure out on a key-value datastore…
Dmitry says:
Added on December 23rd, 2010 at 8:52 amSome problems with plugin.
1) If I define "Map map" or "List list" fields, they are not stored in database, while it has native support for maps and lists.
2) Very bad documentation and unreadable sources. How do I get driver-level data for the domain object? "petInstance.mongo" with "def mongo"? Or …? I even can't imagine. But if I cannot store complex data in domain fields, I need this ability.
3) If I code "hasOne = [pet:Pet]", it's OK. But if I code "Pet pet", it always fails validation.
I'd be glad to improve the plugin by myself, because it is really actual and needed, but cannot get sources.
Ted says:
Added on December 26th, 2010 at 7:40 pmHi,
Trying to use this plugin alongside Hibernate using the mapWith statement, I am connecting to the server but I cannot CRUD.I get the error :
java.lang.IllegalArgumentException: 'ok' should never be null…
at com.mongodb.CommandResult.ok(CommandResult.java:30)
at com.mongodb.DBCollection.getCount(DBCollection.java:657)
at com.mongodb.DBCursor.size(DBCursor.java:582)
at org.grails.datastore.gorm.GormStaticApi.count(GormStaticApi.groovy:147)
We can take this offline if you give me your contact info.
Thanks
donny says:
Added on December 26th, 2010 at 7:44 pmVery useful piece of work, only a few small problems faced:
Its all functional. As Kristian Pedersen requested before, looking forward to embeddable objects as Mongodb documents it as the preferred approach. I naturally tried embedded property on a domain object hoping for a miracle but it did not work
Following the steps on eclipse helios, java 1.5 I installed the mongodb plugin but somehow org.bson.types.ObjectId was not resolved by eclipse nor by grails run-app, even though it was referenced by buildpath in eclipse and jar present. I had to download the mongo driver jar from mongo website and drop it into lib folder and add it eclipse build path to make it work. Just a minor issue and I couldnt find out why it couldnt resolve.
donny says:
Added on December 28th, 2010 at 12:19 pmAnother thing I noticed is the absence of the version field in the mongodb document.
Here is the output from the console:
> db.person.find()
{ "_id" : ObjectId("4d178474b5fef8b81f0cab89"), "firstName" : "Fred", "lastName" : "Flintstone" }
Does that mean optimistic locking is not implemented?
Joseph says:
Added on January 7th, 2011 at 8:54 amWas trying to use a custom userType in this .. does this support hibernate userTypes? Or even some other type of user type?
Graeme Rocher says:
Added on January 11th, 2011 at 4:14 amSorry for the late response to all. Some answers to the questions:
* Embedded objects are not supported yet but will be in the next version (M2)
* Optimistic locking is not supported yet
* Custom user types are Hibernate/RDBMS specific so are not supported
* Querying across associations is planned for M2
Octavian Covalschi says:
Added on January 16th, 2011 at 11:06 pmSweet, thanks!
Couple of issues though:
- unique:true constraint doesn't seem to work
- can't provide additional parameters when creating indexes(unique, dropDups). I understand that's something specific to mongodb and work is in progress.
- can't find a way to create/ensure desc index (-1)
- gorm event beforeInsert fails, def beforeInsert is taken as a property:
java.lang.IllegalArgumentException: can't serialize class me.demo.User$_closure1
at org.bson.BSONEncoder._putObjectField(BSONEncoder.java:188)
at org.bson.BSONEncoder.putObject(BSONEncoder.java:119)
at org.bson.BSONEncoder.putObject(BSONEncoder.java:65)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:131)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:97)
at org.grails.datastore.gorm.GormInstanceApi.save(GormInstanceApi.groovy:130)
at org.grails.datastore.gorm.GormEnhancer$1.call(GormEnhancer.groovy:94)
at me.demo.UserController$_closure4.doCall(UserController.groovy:24)
at me.demo.UserController$_closure4.doCall(UserController.groovy)
at com.springsource.insight.collection.tcserver.request.HttpRequestOperationCollectionValve.invoke(HttpRequestOperationCollectionValve.java:64)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
At this point address index issues I have to use low-level API:
User.collection.ensureIndex([login:1], [unique:true, dropDups:true])
User.collection.ensureIndex([email:1], [unique:true, dropDups:true])
User.collection.ensureIndex([createDate:-1])
PS: Code above was placed in BootStrap, don't know where else I could put it…
Max says:
Added on January 25th, 2011 at 6:19 amHere is an example of secure grails application powered by mongodb:
http://sysgears.com/articles/creating-secure-grails-application-powered-by-mongodb/
Enjoy
Graeme Rocher says:
Added on January 25th, 2011 at 6:41 am@Octavian To get around this problem use methods instead of closures for your events. ie.
def beforeInsert() { }
instead of
def beforeInsert = { }
Chris Smith says:
Added on January 26th, 2011 at 1:21 amThat's funny, I put together a very similar package to Max back in December:
https://github.com/evilangelist/grails-spring-security-mongodb
Very similar code, sorry I didn't publish here earlier.
JoeC. says:
Added on February 3rd, 2011 at 2:52 pmJust what I was looking for!!
Any timeframe on when M2 will be out. Very anxious to use this on an upcoming project but really need embedded document support.
I don't mind being a beta tester if there a way hold of the latest working version if embedded support is already checked in.
Graeme Rocher says:
Added on February 4th, 2011 at 3:04 am@JoeC
We have already committed support for embedded documents. See:
https://github.com/grails/inconsequential/commit/e7839c1e5ac3664ef32843553304dc1a2c8b1ad5
The M2 release is planned for end of next week, failing that early the following one
Octavian Covalschi says:
Added on February 7th, 2011 at 10:48 am@Graeme Rocher
Thanks, that worked. Defining beforeInsert as a method worked.
Great news, waiting for M2! I'm in the same boat as JoeC, waiting for embedded docs to use in my current project. I'm converting a PHP project to Grails mongodb. It's so much fun!
Hunter says:
Added on February 9th, 2011 at 8:17 amI started my project and sort of hit a wall where I'm realizing I will really be _needing_ embedded documents. Great work so far, very easy to get going. I'm waiting eagerly for M2. Thanks for the great work on this!
Hunter
Xander says:
Added on February 26th, 2011 at 6:29 pmPerhaps I'm misunderstanding how embedding works, but it's just not happening for me. For example, if I have these two classes:
package sim
import org.bson.types.ObjectId
class FlightSim {
String uid
String type
Point point
ObjectId id
static mapWith = "mongo"
static embedded = ["point"]
static constraints = {
point(nullable:true,blank:true)
type(nullable:true,blank:true)
}
static mapping = {
collection "Sim"
database "db1"
}
}
and
package sim
class Point {
String lat
String lon
String hae
static constraints = {
lat(nullable:true)
lon(nullable:true)
hae(nullable:true)
}
}
Then the "point" field of a FlightSim object always shows up as null when it is read from MongoDB (yes, the point information is actually in the entries of the FlightSim collection).
If I change the type of "point" to "BasicDBObject" (or String for that matter), then I see the contents of the point. I've tried variations of using/not using the "static embedded" as well as the "mapWith".
I'm using version 1.0-M4 of the plugin.
Doing a db.sim.find() would show things like this:
{ "_id" : ObjectId("4d673f0e8d77d952ad0ccc5b"), "point" : { "hae" : 11837.517600000001, "lat" : -6.16783, "lon" : 94.51823 }, "uid" : "VATSIM-1050449:B772:BOEING:777-200", "detail" : null }
Any thoughts
Matthias says:
Added on February 27th, 2011 at 3:32 am@Xander: I've just tested the embedded document feature on a Person with Address combination:
Person.groovy:
import org.bson.types.ObjectId
class Person {
ObjectId id
String firstName
String lastName
Address address
static embedded = [ 'address' ]
}
Address.groovy:
class Address {
String street
String city
}
And then I've issued the following from Grails console:
new Person(
firstName: "John",
lastName: "Doe",
address: new Address(street: "Broadway", city: "London")
).save(flush: true)
and everything worked fine with the following result stored in the database:
{ "_id" : ObjectId("4d6a0b428c52b329284f9227"), "address" : { "city" : "London", "street" : "Broadway" }, "firstName" : "John", "lastName" : "Doe" }
So I guess it's working just fine
If you could you share an example on github I'd probably be able to help you out with your problem.
Matthias says:
Added on February 27th, 2011 at 3:43 am@grocher: uninstalling the hibernate plugin effectively disables the "run-script" script. It ends prematurely with the following exception:
Error executing script RunScript: org/hibernate/HibernateException
java.lang.NoClassDefFoundError: org/hibernate/HibernateException
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at RunScript.class$(RunScript.groovy)
at RunScript.$get$$class$org$springframework$orm$hibernate3$SessionFactoryUtils(RunScript.groovy)
at RunScript.configureHibernateSession(RunScript.groovy:50)
at RunScript$_run_closure2.doCall(RunScript.groovy:38)
at RunScript$_run_closure1.doCall(RunScript.groovy:28)
at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:427)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:415)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.executeTargets(Gant.groovy:590)
at gant.Gant.executeTargets(Gant.groovy:589)
Caused by: java.lang.ClassNotFoundException: org.hibernate.HibernateException
at org.codehaus.groovy.tools.RootLoader.findClass(RootLoader.java:156)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at org.codehaus.groovy.tools.RootLoader.loadClass(RootLoader.java:128)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
… 18 more
Error executing script RunScript: org/hibernate/HibernateException
Application context shutting down…
Application context shutdown.
Should I file a ticket for this?
Xander says:
Added on February 27th, 2011 at 7:49 amMattaias,
This plugin is just what I need, and hopefully I'm just misunderstanding how grails/mongodb/plugins interact.
Your Person/Address example works as shown for me too – It's not that embedded classes don't work it's my particular example that doesn't work. Note: if I make a Person controller (with scaffold=true) and do a grails run-app, then I see the same null for Address in your example that I do in mine (and trying to create a Person fails because there's no way to give it an address).
Here are a couple of differences: The MongoDB collection I'm using is not created by grails, it is created from a Java class in a servlet; my embedded classes are not domain classes (like your Address), they are created as BasicDBObject().
If I put the class "Point" into the same file as FlightSim, then I see a non-null reference in the view. The standard grails view doesn't show fields of Point, so I'm not sure if it is working right yet. Also, as I noted above, if I declare the field "point" as a BasicDBObject, then the whole thing shows up in the view.
Does onLoad work with the mongodb plugin? I'm not seeing anything, but that may just be me.
Ivan says:
Added on March 13th, 2011 at 10:26 amIt looks like the embedded does not work for a list yet?
How I can make a list of Address to be an embedded document of person?
If it is not supported yet, is there any plan and ETA for that?
Can't wait to start practice MongoDB but lack of such support would be a showstopper…
Ivan
Joseph says:
Added on March 13th, 2011 at 11:10 amIvan u can make an array of string long etc. Object I'd in m1 had an issue though
Ivan says:
Added on March 13th, 2011 at 11:22 am@Joseph,
Thanks, can you paste a few lines of example?
I am getting below exception after changed to Address[] addresses, and embedded…
nested exception is org.springframework.datastore.mapping.model.IllegalMappingException: Mapped identifier [id] for class [[Lcom.test.Address;] is not a valid property
Ivan
Kruttik Aggarwal says:
Added on April 29th, 2011 at 4:25 pm@Ivan
Were you able to get collection embedding to work. If yes, can you please provide an example. I am having hard time embedding collections. I am on 1.0-M5
Kruttik Aggarwal says:
Added on April 29th, 2011 at 4:40 pmWas someone able to get the embedded collection to work. If yes, can you provide an example. I desperately need a solution.
John says:
Added on May 11th, 2011 at 2:46 pmHi – New to Grails (but not to Mongo)…but I'm trying to modify an existing Grails tutorial that I've been working-through to use Mongo. The tutorial includes scaffolded data and a variety of domain classes. And I was able to get my data model working in a JDBC MySQL connector. So the Mongo connector should be easy, right? I guess not! Do I need to modify DataSource.groovy or other /conf source files in any special way? Are there more complete examples out there I can refer to?
– John
Simon says:
Added on June 12th, 2011 at 8:41 pmBug Report:
two domain
EbayShop extends Shop
new EbayShop(…).save()
EbayShop share Shop collecion
low level API:
EbayShop.collection.find() // nothing
Shop.collection.find() //one item
So I thing EbayShop.collection must return the actual collection 'shop',rather than 'ebayShop'
Roger says:
Added on December 21st, 2011 at 8:44 amEmbedded objects do not work at all, with the latest plugin.
example:
class AnalyticItem {
ObjectId id
List ip
Date date
String action
String featureName
String sid
String pid
String uid
String url
String agent
String clientProductId
static constraints = {
featureName(nullable: true)
clientProductId(nullable: true)
pid(nullable:true)
uid(nullable:true)
}
}
class UserSession {
ObjectId id
Date sessionStartDate
String sid
List sessionAnalytics
static constraints = {
user (nullable: true, null: true, blank: true)
}
//static hasMany = [ sessionAnalytics : AnalyticItem ]
static embedded = ['sessionAnalytics']
static mapping = {
sid index: true, indexAttributes: [unique: true, dropDups: true]
}
}
This will produce, 0% of the time, a userSession stored object with analytic items embedded.
As well, I have to do someUserSession.sessionAnalytics = new ArrayList() since it will NPE the first time I try to use it otherwise.
Also, if I do hasMany, I get addToSessionAnalytics as a rruntime created method.. using embedded.. I do not get this .. so I have to do:
someUserSession.sessionAnalytics = [] //to avoid NPE
someUserSession.sessionAnalytics << analyticItem
and then when i save someUserSession, it has *0* embedded documents.
Eli Israel says:
Added on December 26th, 2012 at 6:37 pmWithout the ability to join, is there a way to indicate to GORM to do eager fetching of associations when loading a larger number of documents? Seems hard to get around the N 1 Selects problem, unless I'm missing something. Thx