Les Hill github twitter facebook linked in archives
Posted January 18, 2010

I just released MongoDoc v0.2.1, with mongodb 1.3 and partial update support.

What is it?

MongoDoc is a simple and easy to use ActiveRecord-like object mapper for mongoDB in Ruby.

MongoDoc is also an extension of the Mongo Ruby Driver making it a snap to get Ruby in and out of mongoDB.

MongoDoc is not ActiveRecord for mongoDB. We do not have callbacks, nor do we have dynamic finders. We do have associations, named scopes, and other features.

MongoDoc is simple, easy-to-use, and fast. And it works with Rails (2.3.x at the moment, 3 soonish?).

MongoDoc is designed to work with document data, if you are looking to map relational data in mongoDB, you will have to look elsewhere.

Ruby objects in mongoDB

Lets just get right into it and save some Ruby objects in mongoDB!

1 class Contact
2   attr_accessor :name, :addresses, :interests
3 end
4 
5 class Address
6   attr_accessor :street, :city, :state, :zip, :phone_number
7 end

With MongoDoc, instead of saving JSON1, we can save an object directly:

 1 contact = Contact.new
 2 contact.name = 'Hashrocket'
 3 contact.interests = ['ruby', 'rails', 'agile']
 4 
 5 address = Address.new
 6 address.street = '320 First Street North, #712'
 7 address.city = 'Jacksonville Beach'
 8 address.state = 'FL'
 9 address.zip = '32250'
10 address.phone_number = '877 885 8846'
11 contact.addresses = [address]
12 
13 collection.save(contact)

We can query using the powerful mongoDB query syntax, and have it return Ruby objects:

1 results = collection.find('addresses.state' => 'FL')
2 hashrocket = results.to_a.find {|contact| contact.name == 'Hashrocket'}
3 puts hashrocket.addresses.first.phone_number

Take a look in the examples directory for more code.

Mapping Documents

MongoDoc provides ActiveRecord-like persistence, associations, named scopes, and validations (from Validatable) as well as a mongoDB query language (from Mongoid). MongoDoc also plays nicely with Rails.

MongoDoc::Document provides all these features as a mixin. A MongoDoc::Document can either be a top-level mongoDB document, or an embedded document contained within a top-level document. Top-level documents are stored in collections named after their class: Contact objects are stored in the ‘contacts’ collection (much like ActiveRecord).

Lets define a Contact document with an Address embedded document:

 1 class Address
 2   include MongoDoc::Document
 3 
 4   key :street
 5   key :city
 6   key :state
 7   key :zip_code
 8   key :phone_number
 9 end
10 
11 class Contact
12   include MongoDoc::Document
13 
14   key :name
15   key :interests
16   has_many :addresses
17 
18   named_scope :in_state, lambda {|state| {:where => {'addresses.state' => state}}}
19 end

Since a mongoDB document has no fixed schema, we define the composition of a document directly in our classes. Please note we do not specify types! We can also specify has_one or has_many associations.

Building and saving a document is easy:

1 contact = Contact.new(:name => 'Hashrocket', :interests => ['ruby', 'rails', 'agile'])
2 contact.addresses << Address.new(:street => '320 1st Street North, #712',
3   :city => 'Jacksonville Beach',
4   :state => 'FL',
5   :zip_code => '32250',
6   :phone_number => '877 885 8846')
7 contact.save

Now that we have some data, we can query using our named scope:

1 hashrocket = Contact.in_state('FL').find {|contact| contact.name == 'Hashrocket'}

And we can even perform partial updates:

1 hashrocket.addresses.first.update_attributes(:street => '320 First Street North, #712')

Installation

MongoDoc requires mongoDB v1.3 or later.

sudo gem install mongodoc

Configuration

Configure your database connection in ./mongodb.yml, you do not need one if you are running on localhost with the default port

name: test
host: localhost
port: 27017
options:
  auto_reconnect: true

You can change the location of the configuration file:

1 MongoDoc.config_path = './config/mongodb.yml'

1 The Ruby driver exposes an API that understands JSON.

blog comments powered by Disqus

Thanks to Tom Preston-Werner for the CSS layout, Webby for the blog renderer, and GitHub Pages for the blog hosting.