hive中自定義UDAF、 UDTF、 UDF

回頭看了看之前自定義的UDF,UDAF,UDTF,竟然有種生疏的感覺,因此,對於其中的代碼重新做了註釋,更加的詳細和容易理解,下面就是我自己定義的幾個樣例,比較簡單,主要是通過樣例來了解如何自定義UDF來完成需求。

1、UDAF

需求是找出指定字段的topN,數據類型定義爲double,下面是實現代碼。

package com.wangl.hadoop.udf;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;

@SuppressWarnings("deprecation")
public class TopNUDAF extends UDAF{

	public static class State{
		ArrayList a;//保存topn的結果
		int n;//調用該函數的topN的N
		boolean ascending;//升序還是降序
	}
	
	public static class Evaluator implements UDAFEvaluator{
		private State state;
		public Evaluator(){
			init();
		}
		//初始化Evaluator對象
		public void init() {
			if(state == null){
				state = new State();
			}
			state.a = new ArrayList();
			state.n = 0;
			state.ascending = false;//默認降序
		}
		/**
		 * map任務每行都會調用一次,iterate接收的參數正是調用函數時傳入的參數
		 * @param other 聚合的字段值
		 * @param n topN的N
		 * @return
		 * @throws UDFArgumentException 
		 */
		public boolean iterate(Double other,int n,String asc) throws UDFArgumentException{
			//升降序topn表示,false表示最大值,true表示最小值
				if(asc == null || asc.equals("")){
					throw new UDFArgumentException("third argument like 'asc' or 'desc'");
				}
				if(!asc.equals("asc") && !asc.equals("desc")){
					throw new UDFArgumentException("third argument like 'asc' or 'desc'");
				}
				if(asc.equals("asc")){
					state.ascending = true;
				}
				state.n = n;
				if(other != null){
					//是否插入標示
					boolean doInsert = state.a.size() < n;
					//如果當前的state.a的元素數量小於n時則需要插入操作
					//如果當前的state.a的元素數量大於或者等於n時,則需要將數組中的最後一位與待插入的數進行比較
					//從而確定是否應該插入。
					if(!doInsert){
						Double last = state.a.get(state.a.size()-1);
						if(state.ascending){
							//當ascending爲true時,即爲升序,topN中即爲最小的N位,因此只有待插入的數比最後一位小時,纔會進行插入操作,否則直接返回
							doInsert = other < last;
						}else{
							//當ascending爲false時,即爲降序,topN中即爲最大的N位,因此只有待插入的數比最後一位大時,纔會進行插入操作,否則直接返回
							doInsert = other > last;
						}
					}
					if(doInsert){
						//有順序的插入other的值
						binaryInsert(state.a,other,state.ascending);
						if(state.a.size() > n){
							state.a.remove(state.a.size()-1);
						}
					}
				}
			return true;
		}
		//將value得知按照ascending的順序插入到list中相應的位置
		static > void binaryInsert(List list, T value, boolean ascending){
			//根據順序獲取value在list中的位置,當list中沒有元素的時候,則會返回-1,插入方法是先根據二分法進行查找,然後再插入
			int position = Collections.binarySearch(list, value, getComparator(ascending,(T)null));
			if(position < 0 ){
				position = (-position) - 1;
			}
			list.add(position,value);
		}
		//比較器
		static > Comparator getComparator(boolean ascending,T dummy){
			Comparator comp;
			if(ascending){
				//如果是升序,誰小誰在前
				comp = new Comparator(){
					public int compare(T o1,T o2){
						return o1.compareTo(o2);
					}
				};
			}else{
				//如果是降序,誰大誰排在前
				comp = new Comparator(){
					public int compare(T o1,T o2){
						return o2.compareTo(o1);
					}
				};
			}
			return comp;
		}
		//返回局部topN
		public State terminatePartial(){
			if(state.a.size()>0){
				return state;
			}else{
				return null;
			}
		}
		/**
		 * reduce端,將map的輸出結果進行合併
		 * @param s 將要與當前State合併的State
		 * @return
		 */
		public boolean merge(State s){
			
			if(s != null){
				state.n = s.n;
				state.a = sortedMerge(s.a,state.a,state.ascending,s.n);
			}
			return true;
		}
		
		static > ArrayList sortedMerge(List list1,List list2,boolean ascending,int n){
			Comparator comp = getComparator(ascending, (T)null);
			
			int n1 = list1.size();
			int n2 = list2.size();
			
			int p1 = 0;//當前list1的元素
			int p2 = 0;//當前list2的元素
			//保存結果,有n個元素
			ArrayList output = new ArrayList(n);
			//遍歷並將list1和list2歸併到output 中,歸併過程中保留output最多有n個元素
			while(output.size() terminate(){
			if(state.a.size()>0){
				return state.a;
			}else{
				return null;
			}
		}
	}
	
}

2、UDF

需求是將表中的日期格式轉換成指定的模式:

package com.wangl.hadoop.udf;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;

public class MyDateParse extends UDF{
	private Text result = new Text();
	
	//hive自定義函數,繼承UDF之後,還需要定義一個evaluate方法
	public Text evaluate(Text str){
		String newStr = str.toString();
		//根據實際情況制定日期模式,根據字段的內容對字段進行轉換
		SimpleDateFormat format = new SimpleDateFormat("dd/MMMMM/yyyy:HH:mm:ss Z", Locale.ENGLISH);
		if(str.toString().indexOf("[")>-1){
			newStr = newStr.replace("[", "");
		}
		if(str.toString().indexOf("]")>-1){
			newStr = newStr.replace("]", "");
		}
		try{
			Date date = format.parse(newStr);
			SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			result.set(newFormat.format(date));
			return result;
		}catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}

3、UDAF

需求是將表中的兩個字段按照指定的分隔符進行連接

package com.wangl.hadoop.udf;

import java.util.ArrayList;

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.StringObjectInspector;

public class MergeFields extends GenericUDTF{
	
	String str1;
	String str2;
	Object[] forwardObj = null;
	@Override
	public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
		if(argOIs.length!=2){
			throw new UDFArgumentException("參數異常");
		}
		//初始化輸入輸出的格式
		forwardObj = new Object[1];
		str1 = ((StringObjectInspector)argOIs[0]).getPrimitiveJavaObject(argOIs[0]);
		str2 = ((StringObjectInspector)argOIs[1]).getPrimitiveJavaObject(argOIs[1]);
		ArrayList fieldNames = new ArrayList();
		ArrayList fieldOIs = new ArrayList();
		
		fieldNames.add("rcol1");
		fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
		//指定輸出的字段名稱和輸出內容格式
		return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
	}
	/**
	 * 這裏只是對兩個字段進行連接,主要是瞭解如何自定義UDTF的方法
	 */
	@Override
	public void process(Object[] args) throws HiveException {
		forwardObj[0] = str1 +"---"+ str2;
		
 		forward(forwardObj);
	}

	@Override
	public void close() throws HiveException {
		// TODO Auto-generated method stub
		
	}
	
}


以上的代碼僅供參考,如有問題歡迎指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章