Open Sephi-Chan opened 13 years ago
Hi, I think it works fine but I found an issue (and a workaround) regrding the arrange method which builds a hash - tree of the results.
:node - an element of a tree (Node has_ancestry) :styles - has_and_belongs_to_many association I want to eager load :position - ordering of children
First try. Should work but it didn't. tree= node.subtree.arrange(:include=>[:styles], :order=>[:position]) The query which loads associated styles is performed, but all the all the associated styles evaluate to empty arrays. node.styles -> []
After some digging ancestry's source I managed to make it work: tree = Node.arrange_nodes(node.subtree.all(:include=>[:styles],:order => "(case when ancestry is null then 0 else 1 end), ancestry, position"))
In conclusion it can be done.
what about doing this with same model.
ie Comment has_ancestry and can have other comments as children? how would you go about doing this?
class Comment < ActiveRecord::Base has_ancestry has_many :comment_children, class_name: 'Comment', foreign_key: 'ancestry' end
Comment.roots.eager_load(:comment_children)
All ancestry methods like subtree or descendants are just where clauses. You can chain any includes on joins call on them.
There is a solution for eager loading in this Gem: https://github.com/Natural-Intelligence/index_tree
Which is not depended on the hight of the tree. It should work with Ancestry Gem
@stefankroes:
All ancestry methods like subtree or descendants are just where clauses.
That's irrelevant, isn't it? Rails eager loading needs an association name, not a where
clause, and Ancestry doesn't appear to implement children
as an association.
+1. I need the possibility to eager load subtree, i need to do something like this recursively through all the descendants:
purchase_order_consumable.children.each do |child|
auto_assign_proc.call(child)
end
And the N+1 queries is killing me.
@siraz-provectus how is that suppose to work? column 'ancestry' is a string separated by '/' for each children, if rails tries to join by that column it will fail.
Has anyone found an elegant solution yet?
I suppose when the ancestry
column is of type Array
at least in postgres one could adjust the join clause for such an association to eager_load
?
Still waiting for answer too :)
Any updates on this?
Hi all, just wanted to share a solution that worked for me. Can't promise it will fit your use case but it did mine quite well. I have a list of hierarchical units, each with their own members (via a join table called assignments). I wanted to print a roster of the nested units with their members.
units = Unit.find_by_id(1)
.subtree
.active
@unit_tree = units.arrange(order: :abbr)
@assignments = Assignment.active
.includes(user: :rank)
.where(unit_id: units.ids)
.group_by(&:unit_id)
This completely avoids the n+1 problem for my use case. I then render it in the template by checking @assignments[unit.id]
. Note that my template code has too much logic in it; I know I need to move it to a helper or something but I'm in early stages of development at the moment.
<% def draw_tree(node) %>
<ul>
<% node.each do |parent, children| %>
<li>
<strong><%= parent.abbr %></strong>
<% if @assignments[parent.id] %>
<ul>
<% @assignments[parent.id].each do |assignment| %>
<li><%= assignment.user.short_name %></li>
<% end %>
</ul>
<% end %>
<% if children.any? %>
<% draw_tree(children) %>
<% end %>
</li>
<% end %>
</ul>
<% end %>
<% draw_tree(@unit_tree) %>
Anyway, just thought I'd share in case it's useful to anyone else.
Hi all, I did a workaround for this using the gem 'batch-loader', '~> 2.0.1'
. In my case I have a graphql api and a model called location
with an implementation of the ancestry
gem
class Location < ActiveRecord::Base
has_ancestry
end
module Types
class LocationType < Types::BaseObject
field :id, ID, null: false
field :children, [Types::LocationType], null: false
def children
ancestry_id = object.ancestry.nil? ? object.id : (object.ancestry.to_s + '/' + object.id.to_s)
BatchLoader::GraphQL.for(ancestry_id).batch(default_value: []) do |ancestry_ids, loader|
Location.where(ancestry: ancestry_ids).order('sequence_key ASC').each do |location|
loader.call(location.ancestry) { |locations| locations << location }
end
end
end
end
Hi,
I didn't see anything in the documentation or sources about eager loading.
As I plan to use Ancestry to build some kind of forum so it's an important feature to me : being able to eager load the author of each message, and so on.
How do you deal with that ?
Thanks! ;)