枚举自引用记录时N + 1

时间:2013-05-26 04:12:45

标签: ruby-on-rails

我正在做一个非常基本的事情 - 以拓扑顺序显示类别树,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

0 个答案:

没有答案