如何使用 Rails 和 Ancestry 对模型后代执行预先加载
How to perform eager loading on model descendants using Rails and Ancestry
至于现在,我正在开发一个博客应用程序,该应用程序具有通过 has_many/belongs_to 关联连接的 article/comment 个模型。为了创建嵌套评论功能,我使用了 ancestry gem。但是,我想急切地加载评论的所有后代。有什么想法可以解决这个问题吗?
我尝试使用 join 和 where 但它们似乎产生了 n + 1 个查询。
下面是我如何调用在视图中显示它们的方法。
<%= nested_comments_display comments.arrange(:order => :created_at) %>
这里是nested_comments_display方法
def nested_comments_display(comments)
comments.map do |comment, sub_comments|
render(comment) + content_tag(:div,nested_comments_display(sub_comments),
:class => "nested_comment")
end.join.html_safe
end
我也使用 decent_exposure gem 并且我的 CommentsController 看起来像这样
class CommentsController < ApplicationController
expose(:article)
expose(:comments, ancestor: :article)
expose(:comment, attributes: :comment_params)
....
end
解决这个问题的最简单方法(据我所知)是创建一个对象来保存预加载的整个子树集合,然后只从该内存对象中请求子对象...
class CachedAncestryCollection
def initialize(collection)
@collection = collection.to_a
end
def children_for(parent_id = nil)
@collection.select do |node|
parent_id ? (node.ancestry && node.ancestry.match(/#{parent_id}$/)) : node.ancestry.nil?
end
end
end
# ...
preloaded_subtree = Comment.where(:id => comments.map(&:subtree_ids))
cached = CachedAncestryCollection.new(preloaded_subtree)
def nested_comments_display(cached, parent_id = nil)
content_tag(:div) do
cached.children_for(parent_id).map do |child|
nested_comments_display(cached, child.id)
end
end
end
至于现在,我正在开发一个博客应用程序,该应用程序具有通过 has_many/belongs_to 关联连接的 article/comment 个模型。为了创建嵌套评论功能,我使用了 ancestry gem。但是,我想急切地加载评论的所有后代。有什么想法可以解决这个问题吗? 我尝试使用 join 和 where 但它们似乎产生了 n + 1 个查询。 下面是我如何调用在视图中显示它们的方法。
<%= nested_comments_display comments.arrange(:order => :created_at) %>
这里是nested_comments_display方法
def nested_comments_display(comments)
comments.map do |comment, sub_comments|
render(comment) + content_tag(:div,nested_comments_display(sub_comments),
:class => "nested_comment")
end.join.html_safe
end
我也使用 decent_exposure gem 并且我的 CommentsController 看起来像这样
class CommentsController < ApplicationController
expose(:article)
expose(:comments, ancestor: :article)
expose(:comment, attributes: :comment_params)
....
end
解决这个问题的最简单方法(据我所知)是创建一个对象来保存预加载的整个子树集合,然后只从该内存对象中请求子对象...
class CachedAncestryCollection
def initialize(collection)
@collection = collection.to_a
end
def children_for(parent_id = nil)
@collection.select do |node|
parent_id ? (node.ancestry && node.ancestry.match(/#{parent_id}$/)) : node.ancestry.nil?
end
end
end
# ...
preloaded_subtree = Comment.where(:id => comments.map(&:subtree_ids))
cached = CachedAncestryCollection.new(preloaded_subtree)
def nested_comments_display(cached, parent_id = nil)
content_tag(:div) do
cached.children_for(parent_id).map do |child|
nested_comments_display(cached, child.id)
end
end
end