2

我一直在寻找最好的方法来做到这一点,结果平庸,所以我决定在这里问。

场景如下:我有三个模型,任务、用户和评论,基本上看起来像这样:

class Task < ActiveRecord::Base
  belongs_to :user
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :task
end

class User < ActiveRecord::Base
  has_many :tasks
  has_many :comments
end

我正在尝试输出一个任务列表(假设这个问题的目的是最后 10 个),并为每个任务关联最后一条评论,并且它的作者(用户模型)具有最少的查询可能

谢谢

更新:我结合了 Blue Smith 和 harigopal 的解决方案,因此解决方案如下所示:

class Task < ActiveRecord::Base
  belongs_to :user
  has_many :comments
  has_one :last_comment, -> { order 'created_at' }, class_name: "Comment"
end

然后获取这样的评论:

tasks = Task.joins(last_comment: :user)
  .includes(last_comment: :user)
  .order('tasks.created_at DESC').limit(10).load

它只产生一个查询,这正是我想要的!谢谢!

4

3 回答 3

2

您必须加入表,并预先加载数据以最大限度地减少查询次数。

例如:

tasks = Task.joins(comments: :user)
  .includes(comments: :user)
  .order('tasks.created_at DESC')
  .limit(10).load

comments = tasks.first.comments   # No query, eager-loaded
user = comments.first.user        # No query, eager-loaded

这应该会将查询的数量减少到只有一个(非常复杂的一个),因此您必须确保您的索引符合要求!:-D

关于组合连接和包含的官方文档含糊不清,但应该有所帮助: http: //guides.rubyonrails.org/active_record_querying.html#joining-multiple-associations

编辑:

这是一个与您使用上述方法执行预加载的模型相同的演示应用程序。它使用两个查询来加载数据。启动应用程序以查看它的运行情况。

https://github.com/harigopal/activerecord-join-eager-loading

于 2013-07-06T10:59:46.633 回答
1

您可以尝试以下方法:

class Task
  scope :recent, order('created_at desc')
  scope :last, lambda{|n| limit: n }
end

现在你有了可重用的范围:

Task.recent.last(10)

我猜你想输出给定用户的最后 10 个任务(假设是当前登录的用户)。

current_user.tasks.recent.last(10).includes(comments: user)
于 2013-07-06T10:32:11.673 回答
1

你可以试试这个:

class Task < ActiveRecord::Base
  belongs_to :user
  has_many :comments
  has_one :last_comment, :class_name => 'Comment', :order => 'comments.created_at DESC'
end

@tasks = Task.limit(10).includes(:last_comment => :user)

last_comment是一个“虚拟”关联,只加载一条Comment记录。这种方法的优点是它会使用更少的内存(因为只加载一个Comment和一个相关User的)。如果你使用includes(comments: user)它可能会消耗大量内存(如果有很多评论:)。

于 2013-07-08T06:59:06.490 回答