How to extract values from a custom SQL query - ruby-on-rails

I have the following custom query whereby I am trying to extract the values so they are meaningful for my app
#sizes = Product
.joins(:product_properties)
.joins('INNER JOIN variant_properties on product_properties.property_id = variant_properties.property_id')
.joins('INNER JOIN properties ON properties.id = product_properties.property_id AND properties.id = variant_properties.property_id AND properties.display_name = \'Size\'')
.joins('INNER JOIN variants on variants.product_id = products.id AND variants.id = variant_properties.variant_id')
.pluck('products.id as prod_id', 'LEFT(variant_properties.description,1) as short_desc', 'variants.price as price')
below is the code within my view
- #sizes.each do |size|
- size.each do |var|
= var.price
= link_to var.short_desc, product, class: 'hollow button tiny'
%small= number_to_currency(var.price)
Problem is I am getting an error either stating no fixed num or no defined methods

Got it, pluck will return an array of arrays so you cannot call its elements like calling methods. It should be size[0], size[1]... instead.
For the ease of use I suggest you build a hash for this:
#product_sizes = #sizes.each_with_object({}) do |size, result|
result[size[0]] = {
'short_desc' => size[1],
'price' => size[2]
}
end
Then use it in your view:
- #products.each_with_index do |product, i|
.product-list.grid-block
.small-8.grid-content.text-center
%h4= product.name.titlecase
- if size = #product_sizes[product.id]
= link_to size['short_desc'], product, class: 'hollow button tiny'
%small= size['price']
Good luck!

Related

How to slice individual columns (with values) in an ActiveRecord::Result in Rails?

I have a Rails query which is shown below:
query_results =
User.
joins("INNER JOIN posts ON posts.user_id = users.user_id").
select("posts.topic, posts.thread_id")
query_results contains values of 2 columns: topic and thread_id.
I would like to split query_results into 2 arrays - 1 containing values from all records (from query_results) for column topic alone and the 2nd containing values from all records for column thread_id alone.
How can I achieve this?
Try This out can help you!
here we are going to use pluck.
Yes. According to Rails guides, pluck directly converts a database result into an array, without constructing ActiveRecord objects. This means better performance for a large or often-running query.
topic_arr = []
thread_id = []
query_results = User.joins("INNER JOIN posts ON posts.user_id = users.user_id").pluck("posts.topic, posts.thread_id")
query_results.each do |i|
topic_arr.push(i.first)
thread_id.push(i.last)
end
puts query_results #=>[["topic1", 1], ["topic2", 2], ["topic3", 3]]
puts topic_arr #=>["topic1","topic2","topic3"]
puts thread_id #=>[1,2,3]
I think you can try below code for your requirement :-
query_results =
User.
joins("INNER JOIN posts ON posts.user_id = users.user_id").
pluck("posts.topic, posts.thread_id").to_h
topic_arr = query_results.keys
thread_id_arr = query_results.values
Example
Above query will give you result like:-
query_results = {"topic 1"=>1, "topic 2" => 2}
topic_arr = query_results.keys
topic_arr = ["topic 1", "topic 2"]
thread_id_arr = query_results.values
thread_id_arr = [1, 2]

Combine two ActiveRecord results and sort by a shared joined table attribute

