编写Apache Hive用户自定义函数(UDF)有两个不同的接口,一个非常简单,另一个...就相对复杂点。

如果你的函数读和返回都是基础数据类型(Hadoop&Hive 基本writable类型,如Text,IntWritable,LongWriable,DoubleWritable等等),那么简单的API(org.apache.hadoop.hive.ql.exec.UDF)可以胜任
但是,如果你想写一个UDF用来操作内嵌数据结构,如Map,List和Set,那么你要去熟悉org.apache.hadoop.hive.ql.udf.generic.GenericUDF这个API
简单API: org.apache.hadoop.hive.ql.exec.UDF
复杂API:  org.apache.hadoop.hive.ql.udf.generic.GenericUDF
接下来我将通过一个示例为上述两个API建立UDF,我将为接下来的示例提供代码与测试
如果你想浏览代码:fork it on Github:https://github.com/rathboma/hive-extension-examples
 

简单API

用简单UDF API来构建一个UDF只涉及到编写一个类继承实现一个方法(evaluate),以下是示例:
  1. class SimpleUDFExample extends UDF {
  2. public Text evaluate(Text input) {
  3. return new Text("Hello " + input.toString());
  4. }
  5. }

因为该UDF是一个简单的函数,你可以在规范的测试工具测试它,如JUnit。

  1. public class SimpleUDFExampleTest {
  2. @Test
  3. public void testUDF() {
  4. SimpleUDFExample example = new SimpleUDFExample();
  5. Assert.assertEquals("Hello world", example.evaluate(new Text("world")).toString());
  6. }
  7. }
 

好的,在Hive控制台测试一把,也可以在hive中直接测试这个UDF,特别是当你不完全肯定该函数是否能够正确处理问题的时候

  1. %> hive
  2. hive> ADD JAR target/hive-extensions-1.0-SNAPSHOT-jar-with-dependencies.jar;
  3. hive> CREATE TEMPORARY FUNCTION helloworld as 'com.matthewrathbone.example.SimpleUDFExample';
  4. hive> select helloworld(name) from people limit 1000;

事实上,上述UDF有一个bug,不会去检查null参数,null在一个大的数据集当中是很常见的,所以要适当严谨点。作为回应,这边在函数中加了一个null检查

  1. class SimpleUDFExample extends UDF {
  2. public Text evaluate(Text input) {
  3. if(input == null) return null;
  4. return new Text("Hello " + input.toString());
  5. }
  6. }

然后加了一个测试去验证它

  1. @Test
  2. public void testUDFNullCheck() {
  3. SimpleUDFExample example = new SimpleUDFExample();
  4. Assert.assertNull(example.evaluate(null));
  5. }

用mvn test跑一下测试,来保证所有用例通过。

 

复杂的API

org.apache.hadoop.hive.ql.udf.generic.GenericUDF API提供了一种方法去处理那些不是可写类型的对象,例如:struct,map和array类型。
这个API需要你亲自去为函数的参数去管理对象存储格式(object inspectors),验证接收的参数的数量与类型。一个object inspector为内在的数据类型提供一个一致性接口,以至不同实现的对象可以在hive中以一致的方式去访问(例如,只要你能提供一个对应的object inspector,你可以实现一个如Map的复合对象)。
这个API要求你去实现以下方法:
  1. // 这个类似于简单API的evaluat方法,它可以读取输入数据和返回结果
  2. abstract Object evaluate(GenericUDF.DeferredObject[] arguments);
  3. // 该方法无关紧要,我们可以返回任何东西,但应当是描述该方法的字符串
  4. abstract String getDisplayString(String[] children);
  5. // 只调用一次,在任何evaluate()调用之前,你可以接收到一个可以表示函数输入参数类型的object inspectors数组
  6. // 这是你用来验证该函数是否接收正确的参数类型和参数个数的地方
  7. abstract ObjectInspector initialize(ObjectInspector[] arguments);

可能要通过一个示例才能去了解这个接口,所以接下来往下看。

 

示例

我将通过建立一个UDF函数:containsString,来加深对该API了解,该函数接收两个参数:
一个String的列表(list)
一个String
 
根据该list中是否包含所提供的string来返回true或者false,如下:
  1. containsString(List("a", "b", "c"), "b"); // true
  2. containsString(List("a", "b", "c"), "d"); // false

