我有一个Session
模型,它有一个:created_at
日期和一个:start_time
日期,两者都存储在数据库中:time
。我目前正在一张巨大的桌子上吐出一堆结果,并允许用户使用范围按单个日期和可选的时间范围过滤结果,如下所示:
class Session < ActiveRecord::Base
...
scope :filter_by_date, lambda { |date|
date = date.split(",")[0]
where(:created_at =>
DateTime.strptime(date, '%m/%d/%Y')..DateTime.strptime(date, '%m/%d/%Y').end_of_day
)
}
scope :filter_by_time, lambda { |date, time|
to = time[:to]
from = time[:from]
where(:start_time =>
DateTime.strptime("#{date} #{from[:digits]} #{from[:meridian]}", '%m/%d/%Y %r')..
DateTime.strptime("#{date} #{to[:digits]} #{to[:meridian]}", '%m/%d/%Y %r')
)
}
end
控制器看起来或多或少像这样:
class SessionController < ApplicationController
def index
if params.include?(:date) ||
params.include?(:time) &&
( params[:time][:from][:digits].present? && params[:time][:to][:digits].present? )
i = Session.scoped
i = i.filter_by_date(params[:date]) unless params[:date].blank?
i = i.filter_by_time(params[:date], params[:time]) unless params[:time].blank? || params[:time][:from][:digits].blank? || params[:time][:to][:digits].blank?
@items = i
@items.sort_by! ¶ms[:sort].to_sym if params[:sort].present?
else
@items = Session.find(:all, :order => :created_at)
end
end
end
我需要允许用户使用多个日期过滤结果。我将参数作为字符串格式的逗号分隔列表接收,例如"07/12/2012,07/13/2012,07/17/2012"
,并且需要能够在数据库中查询几个不同的日期范围和这些日期范围内的时间范围,并合并这些结果,例如所有7/12、7/13 和 7/17 下午 6:30 至 7:30 之间的会议。
我一直在到处寻找并尝试了几种不同的方法,但我不知道如何实际做到这一点。这可以使用范围吗?如果不是,最好的方法是什么?
我最接近的猜测看起来像这样,但它没有返回任何东西,所以我知道这是错误的。
scope :filter_by_date, lambda { |date|
date = date.split(",")
date.each do |i|
where(:created_at =>
DateTime.strptime(i, '%m/%d/%Y')..DateTime.strptime(i, '%m/%d/%Y').end_of_day
)
end
}
scope :filter_by_time, lambda { |date, time|
date = date.split(",")
to = time[:to]
from = time[:from]
date.each do |i|
where(:start_time =>
DateTime.strptime("#{i} #{from[:digits]} #{from[:meridian]}", '%m/%d/%Y %r')..
DateTime.strptime("#{i} #{to[:digits]} #{to[:meridian]}", '%m/%d/%Y %r')
)
end
}
另一个复杂之处是开始时间都存储为 DateTime 对象,因此它们已经包含一个固定日期,所以如果我想返回在任何日期在下午 6:30 到晚上 7:30 之间开始的所有会话,我需要弄清楚其他事情也。第三方负责数据,因此我无法更改数据的结构或存储方式,我只需要弄清楚如何进行所有这些复杂的查询。请帮忙!
编辑:
这是我通过结合以下 Kenichi 和 Chuck Vose 的建议得出的解决方案:
scope :filter_by_date, lambda { |dates|
clauses = []
args = []
dates.split(',').each do |date|
m, d, y = date.split '/'
b = "#{y}-#{m}-#{d} 00:00:00"
e = "#{y}-#{m}-#{d} 23:59:59"
clauses << '(created_at >= ? AND created_at <= ?)'
args.push b, e
end
where clauses.join(' OR '), *args
}
scope :filter_by_time, lambda { |times|
args = []
[times[:from], times[:to]].each do |time|
h, m, s = time[:digits].split(':')
h = (h.to_i + 12).to_s if time[:meridian] == 'pm'
h = '0' + h if h.length == 1
s = '00' if s.nil?
args.push "#{h}:#{m}:#{s}"
end
where("CAST(start_time AS TIME) >= ? AND
CAST(start_time AS TIME) <= ?", *args)
}
此解决方案允许我从多个不连续的日期返回会话,或者在完全不依赖日期的情况下返回一段时间内的任何会话,或者结合两个范围以按这些日期内的不连续日期和时间进行过滤。耶!
我忽略的一个重要点是该where
语句必须放在最后——将它放在每个循环中不会返回任何内容。感谢你们俩的帮助!我现在感觉更聪明了。