Order query by day / month / year in rails - ruby-on-rails

Let me explain want I want to do, I've a table Clicks where all clicks for products are saved. And I want to make a graph of how many times something is clicked by customers.
This graph needs a day, month and year view.
What I need for the graph to work is a hash or an object.
My idea is that I'm getting something like:
data( 2012 => [ 01 => 100, 02 => 300, 03 => 50 etc.], 2011 => [ 01 => 100, 02 => 300, 03 => 50 etc.])
How can I retrieve all the data from a postgresql database in such an order? I would like to get the created_at field and extract the date from there. In a later process, all items from a particular month must be counted.
I'm struggling with this problem quite a bit, could someone push me in the right direction?

The snippet you posted is for MySQL - to do the same in PostgreSQL:
#clicks = Click.select("count(*) as c, date_part('year', created_at) as y, date_part('month', created_at) as m").
group("y", "m").
where({:user_id => current_user.id})
This will give you a hash in the format [year, month] => count. To get it in the format you wanted, you'll need to tweak the results a bit:
results = #clicks.inject({}) do |m, kv|
year, month = kv.first
clicks = kv.last
m[year] ||= {}
m[year][month] = clicks
m
end
Or a bit more concise:
results = #clicks.each_with_object({}) do |((year, month), clicks), m|
(m[year] ||= {})[month] = clicks
end

If you are using Postgree(or Heroku whit PG) can make this way:
by Month:
Consumer.all.order("extract(month from created_at) DESC")
by Day
Consumer.all.order("extract(day from created_at) DESC")
by Year
Consumer.all.order("extract(year from created_at) DESC")

Related

How to get weekly records on Rails?

I have Payment model with data and amount attributes. Now I need to get all the records for the current week on the page. I want to use some kind of pagination, e.g.: On the first page I get all records for the current week, on the second page - records from the previous week etc.
And on every page I need to get total amount for this week and average amount per day.
So, I have two questions:
How to get all the records for particular week on the page?
How to count amount for this week?
Now everything I've done was array with weeks and amounts
def self.count_by_week
raw_result = group_by_week_and_state.count
# {['2014-12-01-2014-12-07', 'foo'] => 100, ['2014-12-01-2014-12-07', 'bar'] => 100, '...' => '...'}
raw_result.each_with_object({}) do |(k, v), result|
result[k[0]] ||= {}
result[k[0]][k[1]] = v
end
end
def self.group_by_week_and_state
group("#{weekday_query(0)} || \'-\' || #{weekday_query(6)}").group('amount')
end
# build sql part for day offset of week (0 => mon, 6 => sun)
def self.weekday_query(offset)
"to_char(cast(date_trunc(\'week\', created_at) as date) + #{offset}, \'YYYY-MM-DD\')"
end
You could use the groupdate gem to accomplish this.
https://github.com/ankane/groupdate
Once you have successfully grouped your records, it's just too simple to get the sum for each groups.

ruby how to group by given date

I want to find signup count daily, for the date range say this month. so
starts_at = DateTime.now.beginning_of_month
ends_at = DateTime.now.end_of_month
dates = ((starts_at.to_date)..(ends_at.to_date)).to_a
dates.each_with_index do |date,i|
User.where("created_at >= ? and created_at <= ?", date, date.tomorrow)
end
So nearly 30 queries running, how to avoid running 30 query and do it in single query?
I need something like
group_by(:created_at)
But in group by if there is no data present for particular date it's showing nothing, but I need date and count as 0
I followed this:
How do I group by day instead of date?
def group_by_criteria
created_at.to_date.to_s(:db)
end
User.all.group_by(&:group_by_criteria).map {|k,v| [k, v.length]}.sort
Output
[["2016-02-05", 5], ["2016-02-06", 12], ["2016-02-08", 6]]
There is no data for 2016-02-05 so it should be included with count 0
I can't test it at the moment, but it should be possible to filter your date range and group it with a little help of your dbms like this:
User.select('DATE(created_at)').where("created_at >= ? and created_at <= ?", DateTime.now.beginning_of_month, DateTime.now.end_of_month).group('DATE(created_at)').count
Would this do?
starts_at = DateTime.now.beginning_of_month
ends_at = DateTime.now.end_of_month
User.where(created_at: starts_at..ends_at).group("date(created_at)").count
# => {Tue, 09 Feb 2016=>151, Mon, 08 Feb 2016=>130}
Note that you won't get any results for dates when there has been zero creations, so you might want to do something like this:
Hash[*(starts_at..ends_at).to_a.flat_map{|d| [d, 0]}].merge(
User.where(created_at: starts_at..ends_at).group("date(created_at)").count
)
Not pretty, but what happens there is you first create a hash with all dates in the range having zero values and merging the results from database into that hash.

