WHERE clauses with OR - ruby-on-rails-3

Currently I have the following line:
scope :within_date_range, ->(start_date, end_date) {
where("(start_date BETWEEN ? AND ?) OR (end_date BETWEEN ? AND ?)", start_date, end_date, start_date, end_date)
}
Is there a more elegant way of doing this using something similar to?
scope :within_date_range, ->(start_date, end_date) {
where((:start_date => start_date..end_date) OR (:end_date => start_date..end_date))
}

Using Arel:
t = Post.arel_table
Post.where(t[:start_date].eq(start_date..end_date).or(t[:end_date].eq(start_date..end_date)))
Or
Post.where('start_date BETWEEN (:start_date) AND (:end_date) OR end_date BETWEEN (:start_date) AND (:end_date)', :start_date => (start_date..end_date), :end_date => (start_date..end_date))

Related

Retrieve without timestamp

I am trying to fetch updated_at without timestamp in order by but unable to do so. I am using date_trunc function but unable to get proper result.Apart from this is there any other function which can give me only date not timestamp
users = User.members(current_user,params).order("date_trunc('datepart', updated_at) DESC, upvotes DESC").paginate(:per_page => PER_PAGE,:page => (params[:page] || 1))
Try following.
users = User.members(current_user,params).order("updated_at.to_date DESC, upvotes DESC").paginate(:per_page => PER_PAGE,:page => (params[:page] || 1))
or
users = User.members(current_user,params).order('updated_at.strftime("%Y-%m-%d") DESC, upvotes DESC').paginate(:per_page => PER_PAGE,:page => (params[:page] || 1))
or
users = User.members(current_user,params).order('updated_at.strftime("%F") DESC, upvotes DESC').paginate(:per_page => PER_PAGE,:page => (params[:page] || 1))

Compare Time.now in Rails but ignore year?

