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.
WHO AM I?
I'm Rimas Silkaitis, a mild mannered Ruby dev living in San Francisco. I like building stuff for the web and am particularly interested in machine learning. I also own a set of turntables.
Posts

