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的使用.