I faced a situation in my project where I need to update all the embedded documents. But as we generally know about the mongoid embedded documents we can’t access directly, we must access them through parent documents.
I have millions of such parent documents and they embed many child documents. So in the general solution, to update the child documents I have to access the parent documents and then I need to use the loop on the parent documents to access the child, and then I will use an update query.
Using the loop on millions of documents is bad practice and it is causing slow performance. I used the below data model and queries to understand the problem in detail.
I have one vehicle master model which embeds_many insurances codes
class VehicleMaster
include Mongoid::Document
field :model, type: String
field :submodel, type: String
field :fuel_type, type: String
field :seating_capacity, type: Integer
field :carrying_capacity, type: Integer
embeds_many :insurance_codes
end
I have another model insurance code which is embedded in vehicle_master model
class InsuranceCode
include Mongoid::Document
field :category_code, type: String, default: ''
field :bodytype_id, type: String, default: ''
field :segment_type, type: String, default: ''
field :category_type, type: String, default: ''
embedded_in :vehicle_master
belongs_to :insurance_vendorend
end
General Solution:
I need to update the category_code of the particular vendor with some value. To do that the general solution is given below.
#select the vehicle master documents of a insurance vendor
vehicle_master = VehicleMaster.where('insurance_codes.insurance_vendor_id': InsuranceVendor.tata_aig)
#to update the category code in child element
vehicle_master.each do |vm|
#access the child element and update with value.
ic = vm.insurance_codes.where(insurance_vendor: InsuranceVendor.tata_aig).
ic.update(category_code: 45)
end
As we see here, we are iterating on the records vehicle_master documents, suppose we have such millions of the document then it will take so much time and it is bad practice also. I have run this code on the rails console and calculated the time required to execute.
2.4.2 :070 > time = Benchmark.measure {
2.4.2 :071 > vehicle_master.each do |vm|
2.4.2 :072 > ic = vm.insurance_codes.where(insurance_vendor: InsuranceVendor.tata_aig)
2.4.2 :073?> ic.update(category_code: 45)
2.4.2 :074?> end
2.4.2 :075?> }
=> #<Benchmark::Tms:0x0000561c1ed53730 @label="", @real=9.474939513980644, @cstime=0.0, @cutime=0.0, @stime=0.25, @utime=7.170000000000002, @total=7.420000000000002>
2.4.2 :076 > time
=> #<Benchmark::Tms:0x0000561c1ed53730 @label="", @real=9.474939513980644, @cstime=0.0, @cutime=0.0, @stime=0.25, @utime=7.170000000000002, @total=7.420000000000002>
2.4.2 :077 > time.total
=> 7.420000000000002
Here we required 7.42 seconds to update 3226 documents. Suppose if we have millions of documents, how much time it will take? Now we will see the optimized solution.
Optimized solution:
Rather than iterating on the vehicle_master documents, we can use the update_all methods on vehicle_master documents.
vehicle_master.update_all('$set' => { 'insurance_codes.$.category_code': '45'})
Here we can see the difference we are not iterating on vehicle_master documents. We are directly updating the insurance_codes.
time = Benchmark.measure { vehicle_master.update_all('$set' => { 'insurance_codes.$.category_code': '45'}) }
=> #<Benchmark::Tms:0x0000561c178196e0 @label="", @real=0.19454347199643962, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.00999999999999801, @total=0.00999999999999801>
2.4.2 :050 > time.total
=> 0.00999999999999801
Here We can see we have updated the same records with the same value only in 9ms. So we have reduced the query execution time from 7.42 seconds to 9ms.
Summary:
While discussing the possible threats of embedding parent documents in millions of child documents, this article guides how to mitigate such a threat by simply updating mongoid embedded documents. Please write to us for further details.
Thanks for reading! I hope this article was helpful.
One thought on “How Can You Update Your Mongoid Embedded Documents?”