I have the following three-level hierarchy of models:
class Parent < AR::Base
has_many :children
end
class Child < AR::Base
has_many :grandchildren
belongs_to :parent
attr_accessible :my_number
end
class Grandchild < AR::Base
belongs_to :child
end
It is expected that all Parent
s will have multiple children
, but each Child
may or may not have any grandchildren
.
I want to retrieve all Parent
objects for which all the children
have no grandchildren
. How can I do this? I'd prefer a Rails-way, and I have squeel available; but I'll settle for raw SQL.
Bonus points if you can give me all Parent
objects for which all the children
have no grandchildren
and all the children
have my_number < 5
.
After a bit of researching I think I got the solution by using group
and having
clauses.
You can add this scope to your Parent
model. Notice we need to manually define the joins
in order to use LEFT JOIN
instead of the default INNER JOIN
Rails generates. More info on SQL JOINS here.
class Parent < AR::Base
scope :with_children_having_no_grand_children, -> {
joins("LEFT JOIN `children` ON `children`.`parent_id` = `parents`.`id`
LEFT JOIN `grand_children` ON `grand_children`.`child_id` = `children`.`id`")
.group('parents.id')
.having('COUNT(grand_children.id) = 0 AND COUNT(children.id) > 0')
}
end
And then you can use:
Parent.with_children_having_no_grand_children
For the second question, assuming :my_number
is an attribute stored in DB, you can do:
Parent.with_children_having_no_grand_children.where(children: { my_number: 5 }))