![](/img/trans.png)
[英]to_avro function for schema registry not found in spark-avro
[英]How can I set a logicalType in a spark-avro 2.4 schema?
我們從應用程序中的avro文件中讀取時間戳信息。 我正在測試從Spark 2.3.1到Spark 2.4的升級,其中包括新內置的spark-avro集成。 但是,我無法弄清楚如何告訴avro模式我希望時間戳具有“ timestamp-millis”的邏輯類型,而不是默認的“ timestamp-micros”。
僅使用Databricks spark-avro 4.0.0軟件包在Spark 2.3.1下查看測試avro文件,我們就有以下字段/架構:
{"name":"id","type":["string","null"]},
{"name":"searchQuery","type":["string","null"]},
{"name":"searchTime","type":["long","null"]},
{"name":"score","type":"double"},
{"name":"searchType","type":["string","null"]}
自該歷時存儲很長時間以來,其中的searchTime有毫秒。 一切都很好。
當我將其升級到Spark 2.4和內置的spark-avro 2.4.0軟件包時,我有了這些較新的字段/模式:
{"name":"id","type":["string","null"]},
{"name":"searchQuery","type":["string","null"]},
{"name":"searchTime","type":[{"type":"long","logicalType":"timestamp-micros"},"null"]},
{"name":"score","type":"double"},
{"name":"searchType","type":["string","null"]}
可以看到,底層類型仍然很長,但是現在使用“ timestamp-micros”的邏輯類型進行了擴充。 這正是發行說明所說的那樣,但是,我找不到一種指定模式以使用“ timestamp-millis”選項的方法。
這將成為一個問題,當我將一個Timestamp對象寫入到avro文件中時,該對象初始化為在新紀元后10,000秒時,它將被讀回10,000,000秒。 在2.3.1 / databricks-avro下,它很長,沒有任何相關信息,所以它隨它進入就出來了。
當前,我們通過思考感興趣的對象來構建模式,如下所示:
val searchSchema: StructType = ScalaReflection.schemaFor[searchEntry].dataType.asInstanceOf[StructType]
我試圖通過創建一個修改過的架構來嘗試擴大它,以嘗試替換與searchTime條目相對應的StructField,如下所示:
val modSearchSchema = StructType(searchSchema.fields.map {
case StructField(name, _, nullable, metadata) if name == "searchTime" =>
StructField(name, org.apache.spark.sql.types.DataTypes.TimestampType, nullable, metadata)
case f => f
})
但是,spark.sql.types中定義的StructField對象沒有可以增加其中的dataType的邏輯類型的概念。
case class StructField(
name: String,
dataType: DataType,
nullable: Boolean = true,
metadata: Metadata = Metadata.empty)
我還嘗試了通過兩種方式從JSON表示形式創建模式:
val schemaJSONrepr = """{
| "name" : "id",
| "type" : "string",
| "nullable" : true,
| "metadata" : { }
| }, {
| "name" : "searchQuery",
| "type" : "string",
| "nullable" : true,
| "metadata" : { }
| }, {
| "name" : "searchTime",
| "type" : "long",
| "logicalType" : "timestamp-millis",
| "nullable" : false,
| "metadata" : { }
| }, {
| "name" : "score",
| "type" : "double",
| "nullable" : false,
| "metadata" : { }
| }, {
| "name" : "searchType",
| "type" : "string",
| "nullable" : true,
| "metadata" : { }
| }""".stripMargin
第一次嘗試只是從中創建一個DataType
// here spark is a SparkSession instance from a higher scope.
val schema = DataType.fromJSON(schemaJSONrepr).asInstanceOf[StructType]
spark.read
.schema(schema)
.format("avro")
.option("basePath", baseUri)
.load(uris: _*)
失敗的原因是它無法為searchTime節點創建StructType,因為它具有“ logicalType”。 第二次嘗試是通過傳入原始JSON字符串來簡單地創建模式。
spark.read
.schema(schemaJSONrepr)
.format("avro")
.option("basePath", baseUri)
.load(uris: _*)
不能這樣說:
mismatched input '{' expecting {'SELECT', 'FROM', ...
== SQL ==
{
^^^
我發現在spark-avro API中,有一種方法可以從架構中獲取邏輯類型,但無法弄清楚如何設置它。
如您所見,我在上面的嘗試失敗,我嘗試使用Schema.Parser創建一個Avro模式對象,但是spark.read.schema中唯一接受的類型是String和StructType。
如果有人可以提供有關如何更改/指定此logicalType的見解,我將非常感激。 謝謝
好吧,我想我回答了我自己的問題。 當我修改以編程方式構建的架構以使用顯式的時間戳類型時
val modSearchSchema = StructType(searchSchema.fields.map {
case StructField(name, _, nullable, metadata) if name == "searchTime" =>
StructField(name, org.apache.spark.sql.types.DataTypes.TimestampType, nullable, metadata)
case f => f
})
當我們讀取要讀取的Row對象時,在進行讀取時我沒有改變邏輯。 最初,我們將讀取Long並將其轉換為Timestamp,這是事情發生的地方,因為它以毫秒為單位回讀Long,這使它比我們預期的大1000倍。 通過將讀取更改為讀取Timestamp對象,可以直接讓底層邏輯解決此問題,從而將其從我們(我的手)手中奪走。 所以:
// searchTime = new Timestamp(row.getAs[Long]("searchTime")) BROKEN
searchTime = row.getAs[Timestamp]("searchTime") // SUCCESS
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.