【问题标题】:Active Record :includes with limit活动记录:包括限制
【发布时间】:2011-11-28 08:12:09
【问题描述】:

我有一个包含许多图片的文章表

图像按每篇文章排序,第一个排序的图像是文章缩略图。

在文章控制器索引方法中,我目前正在执行以下操作以限制 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

【问题讨论】:

  • 图片是按什么排序的?
  • 我有一个名为 ordinal 的列,它只是一个 int,所以我希望它们按序数 ASC 排序,实际上我只关心第一个是拇指(序数 = 1)。谢谢
  • 查看@Delba 的更新答案。他的新解决方案比我提出的要好。

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


【解决方案1】:

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

【讨论】:

  • 很好的答案和简单的方法。
  • @BenLee 你有什么要给我看的吗?因为我发帖前试过了,没有发现任何问题: /// 不管你有多少文章,只有1个查询。 /// 仅加载condition(在本例中为ordinal: 1)的文章(每篇文章一个)。 /// 如果有些文章没有拇指,(1)可能是个问题,你是对的。
  • 感谢您的回答,这非常有效,但不幸的是我需要一种方法来显示没有拇指的文章。
  • 您更新的答案非常优雅;我没有考虑为拇指添加新的关联,但它完全有道理。我认为 OP 应该将其更改为接受的答案。
  • @BenLee 感谢您的公平竞争 Ben Lee,我会欣然接受这些宝贵的积分! ;-)
【解决方案2】:

这不是最干净的,但您可以通过选择与具有序数 1 的选定文章关联的所有图像,将其减少到 2 个查询,如下所示:

@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 在 cmets 中的建议,提高了上述代码的效率。

如果您知道每篇文章都至少有一张图片,那么另一种方法是从图片方面来。选择序数为 1 的所有图像,并包含文章。像这样:

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

【讨论】:

  • 可能有用,但不喜欢在其中使用 .map 和 .select 以实现一般可维护性。更喜欢 Delba 解决方案。但不会 -1,因为如果在 Ruby 中更强大,有些人可能更喜欢不同的方法。
  • 谢谢,选项 1 完美运行。同意使用 .map 感觉有点像黑客,但它实际上非常巧妙地将拇指存储在列表中,而且它比我最初做的要干净得多。宁愿用这个运行而不是 find_by_sql,谢谢你的回答!
  • @Axsuul,你到底是什么意思?把它们放在哈希哪里?
  • 对我投反对票的人,愿意解释一下吗?我错过了什么?
  • 喜欢将图像存储在images_hash = images.each_with_object({}) { |img, h| h[img.article_id] = img }中,然后您可以通过调用images_hash[article.id]来检索图像。
猜你喜欢
  • 2014-06-25
  • 1970-01-01
  • 2012-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-30
  • 1970-01-01
相关资源
最近更新 更多