Rails - create or update according to time range - ruby-on-rails

I have a table where I need to create a new entry or update if one of the columns is inside a range of dates (today).
For example, I have a table of shuttles registration with [name, time] where name represents someone and time is when he would like to take the shuttle on.
Each (name) can register at most once a day.
When someone registers, I would like to update an existing row (of the same day), if exists, or create a new one.
The following query extracts the relevant row, if exists:
Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day)
Tried to use first_or_create and equivalents but couldn't find the right syntax to apply the range query.
Any idea?
Thanks

Try this:
Shuttle.where(:name => params[:name]).
where(:time => DateTime.now.beginning_of_day..DateTime.now.end_of_day)).
first_or_create(:time => DateTime.now)
Reference: http://guides.rubyonrails.org/v3.2.13/active_record_querying.html#first_or_create

first_or_create doesn't apply in this scenario, because the condition doesn't make sense as the value of an update. It would be like saying "Find a shuttle where the time is any time on this given day, and if you don't find one then create one where the time is any time on this given day". That doesn't make sense: you need a specific time when you create a shuttle.
You need to think about the logic of what you want to do: it's not obvious to me at least.

If I understand you, I would do the following
#shuttle = Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day).first
#shuttle.columnname = "new value"
#shuttle.save
If you are doing multiple
#shuttles = Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day)
#shuttles.each do |shuttle|
shuttle.columnname = "new value"
shuttle.save
end

What about something like this?
#shuttle = Shuttle.where(
name: params[:name],
time: DateTime.now.beginning_of_day..DateTime.now.end_of_day
).first_or_initialize do |s|
s.attribute = value
s.save
end

Related

Call an instance by most recent dates rails

I have a set of blog post and I want to display them in order of most recent date. How can I call that method in the controller
I tried
#post1 = Post.last
but how would I do the 3 previous to this one?
This:
#post1 = Post.last
doesn't give the last Post in the way you think it does. If you check your logs you'll see something like this:
selector={"$query"=>{}, "$orderby"=>{:_id=>-1}}
so it will be "last in id order", not the last by date. If you want the last by some timestamp then you have to say so:
#post1 = Post.order(:created_at => :desc).first
You'd use some other date or timestamp instead of :created_at if you have one that better matches your intent.
Now, if you want the three previous to that, grab four and throw one away:
other_three = Post.order(:created_at => :desc).limit(4)[1.3] # or any of the ways
Or you could use offset to skip the first one:
other_three = Post.order(:created_at => :desc).offset(1).limit(3)
This should give you what you want:
#post1 = Post.last(4).reverse[0..-2]
Post.last(4).reverse will give you last 4 records in descending order. And, then with this: Post.last(4).reverse[0..-2] you are grabbing all but the last element which is what you want.
If you don't want the descending order, then you can just do this without the reverse part:
#post1 = Post.last(4)[0..-2]
Hope this helps.
Update
You can also do it this way:
#posts = Post.limit(4).order(updated_at: :desc)[1..-1]

Check if a datetime field occurs within a time period

I'm trying to check if an assignment's due_date (datetime data type) occurs within the start and end of the current week (and later on within the next 2 weeks). The logic I need help with is the 'due_date: Date.today.beginning_of_week..Date.today.end_of_week' section. If you could help me write those two queries, that would be awesome. Thanks in advance.
#student_assignments = StudentAssignment.joins(:assignment).where("DATE(assignments.due_date) = ?", Date.today.beginning_of_week..Date.today.end_of_week).all
You are very close. One way to achieve it is:
#student_assignments = StudentAssignment.joins(:assignment).where("Date(assignments.due_date) BETWEEN ? AND ?", Date.today.beginning_of_week, Date.today.end_of_week)

How to update thousands of records

