我渴望加载一个带有关联的模型对象:
user= User.includes(:posts).find(1)
但是在代码中的某些点我想做这样的事情:
user.posts.where(:topic => "x")
但是这只是重新运行查询。所以相反我认为我会这样做:
user.posts.select{|post| post.topic == "x" }
这不会重新运行查询。但我有几个问题。
首先,这是正确的方法吗?
其次,我对这种情况下的选择有点困惑。因为当我运行最后一行时,即使我没有使用包含函数,第一次运行查询,然后再运行它,如果我再次运行它,它不会...那么是否涉及某种缓存? 因为当我使用where子句时,它每次都会运行查询。
谢谢。
答案 0 :(得分:15)
select
是Enumerable上的Ruby方法。第一次运行
user.posts.select{|post| post.topic == "x" }
Post
user
关联的所有:posts
个实例将从数据库加载到内存中;特别是进入ActiveRecord集合对象。然后在此集合上调用select
,过滤掉集合中的所有Post
个实例,其:topic
属性不是"x"
。
第二次运行上面的行不会再次查询数据库,因为您已经将posts
加载到内存中。
当您执行includes
时,如下所示
user= User.includes(:posts).find(1)
它会急切地为返回的每个:posts
实例加载User
关系(在这种情况下,User
:id
1
为Post
。现在,您已将所有user.posts.where(:topic => "x")
个实例加载到内存中,如上一节所述。
如果你那么做
之类的事情Post
您现在告诉我们针对Post
运行新查询,这次查找:topic
为"x"
的所有:user_id
个实例其中:id
是user
的{{1}}。 where
不像内存中ARel集合中的“过滤器”那样工作。
select
是筛选内存中结果集的好方法。如果您永远不需要:posts
的所有相关user
,您可以轻松地将其包含在原始查询中
user.includes(:posts).where("posts.topic = ?", 'x')