我有两种类型:blogs
和posts
。 Post使用closure_tree gem(acts_as_tree
变体)来允许嵌套在帖子下的帖子。此外,每个博客has_many
发布。
class Post < ActiveRecord::Base
acts_as_tree
end
鉴于一组博客(由同一作者说),我希望将这些博客中的所有帖子作为范围(即,作为ActiveRecord :: Relation而不是数组)。
类似的东西:
Blog.all_posts_by('john')
到目前为止,我尝试了两件事:
使用数组(非范围)的方法#1如下:
class Blog
has_many :posts
def self.all_posts_by author_name
self.where(author_name: author_name).map(&:posts).flatten.map(&:self_and_descendants).flatten
end
end
但我希望有一个范围,因为数组映射方法可能无法很好地处理大型数据集。
方法#2:这种方法产生一个真正的范围,但是使用sql联合和sql字符串:
class Blog
has_many :posts
def self.all_posts_by author_name
post_collections = []
Blog.where(author_name: author_name).each do |blog|
post_collections = blog.posts.map(&:self_and_descendants)
end
posts_sql = ""
post_collections.each do |post_collection|
posts_sql << "( #{post_collection.to_sql} ) union "
end
final_sql = posts_sql.chomp('union ')
result = Post.from("
(
#{final_sql}
) #{Post.table_name}
").distinct
end
end
这可能有用,但我正在寻找更好的方法,希望使用一些可用的范围魔法。
答案 0 :(得分:2)
如果您将blog_id
存储在嵌套的帖子上,而不仅仅存储在根级帖子上,您可以执行以下操作,而不需要查询后代:
class Blog
has_many :posts
def self.all_posts_by author_name
self.where(author_name: author_name).includes(:posts).map(&:posts).flatten
end
end
includes
语句急切加载数据库中的所有帖子,这比顺序加载它们要快得多。 http://www.spritle.com/blogs/2011/03/17/eager-loading-and-lazy-loading-in-rails-activerecord/
更新:
如果您想将它们作为范围返回,我认为在Post
模型上实际使用它是最好的,因为这样做更有意义:
class Post
belongs_to :blog
def self.all_by author_name
self.joins(:blog).where(blog: [name: author_name])
end
end
请注意,只有在所有嵌套帖子上设置blog_id时,这才真正有效。
如果它真的是一个高性能的应用程序,我还建议你去寻找像elasticsearch这样的搜索索引引擎,因为它在这种情况下表现得非常好,即使你没有任何搜索字符串。这将允许您构建更多这样的过滤器并将它们组合在一起,但它也会给应用程序基础架构带来更多复杂性。