本篇博文,我将带你通过借助源码以及官方文档来完成自定义UDTF的编写与打包。当你阅读完本篇文章,你已经拥有了编写(UDF、UDAF)的能力,相信即使官方在新的版本上做出较大改动,你一样可以在短时间内驾驭它。
- UDF(User-Defined-Function) 一进一出 eg:length(‘abcedfg’)
- UDAF(User- Defined Aggregation Funcation) 聚合函数,多进一出 eg:COUNT/MAX
- UDTF(User-Defined Table-Generating Functions) 一进多出 eg:split(‘abtcdtef’,'t’)
UDTF Example
1、HiveWiki-UDTF介绍
2、ExampleCode
1)继承GenericUDTF方法
2)重写initialize方法。此方法返回UDTF的字段名信息(定义返回个数,类型)。
3)重写process方法。该方法就是用来处理你的业务逻辑的。
在process中,每一次forward()调用产生一行;如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。
- 最后调用
close方法。
借鉴源码中的例子编写
UDTF。使用IDEA快捷键Ctrl+Shfit+N。随便点一个实现进去后就可以根据源码中的例子手写自己的UDF了。
3、输入地名,返回该地名的上下级关系。
1 北京市
2 山东省
3 昌平区
4 海淀区
5 沙河镇
6 马池口
7 中关村
8 上地
9 烟台市
10 青岛市
11 牟平区
12 芝罘区
13 即墨区
14 城阳
1)重写initialize()
@Override
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
//定义返回列的列名
List<String> colName = Lists.newLinkedList();
colName.add("tag_province");
colName.add("tag_city");
colName.add("tag_area");
//定义返回字段类型
List<ObjectInspector> resType = new ArrayList<>();
resType.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
resType.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
resType.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
// 返回列名和列类型
return ObjectInspectorFactory.getStandardStructObjectInspector(colName, resType);
}
2)重写process()
//定义一个数组,返回一行数据结果要使用数组装起来
private Object[] forwardListObj = new Object[3];
//执行业务代码
@Override
public void process(Object[] args) throws HiveException {
//业务处理
String area = existArea(args[0].toString());
String city = existFather(area);
String province = existFather(city);
//结果装入定义好的数组
forwardListObj[0] = province;
forwardListObj[1] = city;
forwardListObj[2] = area;
//output
forward(forwardListObj);
}
//都写在process()不好看,拎出来。在process()中调用
public String existArea(String value) {
switch (value) {
case "沙河镇":
case "马池口镇":
case "中关村":
case "上地":
case "牟平区":
case "芝罘区":
case "即墨区":
case "城阳":
return value;
default:
return "";
}
}
public String existFather(String value) {
switch (value) {
case "昌平区":
case "海淀区":
return "北京市";
case "沙河镇":
case "马池口镇":
return "昌平区";
case "中关村":
case "上地":
return "海淀区";
case "烟台市":
case "青岛市":
return "山东省";
case "牟平区":
case "芝罘区":
return "烟台市";
case "即墨区":
case "城阳":
return "青岛市";
default:
return value;
}
}
3)清理
@Override
public void close() throws HiveException {
//TODO........
}
4)打包丢进Hive运行
丢之前最好先在本地开发机测试好,提高效率
hive> add jar /home/hadoop/app/xinxingdata-udf/lib/Xinxingdata-udf-1.0.jar;
Added [/home/hadoop/app/xinxingdata-udf/lib/Xinxingdata-udf-1.0.jar] to class path
Added resources: [/home/hadoop/app/xinxingdata-udf/lib/Xinxingdata-udf-1.0.jar]
hive> list jars;
/home/hadoop/app/xinxingdata-udf/lib/Xinxingdata-udf-1.0.jar
//temporary 为临时函数,当前session结束后,jar包就会失效。
//"xinxingdata.bigdata.UDF.AreaSplice"为你编写UDTF路径
hive> create temporary function geoSplit as "xinxingdata.bigdata.UDF.AreaSplice";
OK
Time taken: 0.346 seconds
//如果你想使UDF函数永久生效,请在 hive-site.xml 文件中添加:
<property>
<name>hive.aux.jars.path</name>
<value>file://[jar-localpath],[jar-localpath],[jar-localpath]</value>
</property>
创建永久生效的UDF
hive (default)>create function geoSplit as "xinxingdata.bigdata.UDF.AreaSplice";
删除函数
hive (default)>drop function geoSplit;
注意:在哪个数据库中注册的永久函数,必须在哪个数据库下将该方法删除。
5)查询测试
select tag_province,tag_city,tag_area
from (select geoSplit(name) from address) a
where tag_province !="";
OK
tag_province tag_city tag_area
北京市 昌平区 沙河镇
北京市 昌平区 马池口镇
北京市 海淀区 中关村
北京市 海淀区 上地
山东省 烟台市 牟平区
山东省 烟台市 芝罘区
山东省 青岛市 即墨区
山东省 青岛市 城阳
Time taken: 0.039 seconds, Fetched: 8 row(s)