Avro:将UNION模式转换为RECORD模式

时间:2016-07-01 13:09:31

标签: scala apache-spark avro parquet

我为简单的类层次结构自动生成了Avro架构:

trait T {def name: String}
case class A(name: String, value: Int) extends T
case class B(name: String, history: Array[String]) extends T

看起来像这样:

 [{
  "name": "org.example.schema.raw.A",
  "type": "record",
  "fields": [{
    "name": "name",
    "type": "string"
  }, {
    "name": "value",
    "type": "int"
  }]
}, {
  "name": "org.example.schema.raw.B",
  "type": "record",
  "fields": [{
    "name": "name",
    "type": "string"
  }, {
    "name": "history",
    "type": {
      "type": "array",
      "items": "string"
    }
  }]
}]

此模式适用于使用普通Avro API将数据从JSON读取到GenericRecord。我尝试实现的下一步是使用GenericRecord将所有此类AvroParquetWriter对象存储到单个镶木地板文件中:

val writer = new AvroParquetWriter[GenericRecord](file, schema)
writer.write(record)
writer.close()

此代码在第一行

失败
java.lang.IllegalArgumentException: Avro schema must be a record.
at parquet.avro.AvroSchemaConverter.convert(AvroSchemaConverter.java:96)
at parquet.avro.AvroParquetWriter.writeSupport(AvroParquetWriter.java:137)
at parquet.avro.AvroParquetWriter.<init>(AvroParquetWriter.java:54)
at parquet.avro.AvroParquetWriter.<init>(AvroParquetWriter.java:86)

难怪,AvroSchemaConverter包含以下几行:

if (!avroSchema.getType().equals(Schema.Type.RECORD)) {
      throw new IllegalArgumentException("Avro schema must be a record.");
}

我的架构类型是UNION。将UNION模式映射(合并)到RECORD模式或任何其他建议中的任何想法/帮助都非常感谢。

1)使用union scheme将输入中的JSON读入GenericRecord 2)获取或创建AvroParquetWriter类型:

val writer = writers.getOrElseUpdate(record.getType, new AvroParquetWriter[GenericRecord](getPath(record.getType), record.getShema)

3)将记录写入文件:

writer.write(record)

4)当从输入中消耗所有数据时关闭所有写入器:

writers.values.foreach(_.close())

5)将目录中的数据加载到Spark SQL DataFrame:

sqlContext.option("mergeSchema", "true").parquet("/tmp/data/")

6)数据可以按原样处理或存储 - 它已经由Spark合并:

df.write.format("parquet").save("merged.parquet")

2 个答案:

答案 0 :(得分:0)

要回答有关合并的问题:您可以使用以下case class Merged(name: String, value: Option[Int], history: Option[Array[String]])并使用生成的模式来编写数据。 通常,如果您的架构向前兼容A和B,它将正确写入。

或者,正如你所说,avro不会让你把所有数据都写在同一个文件中,也许你可以按类型分割输出,每种类型写一个文件?我知道在我能想到的大多数用例中我可能会这样做,但也许它并不适合你。

答案 1 :(得分:0)

您可以使用Case类(即Record)来包装特征。

案例类Reord [K](键:K,值:T)