我有一个带有以下型号的Rails4应用程序:
1. Systems (has many devices, has many parameters through devices)
2. Devices (belongs to a system, has many parameters)
3. Parameters (belongs to a Device)
4. Events (polymorphic - Systems, Devices and Parameters can have events)
创建事件时,会为布尔字段(在事件上)分配一个值。错误表示失败。
我的活动有一个范围,只显示失败的事件:
scope :failing, -> { where(okay: false).order('created_at desc') }
我可以按如下方式检索事件:
System.events.failing
Device.events.failing
Parameter.events.failing
我正在尝试返回系统列表,其中包括:
1. the most recent event for the system has failed
2. the most recent event for any of it's devices has failed
3. the most recent event for any parameters of it's devices have failed
我写了这个(可怕的)SQL查询,在控制台中执行时,将系统作为数组返回:
"SELECT * FROM Systems WHERE Systems.id IN (SELECT Devices.system_id FROM Devices WHERE Devices.id IN (SELECT Parameters.device_id FROM Parameters JOIN EVENTS ON Parameters.id=Events.eventable_id WHERE Events.okay ='f')) OR Systems.id IN (SELECT Devices.system_id FROM Devices JOIN Events ON Devices.id=Events.eventable_id WHERE Events.okay='f')")
我需要在System模型或类方法上定义范围,以返回“失败”的列表。系统。你能帮忙吗?
答案 0 :(得分:1)
一个可以简化操作的选项是将外键system_id添加到events表中。然后,您可以执行以下操作:
Event.where(system_id: [system_id], okay: false).order('created_at DESC')
注意:我不会为这个新的外键定义任何关系,我只是用它来过滤事件表。
答案 1 :(得分:1)
您可以混合连接和合并(以合并范围的WHERE子句):
class System
# 1. the most recent event for the system has failed
scope :with_failing_events, -> { joins(:events).merge Event.failing }
# 2. the most recent event for any of it's devices has failed
scope :with_failing_devices, -> { joins(devices: :events).merge Event.failing }
# 3. the most recent event for any parameters of it's devices have failed
scope :with_failing_parameters, -> { joins(devices: { parameters: :events }).merge Event.failing }
end
请注意,将哈希传递给joins
可以启用多个连接,或者至少在我正在使用的应用程序中工作(Rails 4.0.5 postgresql)。
要仅过滤最新事件(警告在postgres中不起作用,在其他适配器上未经测试),您可以附加到此查询:
System.joins(:events).merge(Event.failing).where events: { updated_at: 'MAX("events"."updated_at")' }
在任何情况下,您都可以将所有这些范围与OR
合并为:
class System
scope :failing, -> do
where(
[
with_failing_events,
with_failing_devices,
with_failing_parameters
].map(&:where_clauses).join(' OR ')
)
end
end