在查询中使用属性或关联属性

时间:2019-03-05 11:28:14

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

我有一个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"

我想定义一个类级作用域或查找器方法,该方法将使我能够根据包含在ExtendedEvent内的模型是否与给定时间范围start_time..end_time重叠来查找这些模型。在明显损坏的伪代码中:

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。

2 个答案:

答案 0 :(得分:0)

仅当您在数据库中的字段中搜索时,才可以使用where,因此,在这种情况下,您必须使用纯红宝石(速度会很慢):

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

答案 1 :(得分:0)

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

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“选择”。

如果一个或两个关联都是多态的,就会变得更加复杂。