【问题标题】:Use an attribute or association attribute in a query在查询中使用属性或关联属性
【发布时间】:2019-03-05 11:28:13
【问题描述】:

我有一个 Rails Concern ExtendedEvent 用于封装与具有开始时间和结束时间的事件相关的逻辑,我的应用程序中有几个。目前看起来是这样的:

类中的用法通常如下所示:

class InAndOutEvent < ApplicationRecord

  include ExtendedEvent

  def configure_extended_event
    @ee_start_date_time = enter_date_time
    @ee_end_date_time = exit_date_time
  end
end

其中enter_date_timeexit_date_time 是模型属性。但是,另一种用法是这样的:

class InAndOutEvent < ApplicationRecord
  belongs_to: entry
  belongs_to: exit

  include ExtendedEvent

  def configure_extended_event
    @ee_start_date_time = entry.date_time
    @ee_end_date_time = exit.date_time
  end
end

哦,我的 entryexit 关联实际上可能属于同一类,这意味着在查询中使用别名:

  belongs_to :entry, class_name: "ThingWithDateTime"
  belongs_to :exit, class_name: "ThingWithDateTime"

我想定义一个类级别的范围或查找器方法,它允许我根据它们是否与给定的时间范围 start_time..end_time 重叠来查找包含 ExtendedEvent 的模型。在明显损坏的伪代码中:

require 'active_support/concern'
module ExtendedEvent
  extend ActiveSupport::Concern

  class_methods do
    def overlaps_with(time_range)
      where(ee_start_date_time: time_range)
        .or(self.where(ee_end_date_time: time_range))
    end
  end

  # ... instance methods as above omitted

end

我知道不可能使用这样的通用方法,但我的开始/结束时间是由模型属性设置的,所以感觉应该可以。如果我的属性始终在模型上,我可以用 alias_attribute 替换定义实例变量并使用它,例如:

alias_attribute :ee_start_time, :enter_date_time

但我认为这不适用于关联的属性。

有什么方法可以实现吗?我在 Ruby 2.4 上使用 Rails 5.1。

【问题讨论】:

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


    【解决方案1】:

    只有在从数据库中搜索字段时才能使用where,因此,在这种情况下,您必须使用纯 ruby​​(这会很慢):

    def self.overlaps_with(time_range)
      select do |instance|
        time_range.include?(instance.ee_start_date_time)
        || time_range.include?(instance.ee_end_date_time)
      end
    end
    

    另外,如果你需要一个 ActiveRecord_Relation 而不是数组(这样你以后可以使用where 之类的方法)你可以这样做:

    def self.overlaps_with(time_range)
      ids = select do |instance|
        time_range.include?(instance.ee_start_date_time)
        || time_range.include?(instance.ee_end_date_time)
      end.map(&:id)
    
      where(id: ids)
    end
    

    【讨论】:

    • 感谢您的回复。我应该强调的是,我将始终针对数据库字段进行搜索,但该字段可能与包含 ExtendedEvent 和连接的模型的表相关。
    【解决方案2】:

    您粘贴的模型似乎与查询数据库无关,因此我将使用自己的示例:

    time_range = [1.hour.ago, 1.hour.since]
    
    overlap_records = Model.where('? > models.end_time OR models.start_time < ?', *time_range)
    
    associated_overlap_recs = Assoc.joins(:model).where('? > models.end_time OR models.start_time < ?', *time_range)
    

    确保两个时间列是复合索引的。


    所以假设有:

    class InAndOutEvent < ActiveRecord::Base
      belongs_to :entry, class_name: "ThingWithDateTime"
      belongs_to :exit, class_name: "ThingWithDateTime"
    end
    
    class ThingWithDateTime < ActiveRecord::Base
    end
    

    你应该可以做这样的事情:

    # data "from the association"
    entry_time = thing_with_date_time_foo.date_time
    exit_time = thing_with_date_time_bar.date_time
    
    # to adhere to your api that accepts a `time_range` parameter
    time_range = [entry_time, exit_time]
    InAndOutEvent.joins(:entry, :exit).where(
      '? > thing_with_date_times.date_time OR thing_with_date_times_in_and_out_events.start_time < ?',
      *time_range
    )
    

    具体查询取决于table aliases ActiveRecord“选择”连接。

    如果其中一个或两个关联是多态的,情况会变得相当复杂。

    【讨论】:

    • 对不起,你完全正确,我的例子被破坏了。我已经修复了它们,希望能澄清一下。
    • entryexit 是多态的?
    • 我不想假设它们属于同一类,但实际上它们是肯定的。
    • 不,这与“多态”相反。 1) entry 可以是Fooexit 可以是Bar?或 2) f entryFooexit 也将是 Foo,反之亦然? 3) 在代码中,有多少个entry 类型?只有特定的“Foo”还是多种类型?
    • 就像我说的,如果可能的话,我更愿意假设它们不属于同一类。但在实践中,到目前为止,我唯一的示例将它们属于同一类。
    猜你喜欢
    • 2011-12-28
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多