Rails extra columns in group_by query - ruby-on-rails

My models are like this:
Product belongs_to Category
Product belongs_to OrderItem
I want to select total value of products sold in a certain day, grouped by Categorylike this:
#items = OrderItem.joins(:product => [:category]).where('order_items.sale_date = ?', 1.day.ago.strftime('%Y-%m-%d')).group(:'categories.name').sum(:total_value)
The query works fine, returning an array with Category name and Total value.
I need in the result some extra columns, like the Id of the Category. How can I do that?
Thanks

Try the following to get an array of categories and corresponding sums:
#items = OrderItem.joins(:product => [:category]).where('order_items.sale_date = ?',
1.day.ago.strftime('%Y-%m-%d')).group('categories.id').
sum(:total_value).map {|k, v| {category: Category.find(k), total_value: v}}
Now in the view you can use something like:
<% #items.each do |item| %>
<p><%= item[:category].name %> - <%= item[:total_value] %></p>
<% end; %>

Related

Using nested form to update and create associated items with validation

I have a order form with restrictions on the number of items you can select that accepts nested attributes and I'd like to perform a create and an update on it's associated items when I update the order.
My form looks like:
<%= simple_form_for #food_order do |f| %>
<% #food_order.order_items.each do |oi| %>
<%= f.fields_for :order_items, oi do |oi_form| %>
<%= oi_form.input :quantity %>
<% end -%>
<% end -%>
<% end -%>
My validator looks like:
# OrderItem.rb
validate :food_order_quantity
def food_order_quantity
if (order.limit - order.order_items.sum(:quantity)) < self.quantity
errors.add(:base, "Too many products. Please update your cart.")
end
end
Let's imagine I create an order with a limit of 10 items and select 10 items:
food_order = FoodOrder.create(limit: 10)
order_item_1 = OrderItem.create(order: food_order, quantity: 10)
If I attempt to update the order by reducing order_item_1's quantity by 1 and adding a new order_item with a quantity of 1 I get an error even though the total quantity is correct:
order_item_1.quantity = 9
order_item_2 = OrderItem.new(order: food_order, quantity: 1)
put client_food_order_path(#client, food_order), params: {
food_order: {
id: food_order.id,
order_items_attributes: [
order_item_1.attributes,
order_item_2.attributes,
]
}
}
# returns the following error
#messages={:"order_items.base"=>["Too many products. Please update your cart."]}
I understand that it's attempting to save order_item_2 before updating order_item_1 and in that time, the controller believes there are 11 items (simply because order_item_1 has not yet been updated).
What can I do to allow this sort of operation?

ruby - Order a group_by collection desc?

I have a collection of products users have purchased, grouped by their name, so I can count the number of unique products and how many of each has been purchased:
Controller:
#line_items = Spree::LineItem.joins(:order).where(spree_orders: {state: "complete"})
#products = #line_items.group_by(&:name)
View:
<% #products.each do |name, line_items| %>
<%= name %> - <%= line_items.count %><br>
<% end %>
Is there a way to order the .each loop so that it descends by line_items.count?
Thanks
It will perform better getting the correct data directly from the db:
#products = #line_items.group(:name).order("count_all DESC").count
That will give you the names and counts directly, e.g.
# => { "line_1" => 3, "line_2" => 2, "line_3" => 8 }
There's a bit of Rails magic at work here: the SQL generated using group, order and count will look like:
SELECT COUNT(*) AS count_all, name AS name FROM spree_line_items GROUP BY name ORDER BY count_all DESC
That's where count_all comes from: Rails attaches it to the count column automatically.
Then you can plug this directly into your view:
<% #products.each do |name, line_item_count| %>
<%= name %> - <%= line_item_count %><br>
<% end %>
Alternatively, if you're using the instance variable elsewhere, here's a simple Ruby solution:
#products = #line_items.group_by(&:name).sort_by { |_k, line_items| line_items.count }.reverse
This simply uses sort_by to get records ordered by the relevant count, then reverses to get decending order. There's a good benchmark on doing this here.
Hope that helps - let me know how you get on / if you've any questions.

Ruby on Rails - Search through records with multiple attributes

