Nested dynamic attributes with Grails Mongodb Plugin

Of the cool features of the Grails MongoDB gorm plugin is dynamic attributes – the ability to define properties in domain classes in an ad-hoc manner. This is actually quite cool since you can have very light domain classes in grails and keep your schemas fairly flexible.

In this post, I will show you how to use nested dynamic attributes with the grails mongodb plugin.

Getting Started

To get started, you need to install the mongodb plugin by adding the dependency in your application’s BuildConfig file.

compile ":mongodb:1.1.0.GA"

You also need a mongodb instance. I use the ones provided by MongoHQ since it means I don’t have to keep them in my machine. Add this to your config.groovy via as specified in the plugin documentation.

It should look something like this:

grails {
    mongo {
        host = "alex.mongohq.com"
        port = 10039
        username = "myusername"
        password = "password"
        databaseName = "sample"
    }
}

We can then create a domain class via the grails command line:

grails c-d-c mysample.Product

This domain class has no attributes.

Simple Dynamic Attributes

Let’s launch the grails console

grails console

We can add a dynamic attribute as specified in the documentation:

import mysample.Product

Product p = new Product()

p['dynamicAttribute'] = 1
p['anotherAttribute'] = new Date()
p['thirdAttribute'] = 'Something else'

p.save()

In the last example, we added three dynamic attributes to the product object and saved them. When we look at them in mongohq, we see this:

{
  _id: ObjectId("50fee450036410f8cff6c7bf"),
  dateCreated: ISODate("2013-01-22T19:11:12.089Z"),
  lastUpdated: ISODate("2013-01-22T19:11:12.522Z"),
  version: 1,
  dynamicAttribute: 1,
  anotherAttribute: ISODate("2013-01-22T19:11:12.069Z"),
  thirdAttribute: "Something else"
}

You can use these attributes in dynamic finders by calling, for example:

Product.findByThirdAttribute( 'Something else' )

This will return the product we just created.

Nested Dynamic Attributes

That is really cool, but it turns out that we can go a little further with it.

One of the nice things about the document model in MongoDB is that you are able to create documents of different types within a collection.

So a collection could hold both an album,

{
  sku: "00e8da9b",
  type: "Audio Album",
  title: "A Love Supreme",
  description: "by John Coltrane",
  asin: "B0000A118M",

  details: {
    title: "A Love Supreme [Original Recording Reissued]",
    artist: "John Coltrane",
    genre: [ "Jazz", "General" ],
  },
}

and a book

{
  sku: "00e8444b",
  type: "Book",
  title: "The Definitive Guide To Grails 2",
  description: "by Graeme Rocher and Jeff Brown",
  asin: "B0000A118M",

  details: {
    title: "The Definitive Guide to Grails 2",
    pages: "a lot"
  },

  bookClub:{
    leader: "Glenn Danzig"
  }

}

As you can see, both objects have the same metadata, but they differ in the details part of their description. But they could also have completely different sections as well.

[ Example modified from MongoDB manual - it explains the advantages a little bit more eloquently than I can here -  http://docs.mongodb.org/manual/use-cases/product-catalog/ ]

In the mongoDB blog, they go at great lengths to tell you how to set documents via the underlying mongo connector. http://blog.mongodb.org/post/18510469058/grails-in-the-land-of-mongodb

It turns out, however, that you can model this type of nested dynamic attributes with the MongoDb plugin on the fly without having to generate JSON or use a Builder. You just have to add maps.

In your grails console, you can set:

import mysample.Product

Product p = new Product()

p['shippingInfo'] = [
                      weight: 20,
                      height: 30,
                      preferredCouriers: [ 'Purolator', 'FedEx' ]
                    ]

p['details'] = [ title: 'A book',
                 libraryDetails: [
                                   isbn: 'someXXX',
                                   author: 'abc',
                                   classification: [
                                                     genre: 'horror'
                                                   ]
                                 ]
               ]

p.save()

When we save this object, we get the following in MongoHQ.

{
  _id: ObjectId("50fee4d9036410f8cff6c7c0"),
  dateCreated: ISODate("2013-01-22T19:13:29.440Z"),
  lastUpdated: ISODate("2013-01-22T19:13:29.973Z"),
  version: 1,
  shippingInfo: {
    weight: 20,
    height: 30,
    preferredCouriers: [
      "Purolator",
      "FedEx"
    ]
  },
  details: {
    title: "A book",
    libraryDetails: {
      isbn: "someXXX",
      author: "abc",
      classification: {
        genre: "horror"
      }
    }
  }
}

Of course, we can use the underlying db connection to then set the indices on the fields we are interested in searching.

It’s quite refreshing not to have to define all this domain class structure when building your grails application. It makes it easier to handle a lot of those one-off attributes that a pain to handle in traditional relational databases.

Accessing Nested Dynamic Attributes

One thing to note is that the dynamic properties won’t be set on the product, so if you try to do

p.shippingInfo.weight

this will not work.

So use

p['shippingInfo'].weight

and

p['details'].libraryDetails.isbn

instead.

Happy Mongoing!

About these ads

3 thoughts on “Nested dynamic attributes with Grails Mongodb Plugin

  1. Pingback: An Army of Solipsists » Blog Archive » This Week in Grails (2013-04)

  2. Pingback: Questa settimana in Grails (2013-04) - luca-canducci.com - Il blog di Luca Canducci: notizie, tips e nuove tecnologie dal mondo dell’IT.

  3. Indra

    As of grails 2.3.4 adding dynamic properties like above does not work with the mongodb GORM plugin. I am still trying to find the cause of the problem. The error I get is

    No signature of method: static org.grails.datastore.mapping.mongo.engine.MongoEntityPersister.createEmbeddedCacheEntryKey() is applicable for argument types ….

    The save works fine without adding any dynamic properties. As soon as you add a dynamic property it gives the above error.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s