I have a Convo table and a GroupMeeting table that both are associated with a Msg table.
I want to find all the instances where the current_user has convos or group_meetings with msgs, combine the two, and then show both together to the user in order of the last msg.created_at
Here I have defined both:
#convos = Convo.includes(:msgs).where("sender_id = ? OR recipient_id = ?", current_user, current_user).where.not(:msgs => { :id => nil }).merge(Msg.order(created_at: :desc))
#group_meetings = current_user.group_meetings.includes(:msgs).where.not(:msgs => { :id => nil }).merge(Msg.order(created_at: :desc))
And then combined them together:
#convos = #convos + #group_meetings
What I can't figure out is how to now sort them by msg.created_at
I have tried the following:
#convos = (#convos + #group_meetings).sort_by(&:"#{msg.created_at}")
#convos.order('msg.created_at DESC')
These all seem to be server-side sorting though. How can I sort these based off the join table, after the array has been created?
Please let me know if I need to supply any other details. Thank you!!
You can try the following:
(#convos + #group_meetings).sort_by { |item| item.msgs.minimum(:created_at) }

Multi-dimension instance loop

I am trying to use a custom sql query to display different attributes for a product i.e. Size and Price. The query I have when running in console displays as it should
SELECT products.id, products.name, variant_properties.description, LEFT(variant_properties.description,1) as short_desc, variants.price FROM products
INNER JOIN product_properties ON product_properties.product_id = products.id
INNER JOIN variant_properties on product_properties.property_id = variant_properties.property_id AND variant_properties."primary" = true
INNER JOIN properties ON properties.id = product_properties.property_id AND properties.id = variant_properties.property_id AND properties.display_name = 'Size'
INNER JOIN variants on variants.product_id = products.id AND variants.id = variant_properties.variant_id
In my HAML template I have done the following
- #products.each_with_index do |product, i|
.product-list.grid-block
.small-8.grid-content.text-center
%h4= product.name.titlecase
- #sizes.each do |size|
= link_to size.short_desc, product, class: 'hollow button tiny'
%small= size.price
and in the controller
products = Product.active
# products = Product.active.includes(:variants)
product_types = nil
if params[:product_type_id].present? && product_type = ProductType.find_by_id(params[:product_type_id])
product_types = product_type.self_and_descendants.map(&:id)
end
if product_types
#products = products.where(product_type_id: product_types)
else
#products = products
end
#sizes = Product.find_by_sql("SELECT products.id, LEFT(variant_properties.description,1) as short_desc, variants.price FROM products
INNER JOIN product_properties ON product_properties.product_id = products.id
INNER JOIN variant_properties on product_properties.property_id = variant_properties.property_id
INNER JOIN properties ON properties.id = product_properties.property_id AND properties.id = variant_properties.property_id AND properties.display_name = 'Size'
INNER JOIN variants on variants.product_id = products.id AND variants.id = variant_properties.variant_id")
Ideally I am trying to get it to look something like below, though I am having issues achieving this
The first thing is notice is that Model.find_by_sql will return a list of models and nothing more no matter what you select in your sql query.
So the solution I suggest is trying to convert to ActiveRecord::Relation like this:
Product.joins(:product_properties)
.joins('INNER JOIN variant_properties on product_properties.property_id = variant_properties.property_id')
.joins('INNER JOIN properties ON properties.id = product_properties.property_id AND properties.id = variant_properties.property_id AND properties.display_name = \'Size\'')
.joins('INNER JOIN variants on variants.product_id = products.id AND variants.id = variant_properties.variant_id')
.pluck('products.id', 'LEFT(variant_properties.description,1)', 'variants.price')
I haven't tried yet but I think it could produce an array of arrays contains the value you need.

Rails 3 - Get Multiple Counts from same child table with different Where Clauses

I have a relationship between Job Shifts and People who have indicated whether they want to apply of the jobs (Yes, No or Maybe).
I Want to list a Job Shift and the Count of people that have indicated Yes, No or Maybe.
My Current Query Does not work and I not sure why, I am new to Rails so I would also like to know if there is a more Railish way of doing this.
def report_shift_detail
#headers = ["id", "staffroom_id", "title", "Yes", "No", "Maybe"]
#fields = [:id, :staffroom_id, :title, :count_yes, :count_no, :count_maybe]
#sql = Shift.scoped
.select("shifts.id, shifts.staffroom_id, shifts.title, count(application_yes.id) as count_yes, count(application_no.id) as count_no, count(application_maybe.id) as count_maybe")
.joins("INNER JOIN shift_applications as application_yes ON application_yes.shift_id = shifts.id")
.where("application_yes.sort = 'yes'")
.joins("INNER JOIN shift_applications as application_no ON application_no.shift_id = shifts.id")
.where("application_no.sort = 'no'")
.joins("INNER JOIN shift_applications as application_maybe ON application_maybe.shift_id = shifts.id")
.where("application_maybe.sort = 'maybe'")
.group("shifts.id")
end
I'm posting partial answer as it may be useful. I do not have a nice RUBY answer as I'm not proficient enough to construct this as RUBY but I got the query working by restructing the select statement, I was writing it incorrectly from the start.
Here is the working SQL
SELECT shifts.id, shifts.staffroom_id, shifts.title,
(select count(*) from shift_applications where shift_applications.shift_id = shifts.id AND (shift_applications.sort = 'yes')) as count_yes,
(select count(*) from shift_applications where shift_applications.shift_id = shifts.id AND (shift_applications.sort = 'no')) as count_no,
(select count(*) from shift_applications where shift_applications.shift_id = shifts.id AND (shift_applications.sort = 'maybe')) as count_maybe
FROM "shifts"
WHERE "shifts"."deleted" = 'f'
GROUP BY shifts.id
Here is how I implemented it in Ruby
count_yes = "(select count(*) from shift_applications where shift_applications.shift_id = shifts.id AND (shift_applications.sort = 'yes')) as count_yes"
count_no = "(select count(*) from shift_applications where shift_applications.shift_id = shifts.id AND (shift_applications.sort = 'no')) as count_no"
count_maybe = "(select count(*) from shift_applications where shift_applications.shift_id = shifts.id AND (shift_applications.sort = 'maybe')) as count_maybe"
#sql = Shift.scoped
.select("shifts.id, shifts.staffroom_id, shifts.title, #{count_yes}, #{count_no}, #{count_maybe}")
.group("shifts.id")
Hopefully someone can see a cleaner way to do this
I'm going to take a wild guess here, please post more info like your table columns and names. I'm assuming that you have 2 models named Shift and ShiftApplication, I'd probably do something like this:
In your controller:
def report_shift_detail
#shifts = Shift.all
end
In your report_shift_detail.html.erb, I will have something like this
<% #shifts.each do |shift| %>
<tr>
<td> <%= shift.shift_applications.where(sort: 'yes').count %></td>
<td> <%= shift.shift_applications.where(sort: 'no').count %></td>
<td> <%= shift.shift_applications.where(sort: 'maybe').count %></td>
</tr>
<% end %>
Of course if it where me, I'm going to define a scope in my model instead of doing the where in the views. This is the only thing I can manage based on your example.

missing attributes name in rails association

I have the following query in my rails app but its result does not have attributes name so I am not able to use it in amcharts
EmployeeDepartment.joins(:states).group
("employee_departments.name").count
the result is : {"Academic Support":1}
how to make it like this {"department_name":"Academic Support","department_count":1}
Let's say you have a hash like this:
hash = { "Academic Support" => 1, "Another Department" => 3, "Something Else" => 4 }
You can just use map to transform it into an array of hashes containing what you need.
hash.map { |k, v| { "department_name" => k, "department_count" => v } }
=> [{"department_name"=>"Academic Support", "department_count"=>1},
{"department_name"=>"Another Department", "department_count"=>3},
{"department_name"=>"Something Else", "department_count"=>4}]
If your hash only ever contains one key/value pair and you just want another hash, you could try this:
Hash[[["department_name", "department_count"], hash.first].transpose]
Or even simpler...
{ "department_name" => hash.keys.first, "department_count" => hash.values.first }
I solved my problem by this steps:
the following code:
EmployeeDepartment.joins(:states).group
("employee_departments.name").count
generates this in rails console
SELECT COUNT(*) AS count_all, employee_departments.name AS employee_departments_name FROM "employee_departments" INNER JOIN "tickets" ON "tickets"."employee_department_id" = "employee_departments"."id" INNER JOIN "states" ON "states"."id" = "tickets"."state_id" GROUP BY employee_departments.name
i used what was generated in the console in the following:
#department_count = EmployeeDepartment.find(:all,
:select => 'employee_departments.name AS employee_departments_name, COUNT(*) AS department_counter',
:joins => 'INNER JOIN tickets ON tickets.employee_department_id = employee_departments.id INNER JOIN states ON states.id = tickets.state_id',
:group => 'employee_departments.name')
Now the result in amcharts is:
var chartData = [{"department_counter":1,"employee_departments_name":"Academic Support"}];

Resources