Thứ Tư, 18 tháng 4, 2012

0 Grails in the Land of MongoDB

Grails in the Land of MongoDB



   



Groovy and Grails’ speed and simplicity are a perfect match to the flexibility and power of MongoDB. Dozens of plugins and libraries connect these two together, making it a breeze to get Grooving with MongoDB.


Using Grails with MongoDB


For the purpose of this post, let’s pretend we’re writing a hospital application that uses the following domain class.


class Doctor {   String first   String last   String degree   String specialty }

There are a few grails plugins that help communicate with MongoDB, but one of the easiest to use is the one created by Graeme Rocher himself (Grails project lead). The MongoDB GORM plugin allows you to persist all your domain classes in MongoDB. To use it, first remove any unneeded persistance-related plugins after you’ve executed the ‘grails create-app’ command, and install the MongoDB GORM plugin.


Using MongoDB GORM for Persistence


Step 1: Remove the hibernate dependency from the BuildConfig.groovy file.
Step 2:Install the MongoDB GORM


plugin grails install-plugin mongodb

Step 3:Edit DataSource.groovy to specify connection settings. You can entirely remove the content of this configuration file and replace it with the following, or you can specify different connection settings for each of your environments (dev, test, prod):


grails {   mongo {     host = “localhost”     port = 27107     username = ”user”     password=”secretpassword”     databaseName = “physicians”   } }

And that’s it! You can continue building your Grails application as usual by generating your controllers, views, services, etc.


GORM and Mongo


Although GORM was specifically created to persist and manipulate objects in a ‘relational’ way, you can still use some of its powerful features with MongoDB. These include:


Basic GORM:

Doctor.list() Doctor.get(5) Doctor.getAll(5,6,7) Doctor.list(max:5,sort:'first',order:'desc')Doctor.listOrderBySpecialty() 

Dynamic Finders:

Doctor.findByLast(“House”) Doctor.findAllBySpecialtyAndDegree(“Pediatrics”,”MD”)

Criteria Queries:

Doctor.withCriteria{ eq("specialty", "Pediatrics") }

Projections:

Doctor.withCriteria{ projections {countDistinct('specialty')} }

Query by Example:

def sampledoc = new Doctor(specialty:"Pediatrics") def docs = Doctor.findAll(sampledoc)

Some of the GORM features that are not supported include:


  • Criteria queries on associations
  • HQL
  • Groovy SQL

The Schemaless Universe in a GORM World


This is where it gets exciting: with MongoDB, you can make domain objects do things that are impossible with a relational database. For example, you can now programmatically add properties on the fly based on your run time needs.


Dynamic Attributes


Let’s suppose that you want to add a ‘city’ property to your domain object. You can easily accomplish this without having to modify the original domain class using the subscript operator:


def doc = new Doctor(first:”Julius”,last:”Hibbert”, degree:”MD”,specialty:”General Medicine”) // Add a city dynamically to this doctor doc["city"]="Springfield" // Saving it creates an instance of Doctor with the new field doc.save()

Dynamic Finders even work with dynamically added fields:


def d = Doctor.findByCity(“Springfield”)

Grails Identifiers and MongoDB Identifiers


A word of caution about ID generation. Grails automatically assigns an ID as an integer value. When a Doctor object is saved, the plugin creates an _id within the document as follows:


{ "_id" : NumberLong(1),...}

However, a normal insert in MongoDB, creates it as a BSON ObjectId:


{ "_id" : ObjectId("4f2ca36b25f39d121957cebb"),...}

An extra collection is also created, doctor.next_id, to keep track of the next id.


To make ID generation compatible with MongoDB, you need to declare your ID explicitly as an ObjectID in your domain class:


import org.bson.types.ObjectIdclass Doctor {     ObjectId id     …}

Groovy and MongoDB


You can use Groovy and its scripting and metaprogramming powers with the help of Gmongo.


Gmongo, written by Paulo Poiati, is a convenient wrapper around the MongoDB Java driver. Using Gmongo in your Groovy programs is the closest thing to writing commands directly into Mongo’s interactive shell.


In version 0.9.3, Gmongo can be instantiated by calling it through Grapes:


@Grab(group='com.gmongo', module='gmongo', version='0.9.3') import com.gmongo.GMongo // Instantiate gmongo.GMongo object // It defaults to localhost, but you can pass connection settings through the constructor def mongo = new GMongo() // Get a reference to the dbdef db = mongo.getDB("physicianfinder")

Now you have a database object, which you can attach to your collection to run MongoDB commands. Here’s an example of the syntax:


// [MongoDB database(usually just db)].[name of your collection].[command] // The following example returns the number of documents in a collectionInteger howmanydoctors = db.doctor.count()// Finding documents with Gmongo// Returns a com.mongodb.DBCursor classdef doctors = db.physician.find() // output each document to screen doctors.each {     println it } // Finds and returns one doctordb.doctor.findOne()// Find all doctors with last name 'Wu' db.doctor.find(last:"Wu") // Return last and specialty fields onlydb.hospitals.find([:],[last:1,specialty:1]) // Using a regex, find all docs whose last name starts with 'H'db.doctor.find([last: ~/^H/]) // Find and sort by specialty db.doctor.find().sort([specialty:1]) // Skip by 10, limit results by 5db.doctor.find(specialty:”Pediatrics”).skip(10).limit(5) 

Saving data to MongoDB


Writing documents to MongoDB with Groovy is again very similar to how it’s done through the shell. You can also pass multiple types of collections to the insert method and have it magically saved correctly as a document.


// Inserting a Doctor with two fieldsdb.doctor.insert([first:"Charles",last:"Darwin"]) // Specifying write concerndb.doctor.insert([first:"Gregory",last:"House"], com.mongodb.WriteConcern.NORMAL)// Inserting nested documents: def hospital = [name:"My City Big Hospital", city:"Chicago",                 buildings:[[address:"123 Main St.", name:"Feinberg",num_of_beds:300],                            [address:"345 Hope Ln.", name:"Galter",num_of_beds:100]]] // Saves the map of maps as a nested documentdb.hospitals.insert(hospital)// The following document is then created in the hospitals collection: { "_id" : ObjectId("4f2c98d53b7e58ee6ea885d5"),   "name" : "My City Big Hospital",   "city" : "Chicago",    "buildings" : [ { "address" : "123 Main St.", "name" : "Feinberg", "num_of_beds" : 300 },                    { "address" : "345 Hope Ln.", "name" : "Galter", "num_of_beds" : 100 } ] }

You can also use Groovy builders to generate your documents.


// Create a doctor with a JSON builder def builder = new groovy.json.JsonBuilder() builder.physicians {   doctor { first 'Julius' last 'Hibbert'                      // Named arguments are valid values for objects too              address( city: 'Springield',                     country: 'USA',                     zip: 02105, )            medicalschool: "Johns Hopkins University School of Medicine"            mensamember true            practice 'Dr. Julius Hibbert, M.D. Family Practice' }           } // Parse JSON string into a com.mongodb.util.JSON object def parsed = com.mongodb.util.JSON.parse(builder.toString()) // Save to Mongo db.physicians.insert(parsed)

Updating and removing are also straightforward:


// Look for 'Darwin' and update first name to 'Charles'db.doctor.update([last:"Darwin"],[$set:[first:"Charles"]]) // Delete all 'Darwins' db.doctor.remove(last:"Darwin")

If instead of collections you want to persist Plain Old Groovy Objects(POGOs), you can. Let’s start with our Doctor object from earlier:


class Doctor {      def first      def last      def specialty }

You can insert an instance of it by doing the following:


def doc = new Doctor(first:"Richard",last:"Kimble",specialty:"Pediatrics")

The doc instance also contains properties that we do not need to save into the document, so we remove the class and metaClass property before inserting:


// Get only the properties we want to save def doctor = doc.properties.findAll { !['class', 'metaClass'].contains(it.key) }// Insert it into the doctor collectiondb.doctor.insert(doctor)

And we can retrieve it back as a POGO as well:


// Ignore the _id property Doctor docFromMongo = db.doctor.findOne(last:"Kimble").findAll { it.key != '_id' }

And if you don’t have a POGO declared, you can use the magic of the Expando class to retrieve any document into a Groovy class:


Expando docFromMongo = db.doctor.findOne(last:"Kimble")

We’ve only covered the basics of interacting with MongoDB, but there are many more features you can leverage for making the most of the NoSQL world. To get started, check out the MongoDB GORM plugin.


— Ariel Gamino

0 nhận xét:

Đăng nhận xét

 

Everything for nosql Copyright © 2011 - |- Template created by O Pregador - |- Powered by Blogger Templates