I am creating a filtering partial view, where user can pick a from-date and a to-date using a calendar. These dates are used then within model scope to perform SQL Where clause in database query. If a user does not pick one of dates, the default value should be assigned: minimal available date for from and maximal for to.
unless params[:from].blank? and params[:to].blank?
from = begin Date.parse(params[:from]) rescue ??? end
to = begin Date.parse(params[:to]) rescue ??? end
#model_instances = #model_instances.start_end from, to
end
(...)
scope :start_end, -> (start_date, end_date) { where('(:start_date <= "from" AND "from" <= :end_date ) OR' +
'(:start_date <= "to" AND "to" <= :end_date ) OR' +
'("from" <= :start_date AND :end_date <= "to")',
{start_date: start_date, end_date: end_date}) }
The from and to model Date attributes are also database fields in related table.
In Rails, Date class has a family of methods beginning_of (day, week, month, etc.), but there are no methods such beginning_of_time, end_of_time, Date.min, Date.max.
Are there any methods to obtain the minimal and maximal dates?
You could skip the condition on start and end dates if no params is given:
if params[:from].present? and params[:to].present?
#model_instances.start_end(params[:from], params[:to])
end
And then you will get results not depending on dates since no from and/or end dates have been filled.
You could compare ranges of dates to your model's values and setup default values if params not passed.
#setup default if desired, or you can skip if params are blank
from = params[:from] || default_from
to = params[:to] || default_to
#model_instances.start_end(from, to)
and your scope would look something like this if you used date ranges for activerecord
scope :start_end, ->(start_date, end_date){where(from: start_date..end_date, to:start_date..end_date)}
Related
I have a Range model which has a start_range and an end_range column.
My range.rb model:
class Range < ActiveRecord::Base
validate :range_valid
protected
def range_valid
range = Range.all
range.each do |f|
if (f.start_range..f.end_range).overlaps?(self.start_range..self.end_range)
self.errors.add(:base, 'range is already alloted')
break
end
end
end
end
This code takes the start_range and end_range (say 100 and 500) and matches all the database records if any range overlap (or say the two ranges must be completely exclusive ) with the range which the user have entered.
This code is working fine but this code is not feasible if there are millions of records stored in the database
Can anyone please tell me how can I match the overlapping of the range without using loop and fetching all the records by Range.all so that the code should be feasible for real time.
You can easily query Range to check if an existing range overlaps with the given range.
Range.where("end_date >= ?", start_of_a_range).where("start_date <= ?", end_of_a_range).count
To wrap this into a validator I'd first define a scope
range.rb
scope :in_range, ->(range) { where("end_date >= ?", range.first).where("start_date <= ?", range.last) }
And then add the validator:
validates :range_cannot_overlap
def range_cannot_overlap
if Range.in_range(start_range..end_range).count > 0
errors.add(:base, 'range is already alloted')
end
end
I have a model with attributes start_date and end_date. I have search form where user will put the date and I should get a data from the model if date is in between start_date and end_date.
how should I create a query with thinking sphinx.
You will need to do something like the following:
Add both start_date and end_date as attributes (not fields) to your model's Sphinx index.
Translate form params into a date or time value
Use range filters to limit search queries.
I've opted for very large windows of time, but essentially this ensures the given date is equal to or larger than the start date and less than or equal to the end date.
beginning, ending = Time.utc(1970), Time.utc(2030)
Model.search :with => {
:start_date => beginning..date_from_params,
:end_date => date_from_params..ending
}
I'm trying to find records whose start and end date range over a particular date. Date is random and :start_date and :end_date are attributes of the prices entity.
date = Time.now
record_i_want = Price.where(date => :start_date .. :end_date)
Thank you.
You can simply do
Price.where(:date => start_date..end_date)
This will result in the following SQL( for start and end dates - '2014-03-27', '2014-03-28')
SELECT `prices`.* FROM `prices` WHERE (`prices`.`date` BETWEEN '2014-03-27' AND '2014-03-28')
EDIT:
Realized that this is the query you are looking for. Thanks, Coenwulf for pointing it out
Price.where(['start_date < ? AND end_date > ?', date, date])
You want to select rows where your date is greater than the start_date and less than the end_date. You can specify the appropriate SQL where clause parameterized in your call to where like so:
Price.where([":date >= start_date AND :date <= end_date", {date: Date.today})
That will give you all the prices that match. If you know you'll get only one you can get it by calling first.
Price.where([":date >= start_date AND :date <= end_date", {date: Date.today}).first
Make any appropriate adjustment to the >= and <= if you want to exclude the start_date and/or the end_date from the results. If for example the Price is valid starting on the start_date but isn't valid through the end_date you can change the clause to:
":date >= start_date AND :date < end_date"
This should work:
def get_record_by_date(date)
Price.where([start_date.to_i < date.to_i] && [end_date.to_i > date.to_i])
end
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)
I have an object that has a start date and an end date, in order to represent the time that the object is valid.
Given a date, is there a way to only select those objects that have valid ranges that contain the date?
I tried fiddling with between, but couldn't get the syntax right.
Thanks!
This is often implemented using a named scope that does the appropriate restriction that identifies which records are visible at the current point in time:
class MyRecord < ActiveRecord::Base
named_scope :visible,
:conditions => 'visible_from<=UTC_TIMESTAMP() AND visible_to>=UTC_TIMESTAMP'
end
This can be altered to use place-holders for more arbitrary dates:
class MyRecord < ActiveRecord::Base
named_scope :visible_at, lambda { |date| {
:conditions => [
'visible_from<=? AND visible_to>=?',
date, date
]
}}
end
Presumably your dates are stored as UTC, as it is a considerable nuisance to convert from one local-time to another for the purposes of display.
You can select all visible models like this:
#records = MyRecord.visible.all
#records = MyRecord.visible_at(2.weeks.from_now)
If you were doing this for "given_date".
select *
from table
where start_date <= given_date
and end_date >= given_date
This is how you'd do it using active record.
Foo.find(:all, :conditions => ['valid_from <= ? and valid_to >= ?', valid_date, valid_date])