rails find_or_initialize to update or create large amount of records

| | August 6, 2015

In our Rails 3.2.13 app we need to query an API for Order data which has many associciated objects, and then store it in our database. Some records may already exist, so if they exist we want to update, if new then we want to create. We are importing thousands of records at a time.

I’ve been looking into the activerecord-import gem to help performance optimize this using the import method, and came up with the below code:

def add_details(order, tax_lines)
  tax_lines.each do |shopify_tax_line|
    taxlines_updated << Taxline.where(:order_id => order.id).first_or_initialize(
      :price => tax_line.price,
      :rate => tax_line.rate,
      :title => tax_line.title)
  end
  Taxline.import taxlines_updated, :validate => false
end

The problem is, if the record already exists then it is not updated, it only updates the attributes if the record is new.

How can I get this to work like: “if found -> update attributes” or “if not found -> new” on each record?

Many thanks!

3 Responses to “rails find_or_initialize to update or create large amount of records”

  1. Bjorn Forsberg on November 30, -0001 @ 12:00 AM

    This is the code we ended up using, perhaps not the most efficient but it works:

    def add_details(shopify_orders)
        tax_lines = []
        shopify_orders.each do |shopify_order|
          shopify_order.tax_lines.each do |shopify_tax_line|
            tax_line = Taxline.where(:order_id => shopify_order.id).first_or_initialize
            tax_line.price = shopify_tax_line.price
            tax_line.rate = shopify_tax_line.rate
            tax_line.title = shopify_tax_line.title
            tax_lines << tax_line
          end
        end
        TaxLine.import tax_lines, :validate => false
    end
    
  2. The :synchronize option might work

     def add_details(order, tax_lines)
          taxlines_updated = []
          tax_lines.each do |shopify_tax_line|
            taxlines_updated << Taxline.where(:order_id => order.id).first_or_initialize(
                   :price => tax_line.price,
                   :rate => tax_line.rate,
                    :title => tax_line.title)
          end
          Taxline.import taxlines_updated, :validate => false, :synchronize => taxlines_updated
      end
    

    For the gem docs

        # * +synchronize+ - an array of ActiveRecord instances for the model
        # that you are currently importing data into. This synchronizes
        # existing model instances in memory with updates from the import.
    
  3. Ya. first_or_initialize works that way. Try update_attributes after first_or_initialize.

    Taxline.where(:order_id =>
    order.id).first_or_initialize.update_attributes!(
    :price => tax_line.price, :rate => tax_line.rate, :title => tax_line.title)

Leave a Reply