【问题标题】:ActiveRecord find where many dates don't overlap with many other dates (Rails)ActiveRecord 查找许多日期不与许多其他日期重叠的位置(Rails)
【发布时间】:2016-03-14 09:39:59
【问题描述】:

我正在尝试做的事情非常复杂,但请耐心等待。我正在使用 Rails、ActiveRecord、Postgres。

事件有很多地点,有很多开始和结束时间。用户可以将自己分配给一个事件。

一个用户一次只能在一个地方。因此,在“将自己分配给事件页面”上,我只需要显示与用户已分配的事件没有重叠时间的事件。

我知道你可以通过加入来做到这一点,但我不知道怎么做。

型号:

class Event < ActiveRecord::Base
  has_many :locations
  has_many :event_times
  has_many :assignments
end

class Location < ActiveRecord::Base
  belongs_to :event
  has_many :event_times
end

class EventTime < ActiveRecord::Base
  belongs_to :location
  belongs_to :event
end

class User < ActiveRecord::Base
  has_many :assignments
end

class Assignment < ActiveRecord::Base
  belongs_to :user
  belongs_to :event
end

这总结了我正在尝试做的事情。我知道这很糟糕。

user_events = Assignment.where(user_id: current_user.id).pluck(:event_id)
blocked_event_times = EventTime.where(event_id: user_events)

blocked_query_string = ""

blocked_event_times.each do |bt|
  blocked_query_string += "NOT( event_times.start <= '#{bt.end}'::timestamp AND event_times.end >= '#{bt.start}'::timestamp ) AND "
end
blocked_query_string += "1=1"


return Event.includes(:event_times).where(blocked_query_string).references(:event_times)

我是否在正确的轨道上认为我应该做一些类似(伪代码)的事情:

@acceptable_times = EventTime.all LEFT JOIN user's_assigned_event_times

然后在@acceptable_times 中获取所有事件时间都存在的事件

【问题讨论】:

  • 不要使用end 作为 ActiveRecord 中的列名。 end 是 Ruby 中的保留字 - 如果您尝试在没有显式主题的情况下调用它,则会出现语法错误。这是对principle of least suprise 的极大违反。相反,您可能希望遵循时间戳使用的约定:ends_atstarts_at
  • 另外我会考虑将开始和结束时间存储在事件表中以减少复杂程度。
  • 问题是,一个事件可以有多个开始和结束时间。感谢您对保留字的提醒。

标签: sql ruby-on-rails postgresql activerecord


【解决方案1】:

我不完全确定数据格式或 EventTime 模型的工作方式,所以这里有另一个解决方案,它基于具有 start_timeend_timeAssignment 以及具有 start_timeend_time 的事件.

我猜你可以传入EventTime 的实例,而不是Event,下面是我的例子。

def overlaps_existing_commitment?(event)
  current_user.assignments.where("assignments.start_time < ? AND assignments.end_time > ?, event.start_time, event.end_time).any?
end

所以认为你可以循环遍历事件:

events_to_render = []

@events.each do |event|
  unless overlaps_existing_commitment?(event)
    events_to_render << event
  end
end

然后您可以在视图中使用events_to_render 数组。

我相信有很多更好的方法,但希望能提供一些灵感来找到您满意的解决方案!


其他几件事:

  • 我看的越多,就越相信 EventTime 模型是不需要的。通过将start_timeend_time 属性添加到其他模型,它会让您的生活更轻松。

  • 使用“?”在您的 SQL 查询中。

【讨论】:

  • 感谢您的回复。但是,一个事件可以有多个开始和结束时间。 (例如,早上的多日活动)我实际上做了与我第一次尝试类似的事情,循环遍历每个活动。但其性能是 O(N+1)。
猜你喜欢
  • 2015-01-08
  • 1970-01-01
  • 2013-01-18
  • 1970-01-01
  • 2013-10-03
  • 1970-01-01
  • 1970-01-01
  • 2023-01-09
  • 2012-10-24
相关资源
最近更新 更多