Migrating from MongoMapper to Mongoid

I recently took on the task of migrating Ditty from MongoMapper to Mongoid and it actually wasn't that hard. Fortunately my test suite was adequate enough to catch most of the differences.

Note on Performance

I noticed a 400% improvement -- ~800ms to ~200ms -- in overall render times by moving from MongoMapper to Mongoid.

Updated: 2012-06012

Connecting

The first major step -- after installing the Mongoid Gem -- was to configure the database connection. This basically uses the Ruby Mongo API connection method in a configure block, which makes things pretty easy and consistant with other Mongo implementations.

So for MongoMapper I had it's custom connection setup (easier and less code, true):

MongoMapper.database = "<database name>"
if username && password
  MongoMapper.database.authenticate(username, password)
end

For Mongoid, I use:

Mongoid.configure do |config|
  config.database = Mongo::Connection.new.db("<database name>")
  if username && password
    config.database.authenticate(username, password)
  end
end

Mapping

Second was to update the mapper classes (Modules for Rails developers).

So for MongoMapper I had:

lib/ditty/post.rb

class Post
  include MongoMapper::Document

  key :title,    String
  key :body,     String
  key :tag_ids,  Array

  timestamps!

  many :tags, :in => :tag_ids
end

lib/ditty/tag.rb

class Tag
  include MongoMapper::Document

  key :name,   String, :unique => true

  # go go ugly reverse mapping code
  def posts
    Post.where(:tag_ids => self.id).sort(:update.desc).to_a
  end
  def destroy
    Post.where(:tag_ids => self.id).each do |post|
      post.tag_ids.delete(self.id)
      post.save!
    end
    super
  end
end

The refactor for Mongoid, looks like this:

lib/ditty/post.rb

class Post
  include Mongoid::Document
  include Mongoid::Timestamps

  field :title, :type => String
  field :body,  :type => String

  has_and_belongs_to_many :tags
end

lib/ditty/post.rb

class Tag
  include Mongoid::Document
  validates_uniqueness_of :name

  field :name,  :type => String

  has_and_belongs_to_many :posts
end

Simple enough.

Querying / Sorting

I did run in to some slight differences in querying and sorting.

Querying

MongoMapper: Post.where(:title => "foobar")

Mongoid: Post.where(title: "foobar")

I know, to similar to really matter, but good to know. Also, I believe the MongoMapper syntax actually works for Mongoid as well -- if you prefer it.

Sorting

MongoMapper: Post.sort(:created_at.desc)

Mongoid: Post.desc(:created_at)

Why?

I figured I would take a second to discuss why I decided to migrate. I had working code using MongoMapper, so why go through the trouble of migrating. Well, a couple of reasons.

  1. I had to write some custom methods to make the associations of the two collection function correctly (at at least I needed). MongoMapper hasn't really fleshed out it's associations in it's current iteration and Mongoid's is really strong, basically working the way it should.
  2. When running some basic performance testing, I found that my MongoMapper implementation is extreamly slow, especially where sorting is concerned. At the time of writing this, I haven't yet performance tested my application with Mongoid, but in reading through Mongoid's docs, I found that extensive performance testing has been done and then numbers are impressive. This was really the one that pushed me in to looking away from MongoMapper.
  3. Mongoid seems to be more widely accepted as the source for ActiveRecord like Mongo implementations.

I'm done, enjoy!

Published on in Ruby