Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

eager loading not working with order() clause in rails

I am using this query to get my data

user = User.includes(:skills).order(user: :id)

it is working fine. but when i try to display skills by alphabetical order like below

user.skills.order(name: :asc)

It shows in logs that it goes in the database as order() is an activerecord method. It seems like eager loading is failing here because what's the point to use eager loading if it has to go in the database anyway.

Can anyone guide me what is a good way to do this.

like image 201
Mani Avatar asked Sep 25 '17 23:09

Mani


1 Answers

When you eager load associated records using .includes, you should access the association as it is. Otherwise, if you add more query conditions to the association, that will cause a new DB query.

There are a few ways how you can order the associated eager loaded records.

1. Add order condition to the main scope.

user = User.includes(:skills).order("users.id, skills.name ASC")

In this case, it won't work like include method works by default, making two queries. One query will be performed using 'LEFT OUTER JOIN' to fetch the associated records. This is equivalent to using the eager_load method instead of includes

user = User.eager_load(:skills).order("users.id, skills.name ASC")

2. Add order condition to association when you define it.

In this case whenever you access the association, the associated records will always be ordered by name.

class User < ActiveRecord::Base
  has_many :skills, -> { order(:name) }
end

3. Create another association with required order for using only in this particular case.

This allows you to avoid unnecessary conditions on the main association which is skills.

class User < ActiveRecord::Base
  has_many :skills_ordered_by_name, -> { order(:name) }, class_name: "Skill"
end

# usage
users = User.includes(:skills_ordered_by_name)

users.each do |user|
  # access eager loaded association
  user.skills_ordered_by_name
end

4. Set default order for the association model.

This will cause the condition to be applied to every association and query related to the associated model.

class Skill < ActiveRecord::Base
  default_scope { order(:name) }
end

5. Sort eager loaded records using Ruby code (not ActiveRecord query methods)

This approach is appropriate when there are not many records to sort.

users = User.includes(:skills)

users.each do |user|
  # sorting with Ruby's 'sort_by' method
  user.skills.sort_by(&:name)

  # or something like
  user.skills.sort { |one, another| one.name <=> another.name }
end
like image 165
chumakoff Avatar answered Oct 19 '22 16:10

chumakoff