在SQLContext之外用Java创建SparkSQL UDF

时间:2016-03-27 14:03:06

标签: java apache-spark dataframe apache-spark-sql user-defined-functions

问题

我想在Java中创建一个用户定义函数,可以在Apache Spark运算符链中作为Java方法调用。我无法找到不需要在SQL查询中存在UDF的Java示例。

版本

  • Java 8
  • Scala 2.10.6
  • Apache Spark 1.6.0预构建用于Hadoop 2.6.0

我尝试过的工作

我可以用Java成功创建UDF。但是,我不能使用它,除非它在SQL查询中:

import org.apache.spark.sql.DataFrame;
import org.apache.spark.sql.SQLContext;
import org.apache.spark.sql.types.DataTypes;

sqlContext.udf().register("udfUppercase",
    (String string) -> string.toUpperCase(), DataTypes.StringType);

DataFrame oldDF = // a simple DataFrame with a "name" column
oldDF.registerTempTable("df");
DataFrame newDF = sqlContext.sql("SELECT udfUppercase(name) AS name_upper FROM df");

我被困的地方

我希望Java中的非SQL方法调用样式的UDF看起来像这样:

import static org.apache.spark.sql.functions.udf;
import org.apache.spark.sql.DataFrame;
import org.apache.spark.sql.UserDefinedFunction;
import org.apache.spark.sql.types.DataTypes;

UserDefinedFunction udfUppercase = udf(
    (String string) -> string.toUpperCase(), DataTypes.StringType);

DataFrame oldDF = // a simple DataFrame with a "name" column
newDF = oldDF.withColumn("name_upper", udfUppercase(oldDF.col("name")));

编译这会导致以“UserDefinedFunction”开头的行上的编译器错误,所以很明显我尝试猜测正确的签名是错误的:

error: no suitable method found for udf((String st[...]ase(),DataType)
    UserDefinedFunction udfUppercase = udf((String string) -> string.toUpperCase(), DataTypes.StringType);
method functions.<RT#1>udf(Function0<RT#1>,TypeTags.TypeTag<RT#1>) is not applicable
    (cannot infer type-variable(s) RT#1
    (argument mismatch; Function0 is not a functional interface
    multiple non-overriding abstract methods found in interface Function0))

此错误将继续详细说明每个推断的udf()签名。

我需要什么

我需要修复Java代码,以便我可以定义和使用udfUppercase UDF而无需将其嵌入SQL查询中。我觉得我错过了一些非常简单,基本和可能语法的东西,但可能完全偏离基础。

工作解决方案(由下面的零323提供)

没有很好的方法来注册和使用Java UDF作为Java方法,但是可以使用callUDF()将在SQLContext中注册的UDF插入到运算符链中。

import org.apache.spark.sql.DataFrame;
import org.apache.spark.sql.SQLContext;
import org.apache.spark.sql.types.DataTypes;

sqlContext.udf().register("udfUppercase",
    (String string) -> string.toUpperCase(), DataTypes.StringType);

DataFrame oldDF = // a simple DataFrame with a "name" column
newDF = oldDF.withColumn("name_upper", callUDF("udfUppercase", oldDF.col("name")));

另外,请务必使用callUDF()而不是使用不同方法签名的已弃用的callUdf()。

1 个答案:

答案 0 :(得分:8)

Spark&gt; = 2.3

https://jsfiddle.net/rwqc9bje/在函数对象中添加java UDF API )添加了简化的udf API,类似于Scala和Python:

import static org.apache.spark.sql.functions.*;
import org.apache.spark.sql.expressions.UserDefinedFunction;

UserDefinedFunction udfUppercase = udf(
  (String s) -> s.toUpperCase(), DataTypes.StringType
);

df.select(udfUppercase.apply(col("name")));

Spark&lt; 2.3

长篇短functions.udf方法不是为Java互操作性而设计的。所有变体都需要TypeTags,虽然可以手动生成这些变体(我很确定我已经看到SPARK-22945显示如何在SO上执行此操作)这是您可能想要避免的。

如果由于某种原因你想避免在Scala中编写UDF,最简单的方法是注册UDF Daniel Darabos

sqlContext.udf().register("udfUppercase",
  (String string) -> string.toUpperCase(), DataTypes.StringType);

df.select(callUDF("udfUppercase", col("name")));