Reputation: 12429
I have a Business
model that :belongs_to Category
A sample hierarchy for categories:
I am using acts_as_tree
for Category
hierarchy.
How can I find all Businesses
under the Restaurants category?
Upvotes: 1
Views: 209
Reputation: 16435
If you want to get all descendants of a node in a tree-like hierarchy you have two options:
Use the classic acts_as_tree
, preload the selected category, start a cascade of queries to retrieve all the children, the grandchildren etc, until you get only leaves (nodes without further chidren). This approach is as good as it sounds.
Use a more advanced tree representation, like nested set or closure tree. Under these representation you can get all descendants of a specific node with just one single query.
Then you get the collected categories and query in Businesses:
Business.where(:category => categories)
(technical explanation: nested set)
Under a nested set representation, each node has two indices, assigned in the following way: imagine that each node is a house with two windows, East and West, and the tree is like a bifurcating road where all children are more or less to the north of their parent. So you start to the east of the root house, and put a sequential number on the windows you meet. You never cross any road, you are only allowed to go around houses which has no further road to the north. At the end you'll get again to the root house, and put a number on the west window.
The assigned numbers will have the following properties:
So, while inserting a new element in the tree is costly (it needs to update a number of indices), retrieving the whole descendance (all children, grandchildren, ...) is quite easy, just take the nodes whose "east" and "west" numbers are between your chosen category's east and west. You can actully do slightly better, but it doesn't matter here.
A library like https://github.com/collectiveidea/awesome_nested_set will manage all this for you, and you only call
categories = @category.self_and_descendants.to_a
(technical explanation: closure tree)
This approach need an accessory table, where you store the transitive closure of the child->parent
relationship (see http://en.wikipedia.org/wiki/Reflexive_transitive_closure#P_closures_of_binary_relations )
The table will contain all pairs ancestor-descendant, so you can join in smart ways with that table to get almost any slicing of the hierarchy.
Again, a library such as https://github.com/mceachen/closure_tree will do the hard work for you, and you will be able to do
categories = @category.self_and_descendants.to_a
Upvotes: 2
Reputation: 47542
category = Category.find_by_name("Restaurants")
Then for Business
category.business
IF you want children like (Sushi, Pizza, Chinese.......) Then
category.childrens
To find all the categories add following method in category.rb
def all_children
all = []
self.children.each do |category|
all << category
root_children = category.all_children.flatten
all << root_children unless root_children.empty?
end
return all.flatten
end
and then use
@category.all_children
EDITED To find businesses for category restaurant and all the subcategories of restaurant.
Business.where("category_id = ? OR category_id in (?)", category.id, category.all_children.map(&:id))
Upvotes: 0
Reputation: 7920
Can you not just do something like:
Category.find_by_name("Restaurants").businesses
EDIT:
Didn't realise you wanted businesses in the subcategories of Restaurants too, duh.
For multiple layers of hierarchy you first need to get all the categories and then go over each, finding businesses, then join them together
class Company < ActiveRecord::Base
...
def all_children
all = []
self.children.each do |c|
all << c
root_cs = c.all_children.flatten
all << root_cs unless root_cs.empty?
end
return all.flatten
end
end
Then you could call:
root_category = Category.find_by_name("Restaurants")
categories = root_category.all_children
businesses = categories.map{ |c| c.businesses }.flatten
That should return you a list of businesses. It doesn't seem very nice though, I feel like there should be a more optimal way.
Hopefully it should give you some food for thought anyway.
Upvotes: 0