ฉันมีโมเดล 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, 13/07 และ 17/07 ระหว่าง 18.30 น. และ 19.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 ดังนั้นจึงรวมวันที่ตายตัวไว้แล้ว ดังนั้นหากฉันต้องการส่งคืนเซสชันทั้งหมดที่เริ่มต้นระหว่าง 18.30 น. ถึง 19.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
จะต้องอยู่ลำดับสุดท้าย -- การเก็บไว้ในแต่ละวงจะไม่ส่งคืนสิ่งใดเลย ขอขอบคุณทั้งสองท่านสำหรับความช่วยเหลือทั้งหมดของคุณ! ตอนนี้ฉันรู้สึกฉลาดขึ้นแล้ว