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)"
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
How to Avoid Circular Creation of Associated Models in Factory_Girl
Why Should @@Class_Variables Be Avoided in Ruby
How to Convert a Fraction to Float in Ruby
Regex to Check Alphanumeric String in Ruby
How to Send an Image on The Web in an Xmpp (Jabber) Message
Rails3 Scope for Count of Children in Has_Many Relationship
Error While Starting Puma Server with Workers
Rails3 Activerecord::Statementinvalid:... No Such Table in Every Test
Modifying Text Inside HTML Nodes - Nokogiri
How to Create a Custom Sort Method in Ruby
Rails Routes: Nested, Member, Collection, Namespace, Scope and Customizable
Robust Way to Deploy a Rack Application (Sinatra)
How to Send a Keep-Alive Packet Through Websocket in Ruby on Rails
Automatically Run Rspec When Plain-Old Ruby (Not Rails) Files Change