【问题标题】:Retrieve a list of polymorphic relations in Rails 4在 Rails 4 中检索多态关系列表
【发布时间】:2014-08-13 19:02:44
【问题描述】:

我有一个带有以下型号的 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)

创建事件时,会为(事件上的)布尔字段分配一个值。 False 表示失败。

我的事件有一个范围,只显示失败的事件:

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 模型或类方法上定义一个范围以返回“失败”系统的列表。你能帮忙吗?

【问题讨论】:

    标签: sql ruby-on-rails ruby activerecord scope


    【解决方案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
    

    【讨论】:

    • 您可能想看看@jearlu 的回答。它将为您节省不必要且性能不佳的joins
    【解决方案2】:

    一个可以稍微简化事情的选项是将外键 system_id 添加到事件表中。然后您可以执行以下操作:

    Event.where(system_id: [system_id], okay: false).order('created_at DESC')
    

    注意:我不会为这个新的外键定义任何关系,我只是用它来过滤事件表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-14
      • 2013-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-23
      相关资源
      最近更新 更多