2015-02-18 1 views
1

У меня есть следующая трехуровневая иерархия моделей:Выберите объекты, где все дети не имеют внуков

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 

Ожидается, что все Parent s будет иметь несколько children, но каждый Child могут или не могут иметь какие-либо grandchildren.

Я хочу получить все Parent объектов, для которых у всех children нет grandchildren. Как я могу это сделать? Я бы предпочел Rails-way, и у меня есть squeel available; но я соглашусь на сырой SQL.

Бонусные баллы, если вы можете дать мне все Parent объекты, для которых не все children имеют не grandchildrenи все children имеют my_number < 5.

+0

Хочу отметить, что, конечно, , 'Parent.all.select {| p | p.children.all? {| С | c.grandchildren.empty? && c.my_number <5}} 'выполняет эту работу. Однако это не совсем эффективно. – Chowlett

ответ

1

После небольшого исследования я думаю, что получил решение, используя статьи group и having.

Вы можете добавить эту область в свою модель Parent. Обратите внимание, что нам нужно вручную определить joins, чтобы использовать LEFT JOIN вместо заданных по умолчанию INNER JOIN Rails. Дополнительная информация о 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 

И тогда вы можете использовать:

Parent.with_children_having_no_grand_children 

Что касается второго вопроса, предполагая, что :my_number является атрибутом хранится в БД, вы можете сделать:

Parent.with_children_having_no_grand_children.where(children: { my_number: 5 })) 
+0

Выглядит хорошо - позвольте мне немного поиграть с ним ... – Chowlett