Rails Activerecord Ambiguous column name on .joins - ruby-on-rails

Here is my code. It work fine if I have something in the :search field or if I have something in the :supplier field but if I have something in both i get "Ambiguous column name 'NUMBER'". Is there a way to select AS or something?
#date_from = params[:date_from] || Date.today.beginning_of_month.strftime('%m/%d/%Y')
#date_to = params[:date_to] || Date.today.strftime('%m/%d/%Y')
q = "%#{params[:search]}%"
#products = Product.where("DISCONT = ? AND NONPRODUCT = ?" ,0,0)
#products = #products.where('NUMBER like ?' ,q) if params[:search].present?
#products = #products.joins(:supplier_items).where('SUPPLIER = ?' ,params[:supplier]) if params[:supplier].present?
#products = #products.paginate(page: params[:page], per_page: 25)

Just prefix the number with the table name
For example:
#products = Product.where(:discount => 0, :nonproduct => 0)
#products = #products.where('products.number like ?', query)

I'm guessing your suppliers table and products table both have a column named "number". Honestly, you'd be best off running a migration to change the column names now (maybe supplier_num / product_num) because keeping it something as generic as "number" will likely keep causing you headaches.

Related

How can I query mulltiple tables and order by date in my view

I have a controller with 2 queries :
def index
#invoices = current_company.invoices.order(billed_at: :desc)
#user_invoices = current_user.invoices.order(billed_at: :desc)
end
in my view I have a table with a if to render the table and sometimes I have both to display,
I have a column date in this table,
How can I sort this table by date ?
Simple Rails way - just connect them and sort:
def index
#invoices = current_company.invoices.order(billed_at: :desc)
#user_invoices = current_user.invoices.order(billed_at: :desc)
#all_invoices = (#invoices + #user_invoices).sort_by {|a| a.created_at}.reverse
end
In your view:
- #all_invoices.each do |invoice|
= invoice.id
So simple!
Alternatively if you want to display either company OR user invoices:
def index
if current_company.present?
#invoices = current_company.invoices.order(billed_at: :desc)
elsif current_user.present?
#user_invoices = current_user.invoices.order(billed_at: :desc)
end
end
You could do a union of the two queries and order that...
def index
invoices = current_company.invoices
user_invoices = current_user.invoices
#all_invoices = Invoice.from("(#{invoices.to_sql} UNION #{user_invoices.to_sql}) AS invoices").order(billed_at: :desc)
end
Then you just iterate through #all_invoices. You could test if the invoice is company or user by an if statement if invoice.company == current_company

Ruby on Rails iterate array result into query

Following were my output from the query:
unless #slug == 'all'
#city = '' if #city.blank?
#city = #city.gsub("-", " ")
country = Country.where("lower(name) LIKE ?", "#{#city.downcase}")
gon.country = country
if country.present?
end
if #city.present?
#products = #products.where("lower(city) LIKE ? or lower(country) like ? or lower(state) LIKE ?", "%#{#city.downcase}%", "%#{#city.downcase}%","%#{#city.downcase}%")
#city_obj = City.where("lower(name) LIKE ?", "%#{#city.downcase}%").first
end
end
Here gon.country return result as:
Object
countries
:
Array[2]
0
:
"california"
1
:
"san francisco"
How can I iterate the countries and pass it to get #products result?
You can do it in two ways :
Iterate all country record and then add like query.
#product = []
country.each do |con|
#products << #products.where("lower(city) LIKE ? or lower(country) like ? or lower(state) LIKE ?", "%#{con.downcase}%", "%#{con.downcase}%","%#{con.downcase}%")
end
You can use ILIKE syntax of core sql. Which work like this :
select * from table where value ilike any (array['%foo%', '%bar%', '%baz%']);
# in your case
country_array_with_percent = country.map {|con| "%#{con}%" }
#products.where("name ILIKE ANY ( array[?] )", country_array_with_percent)
You can also make query chaining with or query, More details here

rails conditions based on attributes presence

I was wondering if there's a better way to do this:
def conditions(obj)
if self.setor.present?
obj = obj.joins(:negocios_setores).where("setor_id = ?", self.setor.id)
end
if self.uf.present?
obj = obj.joins(localizacao: [:uf]).where("uf_id = ?", self.uf_id)
end
if self.municipio.present?
obj = obj.joins(localizacao: [:municipio]).where("municipio_id = ?", self.municipio_id)
end
if !self.lucro_liquido_min.to_f.zero?
obj = obj.where("lucro_liquido_anual BETWEEN ? and ?", self.lucro_liquido_min, self.lucro_liquido_max)
end
if !self.faturamento_min.to_f.zero?
obj = obj.where("faturamento_bruto_anual BETWEEN ? and ?", self.faturamento_min, self.faturamento_max)
end
if !self.valor_min.to_f.zero?
obj = obj.where("valor BETWEEN ? and ?", self.valor_min, self.valor_max)
end
obj
end
Does rails 4 provide something to only do the condition if the value is present instead of placing it with a NULL value?
I don't believe that there is any way to do exactly what you mentioned. I've run into the same type of concatenation of queries.
To clean up a bit and make this tighter, you can use one-line if and unless. I think this is a bit cleaner and still readable.
def conditions(obj)
obj = obj.joins(:negocios_setores).where(setor: setor) if setor.present?
obj = obj.joins(localizacao: [:uf]).where("uf_id = ?", uf_id) if uf.present?
obj = obj.joins(localizacao: [:municipio]).where("municipio_id = ?", municipio_id) if municipio.present?
obj = obj.where("lucro_liquido_anual BETWEEN ? and ?", lucro_liquido_min, lucro_liquido_max) unless lucro_liquido_min.to_f.zero?
obj = obj.where("faturamento_bruto_anual BETWEEN ? and ?", faturamento_min, faturamento_max) unless faturamento_min.to_f.zero?
obj = obj.where("valor BETWEEN ? and ?", valor_min, valor_max) unless valor_min.to_f.zero?
obj
end
I also changed the first query to use Rails style queries rather than SQL in the where.

Find all records with two conditions where one condition are true and other false

I'm trying to find all associated records where two conditions are met.
I'm trying this, but it doesn't work, examples:
#students = #group.students
.includes(:attendances)
.where.not(attendances: {student_id: #ids, event_time_id: #event_id})
#students = #group.students
.includes(:attendances)
.where.not(attendances: {event_time_id: #event_id})
.where.not(attendances: {student_id: #ids})
#students = #group.students
.includes(:attendances)
.where("attendances.student_id IN (?) AND NOT attendances.event_time_id = ?", #ids, #event_id)
I want to get all the student records that exist in group and not have attendance with #event_id or have not attendance at all.
I think this might do what you want (untested), not the most elegant looking code...
exists_fragment = "SELECT 1 FROM attendances WHERE student_id = students.id"
#group.students
.joins(:attendance)
.where("attendances.event_id <> ? OR NOT EXISTS(?)", #event_id, exists_fragment)
Working solution:
#students = #group.students.includes(:attendances)
.where("attendances.event_time_id != ?", #event_id)
.references(:attendances)
#students_on = #group.students.includes(:attendances)
.where(attendances: { event_time_id: #event_id })
#students = #students - #students_on
Not sure if this is the best solution but it solves the mentioned problems
UPDATE
Less code version:
#students = #group.students
#students_exist = #group.students.includes(:attendances)
.where(attendances: { event_time_id: #event_id })
#students = #students - #students_exist

How to merge two objects in Ruby on Rails

I'm trying to retrieve from the database two contents: the first one with the field source equal to "imported" (which means that we import it from the excel spreadsheet), and the second one with source != imported (we create it from scratch). Attached is my code:
def index
add_breadcrumb 'Projects', projects_path
add_breadcrumb #project.name, #project
add_breadcrumb "List #{#category.display_name} Content", project_category_contents_path(#project, #category)
#contents_imported = Content.of_project(#project).with_category(#category).imported.order('contents.created_at asc')
#contents_not_imported = Content.of_project(#project).with_category(#category).not_imported.order('contents.created_at desc')
#page = params[:page]
#contents = #contents_not_imported << #contents_imported
#q = #contents.search(params[:q])
#content = #q.result(distinct: true).page(#page).per(20)
end
#contents_imported = Content.of_project(#project).with_category(#category).imported.order('contents.created_at asc')
#contents_not_imported = Content.of_project(#project).with_category(#category).not_imported.order('contents.created_at desc')
And I want to combine the two results before showing it:
#contents = #contents_not_imported << #contents_imported
but it didn't work. How can I do that?
If both of them are arrays and are having same type of objects you can do Result = Arr1 | Arr1
That also removes the duplicates. Its like boolean UNION. In your case #contents = #contents_not_imported | #contents_imported
The problem is that you want to concatenate results, but you also want to continue treating the combined results as an ActiveRelation (call .search on it). Here's a simpler approach that avoids the need for concatenation in the first place. You will need a more complex ORDER BY clause to accomplish this, however:
#page = params[:page]
#contents = Content.of_project(#project).with_category(#category).
order('CASE WHEN source <> "imported" THEN contents.created_at END desc, CASE WHEN source = "imported" THEN contents.created_at END asc')
#q = #contents.search(params[:q])
Concatenating the arrays is done with the plus sign
You are getting undefined method search for Array because, concatenating will return you an array. And you can't call search method on that Array
EDIT
def index
add_breadcrumb 'Projects', projects_path
add_breadcrumb #project.name, #project
add_breadcrumb "List #{#category.display_name} Content", project_category_contents_path(#project, #category)
contents_imported_ids = Content.of_project(#project).with_category(#category).imported.order('contents.created_at asc').map(&:id)
contents_not_imported_ids = Content.of_project(#project).with_category(#category).not_imported.order('contents.created_at desc').map(&:id)
#page = params[:page]
contents_ids = contents_imported_ids + contents_not_imported_ids
contents = Content.where(content_ids)
#contents = content_ids.collect{|id| contents.detect{|c| c.id == id}}
#q = #contents.search(params[:q])
#content = #q.result(distinct: true).page(#page).per(20)
end
Just create a new Relation with the conditions of imported or not imported, after that, order all the records (if order is important to #contents and #content):
def index
add_breadcrumb 'Projects', projects_path
add_breadcrumb #project.name, #project
add_breadcrumb "List #{#category.display_name} Content", project_category_contents_path(#project, #category)
#contents_imported = Content.of_project(#project).with_category(#category).imported.order('contents.created_at asc')
#contents_not_imported = Content.of_project(#project).with_category(#category).not_imported.order('contents.created_at desc')
#page = params[:page]
imported = #contents_imported.where_values.reduce(:and)
not_imported = #contents_not_imported.where_values.reduce(:and)
#contents = Content.where(imported.or(not_ipmorted)).order('CASE contents.imported WHEN true THEN contents.created_at asc ELSE contents.created_at desc END')
#q = #contents.search(params[:q])
#content = #q.result(distinct: true).page(#page).per(20)
end
Now you can call Ransack#search on #contents because it is an ActiveRecord::Relation. I assume that the imported scope take a field contents.imported with value true.
If I wrote this without errors, this must works.

Resources