How do I sort a parent class based on an attribute of the child class?
I have what seemed to me a simple requirement: I want to sort a parent class based on an attribute of its child class.
class owner
has_many :tasks
end
class task
belongs_to :owner
end
Now I want to sort the owners based on the due date on their tasks, so that the owner that has the task with the "closest" due date is first, etc
I can do this in Ruby with array sorting, but my record sets are huge and i need to do this with AR / DBMS..
UPDATE:
As I am pondering this question part of the solution occurred to me, although I don't know how to implement in AR.To sort the owners based on their tasks, only a single task (per owner) is relevant, namely the one with the closest due date.
How开发者_如何学C would I use AR to fetch me all the owners, sorted by their task with the closest due date?
UPDATE 2:
Task.order("due_date DESC").maximum("due_date", :group => 'owner')
Will actually give me the owner objects, sorted in the right order as part of an ordered hash ( [owner] => "due_date"). Hooray! The only problem now is, how do I tell AR to eager_load all the associated tasks with the owner object. Because at this moment its firing a query everytime i call
owner.tasks.each do |t|
in my view.
Any help appreciated, i'm going nuts with this (simple??) problem,
ErwinOk, so in the end I figured it out myself.
Owner.includes(:tasks).joins(:tasks).order("tasks.due_date").group(:id)
will sort the Owners based on the closest due date in their tasks and will eager load all tasks, resulting in a single query.
Update: added the generated SQL in response to Mladen's comment below.
SELECT `owners`.* FROM `owners`
INNER JOIN `tasks` ON `tasks`.`owner_id` = `owner`.`id`
GROUP BY owner.id ORDER BY tasks.due_date DESC
Cheers.
Found this possible only by pure query:
Owner.find_by_sql('SELECT `owners`.*,
(SELECT `tasks`.`due_date`
FROM `tasks`
WHERE `owners`.`id` = `tasks`.`owner_id`
ORDER BY `tasks`.`due_date` DESC LIMIT 1) AS `last_due_date`
FROM `owners`
GROUP BY `owners`.`id`
ORDER BY `last_due_date` DESC;')
If need access to last_due_date
then add to Owner
model:
def last_due_date
if attributes['last_due_date']
attributes['last_due_date']
else
tasks.order(due_date: :desc).limit(1).first.due_date if tasks.length > 0
end
end
精彩评论