[RUBY] Exporter (sortie) une grande quantité de csv comme les informations de journal sur l'application WEB avec l'application Rails

Les données de sortie sont trop volumineuses et prennent du temps

J'ai créé une fonction pour générer des informations de journal à partir de la base de données, car je souhaite analyser les informations de journal du client.
En raison de la sortie du journal après le début de l'opération réelle, il a fallu beaucoup de temps pour sortir le csv si le journal était d'environ 1 ou 2 jours. Cependant, il faut trop de temps pour que toute la période soit utile. Je suis tombé dans une telle situation. Quand je l'ai essayé, il n'était pas sorti même après 15 minutes, et il était dans l'état "Je ne peux pas l'utiliser à coup sûr ...", j'ai donc essayé de sortir le journal en appuyant directement sur SQL.

La méthode utilisée jusque-là

Sortons les données DB au format CSV avec Rails | Notes diverses qui n'ont jamais été vues | note J'avais l'habitude de sortir la sortie csv commune en utilisant la bibliothèque CSV comme celle-ci. (C'est ça? bibliothèque csv (Manuel de référence Ruby 2.7.0)
Cette fois, j'utilise la bibliothèque CSV pour émettre des requêtes avec du SQL brut et des journaux de sortie, mais l'utilisation est légèrement différente.
La méthode to_sql est utilisée pour convertir le SQL émis par ActiveRecord en SQL brut et le sortir.

app/services/large_csv_exporter_service.rb

class LargeCsvExporterService
  require 'csv'

  attr_accessor :table, :column, :host, :username, :password, :database

  def initialize(table, column, host, username, password, database)
    @table = table
    @column = column
    @host = host
    @username = username
    @password = password
    @database = database
  end

  def set_file_name(file_name)
    @csv_file_name = file_name
  end

  def set_query(query)
    results = @client.query(query)
    results
  end

  def write_csv(results)
    # File.write(@csv_file_name, encoding: Encoding::SJIS) unless File.exist? @csv_file_name
    CSV.open("./tmp/csv_export/#{@csv_file_name}", "w:sjis") do |csv|
      csv << results.fields
      results.each do |row|
        csv << row.values
      end
    end
  end

  def export_csv_file
    filepath = "./tmp/csv_export/#{@csv_file_name}"
    stat = File::stat(filepath)
    return filepath, stat, @csv_file_name
  end

  def delete_created_csv_file
    if File.exist?("./tmp/csv_export/#{@csv_file_name}")
      File.delete("./tmp/csv_export/#{@csv_file_name}")
    end
  end

  def set_client
    # cache_rows:Faux pour éviter d'utiliser la mémoire
    @client = Mysql2::Client.new(
      host: host,
      username: username,
      password: password,
      database: database,
      stream: true,
      cache_rows: false,
    )
  end
end

J'utilise le service dans un contrôleur. A partir du milieu csv_export = LargeCsvExporterService.new (" access_logs "," * ", ENV [" DB_D_HOST "], ENV [" DB_D_USERNAME "]," ", ENV [" DB_D_NAME "]). ʻENV ["DB_D_HOST"] `fait référence au nom de base de données ou au nom d'utilisateur de .env (dotenv).
Je ne peux pas écrire un joli code, alors regardez bien.

app/controllers/admins/access_logs_controller.rb

    def export_csv(params_q)
      @q = AccessLog.order(id: :asc).ransack(params_q)
      user_type_param = params[:q][:user_type].empty? ? ["user", "admin", "supplier"] : params[:q][:user_type].split(':')[1]
      methoda_type_param = params[:q][:method_type] == "ALL_TYPE" ? AccessLog.distinct.pluck(:method_type).compact : params[:q][:method_type]
      access_dates = AccessLog.order(access_date: :asc).to_a
      from_time = params[:q][:created_at_gteq].empty? ? access_dates.first.access_date : params[:q][:created_at_gteq].to_time
      to_time = params[:q][:created_at_lt].empty? ? access_dates.last.access_date : params[:q][:created_at_lt].to_time

      sql_query = AccessLog.where(user_type: user_type_param).where(method_type: methoda_type_param).where(created_at: [from_time..to_time]).to_sql

      csv_export = LargeCsvExporterService.new("access_logs", "*", ENV["DB_D_HOST"], ENV["DB_D_USERNAME"], "", ENV["DB_D_NAME"])
      csv_export.set_file_name("AccessLog_#{Time.zone.now.strftime("%Y%m%d%S")}.csv")
      csv_export.set_client
      # results = csv_export.set_query("SELECT * FROM access_logs WHERE created_at BETWEEN '#{from_time}' AND '#{to_time}' AND method_type = '#{methoda_type_param}' AND user_type = '#{user_type_param}'" )
      results = csv_export.set_query(sql_query)
      csv_export.write_csv(results)
      filepath, stat, csv_file_name = csv_export.export_csv_file
      send_result = send_file(filepath, :filename => csv_file_name, :length => stat.size)
      # csv_export.delete_created_csv_file
    end

La requête SQL renvoie quelque chose comme ça sur une seule ligne

SELECT `access_logs`.* 
FROM   `access_logs` 
WHERE  `access_logs`.`user_type` = 'user' 
       AND `access_logs`.`method_type` IN ( 'GET', 'POST' ) 
       AND `access_logs`.`created_at` BETWEEN 
           '2020-08-25 00:00:00' AND '2020-09-27 23:55:35' 

Eh bien, comme ça, je n'ai pas réellement mesuré le temps, mais maintenant je peux produire environ 100 000 fichiers en environ 10 secondes.

Recommended Posts

Exporter (sortie) une grande quantité de csv comme les informations de journal sur l'application WEB avec l'application Rails
À peu près le flux de développement d'applications Web avec Rails.
Créez un grand nombre d'enregistrements avec une seule commande à l'aide du fichier seeds.rb Ruby on Rails
La première application WEB avec Spring Boot-Making a Pomodoro timer-
Vérifier le fonctionnement de deux rôles avec une application de chat
Une note sur la fonction de départ de Ruby on Rails
Comment résoudre le problème de non-sortie du journal de connexion lorsque l'application Web est arrêtée
Représentez graphiquement les informations du capteur de Raspberry Pi en Java et vérifiez-les avec un navigateur Web
L'histoire du refactoring avec un assistant personnel pour la première fois dans une application Rails
Comparaison du développement d'applications WEB avec Rails et Java Servlet + JSP
[Rails / Heroku / MySQL] Comment réinitialiser la base de données de l'application Rails sur Heroku
Une histoire qui a eu du mal avec l'introduction de Web Apple Pay
J'ai créé un outil pour afficher la différence du fichier CSV
[Rails] Implémentation de la fonction d'exportation CSV
[Ruby on Rails] Fonction de sortie CSV
Créer une application Web avec Javalin
[Java] Déployer une application Web créée avec Eclipse + Maven + Ontology avec Heroku
Volume d'essais pour créer une application Web Java sur Windows Server 2016