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



我想找到所有用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数组找到实际的标签模型,并将它们放入一个数组中,然后我在所有帖子上运行一个选择查询,搜索那些具有相同标签数组的帖子。

有更好更干净的方法吗?

您可以将Tag.find查询滚动到DB的单个请求中,并添加适当的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表。当您使用符号语法时,Rails将使用内部联接
  2. where(:tags => { :id => tags.map { |t| t.id } } )我们想过滤标签,只查找提供的标签。由于我们提供了一个标签对象列表,所以我们使用map来生成一个ID数组。然后在where子句中使用此数组来创建WHERE field IN (list)查询——哈希语法中的哈希用于表示表,然后表示表中的列
  3. group('posts.id')所以现在我们有了一个带有必要标签的帖子列表,但是,如果有多个标签,我们会多次列出帖子(每个匹配的标签一次),所以我们按posts.id分组,这样我们每个帖子只返回一行(这也要求我们可以在步骤4中进行计数)
  4. having("count(*) = ?", tags.length)这是拼图的最后一块。现在我们已经按帖子进行了分组,我们可以统计与此帖子相关联的匹配标签的数量。只要不允许重复的标签,那么如果匹配的标签数量(count(*))与我们搜索的标签数量相同(tags.length),那么我们可以确定帖子中包含了我们搜索的所有标签

通过阅读活动记录查询界面指南

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

最新更新