Обновите MongoDB с помощью массива из таблицы соединений CSV - PullRequest
0 голосов
/ 17 октября 2011

Я хочу добавить встроенный массив Majors в мою коллекцию MongoDB Colleges следующим образом:

{ "_id" : ObjectID("abc123"), 
"code" : 123456, 
"name" : "Stanford University", 

"majors" :["Agriculture", "Business", "Computer Science"... ] }

У меня есть CSV, который, по сути, является таблицей соединений CollegesMajors (в системе отсчета SQL). Похоже:

Код ----- MajorCode ---- Уровень

123456 ---- 98765 --------- 2

123456 ---- 99999 --------- 2

Как мне встроить эти специальности (я могу сначала перевести MajorCode на его настоящее имя) в мою коллекцию MongoDB Colleges?

Должен быть какой-то способ, которым я могу (псевдокод):

db.colleges.update { match the code, update with array }

Может быть, мне нужно сначала превратить CSV в файл JSON?

Заранее спасибо!

UPDATE!

Это ошибка, которую я получаю при попытке выполнить инструкции Тило - это, вероятно, связано с тем, что я плохо знаком с консолью rails. Он считает, что инструкция CSV.read загружает нулевой объект.

ruby-1.9.2-head :024 > csvAA = CSV.read( '/Users/administrator/dropbox/schoolninja/1College_Data/2009formatted/college_majors.csv' );
ruby-1.9.2-head :025 >   headersA = csvAA.shift;
ruby-1.9.2-head :026 >   csvAH = csvAA.map {|row| Hash[*headersA.zip(row).flatten] };
ruby-1.9.2-head :027 >   csvAH.each do |rowH|
ruby-1.9.2-head :028 >     c = College.find_by_code( rowH['Code'] )
ruby-1.9.2-head :029?>     c.majors ||= []
ruby-1.9.2-head :030?>   c.majors << ( rowH['title'] )
ruby-1.9.2-head :031?>   c.save
ruby-1.9.2-head :032?>   end
SyntaxError: (irb):24: unknown regexp options - adtratr
(irb):24: syntax error, unexpected tCONSTANT, expecting ')'
...opbox/schoolninja/1College_Data/2009formatted/college_majors...
...                               ^
(irb):24: syntax error, unexpected tIDENTIFIER, expecting $end
...nja/1College_Data/2009formatted/college_majors.csv );
...                               ^
    from /Users/administrator/.rvm/gems/ruby-1.9.2-head/gems/railties-3.0.9/lib/rails/commands/console.rb:44:in `start'
    from /Users/administrator/.rvm/gems/ruby-1.9.2-head/gems/railties-3.0.9/lib/rails/commands/console.rb:8:in `start'
    from /Users/administrator/.rvm/gems/ruby-1.9.2-head/gems/railties-3.0.9/lib/rails/commands.rb:23:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

1 Ответ

2 голосов
/ 17 октября 2011

ОБНОВЛЕНИЕ:

Пожалуйста, обратите внимание на новый Ruby Gem " smarter_csv " на случай, если вы хотите обновить MongoDB из записей CSV-файлов.Он имеет множество полезных функций, в том числе фрагментирование больших файлов, и возвращает результаты в виде фрагментов в виде массивов хэшей.

См. Также:

Обновленный ответ:

require 'smarter_csv'
filename = '/tmp/college_majors.csv'
# The :key_mapping renames one column and ignores the column "Level"
# Each line of the CSV-file is converted into a hash with keys: :major_code, :code
n = SmarterCSV.process(filename, {:chunk_size => 10,
                       :key_mapping => {:level => nil} }) do |chunk|
  # We're passing a block in to process each resulting hash / row (block takes array of hashes).
  # If the CSV-file is large, we can process it in parallel in chunks (using Resque Workers).
  # For this we would extract the following block into a Resque worker and instead just create 
  #   a new Resque job for each chunk.

  chunk.each do |hash|
    c = College.find_by_code( hash[:code] )
    c.majors ||= []
    c.majors << majorcode_to_name( hash[:major_code] )
    c.save
  end
end

Предыдущий ответ:

Предполагая, что у вас уже есть метод majorcode_to_name ():

require 'csv'

csvAA = CSV.read( csv_filename )   # returns an array of arrays
# => [["Code", "MajorCode", "Level"], ["123456", "98765", "2"], ["123456", "99999", "2"]] 

headersA = csvAA.shift   # extract the headers into an array
# => ["Code", "MajorCode", "Level"]

# turn the "array of arrays" which contain the CVS data, into an "Array of Hashes":
csvAH = csvAA.map {|row| Hash[*headersA.zip(row).flatten] }
# => [{"Code"=>"123456", "MajorCode"=>"98765", "Level"=>"2"}, {"Code"=>"123456", "MajorCode"=>"99999", "Level"=>"2"}] 

csvAH.each do |rowH|
  c = College.find_by_code( rowH['Code'] )
  c.majors ||= []                                 # initialize as empty array if it doesn't exist
  c.majors << majorcode_to_name( rowH['MajorCode'] )
  c.save
end

Если ваш CSV-файл действительно большой, вы можете немного изменить этот код, чтобы вам не приходилось считывать все данные в ОЗУ

require 'csv'

headersA = nil
CSV.foreach do |row|
  if headersA.nil?
    headersA = row 
  else
    rowH = Hash[*headersA.zip(row).flatten]

    c = College.find_by_code( rowH['Code'] )
    c.majors ||= []                                 # initialize as empty array if it doesn't exist
    c.majors << majorcode_to_name( rowH['MajorCode'] )
    c.save
  end
end

РЕДАКТИРОВАТЬЯ должен был упомянуть, почему я преобразовал результат CSV.read в массив хэшей. Основная причина - сделать код независимым от порядка заголовков в файле CSV, что делает код немного более устойчивым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...