不同于UDF接口,这个GenericUDF接口需要更啰嗦点。

  1. class ComplexUDFExample extends GenericUDF {
  2. ListObjectInspector listOI;
  3. StringObjectInspector elementOI;
  4. @Override
  5. public String getDisplayString(String[] arg0) {
  6. return "arrayContainsExample()"; // this should probably be better
  7. }
  8. @Override
  9. public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
  10. if (arguments.length != 2) {
  11. throw new UDFArgumentLengthException("arrayContainsExample only takes 2 arguments: List<T>, T");
  12. }
  13. // 1. 检查是否接收到正确的参数类型
  14. ObjectInspector a = arguments[0];
  15. ObjectInspector b = arguments[1];
  16. if (!(a instanceof ListObjectInspector) || !(b instanceof StringObjectInspector)) {
  17. throw new UDFArgumentException("first argument must be a list / array, second argument must be a string");
  18. }
  19. this.listOI = (ListObjectInspector) a;
  20. this.elementOI = (StringObjectInspector) b;
  21. // 2. 检查list是否包含的元素都是string
  22. if(!(listOI.getListElementObjectInspector() instanceof StringObjectInspector)) {
  23. throw new UDFArgumentException("first argument must be a list of strings");
  24. }
  25. // 返回类型是boolean,所以我们提供了正确的object inspector
  26. return PrimitiveObjectInspectorFactory.javaBooleanObjectInspector;
  27. }
  28. @Override
  29. public Object evaluate(DeferredObject[] arguments) throws HiveException {
  30. // 利用object inspectors从传递的对象中得到list与string
  31. List<String> list = (List<String>) this.listOI.getList(arguments[0].get());
  32. String arg = elementOI.getPrimitiveJavaObject(arguments[1].get());
  33. // 检查空值
  34. if (list == null || arg == null) {
  35. return null;
  36. }
  37. // 判断是否list中包含目标值
  38. for(String s: list) {
  39. if (arg.equals(s)) return new Boolean(true);
  40. }
  41. return new Boolean(false);
  42. }
  43. }

代码走读

函数的调用模块如下:

1、该UDF用默认的构造器来初始化

2、udf.initialize() 被调用,传人udf参数的object instructors数组,(ListObjectInstructor, StringObjectInstructor)
1) 检查传人的参数有两个与该参数的数据类型是正确的(见上面)
2) 我们保存object instructors用以供evaluate()使用(listOI, elementOI)
3) 返回 object inspector,让Hive能够读取该函数的返回结果(BooleanObjectInspector)
3、对于查询中的每一行,evaluate方法都会被调用,传人该行的指定的列(例如,evaluate(List(“a”, “b”, “c”), “c”) )。
1) 我们利用initialize方法中存储的object instructors来抽取出正确的值。
2) 我们在这处理我们的逻辑然后用initialize返回的object inspector来序列化返回来的值(list.contains(elemement) ? true : false)。
 

测试

测试该函数比较复杂的部分是初始化,一旦调用顺序明确了,我们就知道怎么去构建该对象测试流程,非常简单。

  1. public class ComplexUDFExampleTest {
  2. @Test
  3. public void testComplexUDFReturnsCorrectValues() throws HiveException {
  4. // 建立需要的模型
  5. ComplexUDFExample example = new ComplexUDFExample();
  6. ObjectInspector stringOI = PrimitiveObjectInspectorFactory.javaStringObjectInspector;
  7. ObjectInspector listOI = ObjectInspectorFactory.getStandardListObjectInspector(stringOI);
  8. JavaBooleanObjectInspector resultInspector = (JavaBooleanObjectInspector) example.initialize(new ObjectInspector[]{listOI, stringOI});
  9. // create the actual UDF arguments
  10. List<String> list = new ArrayList<String>();
  11. list.add("a");
  12. list.add("b");
  13. list.add("c");
  14. // 测试结果
  15. // 存在的值
  16. Object result = example.evaluate(new DeferredObject[]{new DeferredJavaObject(list), new DeferredJavaObject("a")});
  17. Assert.assertEquals(true, resultInspector.get(result));
  18. // 不存在的值
  19. Object result2 = example.evaluate(new DeferredObject[]{new DeferredJavaObject(list), new DeferredJavaObject("d")});
  20. Assert.assertEquals(false, resultInspector.get(result2));
  21. // 为null的参数
  22. Object result3 = example.evaluate(new DeferredObject[]{new DeferredJavaObject(null), new DeferredJavaObject(null)});
  23. Assert.assertNull(result3);
  24. }
  25. }
 

结束语

希望这篇文章能够让你了解通过集成怎么去编写hive的自定义函数。
虽然在这篇文章中有一些其他的东西没提及到,但是另外有UDAF函数与UDTF函数,UDAF函数能够在一个函数中处理与聚集多行数据,如果你更感兴趣,这里有一些资源可以提供帮助。
另外,值得一读的书籍有Apache Hive Book from O’Reilly该数包含UDF与UDAF的简明的教程,和代码示例,更容易让你们明白如何去构建这些函数、什么异常你必须要指定、什么类型你必须返回
 

翻译来自于

http://blog.matthewrathbone.com/2013/08/10/guide-to-writing-hive-udfs.html

