Le code suivant génère une exception provoquée par: Java.lang.UnsupportedOperationException: fieldIndex sur une ligne sans schéma n'est pas défini. Cela se produit lorsqu'un élément d'une image a été renvoyé après une invocation de type groupByKey et flatMap sur un élément de données utilisant ExpressionEncoder, groupedByKey et une propriété flatMap.
Flux logique: OriginalDf-> groupByKey-> flatMap-> groupByKey-> flatMap-> show
import org.Apache.spark.sql.catalyst.encoders.RowEncoder
import org.Apache.spark.sql.{Row, SparkSession}
import org.Apache.spark.sql.types.{ IntegerType, StructField, StructType}
import scala.collection.mutable.ListBuffer
object Test {
def main(args: Array[String]): Unit = {
val values = List(List("1", "One") ,List("1", "Two") ,List("2", "Three"),List("2","4")).map(x =>(x(0), x(1)))
val session = SparkSession.builder.config("spark.master", "local").getOrCreate
import session.implicits._
val dataFrame = values.toDF
dataFrame.show()
dataFrame.printSchema()
val newSchema = StructType(dataFrame.schema.fields
++ Array(
StructField("Count", IntegerType, false)
)
)
val expr = RowEncoder.apply(newSchema)
val tranform = dataFrame.groupByKey(row => row.getAs[String]("_1")).flatMapGroups((key, inputItr) => {
val inputSeq = inputItr.toSeq
val length = inputSeq.size
var listBuff = new ListBuffer[Row]()
var counter : Int= 0
for(i <- 0 until(length))
{
counter+=1
}
for(i <- 0 until length ) {
var x = inputSeq(i)
listBuff += Row.fromSeq(x.toSeq ++ Array[Int](counter))
}
listBuff.iterator
})(expr)
tranform.show
val newSchema1 = StructType(tranform.schema.fields
++ Array(
StructField("Count1", IntegerType, false)
)
)
val expr1 = RowEncoder.apply(newSchema1)
val tranform2 = tranform.groupByKey(row => row.getAs[String]("_1")).flatMapGroups((key, inputItr) => {
val inputSeq = inputItr.toSeq
val length = inputSeq.size
var listBuff = new ListBuffer[Row]()
var counter : Int= 0
for(i <- 0 until(length))
{
counter+=1
}
for(i <- 0 until length ) {
var x = inputSeq(i)
listBuff += Row.fromSeq(x.toSeq ++ Array[Int](counter))
}
listBuff.iterator
})(expr1)
tranform2.show
}
}
Voici le stacktrace
18/11/21 19:39:03 WARN TaskSetManager: Lost task 144.0 in stage 11.0 (TID 400, localhost, executor driver): Java.lang.UnsupportedOperationException: fieldIndex on a Row without schema is undefined.
at org.Apache.spark.sql.Row$class.fieldIndex(Row.scala:342)
at org.Apache.spark.sql.catalyst.expressions.GenericRow.fieldIndex(rows.scala:166)
at org.Apache.spark.sql.Row$class.getAs(Row.scala:333)
at org.Apache.spark.sql.catalyst.expressions.GenericRow.getAs(rows.scala:166)
at com.quantuting.sparkutils.main.Test$$anonfun$4.apply(Test.scala:59)
at com.quantuting.sparkutils.main.Test$$anonfun$4.apply(Test.scala:59)
at org.Apache.spark.sql.execution.AppendColumnsWithObjectExec$$anonfun$9$$anonfun$apply$3.apply(objects.scala:300)
at org.Apache.spark.sql.execution.AppendColumnsWithObjectExec$$anonfun$9$$anonfun$apply$3.apply(objects.scala:298)
at scala.collection.Iterator$$anon$11.next(Iterator.scala:409)
at scala.collection.Iterator$$anon$11.next(Iterator.scala:409)
at org.Apache.spark.shuffle.sort.BypassMergeSortShuffleWriter.write(BypassMergeSortShuffleWriter.Java:149)
at org.Apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:96)
at org.Apache.spark.scheduler.ShuffleMapTask.runTask(ShuffleMapTask.scala:53)
at org.Apache.spark.scheduler.Task.run(Task.scala:109)
at org.Apache.spark.executor.Executor$TaskRunner.run(Executor.scala:345)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1149)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:624)
at Java.lang.Thread.run(Thread.Java:748)
Comment réparer ce code?
Le problème signalé pourrait être évité en remplaçant la version fieldname
de la méthode getAs [T] (utilisée dans la fonction pour groupByKey
):
groupByKey(row => row.getAs[String]("_1"))
avec la version field-position
:
groupByKey(row => row.getAs[String](fieldIndexMap("_1")))
où fieldIndexMap
fait correspondre les noms de champs à leurs index de champs correspondants:
val fieldIndexMap = tranform.schema.fieldNames.zipWithIndex.toMap
En remarque, votre fonction pour flatMapGroups peut être simplifiée comme ci-dessous:
val tranform2 = tranform.groupByKey(_.getAs[String](fieldIndexMap("_1"))).
flatMapGroups((key, inputItr) => {
val inputSeq = inputItr.toSeq
val length = inputSeq.size
inputSeq.map(r => Row.fromSeq(r.toSeq :+ length))
})(expr1)
Le comportement incohérent entre l'application des méthodes groupByKey/flatMapGroups
d'origine à "dataFrame" et "tranform" est apparemment lié à la manière dont les méthodes gèrent une DataFrame
par rapport à Dataset[Row]
.
Solution fournie par JIRA sur le projet Spark: https://issues.Apache.org/jira/browse/SPARK-26436
Ce problème est dû à la façon dont vous créez la ligne:
listBuff += Row.fromSeq(x.toSeq ++ Array[Int](counter))
Row.fromSeq crée un GenericRow et le fieldIndex de GenericRow n'est pas implémenté car GenericRow n'a pas de schéma.
Changer la ligne pour créer GenericRowWithSchema peut le résoudre:
listBuff += new GenericRowWithSchema((x.toSeq ++ Array[Int](counter)).toArray, newSchema)