Reputation: 23288
I have a controller which needs to implement bulk update. (However, it needs to set specific values to each object vs the same value for all objects).
Here is the array which the controller will get
[
{
"task_id": 1,
"some_property": "value1"
},
{
"task_id": 2,
"some_property": "value2"
},
]
I need to find all tasks and for each task update the property to a provided value.
The obvious solution is
task_ids = params[::_json].map { |task| task[:task_id] }
tasks = Task.where(id: task_ids)
tasks.each do |task|
params[::_json].each do |task_from_params| do
if task.id == task_form_params[:task_id]
task.some_property = task_form_params[:some_property]
task.save!
end
end
end
The thing which I don't like (big time) is the code where we do N^2 comparisons.
I am pretty sure there should be a better way to do in Rails. I am looking for something more concise which doesn't require N^2 comparisons.
Upvotes: 0
Views: 1554
Reputation: 2934
ActiveRecord::Relation#update_all
If you don't care about validations and callbacks then you can simply use:
params.each do |param|
Task.where(id: param.fetch('task_id')).
update_all(some_property: param.fetch('some_property')
end
This will iterate over N items in params
and issue N UPDATE
s and no SELECT
s.
If you do care about validations or callbacks then you can convert your input array to a hash first:
# Convert params to a hash. Iterate over N items.
id_to_property = params.map do |param|
[param.fetch('task_id'), param.fetch('some_property')]
end.to_h
# Load corresponding tasks. Iterate over N keys.
Task.where(id: id_to_property.keys).find_each do |task|
# Look up the property value - O(1).
task.update!(some_property: id_to_property[task.id])
end
Upvotes: 1