Rails - order by hour

#events = Event.all(:order => "date DESC")
to order my events by date, a datetime column.
Is it possible to order them not by date, but only by hour? (I'm suing sqlite3 database)
Thanks!
For SQLite,
#events = Event.all(:order => "time(date) DESC")
Use that with care, because it ignores the date. (And see "Moments later" below.)
CREATE TABLE test (date datetime primary key);
INSERT INTO "test" VALUES('2011-01-01 08:00:00');
INSERT INTO "test" VALUES('2011-01-01 08:13:00');
INSERT INTO "test" VALUES('2011-01-01 09:23:00');
INSERT INTO "test" VALUES('2011-01-02 09:15:00');
Only one of the dates is on Jan 2.
sqlite> select * from test order by time(date) desc;
2011-01-01 09:23:00
2011-01-02 09:15:00
2011-01-01 08:13:00
2011-01-01 08:00:00
Moments later . . .
I realized you wanted to sort by hour, not by time. That questionable requirement takes a different expression, and sorts differently.
#events = Event.all(:order => "strftime('%H', date) DESC")
sqlite> select date from test order by strftime('%H', date) desc;
2011-01-01 09:23:00
2011-01-02 09:15:00
2011-01-01 08:00:00
2011-01-01 08:13:00
The last two rows are sorted correctly by hour, incorrectly by time.
Still later . . .
The OP deploys on Heroku, which doesn't support SQLite. To sort by hour descending, the OP probably needs something like
#events = Event.all(:order => "extract (hour from date) DESC")
And stop using one platform for development and a different platform for deployment.
Since Heroku uses PostgreSQL, you can use:
#events = Event.all(:order => "date_part(hour, date) DESC")
You'll have problems developing if you use SQLite3 locally and PostgreSQL for deployment - SQL is not consistent between platforms.

Query for all rows with created_at between certain hours?

I need to retrieve all rows from a table where the created_at timestamp is during a certain hour ... say 04:00 and 05:00. Anyone know how to do this?
RecordNameHere.find_by_sql("SELECT * FROM `table_name_here` WHERE HOUR(created_at) = HOUR('4:01:00')")
The MySQL documentation is awesome: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_hour
For multiple hour range (eg: records between in 4:00 to 6:00)
User.all(:conditions => "HOUR(created_at) BETWEEN ? AND ?", 4, 5)
For single hour use the following syntax:
User.all(:conditions => "HOUR(created_at) = ?", 4)
Note 1
The HOUR method returns the hour in 24 hour format. Provide the hour value accordingly.

Output array based on date (sales figures by day)

I have a table with a float called 'cost' and timestamp called'created_at'.
I would like it to output an array with the summing the costs for each particular day in the last month.
Something like:
#newarray = [] #creating the new array
month = Date.today.month # Current Month
year = Date.today.year # Current Year
counter = 1 # First Day of month
31.times do #for each day of the month (max 31)
#adding sales figures for that day
#newarray.push(Order.sum(:cost, :conditions => {:created_at => "#{year}-#{month}-#{counter}"}))
counter = counter + 1 #go onto next day
end
However this doesn't work as all the timestamps have a time as well.
Apologies in advance for the poor title, I can't seem to think of a sensible one.
You should be able to use code like the following:
sales_by_day = Order.sum(:cost,
:group => 'DATE(created_at)',
:conditions => ['DATE(created_at) > ?', 31.days.ago])
(0..30).collect { |d| sales_by_day[d.days.ago.to_date.to_s] || 0 }.reverse
This will sum the order cost by day, then create an array with index 0 being the last 24 hours. It will also take only one query, whereas your example would take 31.
To change the cut off date, replace 31.days.ago with an instance of the Time class. Documentation here: http://ruby-doc.org/core/classes/Time.html
Good luck!
This should work:
(Date.today.beginning_of_month..Date.today.end_of_month).map { |d|
Order.sum(:cost, :conditions => ['created_at >= ? AND created_at < ?', d, d+1])
}
Although I think you should try getting it using a single query, instead of making one for each day.

Resources