Spark CSV - 找不到实际参数的适用构造函数/方法

时间:2017-12-29 17:17:07

标签: java apache-spark apache-spark-sql apache-spark-dataset

我在java spark应用程序中对过滤器和类型化数据集的映射使用lambda函数时遇到问题。

我收到此运行时错误

ERROR CodeGenerator: failed to compile: org.codehaus.commons.compiler.CompileException: File 'generated.java', Line 130, Column 126: No applicable constructor/method found for actual parameters "org.apache.spark.unsafe.types.UTF8String"; candidates are: "public static java.sql.Date org.apache.spark.sql.catalyst.util.DateTimeUtils.toJavaDate(int)"

我正在使用以下类和spark 2.2.0。 https://gitlab.com/opencell/test-bigdata

中提供了包含样本数据的完整示例
Dataset<CDR> cdr = spark
            .read()
            .format("csv")
            .option("header", "true")
            .option("inferSchema", "true")
            .option("delimiter", ";")
            .csv("CDR_SAMPLE.csv")
            .as(Encoders.bean(CDR.class));

    long v = cdr.filter(x -> (x.timestamp != null && x.getAccess().length()>0)).count();

    System.out.println("validated entries :" + v);

CDR文件定义为gitlab link

修改

val cdrCSVSchema = StructType(Array(
  StructField("timestamp", DataTypes.TimestampType),
  StructField("quantity", DataTypes.DoubleType),
  StructField("access", DataTypes.StringType),
  StructField("param1", DataTypes.StringType),
  StructField("param2", DataTypes.StringType),
  StructField("param3", DataTypes.StringType),
  StructField("param4", DataTypes.StringType),
  StructField("param5", DataTypes.StringType),
  StructField("param6", DataTypes.StringType),
  StructField("param7", DataTypes.StringType),
  StructField("param8", DataTypes.StringType),
  StructField("param9", DataTypes.StringType),
  StructField("dateParam1", DataTypes.TimestampType),
  StructField("dateParam2", DataTypes.TimestampType),
  StructField("dateParam3", DataTypes.TimestampType),
  StructField("dateParam4", DataTypes.TimestampType),
  StructField("dateParam5", DataTypes.TimestampType),
  StructField("decimalParam1", DataTypes.DoubleType),
  StructField("decimalParam2", DataTypes.DoubleType),
  StructField("decimalParam3", DataTypes.DoubleType),
  StructField("decimalParam4", DataTypes.DoubleType),
  StructField("decimalParam5", DataTypes.DoubleType),
  StructField("extraParam", DataTypes.StringType)))

我使用此命令加载CSV文档

val cdr = spark.read.format("csv").option("header", "true").option("delimiter", ";").schema(cdrCSVSchema).csv("CDR_SAMPLE.csv")

然后尝试使用此命令编码并运行lambda函数,但我仍然收到错误

cdr.as[CDR].filter(c => c.timestamp != null).show

1 个答案:

答案 0 :(得分:1)

TL; DR 明确定义架构,因为输入数据集没有用于推断(java.sql.Date字段)类型的值。

对于您的情况,使用无类型数据集API可能是一种解决方案(也许是一种解决方法,老实说我建议它避免从内部行格式进行不必要的反序列化):

cdr.filter(!$"timestamp".isNull).filter(length($"access") > 0).count

(它的Scala和我正在将它翻译成Java作为家庭练习)。

问题在于您使用inferSchema选项,输入CDR_SAMPLE.csv文件中的大多数字段都不可用,这些字段构成String类型的大多数字段(当没有可用于推断更具体的值时,这是默认类型类型)。

这使得java.sql.Date类型的字段,即dateParam1最多为dateParam5,类型为String。

import org.opencell.spark.model.CDR
import org.apache.spark.sql.Encoders
implicit val cdrEnc = Encoders.bean(classOf[CDR])
val cdrs = spark.read.
  option("inferSchema", "true").
  option("delimiter", ";").
  option("header", true).
  csv("/Users/jacek/dev/sandbox/test-bigdata/CDR_SAMPLE.csv")
scala> cdrs.printSchema
root
 |-- timestamp: timestamp (nullable = true)
 |-- quantity: integer (nullable = true)
 |-- access: string (nullable = true)
 |-- param1: string (nullable = true)
 |-- param2: string (nullable = true)
 |-- param3: string (nullable = true)
 |-- param4: string (nullable = true)
 |-- param5: string (nullable = true)
 |-- param6: string (nullable = true)
 |-- param7: string (nullable = true)
 |-- param8: string (nullable = true)
 |-- param9: string (nullable = true)
 |-- dateParam1: string (nullable = true)
 |-- dateParam2: string (nullable = true)
 |-- dateParam3: string (nullable = true)
 |-- dateParam4: string (nullable = true)
 |-- dateParam5: string (nullable = true)
 |-- decimalParam1: string (nullable = true)
 |-- decimalParam2: string (nullable = true)
 |-- decimalParam3: string (nullable = true)
 |-- decimalParam4: string (nullable = true)
 |-- decimalParam5: string (nullable = true)
 |-- extraParam: string (nullable = true)

请注意,感兴趣的字段,即dateParam1dateParam5,都是字符串。

 |-- dateParam1: string (nullable = true)
 |-- dateParam2: string (nullable = true)
 |-- dateParam3: string (nullable = true)
 |-- dateParam4: string (nullable = true)
 |-- dateParam5: string (nullable = true)

当你&#34;假装&#34;通过使用CDR类中定义的编码器来说明字段的类型是不同的:

private Date dateParam1;
private Date dateParam2;
private Date dateParam3; 
private Date dateParam4; 
private Date dateParam5; 

这是问题的根本原因。 Spark可以从课堂上推断出什么是不同的。没有转换,代码就会起作用,但是因为你坚持......

cdrs.as[CDR]. // <-- HERE is the issue = types don't match
  filter(cdr => cdr.timestamp != null).
  show // <-- trigger conversion

您在filter运算符中访问的字段并不重要。问题是转换会导致执行错误(以及整个Java代码生成)。

我怀疑Spark可以做很多事情,因为你请求inferSchema的数据集没有用于类型推断的值。最好的办法是明确定义架构并使用schema(...)运算符进行设置。