当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数(UDF(UDF:user defined function).
自定义函数类别
UDF 作用于单个数据行,产生一个数据行作为输出。(数学函数,字符串函数)
UDAF(用户定义聚集函数):接收多个输入数据行,并产生一个输出数据行。(count,max)
UDF开发实例:
客户需求: 现有一个json脚本,需要将里面的字符串解析存储到Hive仓库中,要求每个字段对应相应的数据,并且将其中时间字段对应的数据转换成天数。
json 脚本:https://pan.baidu.com/s/1j7UnzJir155YVXmWDIsGvw 提取码: 6r29
通过上面的需求,我们知道hive提供的内置函数已经无法满足客户需求,所以这时需要我们自行定义内置函数。
使用IDEA进行代码编写 JsonParser 类继承UDF类 、 MovieRateBean类
包结构:
MovieRateBean类
package com.lyz.bigdata.udf;
/**
*@Author:[email protected] Lyz
*@Date: 2019/3/18 12:43
*@Description:
**/
//{"movie":"1721","rate":"3","timeStamp":"965440048","uid":"5114"}
public class MovieRateBean{
private String movie;
private String rate;
private String timeStamp;
private String uid;
public String getMovie() {
return movie;
}
public void setMovie(String movie) {
this.movie = movie;
}
public String getRate() {
return rate;
}
public void setRate(String rate) {
this.rate = rate;
}
public String getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
@Override
public String toString() {
return movie + "\t" + rate + "\t" + timeStamp + "\t" + uid;
}
}
JsonParser 类
package com.lyz.bigdata.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.codehaus.jackson.map.ObjectMapper;
import java.io.IOException;
/**
*@Author:[email protected] Lyz
*@Date: 2019/3/18 9:56
*@Description:
**/
public class JsonParser extends UDF {
public String evaluate(String jsonLine){
ObjectMapper objectMapper = new ObjectMapper();
try {
MovieRateBean bean = objectMapper.readValue(jsonLine, MovieRateBean.class);
return bean.toString();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
将上述打包成jar包,上传到HDFS中,并且将json的数据也导入HDFS中
上述打的jar包: https://pan.baidu.com/s/1ltOrO9dtcKhSZZ3StDRiYQ 提取码: ivc6
启动hive数据仓库,然后在里面创建一个表,来后将json数据集导入表中,先将他们解析成一条一条的字符串
操作命令
create table t_json(line string) row format delimited;
load data local inpath '/home/hadoop/rating.json' into table t_json;
select * from t_json limit 10; //因为数据量过大,所以只显示10条
导入表中 因为这些数据现在只是字符串形式,所以需要将他们解析成数据库的字段形式,每个字段对应响应的数据
这时候将要用到我们定义的函数。 操作步骤如下:
命令如下:
add JAR /home/hadoop/hiveudf.jar;
create temporary function parsejson as'com.lyz.bigdata.udf.JsonParser'; //parsejson 是起的函数名
select parsejson(line) from t_json limit 10;
我们可以看到,用函数解析之后,因为没有字符分割,所以只是将数据给整理好,但是对应的字段没有显示出来,所以接下来 我们需要重新创建一个表,然后分割成四段,让每一段数据对应他的字段
命令如下:
create table t_rating as
select split(parsejson(line),'\t')[0]as movieid,
split(parsejson(line),'\t')[1] as rate,
split(parsejson(line),'\t')[2] as timestring,
split(parsejson(line),'\t')[3] as uid from t_json limit 10;
执行完之后进行查询:
select * from t_rating limit 10;
查询结果如下:
接下来我们实现客户的另一个需求 将时间字段对应的秒改为天
技术实现:Hive的 Transform 关键字提供了在SQL中调用自写脚本的功能适合实现Hive中没有的功能又不想写UDF的情况
现在使用Transform关键字来实现
1.创建一个python脚本 然后在HDFS中创建一个文件将脚本复制进去并保存, 文件命名为weekday_mapper.py
#!/bin/python
import sys
import datetime
for line in sys.stdin:
line=line.strip()
movieid,rating,unixtime,userid = line.split('\t')
weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday()
print '\t'.join([movieid,rating,str(weekday),userid])
2.将脚本添加到指定路径下 3. 重新创建一个表 使用上述的python 脚本 4.待程序执行完之后进行查询 相应指令如下:
命令如下:
add FILE /home/hadoop/weekday_mapper.py;
create table u_data_new as select transform(movieid,rate,timestring,uid)
using 'python weekday_mapper.py' as (movieid,rate,weekday,uid) from t_rating;
select * from u_data_new limit 10;
从表中我们可以看到时间由秒变成了天 实现了客户的需求!