Tips for working with MongoDB & Mongoid

As with anything I do on this blog, it's to make sure that I don't forget what I just learned. Below are some of the things that I've learned while using Mongoid on my current project, Be The Builders. Just to note, I'm working off the current beta version of Mongoid 2.0.0.beta4 and MongoDB 1.4.0.

  • Document != Embedded Document - This seem pretty obvious, but it didn't to me at first. Be The Builders is my first attempt at using a document oriented db, and since I came from a very heavy relational db world (read: business intelligence), this kinda screwed me up. Once you write embedded_in in your model, that model then becomes an embedded document and some things from documents don't apply any more:
    • Mongoid::Versioning
    • indexes - Well sorta, you'll see later that you can get around this
  • Don't create indexes on Embedded Docs! (sorta) - When I initially started to create my Mongoid models, I thought I could do something like this:
    class Post 
      include Mongoid::Document
      field :author
      field :text
      field :publish_date, :type => Date
      embeds_many :comments
      index [:author, :asc]
    end
    
    class Comment
      include Mongoid::Document
      field :author
      field :text
      field :publish_date, :type => Date
      embedded_in :post, :inverse_of => :comments
      index [:publish_date, :desc]
    end
    Unfortunately, I kept getting an InvalidCollection Error from Mongoid and for the life of me couldn't figure it out. I knew MongoDB allowed its users to create indexes on an embedded document. After a little testing, I came up with this instead:
    class Post 
      include Mongoid::Document
      field :author
      field :text
      field :publish_date, :type => Date
      embeds_many :comments
      index [:author, :asc]
      index ["comments.publish_date", :desc]
    end
    
    class Comment
      include Mongoid::Document
      field :author
      field :text
      field :publish_date, :type => Date
      embedded_in :post, :inverse_of => :comments
    end
    Lesson: If you want to create indexes on embedded documents, create the index in the parent document. Then use a string to denote what you want indexed.
  • Don't hesitate to drop into the lower level 'mongo' driver - There's a lot that Mongoid already does that's not even listed on its website, like cursors. Yet, I find that I want to do things like map reduce on a collection. Fortunately for us, the mongo driver is a requirement for Mongoid. So when you call Post.collection you actually get a collection object from the mongo driver which then we can run map_reduce. Chaining this example together, it would look something like this:
    m = 'function(){ this.tags.forEach( function(z){ emit( z , { count : 1 } ); } ); };'
    r = <<-REDUCE
    function( key , values ){
      var total = 0;
      for ( var i=0; i < values.length; i++ )
        total += values[i].count;
      return { count : total };
    };
    REDUCE
    Post.collection.map_reduce(m, r)
    
    There are plenty more functions that can be found in the mongo driver, but if you're looking for map_reduce, you can find it in the main docs and the RDoc.

That's all I've got for now. When I figure out more you can bet that I will be posting it here. MongoDB / Mongoid have been a joy to work with.

blog comments powered by Disqus