J'ai fait un UDF simple pour convertir ou extraire certaines valeurs d'un champ temporel dans un tentablabl dans spark. J'enregistre la fonction, mais lorsque j'appelle la fonction à l'aide de sql, elle déclenche une exception NullPointerException. Voici ma fonction et le processus de son exécution. J'utilise Zeppelin. Étrangement, cela fonctionnait hier, mais il a cessé de fonctionner ce matin.
Fonction
def convert( time:String ) : String = {
val sdf = new Java.text.SimpleDateFormat("HH:mm")
val time1 = sdf.parse(time)
return sdf.format(time1)
}
Enregistrer la fonction
sqlContext.udf.register("convert",convert _)
Testez la fonction sans SQL - Cela fonctionne
convert(12:12:12) -> returns 12:12
Testez la fonction avec SQL dans Zeppelin cette ÉCHEC.
%sql
select convert(time) from temptable limit 10
Structure de tentable
root
|-- date: string (nullable = true)
|-- time: string (nullable = true)
|-- serverip: string (nullable = true)
|-- request: string (nullable = true)
|-- resource: string (nullable = true)
|-- protocol: integer (nullable = true)
|-- sourceip: string (nullable = true)
Une partie du stacktrace que je reçois.
Java.lang.NullPointerException
at org.Apache.hadoop.Hive.ql.exec.FunctionRegistry.getFunctionInfo(FunctionRegistry.Java:643)
at org.Apache.hadoop.Hive.ql.exec.FunctionRegistry.getFunctionInfo(FunctionRegistry.Java:652)
at org.Apache.spark.sql.Hive.HiveFunctionRegistry.lookupFunction(hiveUdfs.scala:54)
at org.Apache.spark.sql.Hive.HiveContext$$anon$3.org$Apache$spark$sql$catalyst$analysis$OverrideFunctionRegistry$$super$lookupFunction(HiveContext.scala:376)
at org.Apache.spark.sql.catalyst.analysis.OverrideFunctionRegistry$$anonfun$lookupFunction$2.apply(FunctionRegistry.scala:44)
at org.Apache.spark.sql.catalyst.analysis.OverrideFunctionRegistry$$anonfun$lookupFunction$2.apply(FunctionRegistry.scala:44)
at scala.Option.getOrElse(Option.scala:120)
at org.Apache.spark.sql.catalyst.analysis.OverrideFunctionRegistry$class.lookupFunction(FunctionRegistry.scala:44)
Utilisez udf au lieu de définir directement une fonction
import org.Apache.spark.sql.functions._
val convert = udf[String, String](time => {
val sdf = new Java.text.SimpleDateFormat("HH:mm")
val time1 = sdf.parse(time)
sdf.format(time1)
}
)
Le paramètre d'entrée d'un udf est Colonne (ou Colonnes). Et le type de retour est Colonne.
case class UserDefinedFunction protected[sql] (
f: AnyRef,
dataType: DataType,
inputTypes: Option[Seq[DataType]]) {
def apply(exprs: Column*): Column = {
Column(ScalaUDF(f, dataType, exprs.map(_.expr), inputTypes.getOrElse(Nil)))
}
}
Vous devez définir votre fonction comme UDF.
import org.Apache.spark.sql.expressions.UserDefinedFunction
import org.Apache.spark.sql.functions.udf
val convertUDF: UserDefinedFunction = udf((time:String) => {
val sdf = new Java.text.SimpleDateFormat("HH:mm")
val time1 = sdf.parse(time)
sdf.format(time1)
})
Ensuite, vous appliqueriez votre UDF sur votre DataFrame.
// assuming your DataFrame is already defined
dataFrame.withColumn("time", convertUDF(col("time"))) // using the same name replaces existing
Maintenant, en ce qui concerne votre problème réel, l'une des raisons pour lesquelles vous recevez cette erreur pourrait être parce que votre DataFrame contient des lignes qui sont nulles. Si vous les filtrez avant d'appliquer l'UDF, vous devriez pouvoir continuer sans problème.
dataFrame.filter(col("time").isNotNull)
Je suis curieux de savoir ce qui cause une exception NullPointerException lors de l'exécution d'un UDF autre que celui rencontrant un null, si vous avez trouvé une raison différente de ma suggestion, je serais heureux de le savoir.