簡體   English   中英

如何在spark-avro 2.4模式中設置邏輯類型?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM