MongoDB with MongoMapper and Ruby on Rails

I'm sure we've all heard the pros & cons of the NoSQL movement so these will not be covered here. I've been experimenting with a number of alternatives to RDBMS for a while such as CouchDB, TokyoCabinet, Redis and recently MongoDB.

MongoDB (from "humongous") is a scalable, high-performance, open source, schema-free, document-oriented database. Written in C++, MongoDB features:

MongoDB bridges the gap between key-value stores (which are fast and highly scalable) and traditional RDBMS (which provide structured schemas and powerful queries).

- from the MongoDB site.

The reason I've settled on MongoDB is (for me) it's been the most useful, more so than straight KV stores such as Redis and Tokyo. Technically CouchDB is very similar to MongoDB being a document store too, however MongoDB seems to 'gel better' (that's a technical term).

To be able to use this MongoDB goodness in my Rails app, I'm making use of the MongoMapper gem as it's the most popular one (github forks and watchers metric). There are other ORMs which you can use with MongoDB, Mongoid seems like a good alternative and the rest are documented here: http://www.mongodb.org/display/DOCS/Ruby+Language+Center.

Installation

Download and run mongod http://www.mongodb.org/display/DOCS/Downloads

Install the gems:

Configure your environment, and remove AR:

Add a config file for your database:

And add an initializer to setup MongoMapper and friends:

Getting stuff done

Now I know what you might be thinking, 'oh no not another ORM all my favorite gems won't work!', that is not the case! (*in some cases)

Usage

These Models are stripped down but you get the idea. Let say you're using Devise, then your User model could look like this:

And if that user had comments the model could look like:

These are just to help kick-start your app development, for more complete examples see the open source apps and http://www.mongodb.org/display/DOCS/MongoDB+Data+Modeling+and+Rails :

Open Source Ruby Applications using MongoDB (and MongoMapper)

NewsMonger - A simple social news application demonstrating MongoDB and Rails


Oupsnow - A bugtracker in Rails/MongoMapper
http://github.com/shingara/oupsnow/

Watchtower - An example app built with Sinatra, Mustache and MongoDB
http://github.com/kneath/watchtower/

Shapado - stackoverflow like question and answer site: 
http://gitorious.org/shapado/shapado

mmmblog - a blogging engine by the same guys as Shapado (http://blog.ricodigo.com/)
http://gitorious.org/mmmblog

More at http://wiki.github.com/jnunemaker/mongomapper/projects-using-mongomapper

Pro Tips

  • MongoMapper uses a fork of the validatable gem - which has some differences with AR validations
  • Denormalization is needed to reduce round-trips to the DB
  • Change how you think of your data models - this is closer to an object store
  • The finders have nifty options checkout http://wiki.github.com/jnunemaker/mongomapper/whats-new (0.5.7)
  • Don't be afraid to drop down and use to the Ruby MongoDB driver directly http://www.mongodb.org/display/DOCS/Ruby+Tutorial
  • Keep an eye out on the github commit feed as it's a bit of a moving target still

Summary
No migrations (yes!), storing arrays and hashes as attribute keys (sweet), and inplace updates (ftw). We're not going to use MongoDB to build transactional systems any time soon, but for the majority of web applications it's a perfect fit.


Bit Zesty - Ruby on Rails Development (and MongoDB) UK

Loading mentions Retweet

9 comments

Jan 04, 2010
Bit Zesty said...
gist formatting is a bit broken - working on it, you can see the entire gist http://gist.github.com/268948 .
Jan 04, 2010
Alex Popescu said...
This is an excellent addition to the set of MongoDB usecases I've published yesterday: http://nosql.mypopescu.com/post/316345119/mongodb-usecases
Jan 04, 2010
Bit Zesty said...
@alex you've got a good tumblr there going to add it in the links
Jan 05, 2010
Kyle said...
Great article, Matt! I'll be sure to note some of this on the MongoDB wiki.
Jan 05, 2010
John Nunemaker said...
This:

User.collection.update({"_id" => self.user_id}, {"$inc" => {"comment_count" => 1}})

Can now be:

User.increment(user_id, :comment_count => 1)

A little shorter and cleaner as of 0.6.10.

Jan 05, 2010
Matthew Ford said...
@john Thanks! I've updated the example and noticed pop was missing - pull request sent.
Jan 05, 2010
dc said...
great article!

do not forget magent, a queue system for mongodb
http://github.com/dcu/magent

Jan 16, 2010
Great article, Matt!
Mar 04, 2010
M. E. Patterson said...
Good stuff. I'll humbly throw my 2 mongomapper plugins into the ring also:

Tagging -- http://github.com/mepatterson/acts_as_mongo_taggable
Rating -- http://github.com/mepatterson/acts_as_mongo_rateable

Leave a comment...