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.
When you eager load associated records using .includes
, you should access the result collection as it is. Otherwise, if you apply more query conditions to the result, it will cause a new DB query. You cannot change the rules in the middle of the game.
There are a few ways how you can order the associated eager loaded records.
1. Add order condition for the association in 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. Define additional association with required order.
class User < ActiveRecord::Base
has_many :skills_ordered_by_name, -> { order(:name) }, class_name: "Skill"
end
Keep in mind that in this case the association should be preloaded and accessed by the defined association name:
# preloading the association
users = User.includes(:skills_ordered_by_name)
users.each do |user|
# accessing the association
user.skills_ordered_by_name
end
4. Set default order for the associated 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