I have a product model and orders associated to the product. I wanted to analyze all orders from creation of product to current time. I was thinking to optimize, I would take products created at day, and current time as start and end points. The next step would be to automatically pull 10 equally spaced times between start and current time and place them in an array. For each one of these dates, query orders for the 10 dates provided.
Question is, is this the best approach to analyzing order data / performance on the query? If so, how do you pull the 10 dates in between the created at and current time range in Rails.
I have the following pseudocode --
Products.where(event_id: event)[Products.where(event_id: event).first.created_at.to_i..Time.now.to_i)].each_slide(10) do |p|
# Loop through orders of the 10 pulled days
Orders.where(product_id: p.id).each do |o|
# Add products to one of the 10 pulled days
end
end
Example Pseudocode:
1st Getting the last Product's created_at value
require 'date'
prod_date = Products.where(event_id: event).last.created_at
prod_date = prod_date.to_time.strftime("%Y-%m-%d %H:%M:%S")
2nd Getting last 10 records in products table based on prod_date & date_today.
date_today = DateTime.now.strftime("%Y-%m-%d %H:%M:%S")
Products.where('event_id ? AND (created_at BETWEEN ? AND ?)', event, prod_date, date_today).limit(10)
You can also arrange it if you want by adding e.g. .order("created_at DESC")
3rd Start to iterate with you orders data from the result above.
Orders.where(product_id: p.id).each do |o|
# Add products to one of the 10 pulled days
end
====================================
I understand want you plan to do. Honestly I haven't tried that.
But, my idea for that is, for ex. you have 10 data & you want to get 3 equally spaced values.
Why not try to iterate it by 3 (but get the first value).
Imagine this is your data: 1 2 3 4 5 6 7 8 9 10
get the first = 1
iterate the next by (3) = 4, 7, 10
Result = 1, 4, 7, 10
You may need to get the first & last data, depends on how many
3 equally spaced values
you want to get from total result count.
Related
I need to save a reference number every time I save a record of a certain model, the reference should be composed of 10 numbers, the first 8 numbers are related to the creator id and date, but the last 2 digits should be an incremental number starting at 00 and ending at 99, this count should be reset every single day.
For example:
Records created the same day:
SD01011800
GF01011801
MT01011802
...
GH01011899
------------------------------------------------------------------------------
Records created the next day:
SD02011800
GF02011801
MT02011802
...
GH02011899
Where the first 2 letters are the initials of a name, the next 2 are the current day, next 2 current month, next 2 current year, next 2 incremental number (from 0 to 99, reset daily)
Also every reference HAS TO be unique.
I'm missing the last two digits part, any idea on how to grant this ?
Thanks for reading.
Where the first 2 letters are the initials of a name, the next 2 are the current day, next 2 current month, next 2 current year, next 2 incremental number (from 0 to 99, reset daily).
As folks in the comments have pointed out, this assumes there is a maximum of 100 entries per day, and it will have problems in 2100. One is more pressing than the other. Maybe if you go over 100 you can start using letters?
Also every reference HAS TO be unique.
For globally unique identifiers UUIDs, Universally Unique IDentifiers, are generally the way to go. If you can change this requirement it would be both simpler (databases already support UUIDs), more robust (UUIDs aren't limited to 100 per day), and more secure (they don't leak information about the thing being identified).
Assuming you can't change the requirement, the next number can be gotten by adding up the number of existing rows that day.
select count(id)
from stuff
where date(created_at) == date(NOW());
However there is a problem if two processes both insert a new record at the same time and get the same next number. Probably highly unlikely if you're expecting only 100 a day, but still possible.
Process 1 Process 2 Time
select count(id) ... 1
select count(id) ... 2
insert into stuff ... 3
insert into stuff... 4
A transaction won't save you. You could get an exclusive lock on the whole table, but that's dangerous and slow. Instead you can use an advisory lock for this one operation. Just make sure all code which writes new records uses this same advisory lock.
Stuff.with_advisory_lock(:new_stuff_record) do
...
end
Alternatively, store the daily ID in a column. Add a database trigger to add 1 on insert. Set it back to 0 with a scheduled job at midnight.
I will assume your class is named Record and it has an attribute called reference_number.
If that is the case, you can use the following method to fetch the two last digits.
def fetch_following_two_last_digits
if Record.last.created_at < Time.current.beginning_of_day
"00"
else
(Record.last.reference_number.last(2).to_i + 1).to_s
end
end
Also assuming you never reach 100 records a day. Otherwise, you'd end up having three last digits.
This is more of a brains storm question
I would like to run something like
Student Model
id
name
age
so on...
GameStatus Model
id
game_id
student_id
status
created_at
Student has many game_statuses.
so every time a student opens a game it is added to the game_status model
I want to send little messages like... Congratulation! You have completed 5 challenges in a row.
So the marks are 5, 10, 15, 20 and 30.
It's pretty simple till here. I just loop through students and then loop through their game_statuses and if the count comes up to one of those marks I'll send a message.
But I want to make sure they hit those marks in a row.
As in 5 in the last five days or 10 in the last 10 days not broken.
So 3 times the week after last 1 time last week and 1 time this week will return 5 but it should not trigger the reward.
I was thinking of looking into the created_at date and check if they are in a array of dates such as [today, today -1, today -2, today -3, today -4]
#student = Student.find(param[:id])
#games = #student.game_statuses
total_games = []
for game in #games do
if game.status = 'completed'
total_games += game.id
end
end
return total_games.count
Would it be possible to achieve this in a more elegant way?
I have a view that displays uses a five element array to display some numbers where the elements relate to mon,tue,wed,thur,fri
[100,200,50,300,200]
this is built ( not very cleanly) by the method
def this_weeks_sales
x = self.sales.where(sale_date: (Date.today.beginning_of_week..(Date.today.beginning_of_week + 5)))
.group("DATE(sales.sale_date)").sum("sales.amount").sort.to_h.values.in_groups_of(5,0)[0]
end
If there are no sales for a day it should have '0' as that element and
always have 5 elements for mon-friday
I've run some tests and it's not working as I thought it would, if there is a £100 sale for tuesday but nothing for monday then I get the array [100,0,0,0,0] instead of the expected [0,100,0,0,0] i.e the first sale of the week will always be element[0]
I don't want to change all my views, how can I get the desired output?
self.sales.where(sale_date: (Date.today.beginning_of_week..(Date.today.beginning_of_week + 5)))
.group("DATE(sales.sale_date)").sum("sales.amount").sort.to_h
returns a hash e.g from the example above if there's only a sale on tuesday {Tue, 28 Jul 2015=>100}
ETA: You're grouping and sorting correctly, but then transforming the sorted array back into a hash and pulling the (unordered) values. You just need to leave it as an array and map it to the sum:
sales.where(sale_date: Time.zone.now.all_week).group("DATE(sales.sale_date)").sum(:amount).sort_by{|date,sum| date}.map{|date,sum| sum}
Edit 2: If you want to get 0 for dates that don't exist in the database, you'll have to loop through the desired dates:
daily_sale_totals = sales.where(sale_date: Date.today.all_week).group("DATE(sales.sale_date)").sum(:amount)
Date.today.all_week.map{|date| daily_sale_totals[date] || 0}
I have the an Order model in the following
If today is 5/3
and I want to sum the previous 3 months data of order, how to do it ?
I mean I want to show the 2/1 ~ 4/30 excluding the orders in May.
If today is 2014/4/20, and I want to show the sum of previous 3 weeks data. 2014/2/1~2/15
How to do it in Rubic way ?
You want something along the lines:
date = DateTime.now.utc
Order.where('created_at >= ? and created_at <= ?', date.beginning_of_month, date.utc.end_of_month).sum('price')
Where price is the column you want to sum.
You can reuse the logic of #Santosh in order to get the dates you want =)
start_date = 3.months.ago.beginning_of_month
end_date = 1.month.ago.end_of_month
You can write your query based on these dates. Same logic can be applied for weeks also.
I want to create an array of the number of items created each hour, each day.
I'm tracking how people are feeling, so my model is called TrackMood It just has a column called mood and the timestamps.
If I do
TrackMood.where(mood: "good").group("hour(created_at)").count
I get something like
{11=>4, 12=>2, 13=>2, 15=>1}
I've got 2 issues here
1 How do I add the day into this so it doesn't just add the items created yesterday at 11 o'clock to the items added today at 11 o'clock?
2 How do I make sure it says 0 for hours when nothing is created?
1) Instead of grouping on just the hours part of the date you'll need to group part of the date that is relevant i.e. the date up to the hours and not including anything more specific than that. E.g.
TrackMood.where(mood: "good").group("date_format(created_at, '%Y%m%d %H')").count
2) You're always going to get a hash back from this call even if it doesn't find any groups. If you want to check how many groups there are you can call .size or .count on it.
For PostgreSQL you can use date_part
SO-post - Rails & Postgresql: how to group queries by hour?