I have the following code in my Rails 3 application:
scope :active, lambda {
where("starts_at <= ? AND ends_at >= ?", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("updated_at > ? OR starts_at > ?", hide_time.utc, hide_time.utc) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
However, I want to return the results regardless of whether the year matches the current year or not. So long as the day and month match it's fine. Is this possible?
The starts_at and ends_at columns are datetime formatted. So they will automatically include a year even if I dont set one in the form:
<%= f.datetime_select :starts_at, :order => [:day, :month], :discard_year => true %>
Any pointers would be appreciated.
As Old Pro points out in the comments this does pose a problem with leap years. You likely want to split each dayofyear into MONTH(x) AND DAYOFMONTH(x) instead.
You can use the dayofyear function
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_dayofyear
scope :active, lambda {
where("dayofyear(starts_at) <= dayofyear(?) AND
dayofyear(ends_at) >= dayofyear(?)", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("dayofyear(updated_at) > dayofyear(?) OR
dayofyear(starts_at) > dayofyear(?)", hide_time.utc, hide_time.utc) if hide_time
}
in mssql it's
datepart(dayofyear,date)
in postgres it's
extract(DOY from date)
in sqlite3 it's
strftime("%j",date)

Why won't my Rails model save into the Database?

def self.build_prior_month(payee, as_of = Time.current, opts = {})
start_date, end_date, year, month = effective_dates(as_of)
statement = payee.earnings_statements.build(
:year => year,
:month => month,
:balance => 0,
:revenue => 0
)
statement.generate(start_date, end_date, year, month)
statement.update_totals
statement
end
That's a function that I have in a model of mine. It returns statement properly, but doesn't save to the database. Why is that?
The generate function looks like:
def generate_ledger_items(start_date, end_date, year, month)
payee.ledger_entries.for_month_and_year(month, year).each do |entry|
earnings_statement_items.build(
:items => entry.item_group,
:units => entry.units,
:net_revenue => entry.net_revenue,
:net_revenue_per_unit => [entry.net_revenue, entry.units].all? ? (entry.net_revenue / entry.units).round(2) : nil,
:fees => entry.service_fees,
:payments_collected => entry.spendings,
:fee_terms => entry.terms || entry.description, # When would terms be set?
:due => entry.credit || -entry.debit
)
end
end
def update_totals
self.revenue = earnings_statement_items.net_revenue
self.balance = earnings_statement_items.total
end
The build method on your association does not actually save the record into the database. create will. It is similar to the difference between new and create on your models.
statement = payee.earnings_statements.create(
:year => year,
:month => month,
:balance => 0,
:revenue => 0
)
Will save your record. See the documentation.
.build doesn't save the object
call:
statement.save

ActiveRecord DateTime comparison

I understand that Rails store the Time in UTC.
I have out_date and in_date instances of DateTime
I have this query:
reservations = Reservation.where("bookable_id = :bookable_id AND bookable_type = :bookable_type AND status <> :status AND ((:out_date >= check_out_date AND :out_date <= check_in_date) OR (:in_date <= check_in_date AND :in_date >= check_out_date) OR (:out_date <= check_in_date AND :in_date >= check_in_date))", :bookable_id => params[:bookable_id], :bookable_type => params[:bookable_type], :status => Reservation::STATUS_CHECKED_IN, :out_date => out_date, :in_date => in_date)
I always get a null set even though I should get a return tuple.
I have tried these varients:
out_date.utc
out_date.utc.to_s(:db)
Nothing seems to be working. How to construct this query?
Controller Code:
in_date = DateTime.strptime params[:checkin], "%Y-%m-%d %H:%M"
out_date = DateTime.strptime params[:checkout], "%Y-%m-%d %H:%M"
#check collision with non-recurring reservations
reservations = Reservation.where("bookable_id = :bookable_id AND bookable_type = :bookable_type AND status <> :status AND ((:out_date >= check_out_date AND :out_date <= check_in_date) OR (:in_date <= check_in_date AND :in_date >= check_out_date) OR (:out_date <= check_in_date AND :in_date >= check_in_date))", :bookable_id => params[:bookable_id], :bookable_type => params[:bookable_type], :status => Reservation::STATUS_CHECKED_IN, :out_date => out_date, :in_date => in_date)
logger.info(reservations)
if !reservations.empty?
#error_message = "This Asset has already been reserved for those dates"
return
end
Also on the Rails Console, the simpler query fails:
1.9.3p0 :007 > Reservation.find_by_id 31
Reservation Load (0.7ms) SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`id` = 31 LIMIT 1
=> #<Reservation id: 31, bookable_id: 11, bookable_type: "Asset", user_id: 1, check_out_date: "2012-07-07 08:00:00", check_in_date: "2012-07-07 10:00:00", notes: "rec", status: "Ready", is_recurring: true, repeat_count: 5>
1.9.3p0 :009 > Reservation.where " ? >= check_out_date AND ? <= check_in_date",DateTime.new(2012,7,7,9),DateTime.new(2012,7,7,9)
Reservation Load (0.5ms) SELECT `reservations`.* FROM `reservations` WHERE ( '2012-07-07 09:00:00' >= check_out_date AND '2012-07-07 09:00:00' <= check_in_date)
=> []
I read the updated code and, apparently, it's correct. Make sure the out_date and in_date are DateTime objects and not null objects.
You don't need to format the query, it should be handled for you.
If you want to debug the query SQL, you can use the .to_sql
reservations = Reservation.where("...") # your query
puts reservation.to_sql
# => "SELECT ..."
Print the query and check if the value is correctly formatted.
The mistake was because of the the database time being stored in UTC and the local time being different and not satisfying the query condition.

NHibernate query ( hql vs criteria )

I have an hql query string
from MyTable table
where table.StartTime + table.Duration >= : startTime
and table.MyId = : id
How can I write this without HQL in NHibernate (using criteria)?
This might be of interest regarding the DateTime + TimeSpan issue.
This will work in your case:
QueryOver:
int id = 1;
DateTime startTime = DateTime.Now.AddDays(5.0);
var vacations = session.QueryOver<Vacation>()
.Where(v => v.Employee.Id == id)
.And(v => v.StartDate > startTime
|| (v.StartDate == startTime.Date && v.Duration >= startTime.TimeOfDay))
.List();
ICriteria:
var vacationsCrit = session.CreateCriteria(typeof(Vacation))
.Add(Expression.Eq("Employee.Id", id))
.Add(Expression.Disjunction()
.Add(Expression.Gt("StartDate", startTime))
.Add(Expression.Conjunction()
.Add(Expression.Eq("StartDate", startTime.Date))
.Add(Expression.Ge("Duration", startTime.TimeOfDay))))
.List();
Both will output the exact same SQL. It should be mentioned that you cannot do something like this, as described in the link above:
var vacations = session.QueryOver<Vacation>()
.Where(v => v.Employee.Id == id)
.And(v => v.StartDate.Add(v.Duration) >= startTime) // <-- this will NOT work
.List();