Rails 5 Pluck query changes over Rails 4
Rails 5 has improved the way query was done on database when pluck was called on a model. Before Rails 5, when pluck was called, it used to query database even if the activerecord collection is queried before. There are some posts explaining don’t pluck unnecessarily In this post we will discuss how Rails 5 has improved the way pluck used to work.
Before Rails 5
The Rails used to query database without checking if the query is already done on the database. This behavior and the difference between Pluck and Map/collect is discussed in the article here.
Let’s say we have a model called Product
with following attributes.
- id
- name
- price
We want fetch all the products having price > 20
.
We will write query something like,
products = Product.where('price > ?', 20)
Once we have this data, we can use products
which is
Advertisement::ActiveRecord_Relation
The query is sent to the database whenever operations are performed over this
ActiveRecord_Relation
object. The operations can be each
, map
, all
etc.
Let’s say we perform each
operation on the products
ActiveRecord_Relation
object.
products.each do |product|
puts "The product details are: #{product.inspect}"
end
Now, if we try to pluck
on products
, then it will query again on database
to get the required result instead of just fetching the data from the records
loaded in the memory.
products.pluck(:price)
# SELECT "products."id" FROM "products" WHERE (price < 20)
This behavior is shown in the script given below.
If we run the above given script, it will give output as shown below.
-- create_table(:products, {:force=>true})
Calling products.each -> First Query is done here
D, [2017-07-12T12:07:17.777584 #22312] DEBUG -- : Product Load (0.1ms) SELECT "products".* FROM "products" WHERE (price > 20)
Calling products.pluck(:price) -> Second Query is done here
D, [2017-07-12T12:07:17.781785 #22312] DEBUG -- : (0.1ms) SELECT "products"."price" FROM "products" WHERE (price > 20)
Here we can clearly see two queries being done on products
table.
Thus, we had to keep an eye on when exactly the query is sent to the database.
Usually, we should try to avoid querying as much as possible. Thus, this
behavior posed some concerns on freely using pluck
in Rails.
After Rails 5
Rails 5 has
introduced a fix
for the issue discussed above.
Now, Rails tries to detect if the records are already loaded by usage of any of
the operations such as each
, map
, all
and others.
E.g. Taking the same example as above. Let’s say we have ActieRecord_Relation as follows.
products = Product.where('price > ?', 20)
And the query is sent because we perform some operation on products
as given
below.
product.each do |product|
puts "Product details are: #{product.inspect}"
end
Now, if we try to use pluck
over the products
then, it will not result into
a separate query to the database as records are already loaded into memory.
product.pluck(:price)
Once we run the above script, it will give output as shown below.
-- create_table(:products, {:force=>true})
Calling products.each -> First Query is done here
D, [2017-07-12T12:14:10.295796 #22780] DEBUG -- : Product Load (0.1ms) SELECT "products".* FROM "products" WHERE (price > 20)
Calling products.pluck(:price) -> Second Query is avoided from here
Here we can clearly see that second query is avoided.
The products.each
call sends the query and records are loaded in the memory.
Thus, when products.pluck(:price)
is called, query is avoided.
Conclusion
Thus, Rails 5 has improved the way query is done on the database.
Developers don’t need to remember when exactly queries are happening.
pluck
can be used more freely with the release of Rails 5.
Subscribe to Ruby in Rails
Get the latest posts delivered right to your inbox