返回动态数据类型的Apache Spark UDF

时间:2017-01-23 20:03:45

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

我有UDF处理JSON并返回每行的动态数据结果。在我的情况下,我需要这个来验证数据并返回验证数据。

每行的架构都很灵活。这意味着我无法为每种情况创建case class(我的一些数据可以嵌套)。

我试图从我的UDF函数返回元组,但我也没有运气(因为我需要从列表转换为元组),而我没有找到一个优雅的解决方案。

我返回的数据类型有StringIntegerDoubleDateTime,顺序不同。

我尝试在DataFrame上使用map,但我的架构出现问题。

import spark.implicits._

def processData(row_type: String) = {
  /*
  completely random output here. Tuple/List/Array of 
  elements with a type Integer, String, Double, DateType.
  */

  // pseudo-code starts here

  if row_type == A
     (1, "second", 3)
  else
     (1, "second", 3, 4)
}

val processDataUDF = udf((row_type: String) => processData(row_type))

val df = Seq((0, 1), (1, 2)).toDF("a", "b")
val df2 = df.select(processDataUDF($"a"))
df2.show(5)
df2.printSchema()

结果

+------------+
|      UDF(a)|
+------------+
|[1,second,3]|
|[1,second,3]|
+------------+

我该如何处理这个问题?我们每row_type有不同的处理结果。所有row_type都是动态设置的。对于每个Schema,我可以很好row_type,但我不能使用不同的模式生成相同的UDF返回结果。

使用map是唯一的方法吗?

1 个答案:

答案 0 :(得分:5)

Spark Dataset是一个列式数据结构,这里没有灵活架构的地方。 Schema必须是同类的(所有行必须具有相同的通用结构)并且已知(如果使用UDF,它必须返回定义良好的SQL类型)。

您可以通过以下方式获得一些灵活性:

  • 定义表示所有可能字段的超集的模式,并将各列标记为nullable。只有在没有类型冲突的情况下才可以执行此操作(如果Row包含字段foo,则始终使用相同的SQL类型表示它。)
  • 使用集合类型(MapTypeArrayType)表示可变大小的字段。所有值和/或键必须属于同一类型。
  • 将原始数据重塑为可通过固定架构实际表示的点。 Spark包含json4s作为其依赖项,它为merging, diffingquerying JSON数据提供了一组工具。如果需要,它可用于应用相对复杂的转换。

如果这不实用,我建议保留JSON字段"按原样#34;并仅按需解析它以提取特定值。您可以使用get_json_object和显式类型转换。这允许测试不同的场景:

coalesce(Seq("$.bar", "$.foo.bar", "$.foobar.foo.bar")
  .map(get_json_object($"json_col", _)): _*).cast(DoubleType)

不假设单一文档结构。

您可以使用二进制EncodersEncoders.kryoEncoders.java)或RDD API获得更多灵活性,这些API可用于存储联合类型(甚至{ {1}}),但如果您真的希望完全随机输出,则表明存在严重的设计或数据建模问题。即使您可以存储已分析的数据,也很难使用它。