Automatic Eager Loading for Mongoid


Even in MongoDB, sometimes your models may still have reference relations. Like ActiveRecord, the popular MongoDB ORM, mongoid, provides [```#includes```](http://www.rubydoc.info/github/mongoid/mongoid/Mongoid%2FCriteria%3Aincludes) method to perform [eager loads](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations).
## Problems
However, it is a pain to specify them manually in codes. In most scenes, you just need the eager loading when you what to access the relation.

As described in [Joel Turkel's post](http://blog.salsify.com/engineering/automatic-eager-loading-rails) about [Goldiloader](https://github.com/salsify/goldiloader), the eager loading should neither be a duty of controllers (why should they care about how the data is fetched) nor be one of views (they just present your data!).

Eager loading is more likely a job that the model it self should handle.

## Better Practice
Just like Goldiloader for ActiveRecord, there can also be a plugin that makes mongoid do the eager loading automatically.

Say you have the following models for your blog site:

```ruby
class User
  include Mongoid::Document
  has_one :device
  has_many :posts
end

class Post
  include Mongoid::Document
  belongs_to :user
  has_and_belongs_to_many :tags
end

class Tag
  include Mongoid::Document
  has_and_belongs_to_many :posts, fully_load: true
end
```

When you call

```ruby
@user = User.find(id)
@posts = @user.posts.limit(10) # 

# access the tags
@posts.each do |post|
  puts post.title
  puts post.tags.map(&:name).join(', ')
end
```

Mongoid will perform 12 queries: 1 on users, 1 on posts and **10** on tags:

```javascript
db.users.find('_id' : id);
db.posts.find('user_id' : user_id);
db.tags.find('post_ids' : post0_id);
db.tags.find('post_ids' : post1_id);
...
db.tags.find('post_ids' : post9_id);
```

But as an experienced developer, you could easily figure out that the eager loading on ```:tag``` relation should be performed. In fact, adding ```#includes``` to the criteria is a default action, manually done by the developers.

*Why not make it a default to mongoid?*

When you access the tag relation and is about to query it in your db, perform an eager loading on ```:tag``` just before the query:

```ruby
# before query a relation
# the query loading method of a relation is
# Mongoid::Relations::Targets::Enumerable#unloaded_documents

def before_unloaded_documents
  owner = relation.base # get the relation owner
  metadata = relation.relation_metadata # get the relation metadata

  # DO THE EAGER LOADING
  # 
  #   Requires some trick to access the owners collection through the owner.
  #   Add an reference to the collection into model
end
```

Then when you run the previous ```@posts.each...```, it will results to only 3 quires, 1 for each:

```javascript
db.users.find('_id' : id);
db.posts.find('user_id' : user_id);
db.tags.find('post_ids' : {'$in' : post_ids});
```

Wow, you do not need to specify any eager loads. (The modified) Mongoid will do it automatically for you!

## Conclusion
Eager loads are performance boosters, and should be an default behaviour in most situations. The developers should be aware of them, but not repeatly code ```.includes(:...)``` just because you need it everywhere. 
I've written a gem [mongoload](https://rubygems.org/gems/mongoload) to make your mongoid an automatic eager loader. Check it on [GitHub](https://github.com/karloku/mongoload).