Hive-UDTF

UDTF

上一篇介绍了基础的UDF——UDF和GenericUDF的实现,这一篇将介绍更复杂的用户自定义表生成函数(UDTF)。用户自定义表生成函数(UDTF)接受零个或多个输入,然后产生多列或多行的输出,如explode()。要实现UDTF,需要继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF,同时实现三个方法

// 该方法指定输入输出参数:输入的Object Inspectors和输出的Struct。  
abstract StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException;   
  
// 该方法处理输入记录,然后通过forward()方法返回输出结果。  
abstract void process(Object[] record) throws HiveException;  
  
// 该方法用于通知UDTF没有行可以处理了。可以在该方法中清理代码或者附加其他处理输出。  
abstract void close() throws HiveException;  

其中:在0.13.0中initialize不需要实现。

定义如下:

public abstract class GenericUDTF {
    Collector collector;

    public GenericUDTF() {
        this.collector = null;
    }

    public void configure(MapredContext mapredContext) {
    }

    public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
        List inputFields = argOIs.getAllStructFieldRefs();
        ObjectInspector[] udtfInputOIs = new ObjectInspector[inputFields.size()];
        for (int i = 0; i < inputFields.size(); ++i) {
            udtfInputOIs[i] = ((StructField) inputFields.get(i)).getFieldObjectInspector();
        }
        return initialize(udtfInputOIs);
    }

    @Deprecated
    public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
        throw new IllegalStateException("Should not be called directly");
    }

    public abstract void process(Object[] paramArrayOfObject) throws HiveException;

    public abstract void close() throws HiveException;

    public final void setCollector(Collector collector) {
        this.collector = collector;
    }

    protected final void forward(Object o) throws HiveException {
        this.collector.collect(o);
    }

看一个例子

FUNC(a) - separates the elements of array a into multiple rows, or the elements of a map into multiple rows and columns

/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/
package org.apache.hadoop.hive.ql.udf.generic;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.TaskExecutionException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.serde2.objectinspector.ListObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.MapObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;

@Description(name = "explode", value = "_FUNC_(a) - separates the elements of array a into multiple rows, or the elements of a map into multiple rows and columns ")
public class GenericUDTFExplode extends GenericUDTF {
    private transient ObjectInspector inputOI;
    private final transient Object[] forwardListObj;
    private final transient Object[] forwardMapObj;

    public GenericUDTFExplode() {
        this.inputOI = null;

        this.forwardListObj = new Object[1];
        this.forwardMapObj = new Object[2];
    }

    public void close() throws HiveException {
    }

    public StructObjectInspector initialize(ObjectInspector[] args)
    throws UDFArgumentException
  {
    if (args.length != 1) {
      throw new UDFArgumentException("explode() takes only one argument");
    }

    ArrayList fieldNames = new ArrayList();
    ArrayList fieldOIs = new ArrayList();

    switch (1.$SwitchMap$org$apache$hadoop$hive$serde2$objectinspector$ObjectInspector$Category[args[0].getCategory().ordinal()])
    {
    case 1:
      this.inputOI = args[0];
      fieldNames.add("col");
      fieldOIs.add(((ListObjectInspector)this.inputOI).getListElementObjectInspector());
      break;
    case 2:
      this.inputOI = args[0];
      fieldNames.add("key");
      fieldNames.add("value");
      fieldOIs.add(((MapObjectInspector)this.inputOI).getMapKeyObjectInspector());
      fieldOIs.add(((MapObjectInspector)this.inputOI).getMapValueObjectInspector());
      break;
    default:
      throw new UDFArgumentException("explode() takes an array or a map as a parameter");
    }

    return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
  }

    public void process(Object[] o)
    throws HiveException
  {
    Iterator i$;
    switch (1.$SwitchMap$org$apache$hadoop$hive$serde2$objectinspector$ObjectInspector$Category[this.inputOI.getCategory().ordinal()])
    {
    case 1:
      ListObjectInspector listOI = (ListObjectInspector)this.inputOI;
      List list = listOI.getList(o[0]);
      if (list == null) {
        return;
      }
      for (i$ = list.iterator(); i$.hasNext(); ) { Object r = i$.next();
        this.forwardListObj[0] = r;
        forward(this.forwardListObj);
      }
      break;
    case 2:
      MapObjectInspector mapOI = (MapObjectInspector)this.inputOI;
      Map map = mapOI.getMap(o[0]);
      if (map == null) {
        return;
      }
      for (Map.Entry r : map.entrySet()) {
        this.forwardMapObj[0] = r.getKey();
        this.forwardMapObj[1] = r.getValue();
        forward(this.forwardMapObj);
      }
      break;
    default:
      throw new TaskExecutionException("explode() can only operate on an array or a map");
    }
  }

    public String toString() {
        return "explode";
    }
}

一个分割字符串的例子:

@Description(  
    name = "explode_name",  
    value = "_FUNC_(col) - The parameter is a column name."  
        + " The return value is two strings.",  
    extended = "Example:\n"  
        + " > SELECT _FUNC_(col) FROM src;"  
        + " > SELECT _FUNC_(col) AS (name, surname) FROM src;"  
        + " > SELECT adTable.name,adTable.surname"  
        + " > FROM src LATERAL VIEW _FUNC_(col) adTable AS name, surname;"  
)  
public class ExplodeNameUDTF extends GenericUDTF{  
  
    @Override  
    public StructObjectInspector initialize(ObjectInspector[] argOIs)  
            throws UDFArgumentException {  
          
        if(argOIs.length != 1){  
            throw new UDFArgumentException("ExplodeStringUDTF takes exactly one argument.");  
        }  
        if(argOIs[0].getCategory() != ObjectInspector.Category.PRIMITIVE  
                && ((PrimitiveObjectInspector)argOIs[0]).getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.STRING){  
            throw new UDFArgumentTypeException(0, "ExplodeStringUDTF takes a string as a parameter.");  
        }  
          
        ArrayList<String> fieldNames = new ArrayList<String>();  
        ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>();  
        fieldNames.add("name");  
        fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);  
        fieldNames.add("surname");  
        fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);  
              
        return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);  
    }  
      
    @Override  
    public void process(Object[] args) throws HiveException {  
        // TODO Auto-generated method stub  
        String input = args[0].toString();  
        String[] name = input.split(" ");  
        forward(name);  
    }  
  
    @Override  
    public void close() throws HiveException {  
        // TODO Auto-generated method stub  
          
    }  
  
}  

记住 最后调用forward函数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容