I'm eager loading a model object with its associations:
user= User.includes(:posts).find(1)
But then at certain points in the code I would like to do something like this:
user.posts.where(:topic => "x")
But that just re-runs the query again. So instead I thought I'd do this:
user.posts.select{|post| post.topic == "x" }
That doesn't re-run the query. But I have a couple of questions.
First, Is this the right way to do it?
Second, I'm a bit confused about what select does in this case. Because when I run the last line even when I haven't used the includes function, the first time it runs the query and then after that if I run it again, it doesn't .. so is there some sort of caching involved? Because when i use the where clause it runs the query every single time.
Thank you.
select
is a Ruby method on Enumerable. The first time you run
user.posts.select{|post| post.topic == "x" }
all Post
instances for user
's :posts
association will be loaded from the database into memory; specifically into an ActiveRecord collection object. select
is then called on this collection, filtering out all Post
instances in the collection with a :topic
attribute that is anything other than "x"
.
Running the above line a second time won't query the database again, because you've already loaded the posts
into memory.
When you do an includes
like below
user= User.includes(:posts).find(1)
it is going to eager load the :posts
relation for each User
instance returned (in this case, a single User
where :id
is 1
). You now have all Post
instances loaded into memory as described in the previous section above.
If you then do something like
user.posts.where(:topic => "x")
you are now telling rails to run a new query against Post
, this time finding all Post
instances where :topic
is "x"
and where :user_id
is the :id
of the user
. where
doesn't work like a "filter" on an in-memory ARel collection.
select
is a fine way to filter the in-memory result set.If you never need all related :posts
for user
, you can easily include this in the original query
user.includes(:posts).where("posts.topic = ?", 'x')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With