Автор Киба здесь!
Это обычное требование, но оно может (и не относится к Кибе) быть более или менее сложным в обращении. Вот несколько моментов, о которых вам нужно подумать.
Обработка внешних ключей
Основная проблема заключается в том, что вы захотите сохранить отношения между службами и бронированиями, как только они будут вставлены.
Внешние ключи с использованием бизнес-ключей
Первый (самый простой) способ справиться с этим - использовать ограничение по внешнему ключу для «номера бронирования» и обязательно указывать этот номер бронирования в каждой строке обслуживания, чтобы вы могли использовать его позже в своих запросах. , Если вы сделаете это (см. https://stackoverflow.com/a/18435114/20302), вам нужно будет установить уникальное ограничение на «номер бронирования» в целевой таблице заказов.
Внешние ключи с использованием первичных ключей
Если вместо этого вы предпочитаете иметь booking_id
, который указывает на ключ bookings
table id
, все немного сложнее.
Если это однократный импорт, ориентированный на пустую таблицу, я рекомендую произвольно форсировать первичный ключ, используя что-то вроде:
transform do |r|
@row_index ||= 0
@row_index += 1
r.merge(id: @row_index)
end
Если это не разовый импорт, вам необходимо:
* Upsert бронирования в первый проход
* Во втором проходе ищите (через SQL-запросы) «заказы», чтобы выяснить, что id
хранить в booking_id
, а затем запустите службы
Как вы видите, это немного больше работы, поэтому придерживайтесь варианта 1, если у вас нет строгих требований по этому поводу (хотя вариант 2 более устойчив в долгосрочной перспективе).
Пример реализации (с использованием Kiba Pro и бизнес-ключей)
Самый простой способ добиться этого (при условии, что ваша цель - Postgres) - это использовать назначение массовой вставки / переноса в Kiba Pro .
Это будет так (за один проход):
extend Kiba::DSLExtensions::Config
config :kiba, runner: Kiba::StreamingRunner
source Kiba::Common::Sources::Enumerable, -> { Dir["input/*.json"] }
transform { |r| JSON.parse(IO.read(r)).fetch('bookings') }
transform Kiba::Common::Transforms::EnumerableExploder
# SNIP (remapping / renaming of fields etc)
first_destination = nil
destination Kiba::Pro::Destinations::SQLBulkInsert,
row_pre_processor: -> (row) { row.except("services") },
dataset: -> (dataset) {
dataset.insert_conflict(target: :booking_number)
},
after_read: -> (d) { first_destination = d }
destination Kiba::Pro::Destinations::SQLBulkInsert,
row_pre_processor: -> (row) { row.fetch("services") },
dataset: -> (dataset) {
dataset.insert_conflict(target: :service_number)
},
before_flush: -> { first_destination.flush }
Здесь мы перебираем каждый входной файл, анализируем его и собираем «резервирования», затем генерируем по одной строке на элемент «резервирования».
У нас есть 2 пункта назначения, выполняющих «upsert» (вставка или обновление), плюс один трюк, чтобы мы сохранили родительские строки перед тем, как вставить дочерние, чтобы избежать сбоя из-за пропущенной остроконечной записи.
Конечно, вы можете реализовать это самостоятельно, но это немного работы!
Если вам нужно использовать внешние ключи на основе первичного ключа, вам придется (вероятно) разделить на 2 прохода (по одному для каждого адресата), а затем добавить некоторую форму поиска в середине.
Заключение
Я знаю, что это не тривиально (в зависимости от того, что вам нужно, и будете ли вы использовать Kiba Pro или нет), но, по крайней мере, я делюсь шаблонами, которые я использую в таких ситуациях.
Надеюсь, это немного поможет!