Get All Products of Category and Child Categories (Rails, Awesome_Nested_Set)

get all products of category and child categories (rails, awesome_nested_set)

The key with awesome_nested_set is to use a range in the lft column.
Here's a code sample of how I do it with a direct association (category has_many articles)

  module Category
extend ActiveSupport::Concern
included do
belongs_to :category
scope :sorted, includes(:category).order("categories.lft, #{table_name}.position")
end

module ClassMethods
def tree(category=nil)
return scoped unless category
scoped.includes(:category).where([
"categories.tree_id=1 AND categories.lft BETWEEN ? AND ?",
category.lft, category.rgt
])
end
end # ClassMethods
end

Then somewhere in a controller

@category = Category.find_by_name("fruits")
@articles = Article.tree(@category)

that will find all articles under the categories apples, oranges, bananas, etc etc.
You should adapt that idea with a join on categorizations (but are you sure you need a many to many relationship here?)

Anyway I would try this :

class Product < AR
has_many :categorizations

def self.tree(category=nil)
return scoped unless category
select("distinct(products.id), products.*").
joins(:categorizations => :category).where([
"categories.lft BETWEEN ? AND ?", category.lft, category.rgt
])
end
end

Let me know if there's any gotcha

Want to get all user's products by category including category that have no product

You can override as_json method

Category.includes(:products).as_json(user_id: current_user.id)

## model/category.rb
def as_json(options = {})
json = serializable_hash(options)
json['products'] = products.where(user_id: options[:user_id]).as_json
json
end

How to query for the products belonging to category and sub categories the correct way

class Category < ActiveRecord::Base
has_many :products
has_many :sub_categories
end

class SubCategory < ActiveRecord::Base
has_many :products
belongs_to :category
#Sub_categories table now have a reference to category, i.e., category_id column
end

class Product < ActiveRecord::Base
belongs_to :category
belongs_to :sub #You need to add this too
end

Sum of counts for nested models

You could extend the idea of the counter_cache manually for a sort of sum_cache, lets call it nested_ads_count.

Then there would be 4 general cases that need to be handled

  • a category gets a new add
  • a category is "Moved/added" to a new parent category"
  • a category gets deleted (or removed from a parent)"

An after_update callback that updates the parent's nested_ads_count when either the current's nested_ads_count or ads_count updates. solves the first case.

awesome_nested_set solves the other 2 cases

using an after_add and after_remove callbacks recalculate the nested_ads_count.

The method to calculate and cache the nested_ads_count would look like this

def reset_nested_ads_count
self.nested_ads_count = some_nested_categories.sum(:nested_ads_count) + self.ads_count
end

This method is optimal when the frequency that the count is used is greater than the frequency that it is updated, as it only does the time expensive queries when the number would be updated, and not when the number needs to be seen.

There is one possible pitfall that can happen and that is if you have a loop in your nesting (a > b > a, or even more insidious a > b > c > ... > a) you can get yourself in an infinite loop. I have not seen anything explicitly stating that awesome_nested_set blocks this case.

rails search a category for sub categories

Move the named scope to your Product model, since that's what you're trying to display.

# product.rb    
named_scope :for_category_ids, lambda {|ids| {:conditions => {:category_id => ids}}}

Then in your controller:

@products = Product.for_category_ids(@category.descendants.map(&:id))

Rails method to get all descendant categories

Try this:

def descendants
children.map(&:descendants).flatten
end

def ancestors
[parent, parent.try(:ancestors)].compact.flatten
end

Manual tree traversing has performance implications. You should consider using a gem like ancestry which does a very good job of tree representation.

Edit 1

If you are using the awesome_nested_set gem, please note that it has ancestors and descendants methods on AR instance. You can sort the results of these methods using ruby (If you are dealing with a small set < 100).

category.descendants.sort_by(&:name)

Find Products matching ALL Categories (Rails 3.1)

You can do this with a having clause:

@ids = [1,5,8]
query = Product.select('products.id,products.name').joins(:categories) \
.where(:categories => {:id => @ids}) \
.group('products.id, products.name') \
.having("count(category_products.category_id) = #{@ids.length}")


Related Topics



Leave a reply



Submit