# 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…
Author: Matthew Ford
Published: 2009-10-03

<p>I’m sure we’ve all heard the pros &amp; 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 <a href="https://couchdb.apache.org/">CouchDB</a>, <a href="https://1978th.net/tokyocabinet/">TokyoCabinet</a>, <a href="https://code.google.com/p/redis/">Redis</a> and recently <a href="https://www.mongodb.org/display/DOCS/Home">MongoDB</a>.</p><p>MongoDB (from “humongous”) is a scalable, high-performance, open source, schema-free, document-oriented database. Written in C++, MongoDB features:</p><ul><li>Document-oriented storage (the simplicity and power of <a href="https://www.mongodb.org/display/DOCS/BSON">JSON-like</a> data schemas)</li><li>Dynamic <a href="https://www.mongodb.org/display/DOCS/Querying">queries</a></li><li>Full <a href="https://www.mongodb.org/display/DOCS/Indexes">index</a> support, extending to inner-objects and embedded arrays</li><li>Query <a href="https://www.mongodb.org/display/DOCS/Database+Profiler">profiling</a></li><li>Fast, <a href="https://blog.mongodb.org/post/248614779/fast-updates-with-mongodb-update-in-place">in-place updates</a></li><li>Efficient storage of binary data <a href="https://www.mongodb.org/display/DOCS/GridFS">large objects</a> (e.g. photos and videos)</li><li><a href="https://www.mongodb.org/display/DOCS/Replication">Replication</a> and fail-over support</li><li><a href="https://www.mongodb.org/display/DOCS/Sharding">Auto-sharding</a> for cloud-level scalability</li><li><a href="https://www.mongodb.org/display/DOCS/MapReduce">MapReduce</a> for complex aggregation</li><li><a href="https://www.mongodb.org/display/DOCS/MongoDB+Commercial+Services+Providers">Commercial Support, Hosting, and Consulting</a></li></ul><p>“MongoDB bridges the gap between key-value stores (which are fast and highly scalable) and traditional RDBMS (which provide structured schemas and powerful queries).”</p><p>– the <a href="https://www.mongodb.org/">MongoDB</a> site.</p><p>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).</p><p>To be able to use this MongoDB goodness in my Rails app, I’m making use of the <a href="https://github.com/jnunemaker/mongomapper">MongoMapper</a> gem as it’s the most popular one (github forks and watchers metric). There are other ORMs which you can use with MongoDB, <a href="https://github.com/durran/mongoid">Mongoid</a> seems like a good alternative and the rest are documented here: <a href="https://www.mongodb.org/display/DOCS/Ruby+Language+Center">https://www.mongodb.org/display/DOCS/Ruby+Language+Center</a>.</p><h3 id="installation">Installation</h3><p>Download and run mongoDB <a href="https://www.mongodb.org/display/DOCS/Downloads">https://www.mongodb.org/display/DOCS/Downloads</a></p><p>Install the gems:</p><p>gem install mongo_mappergem install mongo_ext #the c extensions (for production)</p><p>Configure your environment, and remove AR:</p><p># config/enviroment.rb config.gem 'mongo'config.gem 'mongo_mapper' # remove ARconfig.frameworks -= [ :active_record, :active_resource ]</p><p>Add a config file for your database:</p><p># config/mongodb.yml base: &amp;base adapter: mongodb database: coolapp #These are needed to authenticate with your db #should it run on another server host: genesis.mongohq.com username: your-username password: your-password development: &lt;&lt;: *base test: &lt;&lt;: *base database: coolapp-test production: &lt;&lt;: *base</p><p>And add an initializer to setup MongoMapper and friends:</p><p># config/initializers/mongodb.rb include MongoMapper db_config = YAML::load(File.read(File.join(Rails.root, "/config/mongodb.yml"))) if db_config[Rails.env] &amp;&amp; db_config[Rails.env]['adapter'] == 'mongodb' mongo = db_config[Rails.env] MongoMapper.connection = Mongo::Connection.new(mongo['host'] || 'localhost', mongo['port'] || 27017, :logger =&gt; Rails.logger) MongoMapper.database = mongo['database'] if mongo['username'] &amp;&amp; mongo['password'] MongoMapper.database.authenticate(mongo['username'], mongo['password']) end end ActionController::Base.rescue_responses['MongoMapper::DocumentNotFound'] = :not_found # Used for image uploads # CarrierWave.configure do |config| #  mongo = db_config[Rails.env] #  config.grid_fs_database = mongo['database'] #  config.grid_fs_host = mongo['host'] || 'localhost' #  config.grid_fs_access_url = "gridfs" #  config.grid_fs_username = mongo['username'] #  config.grid_fs_password =mongo['password'] # end # It's also possible to define indexes in the the model itself; however, # a few issues are being worked out still. This is a temporary solution. # Comment.ensure_index([["story_id", 1], ["path", 1], ["points", -1]]) # MongoMapper.ensure_indexes! # Handle passenger forking. # if defined?(PhusionPassenger) #  PhusionPassenger.on_event(:starting_worker_process) do |forked| #   MongoMapper.database.connect_to_master if forked #  end # end</p><h3 id="getting-stuff-done">Getting stuff done</h3><p>Now I know what you might be thinking, ‘oh no not another ORM all my favourite gems won’t work!’, that is not the case! (*in some cases)</p><ul><li>Authentication – Devise <a href="https://github.com/plataformatec/devise">https://github.com/plataformatec/devise</a> (just configure the ORM option)</li><li>Image uploading – CarrierWave – <a href="https://github.com/jnicklas/carrierwave">https://github.com/jnicklas/carrierwave</a> with <a href="https://gist.github.com/264077">a Rails meta</a>l or Grip <a href="https://github.com/twoism/grip">https://github.com/twoism/grip</a></li><li>Formtastic works <a href="https://github.com/justinfrench/formtastic">https://github.com/justinfrench/formtastic</a></li><li>Many more – add your favourite in the comments should you know of good gems with MM support</li></ul><h3 id="usage">Usage</h3><p>These Models are stripped down but you get the idea. Let’s say you’re using Devise, then your User model could look like this:</p><p># models/user.rb class User # Class Configuration ::::::::::::::::::::::::::::::::::::::::::::: include MongoMapper::Document devise :authenticatable, :recoverable, :rememberable # Attributes :::::::::::::::::::::::::::::::::::::::::::::::::::::: key :email,String key :username,String key :comment_count, Integer key :encrypted_password, String key :password_salt, String key :reset_password_token, String key :remember_token, String key :remember_created_at, Time key :sign_in_count, Integer key :current_sign_in_at, Time key :current_sign_in_ip, String timestamps! # Validations ::::::::::::::::::::::::::::::::::::::::::::::::::::: RegEmailName = '[\w\.%\+\-]+' RegDomainHead= '(?:[A-Z0-9\-]+\.)+' RegDomainTLD = '(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)' RegEmailOk = /\A#{RegEmailName}@#{RegDomainHead}#{RegDomainTLD}\z/i validates_length_of :email, :within =&gt; 6..100, :allow_blank =&gt; true validates_format_of :email, :with =&gt; RegEmailOk, :allow_blank =&gt; true # Assocations ::::::::::::::::::::::::::::::::::::::::::::::::::::: many :comments end</p><p>And if that user had comments the model could look like:</p><p># models/comment.rb class Comment # Class Configuration ::::::::::::::::::::::::::::::::::::::::::::: include MongoMapper::Document # Attributes :::::::::::::::::::::::::::::::::::::::::::::::::::::: key :message, String key :message_html, String key :user_id,ObjectId key :username, String timestamps! # Validations ::::::::::::::::::::::::::::::::::::::::::::::::::::: validates_presence_of :message # Assocations ::::::::::::::::::::::::::::::::::::::::::::::::::::: belongs_to :user # Callbacks ::::::::::::::::::::::::::::::::::::::::::::::::::::::: before_create :set_username, :htmlify after_create :increment_comment_count private def increment_comment_count User.increment(user_id, :comment_count =&gt; 1) end def htmlify self.message_html = RedCloth.new(message).to_html end def set_username self.username = self.user.username end end</p><p>These are just to help kick-start your app development, for more complete examples see the open source apps and <a href="https://www.mongodb.org/display/DOCS/MongoDB+Data+Modeling+and+Rails">https://www.mongodb.org/display/DOCS/MongoDB+Data+Modeling+and+Rails</a> :</p><p>Open Source Ruby Applications using MongoDB (and MongoMapper)</p><p>NewsMonger – A simple social news application demonstrating MongoDB and Rails <a href="https://github.com/banker/newsmonger/">https://github.com/banker/newsmonger/</a></p><p>Oupsnow – A bugtracker in Rails/MongoMapper</p><p><a href="https://github.com/shingara/oupsnow/">https://github.com/shingara/oupsnow/</a></p><p>Watchtower – An example app built with Sinatra, Mustache and MongoDB</p><p><a href="https://github.com/kneath/watchtower/">https://github.com/kneath/watchtower/</a></p><p>Shapado – stackoverflow like question and answer site:</p><p><a href="https://gitorious.org/shapado/shapado">https://gitorious.org/shapado/shapado</a></p><p>mmmblog – a blogging engine by the same guys as Shapado (<a href="https://blog.ricodigo.com/">https://blog.ricodigo.com/</a>)</p><p><a href="https://gitorious.org/mmmblog">https://gitorious.org/mmmblog</a></p><p>More at <a href="https://wiki.github.com/jnunemaker/mongomapper/projects-using-mongomapper">https://wiki.github.com/jnunemaker/mongomapper/projects-using-mongomapper</a></p><h3 id="pro-tips">Pro Tips</h3><ul><li>MongoMapper uses a fork of the validatable gem – which has some differences with AR validations</li><li>Denormalization is needed to reduce round-trips to the DB</li><li>Change how you think of your data models – this is closer to an object store</li><li>The finders have nifty options, check out <a href="https://wiki.github.com/jnunemaker/mongomapper/whats-new">https://wiki.github.com/jnunemaker/mongomapper/whats-new</a> (0.5.7)</li><li>Don’t be afraid to drop down and use to the Ruby MongoDB driver directly <a href="https://www.mongodb.org/display/DOCS/Ruby+Tutorial">https://www.mongodb.org/display/DOCS/Ruby+Tutorial</a></li><li>Keep an eye out on the github commit feed as it’s a bit of a moving target still</li></ul><h3 id="notable-sites">Notable Sites</h3><ul><li><a href="https://blog.mongodb.org/">https://blog.mongodb.org/</a></li><li><a href="https://github.com/jnunemaker/mongomapper">https://github.com/jnunemaker/mongomapper</a></li><li><a href="https://groups.google.com/group/mongomapper">https://groups.google.com/group/mongomapper</a></li><li><a href="https://railstips.org/">https://railstips.org/</a> – Author of MongoMapper</li><li><a href="https://railscasts.com/episodes/194-mongodb-and-mongomapper">https://railscasts.com/episodes/194-mongodb-and-mongomapper</a></li><li><a href="https://mongohq.com/home">https://mongohq.com/</a> – MongoDB in the cloud</li><li><a href="https://github.com/derailed/mongo3">Mongo3</a> – visualize your cluster</li><li><a href="https://nosql.mypopescu.com/tagged/mongodb">https://nosql.mypopescu.com/tagged/mongodb</a></li></ul><h3 id="summary">Summary</h3><p>No migrations (yes!), storing arrays and hashes as attribute keys (sweet), and inplace updates. 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.</p>
