Scala 在映射过程中是否有方法跳过/抛出/忽略Spark中的记录?

Scala 在映射过程中是否有方法跳过/抛出/忽略Spark中的记录?,scala,apache-spark,Scala,Apache Spark,我们有一个非常标准的Spark作业,它从s3读取日志文件,然后对它们进行一些处理。非常基本的火花材料 val logs = sc.textFile(somePathTos3) val mappedRows = logs.map(log => OurRowObject.parseLog(log.split("\t"))) val validRows = mappedRows.filter(log => log._1._1 != "ERROR") ...and continue proc

我们有一个非常标准的Spark作业,它从s3读取日志文件,然后对它们进行一些处理。非常基本的火花材料

val logs = sc.textFile(somePathTos3)
val mappedRows = logs.map(log => OurRowObject.parseLog(log.split("\t")))
val validRows = mappedRows.filter(log => log._1._1 != "ERROR")
...and continue processing
其中,
OurRowObject.parseLine
获取原始日志行并将其映射到某个(键、值)对(例如,
((1,2,3,4)、(5,6,7))
,然后我们可以对其进行处理。现在,如果
parseLine
遇到一个“问题”日志(格式错误、空等),它将返回一些前哨值(例如,
(((((“错误”)、…)、(…)
然后过滤步骤会过滤掉它

现在,我一直在试图找到一种方法,就是在映射过程中不包含问题行……告诉spark“嘿,这是一个空的/格式错误的行,跳过它,不要包含一对”,而不是额外的筛选步骤

我还没有找到一种方法来实现这一点,并且发现这个功能(AFAICanFind)并不存在,这非常有趣


谢谢

您可以让解析器返回选项[Value],而不是值。这样您就可以使用flatMap将行映射到行,并删除无效的行

大致如下:

def parseLog(line:String):Option[Array[String]] = {
    val splitted = log.split("\t")
    if (validate(splitted)) Some(splitted) else None
}

val validRows = logs.flatMap(OurRowObject.parseLog(_))

一种方法是使用单参数重载
collect
(而不是
map
flatMap
)还有一个
部分函数
。如果您需要的部分函数不是完全微不足道的,那么这就有点棘手了。事实上,您可能不会这样做,因为您需要解析和验证,我将在下面用两个部分函数建模(尽管第一个函数恰好为所有输入定义)

这是一个非常好的Scala,但它不起作用,因为
splitAndValidate
是不可序列化的,我们使用的是Spark。(注意
split
validate
是可序列化的:问题在于合成!)因此,我们需要制作一个可序列化的
PartialFunction

class LogValidator extends PartialFunction[String, Array[String]] with Serializable {

  private val validate: PartialFunction[Array[String], Array[String]] = {
    case lines if lines.length > 2 => lines
  }

  override def apply(log: String) : Array[String] = {
    validate(log.split("\t"))
  }

  override def isDefinedAt(log: String) : Boolean = {
    validate.isDefinedAt(log.split("\t"))
  }

}
然后我们可以打电话

val validRows = logs.collect(new LogValidator())

有趣的想法,我将试一试。谢谢!虽然我非常喜欢这个解决方案,但似乎您应该能够使用
collect
的单参数重载(而不是
map
flatMap
)实现类似的效果还有一个
PartialFunction
。除了一个简单的例子,我什么都做不到,但你可能会发现这也是一个有趣的探索。这个
选项[Value]
方法似乎更容易使用。(我很乐意分享我的代码,但因为它不太管用,所以不会发布。)@SpiroMichaylov是的,
collect(PF[A,B])
也应该用于进行这种过滤。关于
collect
的一件事是Spark,我不喜欢的是
collect
不带参数的是在
collect(PF)时将所有数据发送给驱动程序
是一种转换,会让人感到困惑。你能把它作为一个答案吗?这是一个非常有效的选择。@maasg我完全同意collect的两个重载令人困惑的说法。就设计而言,这是Spark API中较弱的一个方面。我还发现了在Scala中定义
PartialFunction
的机制有点限制,使用起来有点困难。我会看看是否可以修复我的解决方案并发布:我对组合部分函数的可序列化性有问题。@maasg发布在这里,但这让我有点难过。你可以添加
.option(“mode”,“DROPMALFORMED”)
.option(“mode”,“FAILFAST”),而不是在
映射期间添加
.option(“mode”,“DROPMALFORMED”)
.option(“mode”,“FAILFAST”)
textFile
读取过程中,以有用的异常中断作业。
val validRows = logs.collect(new LogValidator())