AggtiveRecord
NOTE: This gem is not really functional yet. I just like to commit things good and early.
An attempt to formalize the description of common aggregations across timestamped records.
I wrote this gem because I found myself writing, over and over again, these kind of ActiveRecord scope:
To get the number of messages sent in the last two weeks:
Message.where('sent_at >= ?', 14.days.ago).count
To get the rate of messages per week:
Message.count * SECONDS_PER_WEEK / (Message.order('sent_at ASC').last - Message.order('sent_at ASC').first)
Or how about the rate per week for messages sent in the past month (because it's interesting to know if someone has sent more messages recently)?
Message.where('sent_at >= ?', 1.month.ago) * SECONDS_PER_WEEK / (Time.now - 1.month.ago)
This is easily enough to formalize in one application, but I figured maybe it'd be useful to have a convention for every app that I ever do that tries to aggregate records by time.
Let's make some meta-magic
So for the first example, for counting messages in the past two weeks, something like:
Message.count_past_14_days
OK, admittedly that's not much nicer than the standard ActiveRecord chain. But for the second example, it could be simplified to this:
Message.rate_per_week
And for the third example:
Message.rate_during_past_month_per_week
AggtiveRecord is an extension that can be applied to ActiveRecord. Maybe you'll find it useful, maybe not.
This gem takes some inspiration from the highly useful groupdate.
Message.count_during_past_year_per_day
Message.group_by_day.where('sent_at >= ?' 1.year.ago)
Interaction with :has_many
Obviously, this is just speculative
@person.rate_of_messages_per_day
@person.messages_count_during_past_year_per_day
@person.messages_rate_per_day_during_past_year
@person.messages_count_in_past_year_by_day
@person.messages_count_in_past_year_by_day
Message.count_during_past_year
A standard verbose call:
@person.sum_word_count_from_messages_during_past_year_by_day
Under the hood query:
@person.egg.
collate(:sum, :word_count).
from(:messages).
in(:past_year).
by(:day)
Which translates to this in ActiveRecord:
@person.messages.
where('sent_at >= ?', 1.year.ago).
group_by_day(:sent_at).
sum(:word_count)
Result types
Scalars
@messages.count_during_past_year
@messages.rate_per_day_during_past_year
@messages.average_per_day_of_word_count_during_past_year
@messages.sum_of_word_count_during_past_year
Arrays
similar to groupdate
@messages.count_in_past_year_by_day
@messages.count_in_past_year_by_weekday
@messages.count_in_past_year_by_dayhour
@messages.count_in_past_year_by_hour
@messages.rate_per_hour_in_past_year_by_day
@messages.average_per_day_of_word_count_during_past_year_by_month
@messages.sum_of_word_count_during_past_year_by_day
Custom time periods
@messages.count_during_year(year: 2010)
@messages.count_during_years(years: 2008..2012)
@messages.count_during_day(day: '2012-12-01')
@messages.count_during_year_by_week(year: 2010, timezone: 'PST', first_day: 'Sunday')
count
rate_per average_per, of_numeric_value
sum, of_numeric_value
list_of_proper_nouns_during_past_year
counted_list_of_proper_nouns_during_past_year