Sha256: bde7ef942b99b3c8a56ee90805db6819c3efc8d63c3e474bf46843045eb9a23d

Contents?: true

Size: 1.96 KB

Versions: 1

Compression:

Stored size: 1.96 KB

Contents

# frozen_string_literal: true

module Importeur
  class ActiveRecordPostgresLoader
    BATCH_SIZE = 500

    def initialize(model, primary_key)
      @model = model
      @primary_key = primary_key
    end

    def call(data)
      imported_ids = []
      seen = Set.new
      data.each_slice(BATCH_SIZE) do |batch|
        batch_ids = batch.map { |attrs| attrs[primary_key] }
        imported_ids.concat(batch_ids)
        records = records_for_batch(batch_ids)
        store_batch(batch, records, seen)
      end
      delete_old_records(imported_ids)
    end

    private

    attr_reader :model, :primary_key

    def store_batch(batch, records, seen)
      batch.each do |attrs|
        next unless seen.add?(attrs[primary_key])
        store_record(attrs, records)
      end
    end

    def store_record(attrs, records)
      record = records.fetch(attrs[primary_key], model.new)
      record.assign_attributes(attrs)
      record.deleted_at = nil if paranoid?
      return unless record.changed?
      record.imported_at = Time.now
      record.save!
    end

    def records_for_batch(batch_ids)
      relation = model
      relation = relation.with_deleted if paranoid?
      relation
        .joins(batch_lookup_join_sql('INNER', batch_ids))
        .index_by(&primary_key)
    end

    def delete_old_records(imported_ids)
      # Basically `self.class.model.where.not(primary_key => imported_ids)`, but
      # more efficient in this case.
      model
        .joins(batch_lookup_join_sql('LEFT', imported_ids))
        .where('imported.primary_key' => nil)
        .delete_all
    end

    def batch_lookup_join_sql(kind, ids)
      <<-SQL
        #{kind} JOIN (SELECT unnest(ARRAY[#{ids.join(',')}]::int[]) AS primary_key) AS imported
                  ON imported.primary_key = #{model.table_name}.#{primary_key}
      SQL
    end

    def paranoid?
      return @paranoid if defined?(@paranoid)
      @paranoid = model.ancestors.map(&:to_s).include?('ActsAsParanoid::Core')
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
importeur-0.3.0 lib/importeur/active_record_postgres_loader.rb