groupdateで一定の期間毎にリソースを集計する
はじめに
どのようなアプリでも、一定の期間で何かしらのイベントを走らせる、といったことは日常的に行われているものと思います。当然、その期間中のリソースの変化を集計したい、といった要望は多いことでしょう。そうした時に便利なのが、groupdateというgemでございます。
実装
groupdateをインストールします。
gem install groupdate
groupdateは所定のタイムゾーンに基づいて、日時のデータを扱います。そこで利用するタイムゾーンを確認する必要がございます。Railsでは以下のコマンドでタイムゾーンの一覧を取得することができます。
bundle exec rake time:zones:all
つらつらと一覧が出てくるので、その中から要件にあったタイムゾーンを確認の上、groupdateのオプションに与えます。下の例では東京を指定しております。ちなみに東京の場合はTokyoとなります。
irb(main):001:0> Groupdate.time_zone = "Tokyo"
=> "Tokyo"
また、mysqlを利用している場合は、mysqlにタイムゾーンのテーブルを別途ロードする必要があります。
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
groupdateをインストールすると、以下のメソッドが利用可能となります。
- group_by_*
-
- second
- minute
- hour
- day
- week
- month
- quarter
- year
- hour_of_day
- day_of_week
- day_of_month
- month_of_year
-
機能は読んで字の如く、対象のリソースに対して、引数に渡したattributeを参照することで、それぞれメソッド名の期間に応じた集計処理を行います。
irb(main):02:0> Post.group_by_week(:created_at).count
(1.4ms) SELECT COUNT(*) AS count_all, CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(DATE_SUB(created_at, INTERVAL ((1 + WEEKDAY(CONVERT_TZ(created_at, '+00:00', 'Asia/Tokyo') - INTERVAL 0 second)) % 7) DAY) - INTERVAL 0 second, '+00:00', 'Asia/Tokyo'), '%Y-%m-%d 00:00:00') + INTERVAL 0 second, 'Asia/Tokyo', '+00:00') AS convert_tz_date_format_convert_tz_date_sub_created_at_interval_1_weekday_convert_tz_created_at_00_00_asia_tokyo_interval_0_second_7_day_interval_0_second_00_00_asia_tokyo_y_m_d_00_00_00_interval_0_second_asia_tokyo_00_00 FROM `posts` WHERE (created_at IS NOT NULL) GROUP BY CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(DATE_SUB(created_at, INTERVAL ((1 + WEEKDAY(CONVERT_TZ(created_at, '+00:00', 'Asia/Tokyo') - INTERVAL 0 second)) % 7) DAY) - INTERVAL 0 second, '+00:00', 'Asia/Tokyo'), '%Y-%m-%d 00:00:00') + INTERVAL 0 second, 'Asia/Tokyo', '+00:00')
=> {Sun, 08 Jul 2018=>1, Sun, 15 Jul 2018=>0, Sun, 22 Jul 2018=>0, Sun, 29 Jul 2018=>3}
一見すると簡単なように見えて、意外とバグが入り込みやすいのが、時間を扱う処理でございます。それでいてテストが大変だったりもします。そういった処理をgemとして利用できるというのは、とても嬉しいことではないかなと存じます。
Ruby/Rails界隈のこうした文化は素晴らしいと思います。