期間を指定するActiveRecordのクエリ
開始日と終了日がDateで与えられて「 開始日以降終了日まで」みたいなクエリをActiveRecordで書く時、
Event.where("date BETWEEN ? AND ?", start_date, end_date)
とかやるのだけれど、JOINした時などテーブル名が曖昧になって通らなくなったりするので、 ハッシュで書いてテーブル名の修飾をRailsに任せたい。
こういう場合はRangeが使えて、
Event.where(date: start_date...end_date)
とかできる。で、start_date
やend_date
がnil
の時には不等号で評価してほしいんだけど、
上2つの書き方だとどちらも動かない。
range_handler.rbを見る感じだと、Rangeの端の値がinfinity?
に対してtrue
を返せばよさそう。
ruby - Is there a way to express 'infinite time'? - Stack Overflowによると、DateTime::Infinity.new
で無限大の未来を表せるらしい。このクラスのドキュメントさがせなかったけど。
問題は無限大の開始日が指定されなかった時に代わりに使う無限大の過去をどう表すかで、DateTime::Infinity.new(-1)
はRangeに使うとエラーになってしまう。
[1] pry(main)> t = DateTime::Infinity.new(-1) => #<Date::Infinity:0x00007f852411a9d0 ...> [2] pry(main)> t...Date.today ArgumentError: bad value for range
とりあえずinfinity?
に対応できればよさそうなので、特異メソッドで対応してみた。
MIN_DATE = Date.new(2000, 1, 1) #ダミーなので適当に class << MIN_DATE def infinite? true end end start_date ||= MIN_DATE end_date ||= DateTime::INFINITY.new Event.where(date: start_date...end_date)
infinity?
でなく infinite?
なのは、begin_bind
を作るときに
query_attribute.rb
でラップされるから(?)。
github.com このコミット見る感じ、PostgreSQL以外では通用しないのかな?
Ruby 2.5, Rails 5.2, PostgreSQL 10.5 で 開始日、終了日どちらか or どちらも指定した場合ともに動きました。