hive开发UDF及使用

 最近有个数据挖掘的需求,要求统计所给经纬度附近n公里某些事物的数量。涉及到地球两点间的距离计算,需要写UDF进行计算。

一、UDF编写

 根据经纬度计算两点间的距离,网上有很多计算方法,试了几个,发现这篇博客的方法计算的精度差比较小,他的分析方法也很详细,最终采用此方法。

import com.ai.hive.udf.topdomain.StringUtil;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
import org.apache.log4j.Logger;

/**
 * 功能:根据两地经纬度计算两点之间的距离
 * create temporary function LocDistanceCalUDF as 'com.ai.hive.udf.util.LocDistanceCalUDF';
 * @author olicity
 */
public class LocDistanceCalUDF extends UDF{
    private static Logger log = Logger.getLogger(LocDistanceCalUDF.class);

    private Text nullText = new Text("");
    /**
    *根据经纬度计算地球两点间的距离
    */
    private static double distanceCal(double lng1, double lat1,double lng2,double lat2){
        double dx = lng1 - lng2;// 经度差值
        double dy= lat1 - lat2;// 纬度差值
        double b = (lat1 + lat2) / 2.0;// 平均纬度
        double Lx = Math.toRadians(dx)*6367000.0*Math.cos(Math.toRadians(b));// 东西距离
        double Ly = 6367000.0*Math.toRadians(dy);// 南北距离
        return Math.sqrt(Lx*Lx+Ly*Ly);// 用平面的矩形对角距离公式计算总距离(米)
    }
    /**
    *重写evaluate方法
    */
    public Text evaluate(Text longitudeText1, Text latitudeText1,Text longitudeText2, Text latitudeText2){
        try{
            if(longitudeText1==null || latitudeText1==null || longitudeText2==null || latitudeText2==null){
                return nullText;
            }
            if(StringUtil.isEmpty(longitudeText1.toString()) || StringUtil.isEmpty(latitudeText1.toString()) || StringUtil.isEmpty(longitudeText2.toString()) || StringUtil.isEmpty(latitudeText2.toString())){
                return nullText;
            }
            double lng1 = Double.valueOf(longitudeText1.toString());
            double lat1 = Double.valueOf(latitudeText1.toString());
            double lng2 = Double.valueOf(longitudeText2.toString());
            double lat2 = Double.valueOf(latitudeText2.toString());

            double dis = distanceCal(lng1,lat1,lng2,lat2);
            return new Text(String.valueOf(dis));
        }catch (Exception e){
            return nullText;
        }

    }
    /**
    *重写evaluate方法
    */
    public Text evaluate(Text locationA,Text locationB){
        try{
            if (locationA==null||locationB==null){
                return nullText;
            }
            if(StringUtil.isEmpty(locationA.toString()) || StringUtil.isEmpty(locationB.toString())){
                return nullText;
            }
            String locationA2String  = locationA.toString();
            String locationB2String  = locationB.toString();
            double lng1 = Double.valueOf(locationA2String.split(",")[0]);
            double lat1 = Double.valueOf(locationA2String.split(",")[1]);
            double lng2 = Double.valueOf(locationB2String.split(",")[0]);
            double lat2 = Double.valueOf(locationB2String.split(",")[1]);

            double dis = distanceCal(lng1,lat1,lng2,lat2);
            return new Text(String.valueOf(dis));
        }catch(Exception e){
            return nullText;
        }
    }

}

 UDF类要继承org.apache.hadoop.hive.ql.exec.UDF类,类中要实现evaluate。 当我们在hive中使用自定义的UDF的时候,hive会调用类中的evaluate方法来实现特定的功能。

二、UDF导入

1.jar包上传

 右键类名,Copy reference,复制此类全路径得到:com.ai.hive.udf.util.LocDistanceCalUDF。将写完的类打成jar包上传到服务器。路径如:/user/olicity/hive/UDF

2.jar包引入classpath变量中

进入hive,引入jar包,执行命令

add jar /user/olicity/hive/UDF/udf.jar;

查看导入的jar包

list jars;

3.创建函数

创建一个名为LocDistanceCalUDF的临时函数,关联该jar包

create temporary function LocDistanceCalUDF as 'com.ai.hive.udf.util.LocDistanceCalUDF';

查看创建的函数

show functions;

4.注意

 上述方法仅限于当前会话生效,如需要添加一个永久的函数对应的永久的路径,则

create function locUDF.LocDistanceCalUDF 
  as 'com.ai.hive.udf.util.LocDistanceCalUDF' 
  using jar 'hdfs://hdfs路径/udf.jar';
use LocDistanceCalUDF;

需要将jar包放到hdfs上,然后创建函数关联路径即可。
另外还看到过另一种方法,配置hive-site.xml文件中的hive.aux.jars.path

配置参考如下:
   <property>
       <name>hive.aux.jars.path</name>
       <value>file:///home/hdfs/fangjs/DefTextInputFormat.jar,file:///jarpath/test.jar</value>
   </property>

三、UDF使用

 准备工作已经就绪,可以开始查表了。emmmmm,就简单的俩表查吧,表结构和表数据就不展示了,示例表也就不建了,所给经纬度表叫A表,需要查询的表叫B表,临时中间表叫c表,经纬度的表中字段定义是loc,距离就算2公里吧。

create table C
as
select B.* from B join A where (LocDistanceCalUDF(A.loc,B.loc)<=2000);

OK.

四、总结

 关于sql语句还是需要再多加练习,尤其是多表联查。Hadoop之路任重而道远。

猜你喜欢

转载自blog.csdn.net/weixin_43157082/article/details/83412904