3

I am using active-record import gem in a class method to import array of listings read from csv file as shown in code below:

def self.import_listings(file)  
        start = Time.now    
        count = 0
        valid_listings = Array.new

        CSV.foreach(file.path, headers: true, encoding:'iso-8859-1:utf-8') do |row|

            data = {}

            row.to_hash.each do |k, v|
                key = MAP[k]
                data[key] = v
            end

            unless data[:vin] == nil            
                listing =  Listing.new                      
                listing.title = "#{data[:year]} #{data[:make]} #{data[:model]}"


                listing.approved = true


                unless data[:all_images] == nil

                    listing_images = data[:all_images].split(",")
                    i = 0

                    [:image, :imagefront, :imageback, :imageleft, :imageright, :frontinterior, :rearinterior].each do |image|               
                        unless listing_images.size < 1
                            data[image] = CsvUploading::picture_from_url(listing_images[i])
                            i += 1
                        end

                    end

                end


                data.delete(:all_images)

                data[:approved] = true


                listing.attributes = data


                valid_listings << listing

             end
        end



        begin

            Listing.import valid_listings, on_duplicate_key_update: { conflict_target: [:title, :vin], columns: [user_id: :user_id, newused: :newused]}# , :stocknumber, :model, :year, :trim, :miles, :enginedescription,:cylinder,:fuel,:transmission,  :price, :color, :interiorcolor, :options, :description, :image, :image, :imagefront, :imageback, :imageleft, :imageright, :frontinterior, :rearinterior]

        rescue
            p "some issue"
        end

        finish = Time.now
        puts diff = finish - start                          
    end

As per active-record import documentation, i am trying to set a watcher on the title and VIN fields of listing as conflictied targets.In case of conflict of VIN field of a listing, I want to do an update instead of create.

But right now, it's creating a new listing from Listing.import every time I am running a CSV upload and not checking if its conflicted.

Where am I going wrong?

enter image description here

Saurav Prakash
  • 1,177
  • 1
  • 11
  • 25

1 Answers1

3

Your screenshot shows that you are using SQLite, but the documentation says that this feature is only supported for MySQL and PostgreSQL. You will need to use a supported database!


My first answer, in case it's useful to someone in the future:

The On Duplicate Key Update behaviour depends on there being a unique constraint on those columns in the database itself – because you're seeing duplicate records, it's safe to assume that you don't have such a constraint.

As the database isn't reporting a conflict on a unique constraint, there's no reason it would update an existing row! The answer in this Stack Overflow question, "How to correctly do upsert in postgres 9.5" explains this in a bit more depth and shows the kind of query that is being issued.

You should be able to solve this by adding a unique constraint or index on the VIN and title columns, for example a Rails migration that does something like this:

class AddUniqueIndexToListingsOnVinAndTitle < ActiveRecord::Migration
  def change
    add_index :listings, [:vin, :title], unique: true
  end
end

Some more info you might find useful (not sure whether you're using PostgreSQL or MySQL):

jpn
  • 786
  • 6
  • 20
  • This is what I was thinking too (I was doing some research on this question as you posted your answer). In my test, the import throws a `PG::InvalidColumnReference: ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification` when you don't have a unique index on the conflict target column. I wasn't able to reproduce the duplicate records because of this. – Derek Hopper Jan 01 '18 at 21:36
  • Good catch @DerekHopper! It would be good to know more about the setup (I wonder if it's a database that doesn't support upserting?) @SauravPrakash what database are you using, and are there any constraints or indexes on the listings table? – jpn Jan 01 '18 at 22:19
  • @jpn : Adding a unique index does help but now I am getting a 'ConstraintException: UNIQUE constraint failed' error for duplicate records. Which indicates it does check that it's a duplicate one but on_duplicate_key_update still doesn't work and won't update the data. – Saurav Prakash Jan 02 '18 at 02:27
  • @SauravPrakash if the migration to create the unique constraint is failing, its probably because there is duplicate data in the database which is violating the unique constraint. I think you'll need to clean up the database before you can create the unique constraint. – John Jan 02 '18 at 02:40
  • @John I took care of the migration error. This error is arising when I am using the import function while uploading a csv twice. First time, its all dandy. Second time I get the error. What should happen is it should update the changes to the records found but it isn't. – Saurav Prakash Jan 02 '18 at 02:49
  • @SauravPrakash in the image you attached to this post, the exception shows that you're running SQLite3 as your database. The [`on_duplicate_key_update` AR-Import documentation](https://github.com/zdennis/activerecord-import/wiki/On-Duplicate-Key-Update) only lists MySQL and PostgresQL as supported. – John Jan 02 '18 at 07:49
  • @SauravPrakash I've updated my answer based on your screenshot: you cannot do this with SQLite and activerecord-import – jpn Jan 02 '18 at 07:49
  • @jpn LOL, same timing :) – John Jan 02 '18 at 07:51
  • Thank you, everyone! I switched to MySQL and it works. Will test on PG as well. This is worth a blog post! :) – Saurav Prakash Jan 03 '18 at 02:05