将RDD [T]过滤到类型为T

时间:2017-05-04 12:18:39

标签: scala apache-spark

我使用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来保持类型安全,同时实现这一目标。

有一个简单的解决方案吗?或者我是以错误的方式解决问题?

提前致谢。

1 个答案:

答案 0 :(得分:3)

您应该使用collect(f: PartialFunction[T, U]): RDD[U]方法(不要与collect(): Array[T] 混淆,后者将结果作为数组发送给驱动程序):

val webEvents = events.collect{
  case w: Web => w 
}.toDF()

collectmapfilter之间的混合:如果输入与模式匹配中给出的一种情况匹配,它将输出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)