Spark中給RDD[Row]中的Row動態增加一個或者多個字段

Spark 中動態的給Row新增字段

我們知道,在Spark中,我們讀取csv或者MySQL等關係型數據庫時,可以直接得到DataFrame.我們要想新增一個字段,可以通過DataFrame的API或者註冊一個臨時表,通過SQL語句能很方便的實現給增加一個或多個字段.

但是,當我們將DataFrame轉化成RDD的時候,RDD裏面的類型就是Row,如果此時,要想再增加一個字段,該怎麼辦呢?

Show Time

package com.emmm.test.scala

import org.apache.spark.SparkConf
import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema
import org.apache.spark.sql.types.{StringType, StructType}
import org.apache.spark.sql.{Row, SparkSession}

object Emmm {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    conf.setMaster("local[*]")
    conf.setAppName(this.getClass.getSimpleName)
    conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
    conf.set("spark.kryo.registrationRequired", "true")
    conf.registerKryoClasses(Array(
      Class.forName("scala.collection.mutable.WrappedArray$ofRef"),
      Class.forName("org.apache.spark.sql.types.StringType$"),
      classOf[TPerson],
      classOf[org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema],
      classOf[org.apache.spark.sql.types.StructType],
      classOf[org.apache.spark.sql.types.StructField],
      classOf[org.apache.spark.sql.types.Metadata],
      classOf[Array[TPerson]],
      classOf[Array[org.apache.spark.sql.Row]],
      classOf[Array[org.apache.spark.sql.types.StructField]],
      classOf[Array[Object]]
    ))
    val spark = SparkSession.builder()
      .config(conf)
      .getOrCreate()
    import spark.implicits._
    // 使用樣例類創建RDD並轉化成DF後又回到RDD
    spark.sparkContext.parallelize(Seq(TPerson("zs", "21"), TPerson("ls", "25"))).toDF().rdd
      .map(row => {
        // 打印schema
        println(row.schema)
        // 得到Row中的數據並往其中添加我們要新增的字段值
        val buffer = Row.unapplySeq(row).get.map(_.asInstanceOf[String]).toBuffer
        buffer.append("男") //增加一個性別
        buffer.append("北京") //增肌一個地址

        // 獲取原來row中的schema,並在原來Row中的Schema上增加我們要增加的字段名以及類型.
        val schema: StructType = row.schema
          .add("gender", StringType)
          .add("address", StringType)
        // 使用Row的子類GenericRowWithSchema創建新的Row
        val newRow: Row = new GenericRowWithSchema(buffer.toArray, schema)
        // 使用新的Row替換成原來的Row
        newRow
      }).map(row => {
      // 打印新的schema
      println(row.schema)
      // 測試我們新增的字段
      val gender = row.getAs[String]("gender")
      // 獲取原本就有的字段
      val name = row.getAs[String]("name")
      val age = row.getAs[String]("age")
      // 獲取新的字段
      val address = row.getAs[String]("address")
      // 輸出查看結果
      println(s"$name-$age-$gender-$address")
      row
    }).collect()
    spark.stop()
  }

  /**
    * 樣例類
    *
    * @param name name屬性
    * @param age  age屬性
    */
  case class TPerson(name: String, age: String)

}

TODO

PS:不要問爲什麼生成RDD又轉化成DataFrame又轉化成RDD,因爲確實在實際中用到了Row新增字段的需求,這麼轉只是爲了測試.

最後,免費贈送kyro的使用.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章