活动记录:包括限制

时间:2011-11-28 08:12:09

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

我有一张包含许多图像的文章表

图像按照每篇文章进行排序,第一个有序图像是文章缩略图。

在文章控制器索引方法中,我目前正在执行以下操作以限制2个活动记录查询;

@articles = Article.where(:active => true).includes(:images)

并访问缩略图:

# article model
def thumb
 self.images.first if self.images
end

问题是这只有2个查询,但如果每篇文章有10个图像,并且每页有50篇文章,那么我已经将500个图像行加载到内存中。

在活动记录中是否有更有效的方法。希望不必使用find_by_sql

2 个答案:

答案 0 :(得分:17)

2次查询

@articles = Article.where(:active => true).includes(:thumb)

# app/model/article.rb
has_one :thumb, :class_name => 'Image',
                :conditions => { :ordinal => 1 }

1次查询

如果某些文章没有拇指

,请不要使用此解决方案

如果要在联接表上指定条件,可能应该使用joins而不是includes

@articles = Article.joins(:images)
                   .where(:active => true, 
                          :images => { :ordinal => 1 })
                   .includes(:images)

仅加载:ordinal => 1

的图片

我希望它有所帮助

在预先加载的关联中指定条件时, joinsincludes之间存在差异:

“即使Active Record允许您像加入一样指定热切加载关联的条件,但推荐的方法是使用联接。

[...]

includes生成一个包含LEFT OUTER JOIN的查询,而joins方法将使用INNER JOIN函数生成一个。“有关更多帮助,请参阅the Specifying Conditions on Eager Loaded Associations section in Active Record Query Interface Guide

答案 1 :(得分:3)

这不是最干净的,但是您可以通过选择与所选项目相关联的所有图像来保持这一点,这些图像与序号1相关,如下所示:

@articles = Article.where(:active => true).all
images = Image.where(:article_id => @articles.map(&:id), :ordinal => 1).all

虽然不会将对象链接在一起。您可以使thumb成为属性访问器而不是方法,如下所示:

# article model
attr_accessor :thumb

然后,当您加载上述图像时,可以在以下位置设置thumb属性:

@articles = Article.where(:active => true).all
images = Image.where(:article_id => @articles.map(&:id), :ordinal => 1).all
images_hash = images.each_with_object({}) { |img, h| h[img.article_id] = img }
@articles.each { |article| article.thumb = images_hash[article.id] }

这是一种方法。更新:在评论中根据@ Axsuul的建议提高上述代码的效率。

另一种方式,如果你知道每篇文章至少会有一个图像,那就是从图像方面来看。选择序号为1的所有图像,并包含文章。像这样:

@articles = Image.include(:article).where('images.ordinal' => 1, 'articles.active' => true).map(&:article)