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.
Related
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
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.
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)])
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
I have a 10 products but I want particular product default on display. Example If type http://localhost/products then product display should be product xyz.
Thanks,
I'd create a new boolean column like :default_product, then in the model use a scope like "scope :feature_product, first(:conditions => {:default_product => true})". Then, in the controller, use :feature_product to instead of all by doing Product.feature_product in the index method.
It looks like you are using REST so in your products controller:
def index
#products = []
#products << p1 = Product.find_by_name('name')
#products << p2 = Product.find_by_name('name')
#products << p3 = Product.find_by_name('name')
end