Is there a way to update an entire array or named_scope without using a loop in ruby on rails? - sql

I create the following array using searchlogic named_scopes:
todos = Todo.asset_is("Email").asset_id_is(self.id)
For each value in the array, there is an attribute called original_date and current_date.
I need to make changes to those with some logic, such as:
difference = (original_date - date_entered) - self.days
original_date = date_entered + self.days
current_date = current_date - different
What I do not want to do is do an each do-loop. But I don't know if there's an alternative -- something like the "update" in SQL (but without needing to use SQL -- like using searchlogic)

Todo.update_all(["original_date = date_entered + %d, current_date = ... + %d",
self.days, self.days], ["id in (?)", todos.map(&:id)])

Related

Eloquent complex query

I need sql query, but i need to use Eloquent:
"SELECT * FROM Pass WHERE serial_number=$registered_serial_numbers AND (updated_at IS NULL OR updated_at >= $passesUpdatedSince)"
I try but have error:
$registered_passes = Pass::where('serial_number', $registered_serial_numbers)->where("updated_at IS NULL OR updated_at >= $passesUpdatedSince")->get();
How should i do it in Eloquent?
Two ways of doing it:
1) using whereRaw()
$registered_passes = Pass::whereIn('serial_number', $registered_serial_numbers)->whereRaw('updated_at IS NULL OR updated_at >= ?', $passesUpdatedSince)->get();
2) using a closure to do the AND (OR)
$registered_passes = Pass::whereIn('serial_number', $registered_serial_numbers)->where(function($query){
$query->whereNull('updated_at')->orWhere('updated_at', '>=', $passesUpdatedSince);})->get();
https://laravel.com/docs/5.3/queries#where-clauses
Have a look at the "Parameter Grouping" section
The query should be something like this:
$registered_passes = Pass::where("updated_at", $passesUpdatedSince)->whereIn('serial_number',$registered_serial_numbers)->get();
You use whereIn method for where in clause and make sure $registered_serial_numbers is an array of values.
My bad, misread it. If you are fine with serial number, just do a whereRaw like this:
$registered_passes = Pass::where('serial_number',$registered_serial_numbers)->whereRaw('updated_at IS NULL OR updated_at >= ?', $passesUpdatedSince)->get();

domain openerp how to compare 2 date fields in odoo

I create a method when I change a date I received a product and qty to the date of stock.move, I declare start_date = fields.Datetime() in my class
def onchange_project(self, cr, uid,start_date):
"""
onchange handler of start date.
"""
pool_stockmove =self.pool.get('stock.move')
domain =[('date','>=',start_date)]
ids = pool_stockmove.search(cr, uid, domain)
it works fine this method but I want to compare "date of stock" between the start_date date >= start_date AND date <= start_date. I want also to format the date like the method on hr_timesheet
cr.execute('SELECT id \
FROM hr_timesheet_sheet_sheet \
WHERE (date_from <= %s and %s <= date_to) \
AND user_id=%s \
AND id <> %s',(sheet.date_to, sheet.date_from, new_user_id, sheet.id))
if cr.fetchall():
return False
Thanks
Dates are stored in string format. You can compare using sql like in your hr example, but I would suggest compare it using ORM, not raw sql. It is more convenient and it is recommended to always use it, because sql escapes security and other checks that is written in code (but maybe it is not the case in your code).
It's hard to understand which date with which you want to compare and why you can't do it.
I guess you want to do something like this:
[('date', '>=', 'start_date'), ('date', '<=', 'start_date')]
Domain is defined as list of tuples. Such syntax means and is used between tuples on default. You can specify it like this (but it is the same thing):
['&', ('date', '>=', 'start_date'), ('date', '<=', 'start_date')]
It means the same thing as above line (if used '|', means or).
Odoo damains use polish notation: https://en.wikipedia.org/wiki/Polish_notation
And for dates formating, you can use python module datetime to change formatting of date if you need to.
You May try something like this
cr.execute("SELECT id FROM hr_timesheet_sheet_sheet WHERE (date_from >= %s AND date_to <= %s) AND (user_id=%s) AND (id <> %s)",(sheet.date_from,sheet.date_to, new_user_id, sheet.id))
if cr.fetchall():
return False
I hope this should helpful for you :)
this is a simple ex:
employees = self.env['hr.employee'].search([], order='name asc')
for employee in employees:
presence_count = self.env['hr.attendance'].search_count([
('employee_id', '=', employee.id),
('check_in', '>=', date_start_obj.strftime(DATETIME_FORMAT)),
('check_out', '<=', date_end_obj.strftime(DATETIME_FORMAT)),
])
absence_count = date_diff - presence_count
docs.append({
'employee': employee.name,
'presence': presence_count,
'absence': absence_count,
})

Rails where condition with timestamp

