Rails Date Query for setting Expiration - ruby-on-rails

I'm creating a marketplace app where sellers can list items to sell. I want to set an expiry date so listings over 30 days old do not show on the site.
I found some similar examples online but can't get this query to work.
#listings = Listing.where('created_at <= ?', Date.created_at + 30.day)

You want to query items whose created_at time is >= the current date (Date.current) - 30 days (30.day). So the query should simply be:
#listings = Listing.where('created_at >= ?', Date.current - 30.day)
You can also replace Date.current with Time.now or DateTime.now.
UPDATE: as user3334690 mentioned in a comment, it's recommended that you make this a model method since it's something that should be in the Model layer:
# app/models/listing.rb
def self.not_expired
where('created_at >= ?', Date.current - 30.day)
end
# now in controllers you can do something like
#listings = Listing.not_expired

Related

How do you query a Model by parent ID & created at date

I'm trying to find all the records that have a specific channel_id, and were made less than 1 day ago. The query finds the records with the given channel_id but does not respect the date restriction. Instead, it returns records created at any date.
#discussions = Discussion.where('channel_id = ? and created_at > ?', current_user.subscription.pluck(:channel_id), 1.days.ago )
I expected to only get the Discussions that had relevant subscriptions channel_id, and were made less than 1 day ago. But instead, this query ignores the created at restriction but returns the Discussions that had the corresponding subscriptions channel ID.
#discussions = Discussion.where("channel_id IN (?) AND DATE(created_at) >= ?", current_user.subscription.pluck(:channel_id), (Date.today - 1.day))
OR
#discussions = Discussion.where("channel_id IN (?) AND created_at >= ?", current_user.subscription.pluck(:channel_id), (Date.today - 1.day).end_of_day)
Or
Discussion.where("DATE(created_at) >= ?", (Date.today - 1.day))
.where("channel_id IN (?)", current_user.subscription.pluck(:channel_id))
#discussions = Discussion.where("channel_id = :channel AND created_at > :date", channel: current_user.subscription.pluck(:channel_id), date: Date.yesterday )

Rails query: Compare calculation of two attribute values

I have a model, Product, which has both a :created_at timestamp and an :expiration_in_days attribute. Products are considered expired a certain number of days after their creation. How do I write a query that only returns products that have not expired?
Rails 4, Ruby 2.1, PG 0.17
I have been trying queries like this, with no success:
#product.rb
def self.not_expired
where('created_at + expiration_in_days * 86400 >= ?', Time.now)
end
I have a pure Ruby version that works fine, it's just slower:
#product.rb
def self.not_expired
select{ |p| (p.created_at.to_i + p.expiration_in_days * 86400) >= Time.now.to_i}
end
note, the 86400 calculation converts the the :expiration_in_days integer into seconds
Any pointers on more advanced Rails queries than the documentation (http://guides.rubyonrails.org/active_record_querying.html) would be very welcome as well.
Thanks in advance.
Try this:
def self.not_expired
where("created_at + (expiration_in_days * 86400)::text::interval >= ?", Time.now)
end
UPDATE: Add references
You can learn more about date and time function here.
Given your case has a special requirement, which is the value of expiration_in_days is a column in the table, we cannot use created_at + interval expiration_in_days day. Instead, we need to type cast its value to interval. But you can't type cast straight to an integer, that's why we cast it to text first.
A + B > C is true if A <= C - B
So, instead of trying to add the expiration time to created_at, subtract the expiration time from Time.now.
def expiration_threshold
Time.now - amount_of_time # I'm not sure what type to use
end
def self.not_expired
where( "created_at > ? ", expiration_threshold )
end
I've always been a little stumped about what type Rails/Ruby will want when dealing with various dates/times, you may have to play around.
I'm not sure if this would work but try something like this
def self.not_expired
where("TIMESTAMP created_at + INTERVAL '1 day' * expiration_in_days >= NOW()")
end

Get data from db relevant with today, yesterday, today -1.week, today -1.month

I have a object and a result table
Object has_many Results
Result belongs_to Object
Result has a column called kg.
I need to get for each object the kg data
where('DATE(created_at) = ?' Date.today AND Date.today - 1.day AND Date.today - 1.week AND Date.today - 1.month)
So my question is, which is my best option:
Get all the records for 1 month (date Between today and today - 1.month) and then get the result for today, yesterday, 1 week ago and 1 month ago)
today = result.where("DATE(created_at) BETWEEN ? AND ?", Date.today - 1.month, Date.today)
Or make 4 queries and get the data for each date?
today = result.where('DATE(created_at) = ?' Date.today)
y = result.where('DATE(created_at) = ?' Date.today - 1.day)
w = result.where('DATE(created_at) = ?' Date.today - 1.week)
m = result.where('DATE(created_at) = ?' Date.today - 1.month)
Depending on how you want to later use the results, it might make sense to query them individually using your second approach. That way, you don't have to further separate them in your resultset.
If you don't actually need the data you query in your first approach, it doesn't make sense to retrieve them from the database. You should then use another, more specific approach.
If your actual queries are more complicated that the one shown in the question or if you just want a single result set, it might make sense to use the following approach which just requires one query:
dates = [
Date.today,
Date.today - 1.day,
Date.today - 1.week,
Date.today - 1.month
]
resultset = Result.where('DATE(created_at) IN (?)', dates)
This query will result in a SQL query similar to this:
SELECT * from results WHERE DATE(created_at) IN ('2013-12-16', '2013-12-15', '2013-12-09', '2013-11-16');
You should be able to build an IN request.
result.where('DATE(created_at) IN (?)', [Date.today, Date.today - 1.day])
Which will retrieve the records which are in between the provided dates.

How to count "days with records" in Rails?

Rails adds and populates a created_at column for new records.
How can I use the to count the number of days that have records within a specified timeframe? (note: counting days, not counting records).
For example, say I have a Post model, how can I calculate how many days in the last year have a Post?
Since you asked for the ruby way, here it is:
Post.where('created_at >= ?', 1.year.ago).map { |p| p.created_at.beginning_of_day }.uniq.size
Update
You can put the following in your Post model
def self.number_of_days
where('created_at >= ?', 1.year.ago).map { |p| p.created_at.beginning_of_day }.uniq.size
end
Then in your controller you can do stuff like
#user.posts.number_of_days
Here's a more efficient way that delegates most of the work to the database (MySQL, not sure if it'll work on others):
Post.where('created_at >= ?', 1.year.ago).group('DATE(created_at)').length

Rails filter by date with MetaSearch

I have some records which I show in a view. They have start_date and end_date.
When I access the view, by default I want it only to show records who's dates are not expired.
Expired being defined as:
End date and start date <= Now, and
End date is later than the start date
I then want to have a checkbox that shows all records including the ones that have been expired.
How do I approach this?
In your controller action, you want to have this somewhere:
params[:search] ||= {} # make sure this is instantiated
# filter to show the expired records
if params[:include_expired] # check if the checkbox is checked
params[:search][:end_date_lte] = Date.today # end_date <= Now
params[:search][:start_date_lte] = Date.today # end_date <= Now
end
#records = params[:include_expired] ? RecordClass.where("end_date > start_date").search(params[:search]) : RecordClass.search(params[:search])
You could also bypass all the search things and just use a scope like this:
#records = params[:include_expired] ? RecordClass.expired : RecordClass.all # or whatever logic you need
# scope in the RecordClass
scope :expired, where("end_date > start_date AND end_date <= ?", Date.today)

Resources