I have to update an age column based on the value in a date of birth column. There are thousands of records to update.
How do I do this using rails?
Is this the right way to do it?
User.update_all(:age => some_method);
def some_method
age = Date.today.year - dob.year
end
Yes, update_all is the right method but no, you can't do it like this. Your some_method will only get called once to set up a database call (I assume you're persisting to a database). You'll then get an error because dob won't be recognised in the scope of the User class.
You'll need to translate your date logic to SQL functions.
Something like (for mysql):
User.update_all("age = year(now()) -
year(dob) -
(DATE_FORMAT(now(), '%m%d') < DATE_FORMAT(dob, '%m%d'))")
(NB. the date_format stuff is so that you get the right age for people who's birthdays are later in the year than the current date - see this question for more details)
The other option is to use one of the batches functionality in rails.
User.where(some_condition).find_in_batches do |group_of_users|
# some logic
# e.g. group_of_users.update_all(:age => some_logic)
end
This would lock your db for less time. Note that you should pretty much always update with a condition in mind. I can't think of many cases you would want to update an entire table every time something happens.
There are a few options checkout the rails docs or the api.
your query is right.
There are many way to update record in a batch/lot.
But, I think that your query is best. Because it is rails query that will support every condition for all database.
for updating more than one attributes
Model.update_all(:column1 => value1, :column2 => value2, ........)
or
you can use :
Model.update_all("column1 = value1, column2 = value2, ........")

Rails 3.2 Query - .exists?

I have about 500 outlets. Each outlet will be monitored a minimum of one time per day. I am trying to get a list of outlets that have been monitored each day.
I am having a problem with the query at the moment, any help is appreciated:
<% for outlet in #outlets %>
<% if Monitoring.exists?( :outlet_id => outlet.id, 'DATE(created_at) = ?', Date.today ) %>
The #outlets is an instance variable containing Outlet.all.
This query leaves me with a syntax error. What would be the correct way to do this? I'm trying to check that the Monitoring belongs to the Outlet, and that the Monitoring record was created today.
Also, I'm not entirely sure of the speed implications of this query. There will be a max of 2000 outlets on a page at one time (it's a dashboard, so they appear as either red or green dots).
Any help greatly appreciated.
You're getting a syntax error because you're trying to mix implicit-Hash and implicit-Array arguments:
Monitoring.exists?(:outlet_id => outlet.id, 'DATE(created_at) = ?', Date.today)
The exists? methods wants a Hash as its single argument. You want to use an SQL function in the query though, that means that you have to use the Model.where(...).exists? form:
Monitoring.where(:outlet_id => outlet.id).where('date(created_at) = ?', Date.today).exists?
That still leaves you hitting the database over and over again to light up your lights. You could precompute the whole mess with something like this:
counts = Monitoring.where('date(created_at) = ?', Date.today).count(:group => :outlet_id)
And then look use counts.has_key? outlet.id in your loop. Adding a where(:outlet_id => outlet_ids) (where outlet_ids are the IDs you're interested in) might make sense as well. You might be able to combine the count query with the query that is generating the #outlets too.

Matching day in datetime field from sqlite

In Ruby on Rails I'm doing something like:
Appointment.find( :first, :conditions => "staff_id = #{staff_id} AND datetimefield = #{datetime}")
... where datetimefield is of course, a datetime field. But, I only want rows where the date is equal to a given day, say 2/12/2011. I don't care about the time. What's an easy way to do this?
Thanks!
SELECT * FROM TheTable WHERE DATE(TheField) = '2010-02-12';
One possibility is to query for the value being between midnight and 23:59:59 on the target day, something like:
datetimefield = BETWEEN #{datetime.strftime('%Y-%m-%d 00:00:00')} AND #{datetime.strftime('%Y-%m-%d 23:59:59')}"
Another option (not recommended if your column is indexed, because you'll almost certainly invalidate the use of that index) could be something like this:
"strftime('%Y-%m-%d', datetimefield) = #{datetime.strftime('%Y-%m-%d')}"

Resources