本文共 5917 字,大约阅读时间需要 19 分钟。
在Hive中,用户可以自定义一些函数,用于扩展HiveQL的功能,而这类函数叫做UDF(用户自定义函数)。UDF分为两大类:UDAF(用户自定义聚合函数)和UDTF(用户自定义表生成函数)。在介绍UDAF和UDTF实现之前,我们先在本章介绍简单点的UDF实现——UDF和GenericUDF,然后以此为基础在下一章介绍UDAF和UDTF的实现。
Hive有两个不同的接口编写UDF程序。一个是基础的UDF接口,一个是复杂的GenericUDF接口。
org.apache.hadoop.hive.ql. exec.UDF 基础UDF的函数读取和返回基本类型,即Hadoop和Hive的基本类型。如,Text、IntWritable、LongWritable、DoubleWritable等。
org.apache.hadoop.hive.ql.udf.generic.GenericUDF 复杂的GenericUDF可以处理Map、List、Set类型。
注解使用:
@Describtion注解是可选的,用于对函数进行说明,其中的FUNC字符串表示函数名,当使用DESCRIBE FUNCTION命令时,替换成函数名。@Describtion包含三个属性:
而且,Hive要使用UDF,需要把Java文件编译、打包成jar文件,然后将jar文件加入到CLASSPATH中,最后使用CREATE FUNCTION语句定义这个Java类的函数:
udf
简单的udf实现很简单,只需要继承udf,然后实现evaluate()方法就行了。evaluate()允许重载。
一个例子:
@Description( name = "hello", value = "_FUNC_(str) - from the input string" + "returns the value that is \"Hello $str\" ", extended = "Example:\n" + " > SELECT _FUNC_(str) FROM src;" ) public class HelloUDF extends UDF{ public String evaluate(String str){ try { return "Hello " + str; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); return "ERROR"; } } }
genericUDF
GenericUDF实现比较复杂,需要先继承GenericUDF。这个API需要操作Object Inspectors,并且要对接收的参数类型和数量进行检查。GenericUDF需要实现以下三个方法:
//这个方法只调用一次,并且在evaluate()方法之前调用。该方法接受的参数是一个ObjectInspectors数组。该方法检查接受正确的参数类型和参数个数。 abstract ObjectInspector initialize(ObjectInspector[] arguments); //这个方法类似UDF的evaluate()方法。它处理真实的参数,并返回最终结果。 abstract Object evaluate(GenericUDF.DeferredObject[] arguments); //这个方法用于当实现的GenericUDF出错的时候,打印出提示信息。而提示信息就是你实现该方法最后返回的字符串。 abstract String getDisplayString(String[] children);
一个例子:判断array是否包含某个值。
/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/package org.apache.hadoop.hive.ql.udf.generic;import org.apache.hadoop.hive.ql.exec.Description;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.serde2.objectinspector.ListObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector.Category;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorUtils;import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;import org.apache.hadoop.io.BooleanWritable;@Description(name = "array_contains", value = "_FUNC_(array, value) - Returns TRUE if the array contains value.", extended = "Example:\n > SELECT _FUNC_(array(1, 2, 3), 2) FROM src LIMIT 1;\n true")public class GenericUDFArrayContains extends GenericUDF { private static final int ARRAY_IDX = 0; private static final int VALUE_IDX = 1; private static final int ARG_COUNT = 2; private static final String FUNC_NAME = "ARRAY_CONTAINS"; private transient ObjectInspector valueOI; private transient ListObjectInspector arrayOI; private transient ObjectInspector arrayElementOI; private BooleanWritable result; public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException { if (arguments.length != 2) { throw new UDFArgumentException("The function ARRAY_CONTAINS accepts 2 arguments."); } if (!(arguments[0].getCategory().equals(ObjectInspector.Category.LIST))) { throw new UDFArgumentTypeException(0, "\"array\" expected at function ARRAY_CONTAINS, but \"" + arguments[0].getTypeName() + "\" " + "is found"); } this.arrayOI = ((ListObjectInspector) arguments[0]); this.arrayElementOI = this.arrayOI.getListElementObjectInspector(); this.valueOI = arguments[1]; if (!(ObjectInspectorUtils.compareTypes(this.arrayElementOI, this.valueOI))) { throw new UDFArgumentTypeException(1, "\"" + this.arrayElementOI.getTypeName() + "\"" + " expected at function ARRAY_CONTAINS, but " + "\"" + this.valueOI.getTypeName() + "\"" + " is found"); } if (!(ObjectInspectorUtils.compareSupported(this.valueOI))) { throw new UDFArgumentException("The function ARRAY_CONTAINS does not support comparison for \"" + this.valueOI.getTypeName() + "\"" + " types"); } this.result = new BooleanWritable(false); return PrimitiveObjectInspectorFactory.writableBooleanObjectInspector; } public Object evaluate(GenericUDF.DeferredObject[] arguments) throws HiveException { this.result.set(false); Object array = arguments[0].get(); Object value = arguments[1].get(); int arrayLength = this.arrayOI.getListLength(array); if ((value == null) || (arrayLength <= 0)) { return this.result; } for (int i = 0; i < arrayLength; ++i) { Object listElement = this.arrayOI.getListElement(array, i); if ((listElement == null) || (ObjectInspectorUtils.compare(value, this.valueOI, listElement, this.arrayElementOI) != 0)) continue; this.result.set(true); break; } return this.result; } public String getDisplayString(String[] children) { assert (children.length == 2); return "array_contains(" + children[0] + ", " + children[1] + ")"; }}
总结
当写Hive UDF时,有两个选择:一是继承 UDF类,二是继承抽象类GenericUDF。这两种实现不同之处是:GenericUDF 可以处理复杂类型参数,并且继承GenericUDF更加有效率,因为UDF class 需要HIve使用反射的方式去实现。
UDF是作用于一行的。转载地址:http://ekvws.baihongyu.com/