Do multi-records swap and updates attributes in RoR
Improve your Ruby language skills by learning fundamentals.
▍scenario
Our service used a table to record each page's annotation place on the PDF.
We received a feature request that change the PDF page sequence, e.g. user swap the page 2 and page 3, so that application need to:1. update the page 2’s 10 annotations to page 3
2. update the page 3’s 15 annotations to page 2.
In the ActiveRecord Model, we could think DB schema like below (Use PostgreSQL):
Then, we have the mission:
- 10 annotation records need updated page attribute from 2 to 3.
- another 15 annotation records need updated page attribute from 3 to 2.
My team member do the first try and then told me can not work.
The result would be all updated from_annot and to_annot records to page 2.
That’s because although we cache the from_annot and to_annot in the parameters when calling RoR update / update_all methods, it will select again the database by using the condition then update the target records.
In line 27, will overwrite line 26’s update. Although using the transaction, it still does not work.
# line 27 sql
SQL (0.7ms) UPDATE "annotations" SET page = '3', WHERE "annotations"."file_id" IS 123# line 28 sql
# then it will overwrite all records before.
SQL (0.7ms) UPDATE "annotations" SET page = '2', WHERE "annotations"."file_id" IS 123
So, how can we do?
I use another way to fix this situation but hopefully not to look stupid. Here is my code:
The tip is to set the value in parameters and then call the save! method at last.
It will temp store the objects in your memory cache and not select the database again. In other words, your change would not be overwritten.
This is the truly happened in my team member’s issue. At that time I already understood that update_all is working for this processing.
Hope the article would help when you faced the same problem. =)