Hive UDF开发指南的更多相关文章

  1. 最强最全面的Hive SQL开发指南,超四万字全面解析

    本文整体分为两部分,第一部分是简写,如果能看懂会用,就直接从此部分查,方便快捷,如果不是很理解此SQL的用法,则查看第二部分,是详细说明,当然第二部分语句也会更全一些! 第一部分: hive模糊搜索表 ...

  2. Hive UDF开发-简介

    Hive进行UDF开发十分简单,此处所说UDF为Temporary的function,所以需要hive版本在0.4.0以上才可以. Hive的UDF开发只需要重构UDF类的evaluate函数即可.例 ...

  3. Hive UDF开发 第一个例子

    package udf; import org.apache.hadoop.hive.ql.exec.UDF; public class helloudf extends UDF{ public St ...

  4. Hive UDTF开发指南

    在这篇文章中,我们将深入了解用户定义表函数(UDTF),该函数的实现是通过继承org.apache.Hadoop.hive.ql.udf.generic.GenericUDTF这个抽象通用类,UDTF ...

  5. Hive UDF开发实例学习

    1. 本地环境配置 必须包含的一些包. http://blog.csdn.net/azhao_dn/article/details/6981115 2. 去重UDF实例 http://blog.csd ...

  6. hive UDF函数

    —虽然Hive提供了很多函数,但是有些还是难以满足我们的需求.因此Hive提供了自定义函数开发 —自定义函数包括三种UDF.UADF.UDTF —UDF(User-Defined-Function) ...

  7. Hive UDAF开发之同时计算最大值与最小值

    卷首语 前一篇文章hive UDAF开发入门和运行过程详解(转)里面讲过UDAF的开发过程,其中说到如果要深入理解UDAF的执行,可以看看求平均值的UDF的源码 本人在看完源码后,也还是没能十分理解里 ...

  8. 2、Hive UDF编程实例

    Hive的UDF包括3种:UDF(User-Defined Function).UDAF(User-Defined Aggregate Function)和UDTF(User-Defined Tabl ...

  9. HADOOP docker(六):hive简易使用指南

    前言1.hive简介1.1 hive组件与相应功能:1.2 hive的表类型1.3 分区表1.3 分隔符1.4 hive的数据存储2.数据类型2.1 基本数据类型2.1 复杂数据类型2.3 NULL3 ...

随机推荐

  1. ASP.NET MVC CheckBoxFor为什么会生成hidden input控件

    自己开发的公众号,可以领取淘宝内部优惠券 @Html.CheckBoxFor(m => m.Bool) 使用CheckBoxFor方法得到的html代码会是下面这个样子 <input ch ...

  2. Ini文件格式说明

    http://www.cnblogs.com/CUIT-DX037/ 百度百科介绍:ini 文件是Initialization File的缩写,即初始化文件,是windows的系统配置文件所采用的存储 ...

  3. JavaSE_2_关键字

    1.介绍一下Syncronized锁,如果用这个关键字修饰一个静态方法,锁住了什么?如果修饰成员方法,锁住了什么? synchronized是Java中的关键字,是一种同步锁.它修饰的对象有以下四种: ...

  4. stm8 全局变量定义 声明

    1.ST Visual Develop 开发环境下.h文件里面不能定义变量,要把变量定义在.C文件里面,然后在.H文件里面声明即可.补充:今天突然发现还有一种情况,变量在一个.h文件里定义后,在另外的 ...

  5. draggable与overflow同时存在,无法拖拽出父元素问题解决

    在使用jquery-ui的拖拽功能对列表内的选项拖拽时,发现无法将选项拖拽出列表的范围,一出范围就自动隐藏在列表下,查找到最后的原因是css中的overflow的原因,overflow存在则不能将选项 ...

  6. Android多媒体框架总结(1) - 利用MediaMuxer合成音视频数据流程分析

    场景介绍: 设备端通过服务器传向客户端(Android手机)实时发送视频数据(H.264)和音频数据(g711a或g711u), 需要在客户端将音视频数据保存为MP4文件存放在本地,用户可以通过APP ...

  7. POJ 3614 Sunscreen(贪心,区间单点匹配)

    把牛的SPF看作一个区间,防晒霜看作点.一个点可以匹配C[i]次,问最大匹配数.可以用图论做. 也可以贪心.贪心的思想是,把区间和点排序以后,考虑最左边的点,加入和这个点相交的区间, 并排除出界的区间 ...

  8. 【洛谷2519】[HAOI2011] problem a(动态规划)

    点此看题面 大致题意: 一次考试共有\(n\)个人参加,第\(i\)个人说有\(a_i\)个人分数比他高,\(b_i\)个人分数比他低.求最少有几个人说谎. 动态规划 刚看完题目可以说是一头雾水. 仔 ...

  9. 2018.5.14 XML文档类型定义----DTD

    1.DTD概述 一个完全意义上的XML文件不仅仅是Well-fromed(格式良好的),而且还应该是使用了一些自定义的标记ValidatingXMl(有效的)文档也就是说他必须遵守文档类型的定义中已声 ...

  10. Nginx学习记录(一)

    1. 什么是nginx Nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器.由俄罗斯的程序设计师Igor Sysoev所开发,官方测试nginx能够支支 ...