hive的UDAF函数做的是多进一出的聚合操作,它的写法和其他的两种函数有些不同,分为两部分,一部分叫做函数类,一部分叫做计算类,计算类在函数类内部,下面我给大家提供一个做计数操作的UDAF例子,希望可以帮到大家
但是我有一句前言告诉大家,Hive的UDAF函数可以说是已经无法使用了,因为它的编写在正式开发的时候不是很方便,所以随着spark的兴起,大家更愿意使用spark去处理数据,同时spark也提供了非常强大的功能,且编写更加便捷,所以对于Hive的UDAF函数,大家知道有这样一个东西就可以,不要去试着开发了,因为我自己当初搞的时候发现Hive的UDAF虽然API没有被删除,但是早已被列为不推荐使用行列,且所依赖的包可能拉取不到,就算你搞到包了,编译的时候也会发现,因为API的过时,所以无法编译成功
pom如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wy</groupId>
<artifactId>func</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.hive/hive-exec -->
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
</project>
代码如下
package com.wy;
import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
/**
* 函数类继承UDAF,其实函数类就相当于整个函数的一个包装,或者说是个盒子,它用来装下这个函数所有的东西
*/
public class MyUDAF extends UDAF {
/*
在函数类中准备自定义函数需要的东西
当前例子是用来做计数的因此准备一个计数用变量
这里可能会有人疑问:只是计数为什么不直接用一个值变量,而是要写一个类
别着急往下看
*/
public static class MyCount{
//最好不要直接写int count=0
int count;
}
/**
* 下面准备的是计算类
* 计算类实现UDAFEvaluator接口
* 重写init、iterate、terminatePartial、merge、terminate
* 但是说是重写,但是由于这个API已经过时了,导致需要的方法出不来,大家手敲一下就好
*/
public static class MyEvaluator implements UDAFEvaluator {
//这里准备计算所需要的资源,为了节省资源最好使用public static修饰一下
public static MyCount count;
public MyEvaluator() {
/*
函数运行时计算类会被实例化
我们要在计算类的构造器中初始化
但是注意,这里初始化的不是我们一般理解的一步到位的赋具体值
而是用只用来初始化资源,如实例化对象等预热操作
而具体的赋值操作在init中执行,这就是为什么我前面写了一个类
就是为了在这里让大家知道计算类的构造器和init方法到底初始化的是什么
*/
count=new MyCount();
/*
这里调用一下init方法,因为我们将执行函数内部更加详细的初始化操作的任务交给了它
这里说的内部我给大家说一个例子大家就明白了:
如果你运行的是group之后的计数操作,那么init方法就是在
一组数据计数之后做计数归零的那个角色,而外面的那个构造器,只是在构造这个函数的时候起效
怎么样看到这里是不是突然想来一句 搜嘎斯内
*/
init();
}
/**
* 这里进行初始化
*/
public void init() {
count.count=0;
}
/**
* iterate接收传入的参数,被函数内部多次调用,它的地位和MapReducer的map阶段等价
* 我们的聚合计算操作就在这里实现
* 但是它的返回值类型是个布尔
* 作用是对所要执行的业务做成功与否判断
* 大家写的时候根据当下的业务需要书写
* 接收的参数类型也是按照业务需要来
* 本例不是很需要具体值所有这里随便写了一个Double型
* 但是我要说一句这个方法体正常的逻辑来说,应该是聚合成功返回一个成功也就是true的返回值,但是由于这个API过时了,没有办法正常运行,所以我不确定为false的时候效果是什么
*/
public boolean iterate(Double o) {
if (o != null) {
count.count++;
}
return true;
}
/**
* terminatePartial无参数
* 它做的工作是用来操作iterate的结果的
* 类似于hadoop的小文件合并
* 过滤不必要数据的操作
* 如果没有需要那么直接返回就需要值好
* 但是工作中为了团队合作的我们还是会书写该方法
* 在这里简单的做一个结果验证操作
* 因为MR的map不止有一个同理iterate也可能不止一个
* 他们的结果我们要过滤一下
*/
public MyCount terminatePartial() {
return count.count == 0 ? null : count;
}
/**
* merge处理terminatePartial的结果做合并结果操作
* 与前面的一样,我也没有成功试出为FALSE的效果是什么
*/
public boolean merge(MyCount c) {
if (c != null) {
count.count+=c.count;
}
return true;
}
/**
* terminate处理并返回最终的聚集函数结果
*/
public int terminate() {
return count.count == 0 ? null : count.count;
}
}
}
733

被折叠的 条评论
为什么被折叠?



