Search code examples
ruby-on-railsruby-on-rails-4activerecordeager-loading

Rails includes method when to add


So I've read a lot about the rails includes method but I'm still a bit confused about what's the best situation to use it.

I have a situation where I have a user record and then this user is related to multiple models like client, player, game, team_player, team, server and server_center.

I need to display specific attributes from the related models in a view. I only need around 1-2 attributes from a specific model and I don't use the others.

I already added delegates for example to get the server.name from player I can use server_name but in this situation do I include all of the tables from which I need the attributes or is there something else I do because I only need a couple of attributes from the model.

My query is as follows at the moment:

@user_profile = User
               .includes({:client => [:player, :team_player => [:team]]}, 
                        :game, 
                        {:server_center => :server})
               .where(game_id: @master.admin.games)

Solution

  • Includes ensures that all of the specified associations are loaded using the minimum possible number of queries.

    Let say we have 2 models named User and Profile :

    class User < ActiveRecord::Base
      has_one :profile
    end
    
    class Profile < ActiveRecord::Base
      belongs_to :user
    end
    

    If we are iterating through each of the users and display the name of each user were name field resides in Profile model which has a association with User model, we would normally have to retrieve the name with a separate database query each time. However, when using the includes method, it has already eagerly loaded the associated person table, so this block only required a single query.

    without includes:

    users = User.all
    users.each do |user|
      puts user.profile.name # need extra database query for each time we call name
    end
    

    with includes

    # 1st query to get all users 2nd to get all profiles and loads to the memory
    users = User.includes(:profile).all
    users.each do |user|
      puts user.profile.name # no extra query needed instead it loads from memory.
    end
    

    Eager Loading is used to prevent N+1 query problems. basically it does left outer join and this plays an important role in speeding up request response or optimizing the queries. eg: if we are having huge amount users and if we want to iterate through those users and their corresponding profile. no of time which we will be hitting database will be equals to number of users. but if we are using includes it will keep all profile into memory later when we iterate through the users it will fetch from this memory instead of querying.

    Eager loading may not always be the best the cure for our N+1 queries for eg: if you are dealing with some complex queries preferably looks for some caching solutions like Russian Doll caching etc.. still both method has his own pros & cons end of the day it's up to you to determine the best approach.

    one useful gem which helps to detect N+1 query is bullet