Reputation: 2169
I have implemented something like this:
class Move < ActiveRecord::Base
has_many :move_categories
has_many :categories, through: :move_categories
end
class Category < ActiveRecord::Base
has_many :move_categories
has_many :moves, through: :move_categories
belongs_to :parent, class_name: 'Category'
has_many :subcategories, class_name: 'Category', foreign_key: 'parent_id'
end
So for a given a Category, I need to find the best way to get all my moves, and also all the ones from my sub-categories, and the ones from the sub-categories of my sub-categories, and so on.
Any thoughts?
EDIT:
Here's the MoveCategory model:
class MoveCategory < ActiveRecord::Base
belongs_to :move
belongs_to :category
validates_uniqueness_of :category_id, scope: :move_id
end
Upvotes: 2
Views: 383
Reputation: 35533
Since you are using PostreSQL, you should take advantage of the WITH RECURSIVE
CTE syntax to do this. See http://hashrocket.com/blog/posts/recursive-sql-in-activerecord for a great writeup.
If you implement the tree
methods, all that's missing is the Move retrieval:
def Category < ActiveRecord::Base
def descendant_moves
tree_id_sql = self.class.tree_sql_for(self)
Move.joins(:move_categories).where("move_categories.category_id IN (#{tree_id_sql})")
end
end
For completeness, here's the code for the tree_sql_for
method from the above listed page:
def self.tree_sql_for(instance)
tree_sql = <<-SQL
WITH RECURSIVE search_tree(id, path) AS (
SELECT id, ARRAY[id]
FROM #{table_name}
WHERE id = #{instance.id}
UNION ALL
SELECT #{table_name}.id, path || #{table_name}.id
FROM search_tree
JOIN #{table_name} ON #{table_name}.parent_id = search_tree.id
WHERE NOT #{table_name}.id = ANY(path)
)
SELECT id FROM search_tree ORDER BY path
SQL
end
Upvotes: 1