-
-
Notifications
You must be signed in to change notification settings - Fork 488
Preload the tree based on a current element depth
You would like to call ".children" on every element in a recursive method to generate a beautiful tree, either in your views, or to render a nested JSON object.
But doing so, you will have a lot of N+1 queries (one call to your database per .children use). Which is really bad for loading speed. So, it would be best to somehow preload your tree, but...
Simply add the below "preload_tree" class method in your model.
class Category < ActiveRecord::Base
acts_as_nested_set :order_column => :name, dependent: :destroy
def self.preload_tree(actual_depth=nil)
# get max_depth
max_depth = Category.unscoped.maximum(:depth)
actual_depth ||= self.minimum(:depth)
return self.all if max_depth.nil? || actual_depth.nil?
preloading = :children
(max_depth - actual_depth).times do
preloading = {children: preloading} # you can include some other preloads here, if you want, like this: [preloading, :articles]
end
self.includes(preloading) # or preload, just a matter of taste here
end
end
Then anywhere you want:
Category.roots.preload_tree.each do |cat_root|
category.children.each do |cat_level1|
cat_level1.children.each ...
end
end
You get the idea. It works best with recursive methods.
Simple, it actually uses the Active Record logic to preload associations.
We are simply generating a big: .includes({:children=>{:children=>{:children=>:children ...}}}
That will do only 1 DB call per depth between min and max depth.
You can also set the "actual depth" if you are working on children of a none root element.
Hope this will help others!