优化has_many上的select查询:通过属性关联

时间:2012-04-03 10:46:03

标签: ruby-on-rails ruby-on-rails-3 activerecord

我想找到所有标记有在params数组中传递的标记的帖子。 帖子通过关联有很多标签。

目前我的代码如下:

if params.has_key?(:tags)
  params[:tags].each do |tag|
    @tags = Array.new if @tags.nil?
    @tag = Tag.find_by_content(tag)
    @tags << @tag if @tag
  end
  @allposts = Post.followed_by(@user).select { |p| p.tags.size != 0 && (p.tags & @tags).size == p.tags.size }
else
  @allposts = Post.followed_by(@user)
end

我基本上做的是根据params数组找到实际的标签模型并将它们放入一个数组中,然后在所有帖子上运行一个select查询来搜索那些具有相同标签数组的帖子。

有更好更清洁的方法吗?

1 个答案:

答案 0 :(得分:1)

您可以将Tag.find查询转换为对数据库的单个请求,并添加适当的where子句以限制返回的帖子:

finder = Post.followed_by(@user)
if params.has_key?(:tags)
  @tags = Tag.where(:content => params[:tags])
  finder = finder.with_tags(@tags)
end

@allposts = finder.all
app / models / post.rb

中的

scope :with_tags, lambda { |tags| joins(:tags).group('posts.id').where(:tags => { :id => tags.map { |t| t.id } } ).having("COUNT(*) = ?", tags.length) }

<强>更新

这是with_tags范围的作用:

  1. joins(:tags)首先,我们将标签表加入posts表。当您使用符号语法
  2. 时,Rails将使用内部联接
  3. where(:tags => { :id => tags.map { |t| t.id } } )我们希望过滤标记,以便只查找提供的标记。由于我们提供了标记对象列表,因此我们使用map生成ID数组。然后在where子句中使用此数组来创建WHERE field IN (list)查询 - 哈希语法中的哈希用于表示表,然后表示表中的列。
  4. group('posts.id')现在我们有一个包含必要标签的帖子列表,但是,如果有多个标签我们会多次列出帖子(每个匹配标签一次),所以我们按帖子分组.id这样我们每个帖子只返回1行(我们也可以在步骤4中进行计数)
  5. having("count(*) = ?", tags.length)这是拼图的最后一部分。现在我们已按照帖子进行分组,我们可以计算与此帖子相关联的匹配标签的数量。只要不允许重复标记,那么匹配标记的数量(count(*))是否与我们搜索的标记数量相同(tags.length)那么我们可以确定帖子有我们正在搜索的所有标签。
  6. 通过阅读Active Record Query Interface Guide

    ,您可以找到有关模型可用的不同查询方法的更多信息