Just wondering what the correct way of structuring statements like this is?
#products = Product.where('release_date <= ?', Date.today AND 'physical_status' == 'active')
Thanks!
What you're looking for is this:
#products = Product.where('release_date <= ? AND physical_status = ?', Date.today, 'active')
You can take a look at this guide: http://guides.rubyonrails.org/active_record_querying.html
Related
Filtering items by title, created_at dates, but one of them would be empty and it raises an error, how could I handle that?
where("country_translations.title ILIKE ? AND country_translations.created_at > ? AND country_translations.created_at < ?", "%#{search[:title]}%", search[:created_at_gt], search[:created_at_lt])
You can do something like this:
YourModel.where(filter_by_translations_title).where(filter_by_translations_created)
def filter_by_translations_title
['country_translations.title ILIKE ?', search[:title]] if search[:title].present?
end
#...add rest of methods here
Chaining #where will join all queries via AND. This way will allow you add as many sub quesries as you want, and control their behavior.
You can chain your where clauses quite easily.
#results = Model.all
#results = #results.where('country_translations.title ILIKE ?', "%#{search[:title]}%") if search[:title].present?
If you're using Postgres you can also use a regex instead of ILIKE to get rid of this %#{}% stuff.
#results = #results.where('country_translations.title ~* ?', search[:title]) if search[:title].present?
and so on for your other fields.
It really depends on how you want to handle that.
First of all, I will decompose the query into multiple wheres, that default to an AND operation. This is for readability:
Model.where("country_translations.title ILIKE ?", "%#{search[:title]}%")
.where("country_translations.created_at > ?", search[:created_at_gt])
.where("country_translations.created_at < ?", search[:created_at_lt])
You could either pass default values using the || operator, like this:
Model.where("country_translations.title ILIKE ?", "%#{search[:title] || ''}%")
.where("country_translations.created_at > ?", search[:created_at_gt] || Time.now)
.where("country_translations.created_at < ?", search[:created_at_lt] || Time.now)
or you can split this into three filters that have to be applied only when needed:
query = Model.all
query = query.where("country_translations.title ILIKE ?", "%#{search[:title]}%") if search[:title]
query = query.where("country_translations.created_at > ?", search[:created_at_gt]) if search[:created_at_gt]
query = query.where("country_translations.created_at < ?", search[:created_at_lt]) if search[:created_at_lt]
# query now is filtered only with present filters.
you can always use scopes in such case, they come handy almost everywhere
scope :filter_by_title, -> (title) { where('title ILIKE ?', "%#{title}%") if title.present? }
scope :filter_by_created_at_lt, -> (date) { where('created_at < ?', date) if date.present? }
scope :filter_by_created_at_gt, -> (date) { where('created_at > ?', date) if date.present? }
Then you can restructure the query as
Model.filter_by_title(search[:title])
.filter_by_created_at_lt(search[:created_at_lt])
.filter_by_created_at_gt(search[:created_at_gt])
I was really happy using this query for the search form. However, just found out that is not working properly.
The failing bit is the .where which like against params[:query]. This like happens 4 times against 4 different fields. However, results shows that this is not happening.
#events_casual = Event.non_timetable.where("events.finish_date >= ?", #time).where((["CAST(headers.title_es as varchar(255)) LIKE ?", "%#{params[:query]}%"] || ["CAST(headers.title_en as varchar(255)) LIKE ?", "%#{params[:query]}%"] || ["CAST(headers.title_eu as varchar(255)) LIKE ?", "%#{params[:query]}%"] || ["CAST(headers.title_fr as varchar(255)) LIKE ?", "%#{params[:query]}%"])).includes(:header).order("events.start_date ASC")
I have been trying to transform this query into:
#events_casual = Event.non_timetable.joins(:header).where(" params[:query] in (?)", [headers.title_es, headers.title_en, headers.title_eu]).order("events.start_date ASC")
But, this does not work. Error: undefined methodtitle_es' for {}:Hash`
I have tried similar syntax. But to not avail.
Any help rewriting that query much appreciated. Thanks
try the following
#events_casual = Event
.non_timetable
.includes(:header)
.order("events.start_date ASC")
.where("events.finish_date >= ?", #time)
.where("CAST(headers.title_es as varchar(255)) LIKE :query OR CAST(headers.title_en as varchar(255)) LIKE :query OR CAST(headers.title_eu as varchar(255)) LIKE :query OR CAST(headers.title_fr as varchar(255)) LIKE :query", { query: "%#{params[:query]}%"])
This query won't return any records, when hidden_episodes_ids is empty.
:conditions => ["episodes.show_id in (?) AND air_date >= ? AND air_date <= ? AND episodes.id NOT IN (?)", #show_ids, #start_day, #end_day, hidden_episodes_ids]
If it's empty, the SQL will look like NOT IN (null)
So my solution is:
if hidden_episodes_ids.any?
*mode code*:conditions => ["episodes.show_id in (?) AND air_date >= ? AND air_date <= ? AND episodes.id NOT IN (?)", #show_ids, #start_day, #end_day, hidden_episodes_ids]
else
*mode code*:conditions => ["episodes.show_id in (?) AND air_date >= ? AND air_date <= ?", #show_ids, #start_day, #end_day]
end
But it is rather ugly (My real query is actually 5 lines, with joins and selects etc..)
Is there a way to use a single query and avoid the NOT IN (null)?
PS: These are old queries migrated into Rails 3, hence the :conditions
You should just use the where method instead as that'll help clean all of this up. You just chain it together:
scope = Thing.where(:episodes => { :show_id => #show_ids })
scope = scope.where('air_date BETWEEN ? AND ?', #start_day, #end_day)
if (hidden_episode_ids.any?)
scope = scope.where('episodes.id NOT IN (?)', hidden_episode_ids)
end
Being able to conditionally modify the scope avoids a lot of duplication.
I'm having a blonde moment and probably a brain freeze.
In my rails3 app, I have users and tasks. My users have many tasks...
I have due and overdue tasks as follows:
#due = Task.find(:all, :conditions => ["dueddate >= ? AND AND status = ?", Date.today, false], :include => :taskcategories, :order => "dueddate asc")
What I want to do in my tasks view, is list the users with due tasks...
For some reason, I can't get my head around it. I have tried this, but it's not working:
#task = Task.all
#user = User.find(:all, :conditions => ["#task.dueddate <= ? AND
#task.status = ?", Date.today + 7.days, false])
I'm sure this is easy, can anyone help me!!?
I guess this should work
updated
User.joins(:tasks)
.where("tasks.dueddate <= ? AND tasks.status = ?", Date.today + 7.days, false).group(:id)
This should work with SQLite and MySQL. However, PostgreSQL requires that you supply all the columns of the table. If it's a small table, you could simply type the names. Or you could add this method to the model:
def self.column_list
self.column_names.collect { |c| "#{self.to_s.pluralize.downcase}.#{c}"}.join(",")
end
and change .group(:id) to .group(User.column_list)
I'm using the Rails3 beta, will_paginate gem, and the geokit gem & plugin.
As the geokit-rails plugin, doesn't seem to support Rails3 scopes (including the :origin symbol is the issue), I need to use the .find syntax.
In lieu of scopes, I need to combine two sets of criteria in array format:
I have a default condition:
conditions = ["invoices.cancelled = ? AND invoices.paid = ?", false, false]
I may need to add one of the following conditions to the default condition, depending on a UI selection:
#aged 0
lambda {["created_at IS NULL OR created_at < ?", Date.today + 30.days]}
#aged 30
lambda {["created_at >= ? AND created_at < ?", Date.today + 30.days, Date.today + 60.days]}
#aged > 90
lamdba {["created_at >= ?", Date.today + 90.days]}
The resulting query resembles:
#invoices = Invoice.find(
:all,
:conditions => conditions,
:origin => ll #current_user's lat/lng pair
).paginate(:per_page => #per_page, :page => params[:page])
Questions:
Is there an easy way to combine these two arrays of conditions (if I've worded that correctly)
While it isn't contributing to the problem, is there a DRYer way to create these aging buckets?
Is there a way to use Rails3 scopes with the geokit-rails plugin that will work?
Thanks for your time.
Try this:
ca = [["invoices.cancelled = ? AND invoices.paid = ?", false, false]]
ca << ["created_at IS NULL OR created_at < ?",
Date.today + 30.days] if aged == 0
ca << ["created_at >= ? AND created_at < ?",
Date.today + 30.days, Date.today + 60.days] if aged == 30
ca << ["created_at >= ?", Date.today + 90.days] if aged > 30
condition = [ca.map{|c| c[0] }.join(" AND "), *ca.map{|c| c[1..-1] }.flatten]
Edit Approach 2
Monkey patch the Array class. Create a file called monkey_patch.rb in config/initializers directory.
class Array
def where(*args)
sql = args[0]
unless (sql.is_a?(String) and sql.present?)
return self
end
self[0] = self[0].present? ? " #{self[0]} AND #{sql} " : sql
self.concat(args[1..-1])
end
end
Now you can do this:
cond = []
cond.where("id = ?", params[id]) if params[id].present?
cond.where("state IN (?)", states) unless states.empty?
User.all(:conditions => cond)
I think a better way is to use Anonymous scopes.
Check it out here:
http://railscasts.com/episodes/112-anonymous-scopes