I have query, that would return me data depending on its created_at timestamp
my query looks like
condition[:created_at] = " > #{Time.now - 2.days}"
model.where(condition)
and this return me following sql
...WHERE `model`.`created_at` = ' > 2000-01-01T02:00:00+02:00'
so here timestamp looks different from what in db
So how do i pass correct timestamp to match AR format?
ActiveSupport's #ago will help:
model.where("created_at > ?", 2.days.ago)
Also, I wrote a gem to contain common scopes for created_at queries and others: https://github.com/neighborland/scopy

Rails 3 query in multiple date ranges

Suppose we have some date ranges, for example:
ranges = [
[(12.months.ago)..(8.months.ago)],
[(7.months.ago)..(6.months.ago)],
[(5.months.ago)..(4.months.ago)],
[(3.months.ago)..(2.months.ago)],
[(1.month.ago)..(15.days.ago)]
]
and a Post model with :created_at attribute.
I want to find posts where created_at value is in this range, so the goal is to create a query like:
SELECT * FROM posts WHERE created_at
BETWEEN '2011-04-06' AND '2011-08-06' OR
BETWEEN '2011-09-06' AND '2011-10-06' OR
BETWEEN '2011-11-06' AND '2011-12-06' OR
BETWEEN '2012-01-06' AND '2012-02-06' OR
BETWEEN '2012-02-06' AND '2012-03-23';
If you have only one range like this:
range = (12.months.ago)..(8.months.ago)
we can do this query:
Post.where(:created_at => range)
and query should be:
SELECT * FROM posts WHERE created_at
BETWEEN '2011-04-06' AND '2011-08-06';
Is there a way to make this query using a notation like this Post.where(:created_at => range)?
And what is the correct way to build this query?
Thank you
It gets a little aggressive with paren, but prepare to dive down the arel rabbit hole
ranges = [
((12.months.ago)..(8.months.ago)),
((7.months.ago)..(6.months.ago)),
((5.months.ago)..(4.months.ago)),
((3.months.ago)..(2.months.ago)),
((1.month.ago)..(15.days.ago))
]
table = Post.arel_table
query = ranges.inject(table) do |sum, range|
condition = table[:created_at].in(range)
sum.class == Arel::Table ? condition : sum.or(condition)
end
Then, query.to_sql should equal
(((("sessions"."created_at" BETWEEN '2011-06-05 12:23:32.442238' AND '2011-10-05 12:23:32.442575' OR "sessions"."created_at" BETWEEN '2011-11-05 12:23:32.442772' AND '2011-12-05 12:23:32.442926') OR "sessions"."created_at" BETWEEN '2012-01-05 12:23:32.443112' AND '2012-02-05 12:23:32.443266') OR "sessions"."created_at" BETWEEN '2012-03-05 12:23:32.443449' AND '2012-04-05 12:23:32.443598') OR "sessions"."created_at" BETWEEN '2012-05-05 12:23:32.443783' AND '2012-05-21 12:23:32.443938')
And you should be able to just do Post.where(query)
EDIT
You could also do something like:
range_conditions = ranges.map{|r| table[:created_at].in(r)}
query = range_conditions.inject(range_conditions.shift, &:or)
to keep it a little more terse
I suggest you try the pure string form:
# e.g. querying those in (12.months.ago .. 8.months.ago) or in (7.months.ago .. 6.months.ago)
Post.where("(created_at <= #{12.months.ago} AND created_at >= #{8.months.ago} ) OR " +
"(created_at <= #{7.months.ago} AND created_at >= #{6.months.ago} )" )
In your case, I would suggest to use mysql IN clause
Model.where('created_at IN (?)', ranges)

Rails date without year

How can I find a record by date, but without the year?
this is what I have :
#date = Date.today
#article = Article.find_by_date(#date)
But really what I wanna do is search for an article that by only the month and the day. The year is not important.
Tested with SQLite:
Model.find(:all, :conditions => ["STRFTIME('%m-%d', field_name) = '?-?'", 12, 25])
The generated query illustrates how this finds everything where field_name is Christmas:
Model Load (16.1ms) SELECT "models".* FROM "models" WHERE (STRFTIME('%m-%d', field_name) = '12-25')
I also had success with the shorter "field_name LIKE '%-MM-DD'" but this was MySQL; according to its manual, it indeed uses strings to read and write dates. YMMV.
I suspect this is dependent on which database you're using. Different implementations have different names/syntax for the EXTRACT or DATE_PART function. On PostgreSQL, this one works:
Model.where('EXTRACT(month FROM field_name) = ? AND EXTRACT(day FROM field_name) = ?', 12, 25)
You can do:
Model.all.where(["DAY(`date`) = ? AND MONTH(`date`) = ?", Date.current., 12])
Simple!
A DB agnostic approach would be to add attributes to your model for date_month and date_day (and set them with a before_save callback). Something like:
before_save :set_date_day_and_month
def set_date_day_and_month
self.date_month = date.month
self.date_day = date.day
end
#untested
Then you can do straight-forward queries that wouldn't involve db-specific functions. Like:
Article.where('date_month = ? AND date_day = ?', 12, 25)
#for articles on Christmas
Or:
Article.where('date_month = ? AND date_day = ?', Date.today.month, Date.today.day)
#for articles on this date any year