Rails необработанный запрос для формата csv, который должен быть возвращен через контроллер

Я использовал активную запись, чтобы получить мои истории, а затем создать CSV, стандартный способ, как это делается в рельсах. Но у меня много строк, и для этого требуется несколько минут. Я думаю, если бы я мог получить posgresql для выполнения рендеринга csv, я мог бы сэкономить некоторое время.

Вот что я имею прямо сейчас:

query = "COPY stories TO STDOUT WITH CSV HEADER;"
results = ActiveRecord::Base.connection.execute(query);

Но результаты для этого запроса пустые:

 => #<PG::Result:0x00000006ea0488 @connection=#<PG::Connection:0x00000006c62fb8 @socket_io=nil, @notice_receiver=nil, @notice_processor=nil>> 
2.0.0-p247 :053 > result.count
 => 0 

Лучший способ узнать:

2.0.0-p247 :059 >   result.to_json
 => "[]" 

Я подозреваю, что мой контроллер будет выглядеть примерно так:

format.csv { send_data raw_results }

Это работает для обычных запросов, я просто не могу понять синтаксис SQL, чтобы результаты CSV возвращались на рельсы.

UPDATE

Получил экспорт CSV с 120000 мсек до 290 мсек

Моя модель:

def self.to_csv(story_ids)

    csv  = []
    conn = ActiveRecord::Base.connection.raw_connection
    conn.copy_data("COPY (SELECT * FROM stories WHERE stories.id IN (#{story_ids.join(',')})) TO STDOUT WITH (FORMAT CSV, HEADER TRUE, FORCE_QUOTE *, ESCAPE E'\\\\');") do
      while row = conn.get_copy_data
        csv.push(row)
      end
    end
    csv.join("\r\n")
  end

Мой контроллер:

send_data Story.to_csv(Story.order(:created_at).pluck(:id))

Ответ 1

AFAIK вам необходимо использовать метод copy_data для базового соединения базы данных PostgreSQL для этого:

- (Object) copy_data (sql)

Вызов-сл:

conn.copy_data( sql ) {|sql_result| ... } -> PG::Result

Выполните процесс копирования для передачи [sic] данных на сервер или с сервера.

Это вызывает команду SQL COPY через #exec. Ответ на это (если в команде нет ошибки) - это объект PG::Result, который передается блоку с кодом состояния PGRES_COPY_OUT или PGRES_COPY_IN (в зависимости от указанного направления копирования). Затем приложение должно использовать #put_copy_data или #get_copy_data для приема или передачи строк данных и после завершения возврата из блока.

И еще пример:

conn.copy_data "COPY my_table TO STDOUT CSV" do
  while row=conn.get_copy_data
    p row
  end
end

Обертка ActiveRecord для подключения к исходной базе данных не знает, что copy_data, но вы можете использовать raw_connection, чтобы развернуть ее:

conn = ActiveRecord::Base.connection.raw_connection
csv  = [ ]
conn.copy_data('copy stories to stdout with csv header') do
  while row = conn.get_copy_data
    csv.push(row)
  end
end

Это оставило бы вас с массивом строк CSV в csv (одна строка CSV для каждой записи массива), и вы могли бы csv.join("\r\n") получить окончательные данные CSV.