Insert Bulk data using active record import

Imagine a scenario where you have to create a report and send it to multiple clients, so lets just say you have a report model and you have client and you have report client model.
lets first check all the association and model structure.

#report model
class Report < ActiveRecord::Base
  has_many :report_clients
  has_many :clients, through: :report_clients
end

#report_client
class ReportClient < ActiveRecord::Base
  belongs_to :report
  belongs_to :client
end

#clients
class Client < ActiveRecord::Base
  has_many :report_clients
  has_many :reports, through: :report_clients
end

No lets say you want to create a report for 30 clients. You can do that by adding nested attributes in rails.

so our report model will became 

class Report < ActiveRecord::Base
  has_many :report_clients
  has_many :clients, through: :report_clients
  accepts_nested_attributes_for :report_clients, :reject_if => proc { |attributes| attributes['client_id'].blank? },  :allow_destroy => true
end

So using nested attributes we can directly save report and report_clients.
But the potential problem is if for one single report if we are trying to create 100 report_clients,  Rails will generate 100 insert query for report_clients basically it will trigger N+1 query problem.

One alternative solutions for the problem is trying to insert all the clients in a single query, and for that we will gonna use activerecord-import gem (https://github.com/zdennis/activerecord-import)

By using the gem we can import all the associated records using one single query.
Steps to use the gem
1) Add the gem in your gem file
    gem 'activerecord-import'

2) Now modify the save method
   Instead of doing report.save
  try to do something like
  report.save_with_nested_attributes(report_clients_attributes)

3) In report model create a method
    def save_with_nested_attributes(report_clients_attributes)
      report_clients_objects = []
      transaction do
        if save
          report_clients_attributes.each do |client_attributes|
            report_clients_objects << report_clients.new(client_attributes)
          end
        end
          ReportClient.import(report_clients_objects)
      end
   end

 Now it will run one single query to import all the report clients records
 INSERT INTO report clients (...) VALUES
  (...),
  (...),
  (...),
  (...),
  ... 

That's it :) . We have lots of customization option for the gem please take a look into their wiki page.
 https://github.com/zdennis/activerecord-import/wiki



Comments

  1. very informative blog and useful article thank you for sharing with us , keep posting Ruby on Rails Online Training

    ReplyDelete
  2. Thanks for sharing this blog post,Nice written skill selenium Online Training Hyderabad

    ReplyDelete

Post a Comment

Popular posts from this blog

Debug Nodejs inside docker container

Swap primary keys between two records in the same table in Postgres

How to add a bootstrap table with fixed header and scrollable body