フィルターの実装(人情的な日付認識)
前々回からの続き。日付を期間として認識して比較する機能は、正規表現を使った文字列判定でやってみた。
- すでにfilter_sqlsテーブルには日付のフィルターの場合は、start_at_period(item)またはend_at_period(item)を呼び出して、最適な日付文字列に変換するように設定してある。
id | name | not_op | operator | wild1 | item | wild2 | option | sort |
---|---|---|---|---|---|---|---|---|
12 | ...と等しい(日時) | BETWEEN | start_at_period(item) | "AND #{end_at_period(item)}" | 12000 | |||
13 | ...と等しくない(日時) | NOT | BETWEEN | start_at_period(item) | "AND #{end_at_period(item)} OR #{column} IS NULL" | 13000 | ||
14 | ...より大きい(日時) | > | end_at_period(item) | 14000 | ||||
15 | ...以上(日時) | >= | start_at_period(item) | 15000 | ||||
16 | ...より小さい(日時) | start_at_period(item) | 16000 | |||||
17 | ...以下(日時) | <= | end_at_period(item) | 17000 |
- 後は、コントローラーにstart_at_period(item)とend_at_period(item)のメソッドの中身を書くだけだ。以下のようにしてみた。
private # 日付を表現する文字列を、期間と認識して、その期間の始まりを'%Y-%m-%d %H:%M:%S'形式で返す。 def start_at_period(str) case str when /(^|\s)\d{1,2}:\d{1,2}:\d{1,2}\s*$/ ## ~7:57:05 Time.parse(str) when /(^|\s)\d{1,2}:\d{1,2}\s*$/ ## ~7:57 Time.parse(str) when /(^|\s)\d{1,2}:$/ ## ~7: Time.parse(str + '0') when /(^|\/)\d{1,2}[\/\-]\d{1,2}\s*$/ ## ~3/3 Time.parse(str) when /^\d{4}[\/\-]?$/ ## 2007 Time.parse(str.sub(/[\/\-]$/,'') + '/1/1') when /^(\d{4}[\/\-]?)?\d{1,2}[\/\-]?$/ ## ~3, ~3/, 2007/3 Time.parse(str.sub(/[\/\-]$/,'') + '/1') else # nilが返る、rescue nilでエラーを無視する。 end.strftime('%Y-%m-%d %H:%M:%S') rescue nil end # 日付を表現する文字列を、期間と認識して、その期間の終わりを'%Y-%m-%d %H:%M:%S'形式で返す。 def end_at_period(str) case str when /(^|\s)\d{1,2}:\d{1,2}:\d{1,2}\s*$/ ## ~7:57:05 Time.parse(str)#.since(1) when /(^|\s)\d{1,2}:\d{1,2}\s*$/ ## ~7:57 Time.parse(str).since(1.minute) - 1.second when /(^|\s)\d{1,2}:$/ ## ~7: Time.parse(str + '0').since(1.hour) - 1.second when /(^|\/)\d{1,2}[\/\-]\d{1,2}\s*$/ ## ~3/3 Time.parse(str).tomorrow - 1.second when /^\d{4}[\/\-]?$/ ## 2007 Time.parse(str.sub(/[\/\-]$/,'') + '/1/1').next_year - 1.second when /^(\d{4}[\/\-]?)?\d{1,2}[\/\-]?$/ ## ~3, ~3/, 2007/3 Time.parse(str.sub(/[\/\-]$/,'') + '/1').next_month - 1.second else # nilが返る、rescue nilでエラーを無視する。 end.strftime('%Y-%m-%d %H:%M:%S') rescue nil end
それでは、実際に試してみる。
- 日付をちゃんと認識してくれるか?(フィルター条件:アップロード日時が「入力値」と等しい)
入力値 | 取得したSQL |
---|---|
2007、2007/ | →" created_at BETWEEN '2007-01-01 00:00:00' AND '2007-12-31 23:59:59'" |
4、4/ | →" created_at BETWEEN '2007-04-01 00:00:00' AND '2007-04-30 23:59:59'" |
2006/4、2006/4/ | →" created_at BETWEEN '2006-04-01 00:00:00' AND '2006-04-30 23:59:59'" |
14: | →" created_at BETWEEN '2007-04-19 14:00:00' AND '2007-04-19 14:59:59'" |
4/17 14: | →" created_at BETWEEN '2007-04-17 14:00:00' AND '2007-04-17 14:59:59'" |
- 4/17以下で、ちゃんと4/17の項目を含めるか?
入力値 | 取得したSQL |
---|---|
4/17以下 | →" created_at <= '2007-04-17 23:59:59' " |
4/17より小さい | →" created_at < '2007-04-17 00:00:00' " |
4/17以上 | →" created_at >= '2007-04-17 00:00:00' " |
4/17より大きい | →" created_at > '2007-04-17 23:59:59' " |
うまくいった!それにしても、人情的なSQLに翻訳するのは骨が折れる...。