如何为Scala集合创建一个Encoder(实现自定义聚合器)?

时间:2018-04-06 19:55:35

标签: scala apache-spark apache-spark-sql apache-spark-encoders

使用Scala 2.11的Spark 2.3.0。我根据文档here实施了自定义Aggregator。聚合器需要3种类型的输入,缓冲和输出。

我的聚合器必须对窗口中的所有先前行进行操作,因此我将其声明为:

case class Foo(...)

object MyAggregator extends Aggregator[Foo, ListBuffer[Foo], Boolean] {
    // other override methods
    override def bufferEncoder: Encoder[ListBuffer[Mod]] = ???
}

其中一个覆盖方法应该返回缓冲区类型的编码器,在本例中为ListBuffer。我无法为org.apache.spark.sql.Encoders找到任何合适的编码器,也没有任何其他编码方式,所以我不知道该返回什么。

我想创建一个新的case类,它有一个ListBuffer[Foo]类型的单一属性,并将其用作我的缓冲类,然后使用Encoders.product,但我不确定是否是必要的,或者如果还有别的东西,我会失踪。感谢您的任何提示。

2 个答案:

答案 0 :(得分:4)

您应该让Spark SQL完成其工作并使用append()找到正确的编码器,如下所示:

ExpressionEncoder

答案 1 :(得分:1)

我在org.apache.spark.sql.Encoders中看不到任何可用于直接编码ListBuffer的东西,或者甚至是List

如你所建议的那样,将一个选项放在案例类中似乎是一个选择:

import org.apache.spark.sql.Encoders

case class Foo(field: String)
case class Wrapper(lb: scala.collection.mutable.ListBuffer[Foo])
Encoders.product[Wrapper]

另一种选择可能是使用kryo:

Encoders.kryo[scala.collection.mutable.ListBuffer[Foo]]

或者最后你可以看一下ExpressionEncoders,它扩展了Encoder:

import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder
ExpressionEncoder[scala.collection.mutable.ListBuffer[Foo]]

这是最好的解决方案,因为它可以使一切对催化剂透明,因此可以进行所有精彩的优化。

在玩游戏时我注意到了一件事:

ExpressionEncoder[scala.collection.mutable.ListBuffer[Foo]].schema == ExpressionEncoder[List[Foo]].schema

我在执行聚合时没有测试过上述任何内容,因此可能存在运行时问题。希望这有用。