web-dev-qa-db-fra.com

La fusion de schémas avec int et double ne peut pas être résolue lors de la lecture d'un fichier de parquet

J'ai deux fichiers de parquet, l'un contenant un champ entier myField et un autre contenant un champ double myField. Lorsque vous essayez de lire les deux fichiers en même temps

val basePath = "/path/to/file/"
val fileWithInt = basePath + "intFile.snappy.parquet"
val fileWithDouble = basePath + "doubleFile.snappy.parquet"
val result = spark.sqlContext.read.option("mergeSchema", true).option("basePath", basePath).parquet(Seq(fileWithInt, fileWithDouble): _*).select("myField")

Je reçois l'erreur suivante

Caused by: org.Apache.spark.SparkException: Failed to merge fields 'myField' and 'myField'. Failed to merge incompatible data types IntegerType and DoubleType

En passant un schéma explicite 

val schema = StructType(Seq(new StructField("myField", IntegerType)))
val result = spark.sqlContext.read.schema(schema).option("mergeSchema", true).option("basePath", basePath).parquet(Seq(fileWithInt, fileWithDouble): _*).select("myField")

Il échoue avec ce qui suit 

Java.lang.UnsupportedOperationException: org.Apache.parquet.column.values.dictionary.PlainValuesDictionary$PlainDoubleDictionary
    at org.Apache.parquet.column.Dictionary.decodeToInt(Dictionary.Java:48)

En cas de doublé

val schema = StructType(Seq(new StructField("myField", DoubleType)))

Je reçois

Java.lang.UnsupportedOperationException: org.Apache.parquet.column.values.dictionary.PlainValuesDictionary$PlainIntegerDictionary
    at org.Apache.parquet.column.Dictionary.decodeToDouble(Dictionary.Java:60)

Est-ce que quelqu'un connaît ce problème autrement que par le retraitement des données source?.

6
John Cragg

En fonction du nombre de fichiers que vous allez lire, vous pouvez utiliser l'une de ces deux approches:

Ce serait mieux pour un plus petit nombre de limes de parquet

def merge(spark: SparkSession, paths: Seq[String]): DataFrame = {
    import spark.implicits._

    paths.par.map {
      path =>
        spark.read.parquet(path).withColumn("myField", $"myField".cast(DoubleType))
    }.reduce(_.union(_))
  }

Cette approche sera préférable pour traiter un grand nombre de fichiers car elle gardera la lignée courte

def merge2(spark: SparkSession, paths: Seq[String]): DataFrame = {
    import spark.implicits._

    spark.sparkContext.union(paths.par.map {
      path =>
        spark.read.parquet(path).withColumn("myField", $"myField".cast(DoubleType)).as[Double].rdd
    }.toList).toDF
  }
1
Mikel