Consider i have a 15 categories and 6 sub-categories and i have table items where i have set of records where i have to fetch in the following manner
category 1 ---> level 1 ---> 3 items with maximum price
category 1 ---> level 2 ---> 3 items with maximum price
...
...
...
category 15 ---> level 6 ---> 3 items with maximum price
and
#categories.each do |value|
#sub-categories.each do |value1|
array = Item.find(:all, :conditions => ["customer_id IN (?) AND category_id = ? AND sub-category_id = ?", #customer, value.id, value1.id], :order => 'price DESC', :limit => 3)
array.each do |value2|
#max_price_item_of_each_customer << value2
end
end
end
but this would take much time as this iterates. So how can i change this in such a way the time can be reduced? Any help is appreciated.
Try:
#max_price_item_of_each_customer = []
#categories.each do |value|
#max_price_item_of_each_customer += Item.find(:all, :conditions => ["customer_id IN (?) AND category_id = ? AND sub-category_id in (?)", #customer, value.id, #sub-categories.map(&:id)], :order => 'price DESC', :limit => 3)
end
This all depends on the scale of records you're working with, but if you're working with a reasonable set, this should be faster and will reduce your queries to 1.
#customer_id = 1
#categories = [1, 2, 3]
#subs = [4, 5, 6]
#max_price_item_of_each_customer = []
items = Item.where(customer_id: #customer, category_id: #categories, subcategory_id: #subcategories)
items.group_by{|item| item.category_id}.each_pair do |category_id, category_items|
category_items.group_by{|item| item.subcategory_id}.each_pair do |subcategory_id, subcategory_items|
#max_price_item_of_each_customer += subcategory_items.sort{|x, y| y.price <=> x.price }.first(3)
end
end
The solution below might work if you use Postgresql.
Select a group of 3 item ids from items table, sorted by price descending and grouped by category_id and subcategory_id. You can use Postgres array_agg to collect the item ids after grouping.
Select items row, where the item ids are in those grouped item ids. After that, order the result by category_id ascending, subcategory_id ascending, and price descending
The result is ActiveRecord::Relation, so you can iterate the items as usual. Since the result is flattened (but already ordered by categories, subcategories, and price), you need to separate the different categories and subcategories yourself.
grouped_item_ids = Item.where(customer_id: customer_id).
select("items.category_id, items.subcategory_id, (array_agg(items.id order by items.price desc))[1:3] AS item_ids").
group("items.category_id, items.subcategory_id").map {|item| item["item_ids"]}
#items = Item.where(id: grouped_item_ids.flatten).
order("items.category_id ASC, items.subcategory_id ASC, items.price desc")
Following query works for me
#max_price_item_of_each_customer =Item.find_by_sql(["SELECT i1.* FROM item i1
LEFT OUTER JOIN item i2 ON (i1.category_id = i2.category_id AND i1.sub-category_id = i2.sub-category_id AND i1.id < i2.id)
WHERE i1.customer_id IN (?) AND i1.category_id IN (?)
GROUP BY i1.id HAVING COUNT(*) < 3
ORDER BY price DESC", #customer, #categories.map(&:id)])
Related
In the existing product controller, we are sorting products based on price.
def index
#products = Product.all.order(price: :asc)
#products = #products.paginate(:page => params[:page], :per_page => 20)
end
Now, I would like to change the sorting so that we will user interests. The over all sorting policy will be like this:
items belonging to user interested categories will be listed at top. Remaining items will be listed afterwards.
Within user interested grouping, products shall be ordered based on price. (similar approach for the non interested grouping).
Users Table
id name interested_categories
1 Alice [ 10, 11 ]
2 Bruce [ 11 ]
3 Carol [ 10, 12 ]
Categories Table
id category_name
10 School
11 Kitchen
12 Camping
Products Table
id product_name price category_id
1 pencil 2 10
you could try this
def index
filtered_products = Product.where(category_id: current_user.interested_categories)
all_products = Product.where.not(category_id: current_user.interested_categories).order(price: :asc)
#final_products = (filtered_products+all_products).paginate(:page => params[:page], :per_page => 20)
end
then your views take the #final_products or just change the variable into #products
filtered_products = Product.where(category_id: current_user.interested_categories).to_a.sort_by { |product| [product.category, product.price] }
remaining_products = Product.where.not(category_id: current_user.interested_categories).to_a.sort_by { |product| product.price }
#products = filtered_products + remaining_products.paginate
Converting to Array allows you to use sort_by to sort by two criteria.
In my rails app, new line items are created daily. I need to be able to have my smart_listing show how many apples and oranges were ordered. For instance:
Line Item QTY
Apple 2
Orange 1
What I am getting is:
Line Item QTY
Apple 1
Apple 1
Orange 1
line_item_scope = LineItem.all
line_item_scope = line_item_scope.where(created_at: Date.today.beginning_of_day..Date.today.end_of_day)
if customer_signed_in?
line_item_scope = line_item_scope.ticket.customer(current_customer.id)
end
#line_items = smart_listing_create(:line_items, line_item_scope, partial: "line_items/listing2", default_sort: {updated_at: "desc"})
My initial thought was to create a .map(&:name).uniq but that returns an array when I need a relationship to go into the smart listing.
If you need to display just LineItem's name and the number of items of that name, then group method can help:
line_item_scope.group(:name).count
This will construct a hash:
result = { "Apple" => 2, "Orange" => 1 }
Then this hash can be iterated to display the values:
result.each do |name, count|
...
end
Or the number of line items can be selected as a column:
line_items_scope =
LineItem.group(:name)
.order(:name)
.select("name, COUNT(*) as count")
Then line_items_scope can be fed to smart_listing_create as a ActiveRecordRelation
Following were my query to get list of products:
#listed_products = #products.where(:step => 5, :status => 1)
.includes(:product_attachments)
.includes(:product_reviews)
.order("created_at desc")
The result return products which also consist many similar country eg:
name:prod1
city:city1
country:A
name:prod2
city:city3
country:A
name:prod3
city:city5
country:B
How can I filter out just unique country A,B from the result query? I just need country list to build a drop down list for user to sort product based on country. Thanks!!
You can try this -
#listed_products = #products.where(:step => 5, :status => 1)
.includes(:product_attachments)
.includes(:product_reviews)
.order("products.created_at desc")
result = #listed_products.pluck(:country).uniq
Try the below code:
#listed_products = #products.where(:step => 5, :status => 1)
.includes(:product_attachments)
.includes(:product_reviews)
.order("created_at desc").uniq.pluck(:country)
If a User has_many Items. And items can be published by setting the :published attribute in items to true.
How do I get the average number of published items, per user that created an item?
I have a scope for items called published, so you can get all published items by writing:
#items = Item.published
#items = #user.items.published
One way is:
avg = Item.published.count.to_f / User.count
EDIT:
Try this:
#published_items = User.joins(:items).where('items.published = ?', true)
avg = #published_items.count.to_f / #published_items.all(:select => 'distinct users.*').count
Or:
avg = Item.published.count.to_f / User.joins(:items).where('items.published = ?', true).all(:select => 'distinct users.*').count
In my rails application I have following models
Transaction
belongs_to :account
belongs_to :agent
belongs_to :program
And here is the query i used to get data
def self.efficiency_report(starts=nil, ends=nil)
sql = "SELECT p.abbreviation,ag.name,
t.miles, t.date
FROM transactions t
inner join accounts a on t.account_id = a.id
inner join programs p on a.program_id = p.id
inner join agents ag on t.agent_id = ag.id
Group by p.id , ag.id"
result_array(sql)
end
def self.result_array(sql)
conn = ActiveRecord::Base.connection
res = conn.execute(sql)
results = []
res.each{|r|
results << r
}
return results
end
I want to render data in view with group by program first then group by agent name under it then miles, like this
Program:AA
Agent:Bob
Miles Date
1234 02/12/2012
5463 03/12/2012
Agent:Ben
Miles Date
234 02/22/2012
344 01/02/2012
Program:BB
Agent:Bob
Miles Date
1234 02/12/2012
5463 03/12/2012
Agent:Ben
Miles Date
234 02/22/2012
344 01/02/2012
For this i m doing following in my view
%h2 Vendor Efficiency Report
- #transactions.group_by(&:row[0]).sort.each { |data, transaction|
%h2= data.row[0]
- transaction.group_by(&:row[1]).sort.each { |data, transaction|
%table#data_table.display{:cellpadding => "0", :cellspacing => "0"}
%h3{:style => "clear:both;margin-left:10px"}= data.row[1]
%thead
%tr.odd
%td.bold{:style => "width:60px;"} Miles
%td.bold{:style => "width:60px;"} Date
- for t in transaction
%tbody
%tr
%td{:style => "width:60px;"}= row[2]
%td{:style => "width:60px;"}= row[3]
-}
-}
= will_paginate #transactions
But i m getting this error
wrong argument type String (expected Proc)
Will anyone let me know what i m doing wrong here or is there any other better way for grouping?
Thanks in advance
The problem is your group_by calls. I'm not sure what &:row[0] is doing, but it's passing a string as argument. I think this is what you're looking for:
#transactions.group_by{ |r| r[0] }...
Edit
Just figured out what &:row[0] was doing. It's a call to the Symbol#[] method, which returns the character at the index given (essentially, as if it were aString). The call :row[0] returns the first character of the string "row".
So basically, it was as if you were calling:
#transactions.group_by(&"r")