Reputation: 12041
I want to query some objects from the database using a WHERE clause similar to the following:
@monuments = Monument.where("... lots of SQL ...").limit(6)
Later on, in my view I use methods like @monuments.first
, then I loop through @monuments
, then I display @monuments.count
.
When I look at the Rails console, I see that Rails queries the database multiple times, first with a limit of 1 (for @monuments.first
), then with a limit of 6 (for looping through all of them), and finally it issues a count() query.
How can I tell ActiveRecord to only execute the query once? Just executing the query once with a limit of 6 should be enough to get all the data I need. Since the query is slow (80ms), repeating it costs a lot of time.
Upvotes: 1
Views: 2811
Reputation: 80090
In your situation you'll want to trigger the query before you your call to first
because while first
is a method on Array
, it's also a “finder method” on ActiveRecord objects that'll fetch the first record.
You can prompt this with any method that requires data to work with. I prefer using to_a
since it's clear that we'll be dealing with an array after:
@moments = Moment.where(foo: true).to_a
# SQL Query Executed
@moments.first #=> (Array#first) <Moment @foo=true>
@moments.count #=> (Array#count) 42
In this case, you can also use first(6)
in place of limit(6)
, which will also trigger the query. It may be less obvious to another developer on your team that this is intentional, however.
Upvotes: 2
Reputation: 6121
AFAIK, @monuments.first
should not hit the db, I confirmed it on my console, maybe you have multiple instance with same variable or you are doing something else(which you haven't shared here), share the exact code and query and we might debug.
Since, ActiveRecord Collections
acts as array, you can use array analogies to avoid querying the db.
Regarding first
you can do,
@monuments[0]
Regarding the count
, yes, it is a different query which hits the db, to avoid it you can use length
as..
@monuments.length
Upvotes: 1