我想用Kiba-ETL将嵌套的JSON有效负载转换为关系表。这是一个简化的伪JSON有效负载:
{
"bookings": [
{
"bookingNumber": "1111",
"name": "Booking 1111",
"services": [
{
"serviceNumber": "45",
"serviceName": "Extra Service"
}
]
},
{
"bookingNumber": "2222",
"name": "Booking 2222",
"services": [
{
"serviceNumber": "1",
"serviceName": "Super Service"
},
{
"serviceNumber": "2",
"serviceName": "Bonus Service"
}
]
}
]
}
如何将有效负载转换为两个表:
我在Wiki,博客等网站上阅读了Kiba::Common::Transforms::EnumerableExploder
的帮助下产生多行的信息
您是通过产生多行(预订和多种服务)来解决我的用例,还是实施Destination
来接收整个预订并调用某些子目的地(即创建或更新服务)?
答案 0 :(得分:1)
木场的作者在这里!
这是一个常见的要求,但它可能(或并非特定于Kiba)变得或多或少复杂。这是您需要考虑的几点。
这里的主要问题是,一旦插入服务和预订,您将希望保持它们之间的关系。
第一种(最简单的)处理方法是对“预订号”使用外键约束,并确保在每个服务行中插入该预订号,以便以后在查询中使用它。如果这样做(请参阅https://stackoverflow.com/a/18435114/20302),则必须在预订表目标中为“预订号”设置唯一约束。
如果您宁愿使用指向booking_id
表bookings
键的id
,则情况会更加复杂。
如果这是一次针对空白表的一次性导入,我建议您使用以下命令任意强制主键:
transform do |r|
@row_index ||= 0
@row_index += 1
r.merge(id: @row_index)
end
如果这不是一次性导入,则必须:
*在第一阶段通过预订
*在第二遍中,(通过SQL查询)查找“预订”以找出要存储在id
中的booking_id
,然后对服务进行升序
如您所见,这还有很多工作要做,因此,如果您对此没有强烈要求,则坚持使用选项1(尽管从长远来看,选项2更可靠)。
实现此目标的最简单方法(假设您的目标是Postgres)是使用Kiba Pro的SQL Bulk Insert/Upsert destination。
它会以这种方式(单次通过):
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 }
在这里,我们遍历每个输入文件,将其解析并获取“ bookings”,然后为“ bookings”的每个元素生成一行。
我们有2个目的地,进行“ upsert”(插入或更新),再加上一个技巧,以确保在插入子代之前保存父行,以避免由于缺少指向记录而导致失败。
您当然可以自己实现,但这还是有些工作!
如果您需要使用基于主键的外键,则必须(可能)分成两遍(每个目标一个),然后在中间添加某种形式的查找。
我知道这并非易事(取决于您的需求,以及是否使用Kiba Pro),但是至少我正在分享在这种情况下使用的模式。 / p>
希望这会有所帮助!