2013-03-20 58 views
7

我渴望加载与其关联的模型对象:Rails的预先​​加载和where子句

user= User.includes(:posts).find(1) 

但随后在代码中的某些点我愿做这样的事情:

user.posts.where(:topic => "x") 

但是,只是再次运行查询。所以相反,我想我会这样做:

user.posts.select{|post| post.topic == "x" } 

这不会重新运行查询。但我有几个问题。

首先,这是正确的做法吗?

其次,我对这种情况下select做了些什么感到困惑。因为当我运行最后一行时,即使我没有使用includes函数,它第一次运行查询,然后如果我再次运行它,它不......所以有某种缓存涉及? 因为当我使用where子句它每次运行查询。

谢谢。

回答

14

select是Enumerable上的Ruby方法。当您第一次运行

user.posts.select{|post| post.topic == "x" } 

user:posts协会将被从数据库到内存中加载所有Post实例;具体到一个ActiveRecord集合对象中。然后在此集合上调用select,使用:topic属性过滤掉集合中的所有Post实例,该属性不是"x"

第二次运行上述行不会再次查询数据库,因为您已经将posts加载到内存中。


当你做一个includes像下面

user= User.includes(:posts).find(1) 

它要在:posts关系每个User实例返回渴望负荷(在这种情况下,单个User其中:id1)。您现在已经将所有Post实例加载到内存中,如上一节中所述。

如果你再这样做

user.posts.where(:topic => "x") 

现在你告诉轨对Post运行查询,这次发现的所有Post实例,其中:topic"x"并在:user_id也是该:iduserwhere不能像内存中的ARel集合中的“过滤器”一样工作。


  • 如果你想避免另一个查询坏,select是过滤在内存中的结果集的好方法。
  • 您可以运行基准测试来发现这是更快:
    1. 查询数据库和重新建另一个AREL收集到内存
    2. 遍历内存enumberable和使用Ruby
  • 运行的过滤器如果你从来没有需要所有相关:postsuser,你可以很容易地包括在这个原始查询

    user.includes(:posts).where("posts.topic = ?", 'x') 
    
+0

这是完美的,谢谢! – user1069624 2013-03-20 14:34:33