How to find data between two dates in ruby on rails - ruby-on-rails

Query for finding data from now to 3 days ago not working.Although data is present but it cant retrieve data.
ViewsLog.where(:created_at=>Time.now..3.day.ago)
ViewsLog Load (0.5ms) SELECT "views_logs".* FROM "views_logs" WHERE ("views_logs"."created_at" BETWEEN '2015-12-09 08:15:21.586416' AND '2015-12-06 08:15:21.586513')
#<ActiveRecord::Relation []>

You have inverted the parameters - it should be
ViewsLog.where(:created_at=>3.days.ago..Time.now)

You can use it like this:
ViewsLog.where("date(created_at) between :start and :end", {start: 3.days.ago.to_date, end: Date.today})
This should work well for you.

Create a scope in the model ViewsLog
scope :record_between, lambda {|start_date, end_date| where("created_at >= ? AND created_at <= ?", start_date, end_date )}
or
scope :record_between, lambda {|start_date, end_date| where(created_at: start_date..end_date )}
Now it will be generic for any two dates data
Now you can do
ViewsLog.record_between(3.day.ago, Time.now)

Related

Using scope, How would I limit my Task model to only Tasks that are due today? I'm building a Tasks manager using Rails [duplicate]

I need to query comments made in one day. The field is part of the standard timestamps, is created_at. The selected date is coming from a date_select.
How can I use ActiveRecord to do that?
I need something like:
"SELECT * FROM comments WHERE created_at BETWEEN '2010-02-03 00:00:00' AND '2010-02-03 23:59:59'"
Just a note that the currently accepted answer is deprecated in Rails 3. You should do this instead:
Comment.where(:created_at => #selected_date.beginning_of_day..#selected_date.end_of_day)
Or, if you want to or have to use pure string conditions, you can do:
Comment.where('created_at BETWEEN ? AND ?', #selected_date.beginning_of_day, #selected_date.end_of_day)
I would personally created a scope to make it more readable and re-usable:
In you Comment.rb, you can define a scope:
scope :created_between, lambda {|start_date, end_date| where("created_at >= ? AND created_at <= ?", start_date, end_date )}
Then to query created between:
#comment.created_between(1.year.ago, Time.now)
Hope it helps.
Rails 5.1 introduced a new date helper method all_day, see: https://github.com/rails/rails/pull/24930
>> Date.today.all_day
=> Wed, 26 Jul 2017 00:00:00 UTC +00:00..Wed, 26 Jul 2017 23:59:59 UTC +00:00
If you are using Rails 5.1, the query would look like:
Comment.where(created_at: #selected_date.all_day)
This code should work for you:
Comment.find(:all, :conditions => {:created_at => #selected_date.beginning_of_day..#selected_date.end_of_day})
For more info have a look at Time calculations
Note: This code is deprecated. Use the code from the answer if you are using Rails 3.1/3.2
I ran this code to see if the checked answer worked, and had to try swapping around the dates to get it right. This worked--
Day.where(:reference_date => 3.months.ago..Time.now).count
#=> 721
If you're thinking the output should have been 36, consider this, Sir, how many days is 3 days to 3 people?
Comment.find(:all, :conditions =>["date(created_at) BETWEEN ? AND ? ", '2011-11-01','2011-11-15'])
I have been using the 3 dots, instead of 2. Three dots gives you a range that is open at the beginning and closed at the end, so if you do 2 queries for subsequent ranges, you can't get the same row back in both.
2.2.2 :003 > Comment.where(updated_at: 2.days.ago.beginning_of_day..1.day.ago.beginning_of_day)
Comment Load (0.3ms) SELECT "comments".* FROM "comments" WHERE ("comments"."updated_at" BETWEEN '2015-07-12 00:00:00.000000' AND '2015-07-13 00:00:00.000000')
=> #<ActiveRecord::Relation []>
2.2.2 :004 > Comment.where(updated_at: 2.days.ago.beginning_of_day...1.day.ago.beginning_of_day)
Comment Load (0.3ms) SELECT "comments".* FROM "comments" WHERE ("comments"."updated_at" >= '2015-07-12 00:00:00.000000' AND "comments"."updated_at" < '2015-07-13 00:00:00.000000')
=> #<ActiveRecord::Relation []>
And, yes, always nice to use a scope!
If you only want to get one day it would be easier this way:
Comment.all(:conditions => ["date(created_at) = ?", some_date])
there are several ways. You can use this method:
start = #selected_date.beginning_of_day
end = #selected_date.end_of_day
#comments = Comment.where("DATE(created_at) BETWEEN ? AND ?", start, end)
Or this:
#comments = Comment.where(:created_at => #selected_date.beginning_of_day..#selected_date.end_of_day)
There should be a default active record behavior on this I reckon. Querying dates is hard, especially when timezones are involved.
Anyway, I use:
scope :between, ->(start_date=nil, end_date=nil) {
if start_date && end_date
where("#{self.table_name}.created_at BETWEEN :start AND :end", start: start_date.beginning_of_day, end: end_date.end_of_day)
elsif start_date
where("#{self.table_name}.created_at >= ?", start_date.beginning_of_day)
elsif end_date
where("#{self.table_name}.created_at <= ?", end_date.end_of_day)
else
all
end
}
You could use below gem to find the records between dates,
This gem quite easy to use and more clear By star am using this gem and the API more clear and documentation also well explained.
Post.between_times(Time.zone.now - 3.hours, # all posts in last 3 hours
Time.zone.now)
Here you could pass our field also Post.by_month("January", field: :updated_at)
Please see the documentation and try it.

How to combine AND and OR operators using ActiveRecord?

I need to make SQL requests that would implement this behavior:
SELECT * FROM table WHERE (created_at > '2010/01/01' AND created_at < '2010/12/01') OR (created_at > '2012/01/01' AND created_at < '2012/12/31')
The goal is to have a request that returns results from a list of selected years (that might not be consecutive, we might have 2010 and 2012 without 2011).
Any clue how I would achieve that ?
You could try some SQL in the where method
Table.where(
'created_at between ? and ? or created_at between ? and ?',
date1,
date2,
date3,
date4
)
Also, as a useful hint for debugging these queries. Once you've got your query, you can call to_sql to see the literal SQL result.
User.where(admin: true).to_sql
Use Arel
t = User.arel_table
results = User.where(t[:created_at].gt( '2010-01-01').and(t[:created_at].lt('2010-12-01')).
or(t[:created_at].gt('2012-01-01').and(t[:created_at].lt('2012-12-31'))))
I prefer using:
where('created_at > ? AND created_at < ? OR created_at > ? AND created_at < ?', Time.new(2010,1,1), Time.new(2010,12,1), Time.new(2010,1,1), Time.new(2012,1,1), Time.new(2012,12,31))
Using the any_of gem:
Table.where.any_of(Table.where(created_at: t1..t2), Table.where(created_at: t3..t4))
t1, t2, t3 and t4, being the dates.

Dates and scopes are inconsistent

I have the following scope:
scope :this_month, :conditions => ["created_at >= ?", Date.today.beginning_of_month]
Which makes the SQL output of a.response_sets.this_month.to_sql:
SELECT "response_sets".* FROM "response_sets" WHERE created_at >= '2012-05-01'
But since today is actually June 1, that date seems wrong. So, I tried bypassing the scope and just doing a condition directly, like so:
a.response_sets.where(["created_at >= ?", Date.today.beginning_of_month]).to_sql
Which then, outputs:
SELECT "response_sets".* FROM "response_sets" WHERE created_at >= '2012-06-01'
Which is correct. So why is there a difference between doing Date.today.beginning_of_month in a scope and doing it directly in where?
When working with dates in scopes you should use a lambda so the scope gets evaluated every time it is called:
scope :this_month, -> { where("created_at >= ?", Date.today.beginning_of_month) }

Rails 3: How to merge queries or scopes for complex query?

I'm building an events app that is very simple, it has a title and start_date and end_date. I would like to filter my query by mixing some of the values, like: if the start_date has passed but the end_date has not, the event is active and should be displayed. If both dates have passed, it should be omitted, too. I think that scopes is the aswer, but I only was able to filter the records within the view using some methods shown below.
I really would like to filter the query that is passed to the controller (#events). I want to show all events that are active, have a future start_date, or a past start_date but are still in progress (Today's date is in range between start_date and end_date)
EDITED
I have made some scopes which return each part of the query. Chaining them actually substracts the results instead of merging them. So i have used this code and actually works do I do not know how solid or DRY this is. Looks kind of ugly to me... is this a decent way to merge queries in rails 3?
scope :active, where("active = ?", true)
scope :not_over_or_in_progress, lambda { where("start_date < ? AND end_date > ? OR end_date IS NULL AND start_date > ? OR end_date IS NOT NULL AND start_date > ?", Date.today, Date.today, Date.today, Date.today) }
scope :valid, not_over_or_in_progress.active.order("start_date DESC")
Try using scopes:
class Event < AR::Base
scope :active, lambda { |date| where("start_date < ? AND end_date > ?", date) }
scope :future, lambda { |date| where("end_date < ?", date }
...
end
# Console
> #active_events = Event.active(Date.today)
> #future_events = Event.future(Date.today)
See http://guides.rubyonrails.org/active_record_querying.html

How do I use .find for the last five days in ruby on rails?

Have a model called contact_email.date_sent
I want to be able to run a report which displays all those where the date_sent range is between date.today and date.today 5 days ago.
I assume I use something like
#send_emails = Contact_Email.find(:conditions=> ???)
But not clear what exactly is the best way. Thanks!
Try this:
ContactEmail.all(:conditions => ["date_sent >= ?", 5.days.ago.to_date])
This approach is faster than using BETWEEN clause( assuming date_sent is indexed)
Caveat:
Value of date_sent column should be less than current date.
Edit 1
To add an index in migration:
add_index :contact_emails, :date_sent
ContactEmail.find(:conditions => ['date_sent BETWEEN ? AND ?', Date.today, 5.day.ago.to_date])
If it's something you will use regularly, why not put a named_scope in the model:
named_scope :recent, lambda { |*args| {:conditions => ["date_sent > ?", (args.first || 5.days.ago)]} }
which will let you write:
ContactEmail.recent
for the last 5 days worth, or use the arg to specify your own time frame e.g. the last two weeks:
ContactEmail.recent(2.weeks.ago)

Resources