I have written some code to search via a couple of attributes in all my Recipe records. The code works, but I would like some input on if it's ok, or how to make it better/faster.
I have a Recipe model with various attributes including name:string and ingredients:[array of integers] (postgres database). The ingredients are the ID's of a separate model Ingredient. This is a learning experience, I don't want to use any gems.
My form
index.html.erb
<%= form_tag recipes_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= collection_select( :ingredients, :ingredient_ids, Ingredient.all, :id, :name, {:include_blank => false, include_hidden: false}, { :multiple => true } ) -%>
<%= submit_tag "Search" %>
</p>
<% end %>
recipes_controller.rb
def index
#recipes = Recipe.search(params[:search], params[:ingredients])
end
recipe.rb
def self.search(search, ids)
array = []
if search && !search.empty?
meals = where('name ILIKE ?', "%#{search}%")
meals.each do |meal|
array.push(meal)
end
if ids && !ids.empty?
ingredients(array, ids)
else
return array
end
elsif ids && !ids.empty?
ingredients(all, ids)
else
all
end
end
def self.ingredients(meals, ids)
newarray = []
if ids
meals.each do |me|
a = me.ingredients
b = ids[:ingredient_ids].map(&:to_i)
if (b - a).empty?
newarray.push(me)
end
end
return newarray
else
return meals
end
end
This works fine at the moment as I don't have many records, but I don't trust that it'll be very fast if I had hundreds or thousands of records. Any advice on improving things?
If you know you're going to be searching on one or more columns frequently, try adding a database-level index for those columns. Without an index, any search will be O(n) time where n is the number of records. However, if you use an index, a search will be O(log(n)) time, because with the data ordered by your search column, you can binary search through it.
Take a look at http://weblog.rubyonrails.org/2011/12/6/what-s-new-in-edge-rails-explain/ for more information at how to check if your query performance can be improved.
Two other things to consider performance-wise:
1) For your all condition, you might be returning waaaay more records than you want. You may want to consider using pagination (I know you mentioned no other gems, but there are some great gems for pagination out there).
2) If you do actually want to return a ton of records at once, consider using ActiveRecord's batching (I usually use #find_each) to ensure you don't load everything into memory at once and end up OOM-ing.

Rails. Sum a specific attribute on a collection

I have a list of invoices...
#invoices_1_week = Invoice.order("due_date DESC").where("status != 'paid' AND due_date >= ? AND due_date < ?", Date.today, 1.week.from_now)
The invoices model has a total attribute. How can I get a sum of the totals in the #invoice_1_week collection?
I know I can do it in the view like this...
<% week_1_total = 0 %>
<% #invoices_1_week.each do |invoice| %>
<% week_1_total = week_1_total + invoice.total %>
<% end %>
<%= week_1_total %>
But I'm wondering if there is a more Railsy way of doing it.
Here's a Rails way, using ActiveRecord's sum method:
#invoices_1_week.sum("total")
Here are the docs.
You might want to consider using the symbol notation
#invoices_1_week.sum(:total)
or use single quotes
#invoices_1_week.sum('total')
In both cases, the attribute name is immutable.

In ruby on rails, how to group records by tag when records have multiple tags

I'm using Rails 3.0 and the acts_as_taggable_on gem. I have a Candy model and candies can have multiple tags for flavors. Let's say
Candy1.tags #['apple', 'orange']
Candy2.tags #['orange', 'banana']
Candy3.tags #['apple', 'kiwi']
I want a list of tags with associated candies below them, like so:
Apple
Candy1
Candy3
Orange
Candy1
Candy2
...etc.
I've tried
Candy.all.group_by{ |candy| candy.tags }
but that treats the array of tags as a single entity, returning something like this:
['apple', 'orange']
Candy1
['orange', 'banana']
Candy2
Lacking a group_by_each method, whats the best way to accomplish this? Another Stack Overflow question explains how to do this in memory with simple hashes, but I wonder if there's a more database-oriented way to do it with ActiveRecord associations.
You can iterate over the candies and store them on a hash base on the tag:
grouped = {}
Candy.all.each do |candy|
candy.tags.each do |tag|
grouped[tag] ||= []
grouped[tag] << candy
end
end
At the end you will get:
{'apple' => [candy1, candy2], 'orange' => [] ...}
Hope this helps you
candies = Candy.all.inject({}) do |memo, candy|
candy.tags.each do |tag|
memo[tag] ||= []
memo[tag] << candy
memo
end
end
Now candies is a hash like this:
{
'apple' => [Candy1, Candy3],
'orange' => [Candy1, Candy2],
...
}
Which you can iterate over nicely:
candies.each do |key, value|
puts "Candy tag: #{key}"
value.each do |candy|
puts " * #{candy}"
end
end
In rails api documentation that group_by is for collecting enumerable. I think you need to try a more classic array iterator like the following since you've strings data
in views/candy/index.html.erb
<% #candys.each do |candy| %>
<%= candy.name %>
<% end %>
<%for tag in candy.tags %>
<li> <%= tag.name(or flavor %> </li> #tag.name_attribute
<% end %>
<% end %>
Let me know if it works

Resources