如何在案例类的数据集上调用Spark UDF?

时间:2018-03-11 19:37:27

标签: scala apache-spark apache-spark-sql user-defined-functions

我有一个简单的案例类:

case class Geometry(id: Int, multiPolygon: MultiPolygon)

MultiPolygon 也是案例类

case class Pt(x: Double, y: Double)
case class Polygon(outer: List[Pt], inner: List[List[Pt]])
case class MultiPolygon(polygons: List[Polygon]) {
    def area(): Double = ...
}

我试图将area函数称为UDF,因此通过像.map(g => (g.id, g.multiPolygon.area))这样的而不是,将整个Geometry对象反序列化在JVM内存中。

由于我需要在 SQL 中使用此功能,我注册了一个UDF:

session.udf.register[Double, MultiPolygon]("my_area", { mp: MultiPolygon => mp.area })

测试数据集为Dataset[Geometry],注册为geometries。好。然后问题是,当我尝试做类似的事情时:

SELECT id, my_area(multiPolygon) FROM geometries

我得到一个例外:

org.apache.spark.SparkException: Failed to execute user defined function(anonfun$19: (struct<polygons:array<struct<outer:array<struct<x:double,y:double>>,inner:array<array<struct<x:double,y:double>>>>>>) => double)

Caused by: java.lang.ClassCastException: org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema cannot be cast to Polygons

数据集的架构是:

root
 |-- id: integer (nullable = false)
 |-- multiPolygon: struct (nullable = true)
 |    |-- polygons: array (nullable = true)
 |    |    |-- element: struct (containsNull = true)
 |    |    |    |-- outer: array (nullable = true)
 |    |    |    |    |-- element: struct (containsNull = true)
 |    |    |    |    |    |-- x: double (nullable = false)
 |    |    |    |    |    |-- y: double (nullable = false)
 |    |    |    |-- inner: array (nullable = true)
 |    |    |    |    |-- element: array (containsNull = true)
 |    |    |    |    |    |-- element: struct (containsNull = true)
 |    |    |    |    |    |    |-- x: double (nullable = false)
 |    |    |    |    |    |    |-- y: double (nullable = false)

使用数据集接口抛出相同的异常:

val areaUDF = udf[Double, MultiPolygon](_.area)
val area = geometries.withColumn("areas", areaUDF('multiPolygon))

虽然外部数据集正确定义为Dataset[Geometry],而不仅仅是DataFrame,并且UDF具有类型MultiPolygon的输入参数,但Spark SQL尝试强制转换附加到名为multiPolygonMultiPolygon的字段的结构对象,失败。

相反,它应该反序列化MultiPolygon返回到结构中的JVM对象表示,这是UDF可以使用与.select(...).as[MultiPolygon].collect()相同的机制消化的唯一类型。

似乎数据集接口将案例类简化为本机类型,结构和数组的混合,而不保留类名,并且没有简单的方法。似乎并不总是可以将兼容的结构反序列化回案例类,或者至少,当它应该自动发生时,它并不总是发生。

0 个答案:

没有答案