Ruby on Rails

rails で 巨大な csv ファイルをストリーム処理する方法

  • POST
1 GB を超えるような csv ファイルを一度に読み込むとメモリエラーが発生する場合がある さらに処理時間もすごくかかるため、ストリーム処理に変更などして処理の高速化した際の方法を備忘録として残しておく 方法 File#open メソッドと CSV#new メソッドを組み合わせて実現する 環境 ruby: 3.0.4 rails: 6.1.6.1 実際のコード csv ファイルの特定の文字列を置換する処理 前提 header 付の csv ファイル リファクタリング前 class Converter def self.convert # 読み取り専用でファイルを開く read_only_file = File.open("{ファイルパス}" , "r") # 保存用バッファ buffer = read_only_file.read() # バッファの中身を変換 buffer.gsub!({置換対象文字列}, {置換文字列}) # ファイルを書き込みモードで開き直す file = File.open("{ファイルパス}" , "w") # 変更内容を出力する file.write(buffer) # close read_only_file.close() file.close() end end リファクタリング後 require 'csv' require 'fileutils' class Converter def self.convert file_basename = "{拡張子を除いたファイル名}" # 読み取り専用でファイルを開く File.open("#{file_basename}.csv", "r") do |file| break if File.zero?("#{file_basename}.csv") # 一時ファイルを書き込みモードで開く write_file = File.open("#{file_basename}_tmp.csv", "w") # csv の header を書き込む write_file.write(file.readline) file.rewind CSV.new(file, headers: true).each do |row| # 読み込んだ行内の文字列を置換する write_file.write(row.to_s.gsub({置換対象文字列}, {置換文字列})) end write_file.close end FileUtils.move("#{file_basename}_tmp.csv", "#{file_basename}.csv") if File.exist?("#{file_basename}_tmp.csv") end end ヘッダは force quote なし ボディは force quote ありのようにしたい場合

rails で別名がつけられているカラムを pluck で取得する方法

  • POST
rails で別名がつけられているカラムを pluck で取得する方法を備忘録として残しておく 方法 シンボルでの指定ではなく、文字列で as 句含めて指定する 環境 rails: 6.1.6.1 実際のコード 例. Book と Category クラスの関係が 1 対 多 である場合 # Book クラス class Book < ApplicationRecord has_many :categories end # Category クラス class Category < ApplicationRecord belongs_to :book end # データ準備 book = Book.create!(name: 'サンプル書籍') book.categories << Category.create(name: 'ビジネス') book.categories << Category.create(name: '自己啓発') # 別名を付与したカラムを取得 category_names = Book.joins(:categories) .select(:name, 'categories.name as category_name') .where(id: 1) .pluck('categories.name as category_name') # (1.2ms) SELECT categories.name as category_name FROM "books" INNER JOIN "categories" ON "categories"."book_id" = "books"."id" WHERE "books"."id" = $1 [["id", 1]] #=> ["ビジネス", "自己啓発"] # 別テーブルの同名カラムも可能 category_names = Book.joins(:categories) .select(:name, 'categories.name as category_name') .where(id: 1) .pluck(:name, 'categories.name as category_name') # (1.0ms) SELECT "books"."name", categories.name as category_name FROM "books" INNER JOIN "categories" ON "categories"."book_id" = "books"."id" WHERE "books"."id" = $1 [["id", 1]] #=> [["サンプル書籍", "ビジネス"], ["サンプル書籍", "自己啓発"]]