UDF简介
在Hive中,用户可以自定义一些函数,用于扩展HiveQL的功能,而这类函数叫做UDF(用户自定义函数)。UDF分为两大类:UDAF(用户自定义聚合函数)和UDTF(用户自定义表生成函数)。在介绍UDAF和UDTF实现之前,我们先在本章介绍简单点的UDF实现——UDF和GenericUDF,然后以此为基础在下一章介绍UDAF和UDTF的实现。
Hive有两个不同的接口编写UDF程序。
一个是基础的UDF接口,一个是复杂的GenericUDF接口。
UDF 基础UDF的函数读取和返回基本类型,即Hadoop和Hive的基本类型。如,Text、IntWritable、LongWritable、DoubleWritable等。
GenericUDF 复杂的GenericUDF可以处理Map、List、Set类型。
注解的使用
@Describtion注解是可选的,用于对函数进行说明,其中的FUNC字符串表示函数名,当使用DESCRIBE FUNCTION命令时,替换成函数名。@Describtion包含三个属性:
- name:用于指定Hive中的函数名。
- value:用于描述函数的参数。
- extended:额外的说明,如,给出示例。当使用DESCRIBE FUNCTION EXTENDED name的时候打印。
而且,Hive要使用UDF,需要把Java文件编译、打包成jar文件,然后将jar文件加入到CLASSPATH中,最后使用CREATE FUNCTION语句定义这个Java类的函数:
hive> ADD jar /root/experiment/hive/hive-0.0.1-SNAPSHOT.jar;
hive> CREATE TEMPORARY FUNCTION hello AS "edu.wzm.hive. HelloUDF";
hive> DROP TEMPORARY FUNCTION IF EXIST hello;
具体的打包方式,在上一篇的坐标转换UDF中有详细的介绍
这次我们重点介绍GenericUDF,继承这个类需要实现三个方法
//这个方法只调用一次,并且在evaluate()方法之前调用。该方法接受的参数是一个ObjectInspectors数组。该方法检查接受正确的参数类型和参数个数。
abstract ObjectInspector initialize(ObjectInspector[] arguments);
//这个方法类似UDF的evaluate()方法。它处理真实的参数,并返回最终结果。
abstract Object evaluate(GenericUDF.DeferredObject[] arguments);
//这个方法用于当实现的GenericUDF出错的时候,打印出提示信息。而提示信息就是你实现该方法最后返回的字符串。
abstract String getDisplayString(String[] children);
需求
这里我们设置一个需求是这样的,在一个sql中查找某列数组是否包含另外一个值。下面这个例子中就是需要实现hello这个函数
//举一个简单的seq例子,,因为在切割后的数组中会包含aaa,所以我们希望的返回结果是true,
select hello(split('aaa,bbb',','),'aaa');
下面是我们的GenericUDF函数的代码
/**
* Copyright (C), 2015-2019, XXX有限公司
* FileName: GenericUDFArrayTest
* Author: 72038714
* Date: 2019/7/24 11:45
* Description: xxx
* History:
* <author> <time> <version> <desc>
* shipengfei 版本号 描述
*/
package udf.generic;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ListObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorUtils;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.BooleanWritable;
/**
* 〈一句话功能简述〉<br>
* 〈xxx〉
*
* @author 72038714
* @create 2019/7/24
* @since 1.0.0
*/
public class GenericUDFArrayTest extends GenericUDF {
private transient ObjectInspector value0I;
private transient ListObjectInspector arrayOI;
private transient ObjectInspector arrayElementOI;
private BooleanWritable result;
public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {
//判断是否输入的参数为2
if (objectInspectors.length != 2){
throw new UDFArgumentException("args must accept 2 args");
}
//判断第一个参数是否是list
if (!(objectInspectors[0].getCategory().equals(ObjectInspector.Category.LIST))){
throw new UDFArgumentTypeException(0, "\"array\" expected at function ARRAY_CONTAINS, but \""
+ objectInspectors[0].getTypeName() + "\" " + "is found");
}
//将参数赋值给私有变量
this.arrayOI = ((ListObjectInspector) objectInspectors[0]);
this.arrayElementOI=this.arrayOI.getListElementObjectInspector();
this.value0I= objectInspectors[1];
//数组元素是否与第二个参数类型相同
if(!(ObjectInspectorUtils.compareTypes(this.arrayOI,this.value0I))) {
throw new UDFArgumentTypeException(1,
"\"" + this.arrayElementOI.getTypeName() + "\"" + " expected " +
"at function ARRAY_CONTAINS, but "
+ "\"" + this.value0I.getTypeName() + "\"" + " is found");
}
//判断ObjectInspector是否支持第二个参数类型
if (!(ObjectInspectorUtils.compareSupported(this.value0I))) {
throw new UDFArgumentException("The function ARRAY_CONTAINS does not support comparison for \""
+ this.value0I.getTypeName() + "\"" + " types");
}
this.result=new BooleanWritable(true);
return PrimitiveObjectInspectorFactory.writableBooleanObjectInspector;
}
public Object evaluate(DeferredObject[] deferredObjects) throws HiveException {
this.result.set(false);
Object array= deferredObjects[0].get();
Object value= deferredObjects[1].get();
Integer arrayLength = this.arrayOI.getListLength(array);
//传入第二个参数是否为nul,或者传入参数长度为0 检验传入参数
if (value == null || arrayLength<=0){
return this.result;
}
//遍历array中的类型,判断是否与第二个参数相等
for (int i=0;i<arrayLength;i++) {
Object listElement = this.arrayOI.getListElement(array, i);
//判断包含如果本次循环的数组元数为null,或者没有匹配成功,跳过本次循环
if (listElement == null || ObjectInspectorUtils.compare(value,value0I,listElement,arrayOI) != 0){
continue;
}
//如果匹配成功,将result设置为true
result.set(true);
break;
}
return result;
}
public String getDisplayString(String[] strings) {
assert (strings.length == 2);
return "array_contains(" + strings[0] + ", " + strings[1] + ")";
}
}
创建函数
- 代码编写完成后,将代码打包
- 将打包后的文件上传到分布式集群
- 启动hive 使用add jar命令 add jar 上传的路径/*.jar文件
- hive执行 create function hello as 'udf.generic.GenericUDFArrayTest';
- 执行需求提出的sql代码,测试放回结果;
ps:为了防止代码书写错误,可以先复制代码创建好函数测试,再做代码的理解
文章参考
作者:raincoffee
链接:https://www.jianshu.com/p/ca9dce6b5c37
有问题欢迎留言讨论