touch
is used to update the updated_at
timestamp.
Rails 6 has added
touch_all
method to ActiveRecord::Relation
in order to touch
multiple records at once.
Before
Before Rails 6, we needed to loop over all the records
and call touch
for each record.
# Finding all the products whose price is 200 and calling touch for each product
Product.where("price >= ?", 100).find_each { |p| p.touch }
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE (price >= 100)
(0.0ms) begin transaction
Product Update (0.4ms) UPDATE "products" SET "updated_at" = ? WHERE "products"."id" = ? [["updated_at", "2019-08-21 12:07:09.428231"], ["id", 1]]
(0.7ms) commit transaction
...
(0.0ms) begin transaction
Product Update (0.2ms) UPDATE "products" SET "updated_at" = ? WHERE "products"."id" = ? [["updated_at", "2019-08-21 12:07:09.433908"], ["id", 4]]
(0.4ms) commit transaction
(0.0ms) begin transaction
Product Update (0.2ms) UPDATE "products" SET "updated_at" = ? WHERE "products"."id" = ? [["updated_at", "2019-08-21 12:07:09.435312"], ["id", 5]]
(0.4ms) commit transaction
#=> [#<Product id: 1, price: 200, name: "Item 1", created_at: "2019-08-18 11:17:29", updated_at: "2019-08-21 12:07:09">, ...]
This results N +1 queries.
There are multitude of ways in which touch could be used:
# Passing column names as created_at
Product.where(price: 200).find_each { |product| product.touch(:created_at) }
# Passing column names as created_at and time as 2 days before time
Product.where(price: 200).find_each { |product| product.touch(:created_at, time: Time.current - 2.days) }
# Passing time as 2 days before time
Product.where(price: 200).find_each { |product| product.touch(time: Time.current - 2.days) }
touch_all
We can now use touch_all
to overcome this looping operation.
touch_all
accepts two arguments:
- column names
time
(optional)
It updates updated_at
column by default, same as touch
.
Default value for time is current time.
# Finding all the products whose price is 200 and calling touch_all
Product.where("price >= ?", 100).touch_all
Product Update All (2.7ms) UPDATE "products" SET "updated_at" = ? WHERE (price >= 100) [["updated_at", "2019-08-21 12:06:06.625813"]]
#=> 5
In comparison to touch
, this results in a single query instead of N+1 queries.
Similarly, we can utilize touch_all
method in mulitple ways:
# Passing column names as created_at
Product.where("price >= ?", 100).touch_all(:created_at)
Product Update All (1.6ms) UPDATE "products" SET "updated_at" = ?, "created_at" = ? WHERE (price >= 100) [["updated_at", "2019-08-21 12:14:34.124214"], ["created_at", "2019-08-21 12:14:34.124214"]]
#=> 5
Time.current
#=> Wed, 21 Aug 2019 12:16:17 UTC +00:00
# Passing column names as created_at and time as 2 days before time
Product.where("price >= ?", 100).touch_all(:created_at, time: Time.current - 2.days)
Product Update All (3.1ms) UPDATE "products" SET "updated_at" = ?, "created_at" = ? WHERE (price >= 100) [["updated_at", "2019-08-19 12:15:25.697535"], ["created_at", "2019-08-19 12:15:25.697535"]]
#=> 5
# Passing time as 2 days before time
Product.where("price >= ?", 100).touch_all(time: Time.current - 2.days)
Product Update All (10.9ms) UPDATE "products" SET "updated_at" = ? WHERE (price >= 100) [["updated_at", "2019-08-19 12:15:49.313987"]]
#=> 5
Summary
touch_all
provides us with a faster and simpler way to touch a collection of objects.