我有一个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_time
和exit_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
哦,我的entry
和exit
关联实际上可能是同一类,这意味着在查询中使用别名:
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。
答案 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“选择”。
如果一个或两个关联都是多态的,就会变得更加复杂。