hive udf簡介
在Hive中,用戶可以自定義一些函數(shù)惕稻,用于擴展HiveQL的功能,而這類函數(shù)叫做UDF(用戶自定義函數(shù))。UDF分為兩大類:UDAF(用戶自定義聚合函數(shù))和UDTF(用戶自定義表生成函數(shù))疾瓮。在介紹UDAF和UDTF實現(xiàn)之前,我們先在本章介紹簡單點的UDF實現(xiàn)——UDF和GenericUDF飒箭,然后以此為基礎在下一章介紹UDAF和UDTF的實現(xiàn)狼电。
Hive有兩個不同的接口編寫UDF程序蜒灰。一個是基礎的UDF接口,一個是復雜的GenericUDF接口肩碟。
org.apache.hadoop.hive.ql. exec.UDF 基礎UDF的函數(shù)讀取和返回基本類型强窖,即Hadoop和Hive的基本類型。如削祈,Text翅溺、IntWritable、LongWritable髓抑、DoubleWritable等咙崎。
org.apache.hadoop.hive.ql.udf.generic.GenericUDF 復雜的GenericUDF可以處理Map、List吨拍、Set類型褪猛。
注解使用:
@Describtion注解是可選的,用于對函數(shù)進行說明羹饰,其中的FUNC字符串表示函數(shù)名伊滋,當使用DESCRIBE FUNCTION命令時,替換成函數(shù)名队秩。@Describtion包含三個屬性:
- name:用于指定Hive中的函數(shù)名新啼。
- value:用于描述函數(shù)的參數(shù)。
- extended:額外的說明刹碾,如燥撞,給出示例。當使用DESCRIBE FUNCTION EXTENDED name的時候打印迷帜。
而且物舒,Hive要使用UDF,需要把Java文件編譯戏锹、打包成jar文件冠胯,然后將jar文件加入到CLASSPATH中,最后使用CREATE FUNCTION語句定義這個Java類的函數(shù):
- 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
簡單的udf實現(xiàn)很簡單锦针,只需要繼承udf荠察,然后實現(xiàn)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實現(xiàn)比較復雜悉盆,需要先繼承GenericUDF。這個API需要操作Object Inspectors馋吗,并且要對接收的參數(shù)類型和數(shù)量進行檢查焕盟。GenericUDF需要實現(xiàn)以下三個方法:
//這個方法只調用一次,并且在evaluate()方法之前調用宏粤。該方法接受的參數(shù)是一個ObjectInspectors數(shù)組脚翘。該方法檢查接受正確的參數(shù)類型和參數(shù)個數(shù)灼卢。
abstract ObjectInspector initialize(ObjectInspector[] arguments);
//這個方法類似UDF的evaluate()方法。它處理真實的參數(shù)来农,并返回最終結果鞋真。
abstract Object evaluate(GenericUDF.DeferredObject[] arguments);
//這個方法用于當實現(xiàn)的GenericUDF出錯的時候,打印出提示信息沃于。而提示信息就是你實現(xiàn)該方法最后返回的字符串涩咖。
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饿肺。這兩種實現(xiàn)不同之處是:GenericUDF 可以處理復雜類型參數(shù)蒋困,并且繼承GenericUDF更加有效率,因為UDF class 需要HIve使用反射的方式去實現(xiàn)敬辣。
UDF是作用于一行的雪标。