悶絶したのでメモ。DBはPostgresです
例えばcreated_atのようにtimestampsのフィールドを日付と時刻それぞれで条件かけて検索したい場合を考える。
filter :created_at
↑の書き方だとデータ型のおかげでdate_rangeであることは認識してくれるがこれだけだと時刻で検索ができない。
考え方としては、
- 日付と時刻を別々にfilterすることを考える
- 日付・時刻検索用のRansackerを定義する
- filterにRansackerを適用する
というアプローチが必要になる。
ransacker :created_date do
Arel.sql("to_char(created_at, 'YYYY-MM-DD')")
end
ransacker :created_time do
Arel.sql("to_char(created_at, 'HH24-MI-SS')")
end
↑モデルにransackerを定義する。
ransackerは検索条件が複雑な場合や、もともとカラムに入っていないものを検索したい場合に定義するとよいとのこと。この場合はcreated_dateとcreated_timeで日付と時刻を別々に定義する。
ちなみに、ブロックの中はArelで書かなければならないらしい。試しにsqueelで書いたら動作しなかった。
#created_times = -> { 日付の文字列形式の配列を返す }
filter :created_date, label: "作成日付", as: :date_range
filter :created_time_gteq, label: "作成時刻(最小)", as: :select, collection: created_times
filter :created_time_lteq, label: "作成時刻(最大)", as: :select, collection: created_times
↑ActiveAdmin側の例。日付に関してはcreated_dateのfilterで同名のransackerを引っ張ってきてdate_rangeの形で表示する。
時刻のフィルタリングに関してはcreated_time_gteq
とcreated_time_lteq
の2つのフィルターで範囲を定義する。
created_time_gteq
のfilterはプルダウンの表示項目にはcreated_timesという名前でラムダ式を使用している。
マスターから引っ張ってくるなりRubyでロジック書くなりして"0:00"から"23:30"までの文字列を配列で返す式を書けば良い。
プルダウンの値はフィルタリングする際の検索条件になる。
この場合created_time_gteq
のプルダウンに"8:00"
と書けばモデルの検索条件にcreated_time >= '8:00'
が追加される。もちろん、created_timeの定義はransackerから与えられて勝手に補ってくれる。
filter名の接尾辞で検索条件を指定できる。この場合gteqで≧、lteqで≦であり、他にもパターンがありそう。
このフィルターを使うと以下のようなSQLが発行される。
SELECT "tables".* FROM "tables"
WHERE (to_char(created_at, 'YYYY-MM-DD') >= '2015-06-01'
AND to_char(created_at, 'YYYY-MM-DD') <= '2015-07-01'
AND to_char(created_at, 'HH24-MI-SS') >= '04:30'
AND to_char(created_at, 'HH24-MI-SS') <= '08:30')
ORDER BY "tables"."id" desc LIMIT 30 OFFSET 0
ransackerは作りがかなりメタいうえにfilterとどう関連づくのかを理解するのが大変だった。ドキュメントの記述がいまいち理解できなかったため試行錯誤で強引に辿り着いたところがある。
公式にも様々な例があり、複雑なのでちょっと目を通しただけだとわけわからないが使いこなすと色々できそうである。