大数据

Hive的UDF编程-GenericUDF编程

2019-07-24  本文已影响0人  ShiPF

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包含三个属性:

而且,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] + ")";
    }
}

创建函数

  1. 代码编写完成后,将代码打包
  2. 将打包后的文件上传到分布式集群
  3. 启动hive 使用add jar命令 add jar 上传的路径/*.jar文件
  4. hive执行 create function hello as 'udf.generic.GenericUDFArrayTest';
  5. 执行需求提出的sql代码,测试放回结果;

ps:为了防止代码书写错误,可以先复制代码创建好函数测试,再做代码的理解

文章参考

作者:raincoffee
链接:https://www.jianshu.com/p/ca9dce6b5c37

有问题欢迎留言讨论

上一篇 下一篇

猜你喜欢

热点阅读