2013-09-04 2 views
0

Вот моя базовая модель иерархия:Rails сложных соединения запроса

class Product 
    has_many :inventories 
end 
class Inventory 
    belongs_to :product 
    has_many :inventory_events 
end 
class InventoryEvent 
    belongs_to :inventory 
end 

InventoryEvent экземпляры магазин изменение состояния + метки время для этих изменений, так inventory.inventory_events.last показывает текущее состояние.

У меня возникают проблемы с созданием запроса в модели продукта, который даст мне все запасы, текущее состояние которых составляет received.

То, что я прямо сейчас:

p = Product.first 
p.inventories.joins(:inventory_events).where(inventory_events: {state: 'received'}).all 

=> # Here I get back all Inventory that ever had the state 'received' but may 
not currently be 'received'. 

My SQL знание довольно ограниченно, но кажется, что какое-то предел для inventory_events: {} опции может работать, но не нашел способ сделать это.


Редактировать: Вот мое обходное решение на данный момент, чтобы показать свою конечную цель. Надеюсь, есть способ моделировать такой запрос.

class Inventory 
    def self.received_items 
    includes(:inventory_events).select {|i| i.current_state == 'received'} 
    end 
    def current_state 
    inventory_events.last.state 
    end 
end 

Product.first.inventories.received_items 
=> # Here I get the correct array of inventories 
+0

Я не думаю, что это идеальный ... но вы можете добавить 'HAS_MANY: inventory_events, через:: inventories' к модели продукта, а затем попробуйте 'p.inventory_events.where (state: 'received'). last.inventory'. Это, по крайней мере, правильное понимание проблемы? – pdobb

+0

@pdobb Ты в шаге. Однако это приведет только к возврату макс. 1 инвентаря. Позвольте мне подробно остановиться на идее: у продукта может быть 5 инвентарей, и у каждого инвентаря может быть 3 инвентаря, но только последний хронологический инвентарь_вент считается текущим состоянием инвентаря. Так, например, p.inventories.received_items должен возвращать все инвентаризации с последним инвентарным_эвентом.state == 'received'. Возможно, он возвращает 3 инвентаря здесь. –

+0

@pdobb Я только что добавил раздел редактирования выше, чтобы показать свою текущую работу, которая должна лучше проиллюстрировать мою цель здесь. –

ответ

0

Я нашел это немного SQL, и это работает для меня:

joins(:inventory_events).where("inventory_events.id IN (SELECT MAX(id) FROM inventory_events GROUP BY inventory_id) AND state = 'received'") 
0

Вы можете выполнить это с помощью областей и метода слияния. Области позволяют вам сохранять условия, когда они модульные. Метод слияния позволит вам выбрать Inventories, у которых есть InventoryEvent, который получен.

# call this to get the inventories for the product that have been recieved 
product.inventories.received 

class InventoryEvent 
    def self.received 
    where("state = ?", "received") 
    end 

    def self.most_recent 
    order("inventory_events.created_at desc").first 
    end 
end 

class Inventory 
    def self.received 
    joins(:inventory_events). 
    merge(InventoryEvent.received). 
    merge(InventoryEvent.most_recent) 
    end 
end 
+0

@streakchaser Не знаком с 'merge', мне нравится синтаксис, но все равно получаю все экземпляры Inventory, которые когда-либо имели состояние« получено ». Когда меняются состояния, я создаю новый InventoryEvent, чтобы оставить историю, поэтому экземпляр Inventory может иметь 2 или более InventoryEvents, и мне нужно соответствовать только самому последнему. –

+0

См. Правки выше. Вы можете ограничить инвентарь_вента самым последним с дополнительной областью и другим слиянием. – steakchaser

+0

'.first' возвращает запись, которая закручивает запрос, я попытался заменить' .limit (1) ', но тогда я получаю только 1 итоговую запись. –