Getting Started with MongoDB & MongoMapper
Clinton R. Dreisbach, Former Viget
As part of our NoSQL exploration, I’ve spent some time lately with MongoDB. MongoDB bills itself as a “schema-free document-oriented database.” In using MongoDB, I’ve found it to be an easy transition from RDBMS’s because of the way it organizes document-based data. Here’s the basics:
MongoDB has collections of data, not tables. Unlike CouchDB, which is also a document-oriented DB, Mongo has namespaces for data. These are schema-less, so any data could go in each namespace. In my practice, I’ve persisted objects of one class into each collection, not unlike ActiveRecord with MySQL or any other RDBMS.
MongoDB has indexes. Even though each collection has no schema, you can still index the data in a collection based off a field. Not all documents in a collection have to have this field.
Given the similarities between MongoDB and a relational database, you’d think it would be easy to use in Ruby in place of ActiveRecord, and you’d be right. John Nunemaker has created a gem called MongoMapper to work as an object mapper to MongoDB. Using MongoMapper, you can create model classes like so:
class Book include MongoMapper::Document key :title, String, :required => true key :author, String key :published_at, Date key :user_id, String timestamps! # HECK YES belongs_to :user many :chapters end
You’ll note several things here. Keys are defined in the model, like in a DataMapper model, although they aren’t defining a schema, only a mapping for this particular model. (If the difference seems subtle, that’s because it is: MongoMapper in many ways lets you treat MongoDB as a relational DB.) The keys can be typecast as I’ve done, although they don’t have to be. I’ve defined relationships to other models, and MongoMapper is smart about this. In the case of
many :chapters, it looks to see if the
Chapter class is embeddable. If so, it will embed
Chapter documents in my
Book document. If not, it will store them in their own collection.
Just because MongoMapper defines a document with keys, you don’t have to stick to the keys. Because collections are schema-less, you can add new attributes at will, like in this example:
book = Book.new(:title => "Moby Dick 2") # => #<Book _id: , title: Moby Dick 2, author: > book.author = "Dan Brown" book.update_attributes(:author => "J.K. Rowling", :isbn => '1-2345-6789-0', :amazon_score => 1.25) book.save book = Book.find_by_title("Moby Dick 2") # => #<Book _id: 4aafe487477a51f0e8000002, # title: Moby Dick 2, # author: J.K. Rowling, # isbn: 1-2345-6789-0, # amazon_score: 1.25>
You can see that I can set keys defined in the class with setters, but I can set any attribute through
MongoMapper’s API is roughly equivalent to ActiveRecord’s, allowing you to use in a Rails application with little difficulty. The only things I’ve had to do are define
human_name on model classes and define
new_record? on embedded documents.
The only other thing you need to know to get started with MongoMapper is how to tell it what database to use. All you have to do is set
MongoMapper.database. In my sample Rails app, I’ve put a file in
config/initializers/ that looks like this:
db_config = YAML::load(File.read(RAILS_ROOT + "/config/database.yml")) if db_config[Rails.env] && db_config[Rails.env]['adapter'] == 'mongodb' mongo = db_config[Rails.env] MongoMapper.connection = Mongo::Connection.new(mongo['hostname']) MongoMapper.database = mongo['database'] end
That should get you started! I’ve really enjoyed using MongoDB so far. For further information, checkout the MongoDB Ruby driver code, the MongoMapper code, and the code for my sample app on GitHub, and look out for more upcoming posts about how we’ve used MongoDB.