2013-07-06 87 views
1

我一直在寻找最好的方法来完成相当一段时间的平庸结果,所以我决定在这里问一下。在Rails中获取最后一条相关记录

的情况如下:我有三个型号,任务,用户和评论,基本上是这样的:

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这个问题的目的),以及相关的最后评论为每个任务和它的作者(用户模型)与最少查询可能

谢谢

UPDATE

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 
因此该解决方案是这样的,我结合由Blue史密斯和harigopal解决方案

其中产生只有一个查询,这正是我是looki ng for!谢谢!

+0

我不认为我的理解正确,所以我发布了有关此评论的进一步解释:这不是我关心的任务限制,数字完全不相关,我试图以某种方式来模拟场景,其中任务只有**一条评论**所以问题是如何**加载最后一条相关评论** –

回答

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

+0

这会产生:PG ::错误:错误:缺少表“任务”的FROM子句条目 –

+0

我已经使用演示应用程序(SQLite3)更新了解决方案。试试看。 –

+0

也许我错了某处,但是当我尝试类似的东西时,我有一个左外连接进行评论,并且它加载了所有评论,而不仅仅是10个,所以如果你有10个任务并且每个评论有10,000个,麻烦 – JustMichael

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) 
+0

不要将'scope'与带参数的lambdas一起使用。改用类方法。 – Hauleth

+0

为什么?你可以解释吗? –

+0

因为[优先方式](http://guides.rubyonrails.org/active_record_querying.html#passing-in-arguments)是使用类方法。 – Hauleth

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)它可能会消耗大量的内存(如果有很多评论:)。

相关问题