Note that there are some explanatory texts on larger screens.

plurals
  1. PON+1 while enumerating self-referencing records
    text
    copied!<p>I'm doing a pretty basic thing - displaying a tree of categories in topological order and ActiveRecord issues extra query for enumerating each category's children.</p> <pre><code>class Category &lt; ActiveRecord::Base attr_accessible :name, :parent_id belongs_to :parent, :class_name =&gt; 'Category' has_many :children, :class_name =&gt; 'Category', :foreign_key =&gt; '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 &lt;&lt; current current.children.each do |child| # SELECT * FROM categories WHERE parent_id = ? queue &lt;&lt; child end end result end end </code></pre> <p>UPD. As far as I understand what's going here is that when a category is referred as a children of some category it's not the same object as the one in the initial list and so it hasn't it's children loaded. Is there a way to implement desired behavior without resorting to creating extra adjacency list?</p> <p>UPD2: Here's the manual adjacency list solution. It uses only one query but I'd really like to use something more idiomatic</p> <pre><code> 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] ||= []) &lt;&lt; c.id else root = c.id end end queue = [root] result = [] while queue.any? current = queue.shift result &lt;&lt; current (adj[current] || []).each{|child| queue &lt;&lt; child} end result.map{|id| cache[id]} end </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload