8

我渴望加载一个模型对象及其关联:

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

但是在代码中的某些点,我想做这样的事情:

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

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

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

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

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

其次,我对 select 在这种情况下的作用有点困惑。因为当我运行最后一行时,即使我没有使用包含函数,它第一次运行查询,然后如果我再次运行它,它不会......所以是否涉及某种缓存?因为当我使用 where 子句时,它每次都会运行查询。

谢谢你。

4

1 回答 1

17

select是 Enumerable 上的 Ruby 方法。第一次跑步

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

关联的所有Post实例都将从数据库加载到内存中;专门放入一个 ActiveRecord 集合对象中。然后在此集合上调用,过滤掉集合中具有除 .之外的任何属性的所有实例。user:postsselectPost:topic"x"

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


当您执行includes以下操作时

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

:posts它将为每个返回的实例急切加载关系User(在这种情况下,单个Userwhere :idis 1)。您现在已将所有Post实例加载到内存中,如上一节所述。

如果你然后做类似的事情

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

您现在告诉 rails 对 运行查询Post,这次查找所有Post实例 where :topicis"x"和 where :user_idis :idthe userwhere不像内存中的 ARel 集合上的“过滤器”那样工作。


  • 如果您想避免另一个糟糕的查询,这select是过滤内存结果集的好方法。
  • 您可以运行基准测试以找出哪个更快:
    1. 查询数据库并将另一个 ARel 集合重新填充到内存中
    2. 遍历内存中的 enumberable 并使用 Ruby 运行过滤器
  • 如果您从不需要所有相关:posts的 for user,您可以轻松地将其包含在原始查询中

    user.includes(:posts).where("posts.topic = ?", 'x')
    
于 2013-03-20T12:51:50.143 回答