将case类传递给Spark UDF

时间:2017-08-24 14:38:16

标签: scala apache-spark user-defined-functions case-class

我有一个scala-2.11函数,它根据提供的类类型从Map创建一个case类。

def createCaseClass[T: TypeTag, A](someMap: Map[String, A]): T = {

    val rMirror = runtimeMirror(getClass.getClassLoader)
    val myClass = typeOf[T].typeSymbol.asClass
    val cMirror = rMirror.reflectClass(myClass)

    // The primary constructor is the first one
    val ctor = typeOf[T].decl(termNames.CONSTRUCTOR).asTerm.alternatives.head.asMethod
    val argList = ctor.paramLists.flatten.map(param => someMap(param.name.toString))

    cMirror.reflectConstructor(ctor)(argList: _*).asInstanceOf[T]
  }

我试图在火花数据帧的上下文中将其用作UDF。但是,我不确定通过案例类的最佳方法是什么。以下方法似乎不起作用。

def myUDF[T: TypeTag] = udf { (inMap: Map[String, Long]) =>
    createCaseClass[T](inMap)
  }

我正在寻找类似的东西 -

case class MyType(c1: String, c2: Long)

val myUDF = udf{(MyType, inMap) => createCaseClass[MyType](inMap)}

感谢您解决此问题的想法和建议。

2 个答案:

答案 0 :(得分:2)

  

但是,我不确定通过案例类

的最佳方法是什么

不可能将case类用作用户定义函数的参数。 SQL StructTypes被映射为动态类型(缺少更好的单词)Row个对象。

如果您想对静态类型的对象进行操作,请使用静态类型Dataset

答案 1 :(得分:1)

从try和error中我了解到,存储在Dataframe或Dataset中的数据结构都是使用org.apache.spark.sql.types

您可以看到:

df.schema.toString

像Int,Double这样的基本类型存储如下:

StructField(fieldname,IntegerType,true),StructField(fieldname,DoubleType,true)

像case类这样的复杂类型被转换为嵌套类型的组合:

StructType(StructField(..),StructField(..),StructType(..))

示例代码:

case class range(min:Double,max:Double)
org.apache.spark.sql.Encoders.product[range].schema

//Output:
 org.apache.spark.sql.types.StructType = StructType(StructField(min,DoubleType,false), StructField(max,DoubleType,false))

在这种情况下,UDF参数类型是Row,或者在存储案例类数组时的Seq [Row]

基本的调试技术是打印到字符串:

 val myUdf = udf( (r:Row) =>   r.schema.toString )

然后,看到发生了:

df.take(1).foreach(println) //