我正在做一件非常基本的事情 - 按拓扑顺序显示类别树,ActiveRecord 发出额外的查询以枚举每个类别的子项。
class Category < ActiveRecord::Base
attr_accessible :name, :parent_id
belongs_to :parent, :class_name => 'Category'
has_many :children, :class_name => 'Category', :foreign_key => 'parent_id'
def self.in_order
all = Category.includes(:parent, :children).all # Three queries as it should be
root = all.find{|c| c.parent_id == nil}
queue = [root]
result = []
while queue.any?
current = queue.shift
result << current
current.children.each do |child| # SELECT * FROM categories WHERE parent_id = ?
queue << child
end
end
result
end
end
UPD。据我了解,当一个类别被称为某个类别的子类别时,它与初始列表中的对象不同,因此它没有加载它的子类别。有没有办法实现所需的行为而无需创建额外的邻接列表?
UPD2:这是手动邻接列表解决方案。它只使用一个查询,但我真的很想使用更惯用的东西
def self.in_order_manual
cache = {}
adj = {}
root = nil
all.each do |c|
cache[c.id] = c
if c.parent_id != nil
(adj[c.parent_id] ||= []) << c.id
else
root = c.id
end
end
queue = [root]
result = []
while queue.any?
current = queue.shift
result << current
(adj[current] || []).each{|child| queue << child}
end
result.map{|id| cache[id]}
end