我使用Spark来读取文本文件。每一行都可以属于不同的案例类。一旦我将行转换为case类描述的对象,我将它们转换为数据帧并写入HDFS(镶木地板)。我遇到的问题是我最终得到了一个抽象类型的RDD,我需要将它约束到特定的case类类型来应用toDF函数。
到目前为止,我已将我的日志事件定义如下:
abstract class LogEvent
final case class Web(datetime: String, ... )
final case class OtherEvent(datetime: String ...)
我正在读取我的文本文件,然后根据模式匹配函数映射行以创建RDD [LogEvent]:
def convertToCase(e: List[String]): LogEvent= e match {
case List(_, _, _, "WEB", _, _, _, _, _, _, _, _, _) =>
Web(getDate(e.head), getTime(e.head), e(1), e(2), e(3), e(4), e(5), e(6), e(7), e(8), e(9), e(10))
case List(_, _, _, "OTHEREVENT", _, _, _, _, _, _, _, _) =>
OtherEvent(getDate(e.head), getTime(e.head), e(1), e(2), e(3), e(4), e(5), e(6), e(7), e(8), e(9), e(10))
}
此时我希望约束到给定的case类并转换为Spark数据帧。类似的东西:
val events = spark.read.textFile(...)
.map(_.split(',').toList)
.map(convertToCase)
然后我想将RDD [LogEvent]缩减为类型为T的RDD,它可以在集合{Web,OtherEvent}中。这就是我所挣扎的。将带谓词的过滤器应用于约束到案例类并不会从LogEvent更改类型,这意味着我无法调用toDF()'因为这必须在RDD [T]上调用,其中T是一个特定的案例类,而不是抽象类RDD [LogEvent]。
val webEvents = events.filter(someLogic).toDF()
我正在寻找一种方法,可以将通用RDD降低到特定案例类的RDD。我试图通过不使用isInstanceOf或asInstanceOf来保持类型安全,同时实现这一目标。
有一个简单的解决方案吗?或者我是以错误的方式解决问题?
提前致谢。
答案 0 :(得分:3)
您应该使用collect(f: PartialFunction[T, U]): RDD[U]
方法(不要与collect(): Array[T]
混淆,后者将结果作为数组发送给驱动程序):
val webEvents = events.collect{
case w: Web => w
}.toDF()
collect
是map
和filter
之间的混合:如果输入与模式匹配中给出的一种情况匹配,它将输出partial函数给出的值。否则,它将忽略(即过滤掉)输入。
请注意,您可能也应该为convertToCase
执行此操作,因为您定义的模式匹配不完整,如果遇到意外事件或损坏的行,您可能会在运行时收到错误。正确的方法是定义
val convertToCase: PartialFunction[List[String], LogEvent] = {
case List(_, _, _, "WEB", _, _, _, _, _, _, _, _, _) =>
Web(getDate(e.head), getTime(e.head), e(1), e(2), e(3), e(4), e(5), e(6), e(7), e(8), e(9), e(10))
case List(_, _, _, "OTHEREVENT", _, _, _, _, _, _, _, _) =>
OtherEvent(getDate(e.head), getTime(e.head), e(1), e(2), e(3), e(4), e(5), e(6), e(7), e(8), e(9), e(10))
}
然后用map(convertToCase)
替换collect(convertToCase)
。