00:00
好,那接下来我们就开始编写udtf函数啊,那现在我们先打开have的udtf的说明文档啊,那在这个文档当中,呃,有一段文字描述,还有一个例子啊,这段文字描述是非常重要的啊,我们要能把它读懂,那基本上就学会了如何去编写udtf了啊,但是呢,要想读懂这段话呀,我们需要先搞清楚两个概念啊,第一个概念就是operator。那还有一个概念呢,就是object ins,对检查器啊,我们现在看第一个啊,也就这个operator,什么叫做operator啊,就是have当中的一条circle语句啊,实际上是会被抽象为一个operator trade啊,那一个operator呢,实际上就是由一系列的operator组成。啊,那什么又叫做operator呢?啊,Operator呢,其实就是一个特定的操作啊,它完成整个的数据处理过程当中的一个功能啊,其实就有点类似于Spark当中的那个算子啊,OK,那常见的operator包括什么呢?包括比如table scan operator用来扫描表的,对吧?啊,Select operator用来选择字段的啊,Filter operator用来过滤的啊,那operator用来做join操作的,Ru by operator用来做分组操作的。
01:23
吧,Atorator概念啊,象检查器啊,那什么是对象检查器?对象检查器的作用又是什么?那接下来我们分析一下啊,那刚才提到了啊,Have会将一条circle抽象为一个operator tree对吧?也就是一系列的operator啊,那数据呢,会按照计划依次历经每个operator的处理啊,那数据在前后两个operator中间进行传递的时候呢,它的数据和类型是分离的。
02:00
啊,那它的数据会被保存在一个Java当中的object对象当中,而数据的类型呢,会被保存在一个object inspect对象当中,也就是对象检查器当中。啊,除此之外,那对象检查器,也就是object inspector呢,还提供了用以解析object对象当中所保存的数据的方法。啊,这个就是object inpector的作用,OK,那了解了这两个概念之后啊,我们就以开始这段内容了,那接下来我们一起来读一下啊,那首先啊,一个自定义的udf函数,需要继承gene UD TF这样的一个抽象类,对吧?并且实现其中的以initial process,还有close方法,当然close方法呢,是可选的啊,那接下来我们分别看一下这几个方法的作用啊,其实主要就是它俩以initialize和process啊好,那我们主要看一下以initialize方法呢,会被have调用。
03:03
啊,Have调用它干啥呢?Have调用它去通知udtf函数,他将要接收到的参数类型。啊,那其实大家可以想一想啊,他是怎么通知的。啊,其实很简单啊,今儿这个have呢,就是调用以尼赖的方法,然后呢,把上一个operator返回的啊对象检查器传给我们的UDTF,原来都知道U这个对象检查器当中是不是保存了我们这个数据的类型啊。啊,其实就这样了,那OK,那接下来继续啊,那UDTF呢,哎,必须得返回一个对象检查器,对吧?那返回谁的对象检查器呢?哎,就是与我们这个UDTF函数输出相对应的对象检查器。啊,那你想一想,你说它返回这个对象检查器的目的是什么呀,或者是作用是啥呀。啊,其实很简单,是不是就是为了通知下一个operator,他即将接收到的这个数据的类型啊。
04:06
就是这样的,那OK,这就是以尼赖的方法,它的主要作用,那接下来我们再继续往下走啊,那一旦以尼赖的方法被调用了,那have呢?呃,就会将一行一行的数据传给UDTF,那怎么传呢?通过调用process方法传,也就是说我们能在process方法当中拿到一行一行的数据,对吧?啊,OK,那在方法当中,那咱们需要干什么事儿呢?哎,咱需要按自己的逻辑啊,比如说去分离咱们的数据,将一行分离成多行,那分离之后呢,再通过调用forward的方法将一行一行的数据,哎,传递给其他的operator。啊,OK,这就是方法和啊这个process方法的主要作用啊,那最后,那当我们把所有的数据都处理完了之后,Have会调用一下close方法,啊,OK,那这就是呃,Have的udtf函数的编写规则。
05:04
那接下来我们就可以编写代码了啊,那我已经提前创建好了一个没文项目啊,在这儿我们需要引入have eec的依赖啊,注意版本是3.1.2啊,CTRLC过来啊,放到po文件当中。来,我们就刷新一下啊,好,依赖已经引进来了,那接下来我创建一个类啊,New加class啊,那我起名为com.at硅谷点j mo.have.udtf啊,那咱起一个类名啊,这个类名呃要和我们后边的函数名保持一致啊,当然这个也不是强制的啊,那最好保持一致,那我们后续的函数名呢?呃,需要命名为呃,Explo杰森。啊,就是炸裂杰森数组对吧,那在这儿呢,哎,我们就起一个类名,也叫做呃,Ex prod explode。Sonray explorelos,好,那这个类我们就创建出来了啊,那接下来呢,我们就可以按照刚才所讲的编码规范去编写haveve的udtf函数了,那当然我们呃,这个官方网站上的说明文档当中给我们提供了一个案例,对吧?其实在这儿呢,我们可以把它的例子粘过来,然后呢,把逻辑修改一下就可以了,那也是咱们可以用一下它这个框架啊好,那现在我们呃复制一下吧,那来CTRLC。
06:23
拿过来哎,放到呃这个位置,然后呢,我们CTRLV啊,在这里边我们需要先把这个类名改回来啊,改成咱自己那个呃,Ex prod,然后呢是杰森啊OK,那然后呢,我们需要把这里边的一些注释,还有一些呃相关的逻辑去掉啊,这是他他的逻辑相关的内容咱给它去掉,那这个呢,咱也不需要。嗯,给它去掉啊,那这个呢,也给它去掉啊,注意啊,以你的方法当中啊,这些呃内容呢,我们后续啊是需要用到的,所以暂时呢就不删除了啊OK,那现在我们这个UDTF函数代码的框架就算是搭好了啊,那接下来我们就可以去编写咱自己的逻辑了,那现在我们先实现以尼射赖的方法啊,那这个方法呢,一共有两个功能啊,咱前面提到过对吧?那第一个啊,Have物会通过以尼射赖的方法通知我们,哎,这个udtf函数来接收到的数据的类型。
07:24
啊,没错吧,那它具体是怎么通知的呢?这很简单啊,呃,就是通过调用以设的方法啊,然后呢,把上一个operator啊输出的对象检查器传递过来啊,大家都知道,呃,对象检查器当中是不是就封装了这个数据的类型啊,对吧?那我们能我们是不是就能够通过它传递进来的参数去获取咱们这个udtf函数接收到的数据的类型了呀。啊,就这么一回事啊,OK,那接下来再看以尼射赖的方法的第二个功能,那第二个功能它要干啥呀?它是不是得返回一个对象检查器啊,当然返回的对象检查器应该是谁的呀?是不是应该是咱这个函数输出结果的对象检查器呀?啊,那返回它干什么呀?返回它是不是就是为了通知下一个operator,那它即将接收到的数据的类型啊?
08:13
OK,这就是呃以尼方法的两个功能啊,那接下来我们分别看一下这个方法的输入参数和返回值啊,大家注意观察啊,它的输入参数和返回值是不是都是跟对象检查器相关的内容,你看啊,输入是一个对象检查器数组,那返回值呢,是一个ru结构体对象检查器。没错吧,啊,那现在我们来分析一下啊,这个对象检查器数组是怎么一回事,那结构体对象检查器又是怎么一回事呢?哎,我们先看输入吧,啊,大家想想啊,你说我们这一样的一个UTF函数是不是可能会接收多个参数啊?对吧,它可能有一个参数,可能有两个,可能有三个,对不对,那所以说这个方法的输入参数呢,就是一个对象检查器数组啊,那正好数组当中的每个元素啊,就分别对应我们输入进来的每个参数啊,OK,那现在我们再来看它的返回值类型啊,返回值类型呢,是一个结构体对象检查器。
09:16
啊,对吧,那我们来想想,为什么这会是一个结构体对象检查器呢?啊,因为大家也知道啊,Udtf函数它的输出是不是可能会有多列,没错吧,那每一列都可以有自己的列名啊,也可以有自己的类型,对不对啊,那所以说你看啊,这样的一个数据结构是不是正好就对应于have当中的一个ru的结构体啊,啊,那所以说在这儿呢,他把输出的类型哎,用一个ruct对象检查器来封装。OK,那这就是以逆晒的方法,它的输入参数和返回值,那接下来我们实现一下这个方法的具体逻辑啊,那我们先思考一个问题啊,大家来看这儿啊。在这咱能拿到一个对象检查器数组,对吧?啊,大家得知道这个对象检查器数组实际上是啥呀。
10:05
是不是就是上一个operator输出结果的对象检查器,也就是说当前这个UDTF函数,它的输入参数的对象检查器?没错吧,那咱们拿到它之后能做啥事儿啊,哎,能做两件事儿,第一件,哎,我们可以根据这个对象检查器啊,去检查或者是去校验啊,咱这个udf函数输入参数的合法性。啊,比如说我们可以校验一下参数的个数啊,或者说校验一下参数的类型啊,那第二件事是什么呢?啊,就是我们可以使用这个对象检查器去解析上一个operator传递过来的数据,对吧?那当然解析数据不是在隐深赖的方法里边去做的,应该是哪个方法呀?应该是它下边的这个process方法吧。注意观察啊,你看到什么类型的,是都是类型的呀,我们需要递进啊,Object inspector去解析process方法当中传递进来的object对象。
11:11
啊,OK,这就是以尼方法的两个作用啊,那现在我们先实验它的第一个功能啊,那也就是是不是校验一下udtf函数的输入参数的合法性啊啊,对吧?那其实首先我们可以先用啊这个数组的长度去校验一下参数的个数对吧?因为刚才说了,那数组当中的每个元素是不是就正好对应哎,这个函数的每个参数啊啊,对吧?啊,那所以说数字的长度啊,就等于啊输入参数的个数啊,那现在咱们校验一下啊,那在校验之前咱先明确一点啊,我们之前规划的这个UDTF它的输入应该是什么样的?咱们去看一下这些咱有规定对吧,那我们的输入是不是就是一个,哎,接算数组字符串啊对吧,所以参数个数就是一啊,那类型什么就是此论类型对吧?啊OK,那接下来咱校验一下,好先校验参数个数,那怎么校验呀,其实很简单,那直接依附一下呗,对吧。
12:04
If啊,咱们这个ROS,然后点Les,如果它不等于一,不等于一,那就说明是不是参数个数非法呀,对吧,那这时候呢,我们就需要抛异常啊,就没必要往下进行了,来,那在这边呢,我们throw一个t roww。一个瑞啊,这个函数呢,呃只能呃接收呃一个参数。没错吧,那现在我们就完成了参数个数的校验了,那紧接着我们再去校验一下参数的类型啊,大家都知道啊,那我们通过这一步是不是已经明确了我们这儿只有一个参数了,对吧?那现在我们就需要从数组当中拿到哎,咱们的第一个元素X0,那这就是我们这个参数的对象检查器对吧,二个OA。
13:01
那接下来我们就可以校验参数的类型了啊,那由于我们的目标类型是string类型,那所以说我们在校验的时候呢,需要分两步去做啊,咱们需要先判断一下啊,看它是不是基本数据类型啊,如果是呃,再去判断啊,它是否是死追类型啊,那具体怎么做呢?我们来看一下。它是if里边呢,我们需要先把RGOI的category,也就它类别获取到里边呢,有一个get category这样的方法,咱点一下,然后呢,看它是否等于呃,Object inspector当中的category当中的啊,这个primitive啊,就是先去校验一下它是否是primitive,也就是否是基本数据类型啊呃,如果等于,呃,那就是正常的,如果不等于,那咱是不是得抛异常啊,对吧?那咱把这个抛异常的语句拿过来,CTRLV啊在这里边修改一下啊,咱们应该说什么呀?要说这个函数只能接收基本数据类型的参数,对吧?啊,基本数据。呃,类型啊的参数啊,这个没有问题对吧?好,那如果说校验通过了,那我们就能确定它是不是就是一个基本数据类型了呀,对吧,那这时候呢,我们需要将其强转为基本数据类型。
14:13
来强转一下。这应该是primitive prime object inspector,这个O放在这一下V啊,那这边呢,我们起名为primitive OA有点长啊,这个名字。OK,那完之后呢,我们再去校验这个primive OA是否为诶死string类型就可以了啊,那接下来我们依附一下,嗯。来,我们调用呃,Primitive OI里边的get primitive category这样的一个方法啊,完之后呢,看它是否等于primitive,呃,Object inspector里边的这个rimitive category里边的啊,这个啊,那如果等于那就正常的对吧?如果不等于,那咱是不是就得抛一常啊,没错吧,那接下来我们CTRLC往下拿来放在这个位置啊,OK,那在这里边我们改一下里边的内容啊,那我们应该怎么说呀?呃,Explore的接S瑞啊,只能接收呃,应该是string类型的参数对吧?S trg OK,那这样一来的话呢,我们就完成了以尼式赖的方法当中的第一个功能了,那就是说是校验输入参数啊,那我们继续啊,那隐射代方法呢,是不是还得再返回一个对象检查器啊,对吧?那它返回的对象检查器应该是谁?是不是应该是我们这个函数的输出结果所对应的对象检查器啊?
15:40
对吧,大家注意观察一下啊,那它的这个对象压器呢,是一个ru的结构体类型的啊,那你说我们应该怎么做呢?啊,那咱是不是就得构造一个ru的结构体对线压器对吧?然后呢,在这个结构体当中应该包含我们udtf函数输出的每一列的信息对吧?包括每一列的列名啊,每一列的这个类型是不是都得包含在这个结构体对象检查器当中啊。
16:06
对吧?那具体怎样去构造这样的一个对象检查器呢?好,我们往下看啊,看一下这段代码,那这段代码呢,是咱们刚刚呃,没有删除保留下来的那部分,对吧?啊,我们看一下它的逻辑是啥,我们先看最后一句啊,最后一个呢,是不是直接return了一个object inspector factory,那这是不是一个对象检查器的工厂类,对吧?然后呢,里边调用了一个get standardru object inspector这样的一个方法,对吧?这个方法是干啥用的呀?看名字是不是就能猜到,是不是就是获取准的结构体对象检查器啊?没错吧,这其实就是在构造我们想要的那个结构体对象检查器啊,OK,那大家看一下啊,这个方法呢,需要传两个参数分别是啥呢?我们来看一下啊,它需要我们给他传两个list集合,对不对?这2LIST集合分别是干啥的啊,第一个是field。
17:01
那第二一个呢,是ruct field object inspector,这个大家应该能能猜到是怎么回事了吧?啊大家都知道一个对象一个一个这个结构体呢,是有多个字段或叫多个列的,对吧?那每一列是不是得有一个自己的列名,没错吧?啊,那由于是有多列,所以说列名得放在一个集合当中,那每一列是不是也有也得有自己的类型啊,对吧?啊,那当然类型呢,是不是通过对象检查器去封装啊,啊,没错吧,那所以说在这儿呢,是不是还得再给他传一个对象检查器的一个集合。啊,是这个道理吧,那咱现在是不是要准备这样的两个集合,一个装列名,一个呢,去装啥呀,装对象检查器啊,没错吧?啊OK,大家往上看啊,那这是不是就诶有两个集合呀,对吧?一个装哎,Field name啊,一个装呢?哎,咱们这个field的OI啊,OK,那继续往下走,那下边我们要做的事呢,是不是就是往集合当中去放上咱们的列啊,它的列名和列的类型啊,对不对?哎,那咱这个UTF函数最终的输出有几列呀?
18:03
啊,咱们去看一下咱的罪终书,是不是就只有一列啊,那这一列是什么类型呢?是不是就是死准类型啊,没错吧,那所以说在这儿呢,我们啊,实际上就往里边放一列就可以了啊那首先列名咱们随便放一个,比如说我叫item it emm啊,那下边我们是不是还得放一个对象检查器啊,也就是对应什么,对应咱这个列的类型对不对?那OK,这应该放啥呀?咱是类型对不对?那这儿呢,是不是也是有一个primitive object inct,呃,Inctor factory啊,对吧,这是基本数据类型的,哎,工厂类啊完里边呢,是不是直接点一下就可以点JA v int object inpector,那你就能得到一个int类型的对象检查器,那我们是死string类型,对吧?那这里边有没有死追类型的呢?诶,咱们一写是不是就能发现它是有的呀。啊,OK,那这样一来,我是不是就已经把咱们想要的这个结构体对象检查器构造出来了呀,那正常返回就可以了,啊,OK,那到现在为止,我们就完成了以尼赖的方法的逻辑编写了。
19:03
我们继续往下进行啊,那现在我们去实现process方法啊,大家都知道啊,我们处理数据的逻辑是不是都在这个方法当中啊,啊对吧,大家来看一下这个方法它的参数是啥?参数是一个object数组。没错吧,咱前面提到过啊,就是两个operator之间传递数据的时候呢,数据和类型是分离的,对吧?数据保存在object对象当中啊,类型保存在object inspect对象当中,没错吧?那现在咱们拿到的就是数据了。好,OK,那大家思考一下,为什么这是一个数组呢?啊,很简单啊,因为是不是咱这个函数或者是一个operator,它接收到的参数是不是可能有多个呀,对吧,那有多个,那那是不是就得有一个数组啊,OK,这个大家要搞清楚啊,但是我们现在已经知道了啊,咱这个输入是不是只有一个参数。没错吧,那就是谁,那是直接咱们能够拿到这个BUG0啊,对吧啊,那大家说我直直接拿会不会有危险啊,会不会有什么呃下标数组越越界这样的危险啊,会不会啊。
20:01
啊,显然是不会的,为啥啊,你想一想,我们在最开始的时候调用以尼生代的方法的时候,是不是就已经校验了参数的个数了呀,对吧?如果参数不合法,早就已经是不是抛异常了,根本就不会进行到process这一步,那所以咱现在可以安全的获取这个数字当中的第一个元素,这个是没有任何问题的啊OK,那接下来咱继续啊,那大家注意观察啊,咱们拿到这个二个零呢,它是一个啥?是不是一个object呀,对吧?那我们期望得到的是什么呀?是string对不对?那这样一来我是不是就得涉及到一个操作,我需要从object对象当中去获取咱们那个string类型的数据啊,对吧?那这个怎么去获取呢?啊,其实很简单,那这里边呢,诶,咱们有一个工具类啊叫什么呢,叫做rimitive object inspector啊里边有一个啥,有一个。这里边儿呢,我们有一个get string方法啊,咱们点进去看一下啊,呃,CTRLB1下来大家看,那这里边儿我们需要传两个参数,一个就是object。
21:02
那一个呢,就是mitive object inspector,那也就说我们需要给他一个object对象,再给他一个与之对应的对检查,那它是不是就能够自动帮我们从对这个object对象当中去解析咱们所需要的,诶这个数据了呀。啊,没错吧,啊,那object咱们知道是谁,是不是就是这个二零没错吧,那它应该来自于哪儿呢?他应该来自于哪儿啊?它是不是应该是来自于啊,咱这个以尼赖的方法,它的输入参数啊,对吧。是这个道理吧,啊OK啊,那所以说我们现在就明确了这个解析思路了啊,那现在我需要先把这个啊二个零哎放进来,完了之后呢,还得再放一个对象检查器,那这个对象检查器咱们刚才知道了,呃,Have呢,会通过隐醚这类方法传给我们,对吧,但是在我在pro方法里边是没有办法获取啊,对吧,那咱要想用那怎么办呢。怎么?怎么办?是不是咱得在这儿声明一个呃属性啊,对吧,声明一个属性之后,那在这儿呢,我是不是就能能用了呀,对不对,那接下来咱们去做一个这个具体的操作,那这个具体怎么做啊,其实很简单,来咱们一起看一下。
22:08
在这呢,我声明一个,呃,Primitive,对pri,然后呢,呃,刚才咱们看到了是不是需要一个primitive object pattern啊,对吧?呃,那OK,咱们把它放过来,那我起个名叫做input啊,OA啊,那当然呢,我们是不是得在以一设代的方法当中去实现一个赋值的逻辑啊,没错吧,那接下来咱们去给它赋值啊,那首先咱们要明确一点啊,咱这需要的是一个什么类型,是不是一个primitive object inspector啊,对吧?那我觉得当初咱在这个位置是不是做过一个强转。没错吧,啊,那OK,那现在咱们要做的事儿是不是就是把这个pmive OA付给我们上边的inputi对吧,让它等于这个pmive OA。啊,没问题对吧,那接下来咱继续,现在咱是不是就可以把这个诶input OI放进来了啊,对吧,放下来之后CTRLV,那我们就能够得到咱们所需的那个词缀类型的字符串了,来我们处理一下啊。
23:02
来controlr位,好,那得到一个缀,那当然大家都知道这个缀应该是啥呀,是不是应该就是我们传给他的那个杰森数组字符串啊,对吧?所以我们叫森瑞str,好,那接下来我们继续往下进行,拿到这个杰森A瑞str之后,咱们应该做什么工作呀?那我们要做的工作应该是啥啊,其实很简单,我是不是得解析这个杰森数组对不对,咱们需要遍历数组当中的每个元素对吧?然后呢,把每个元素是不是当做一行给它FOR2的出去啊,对吧?啊,OK,那这里边具体怎么做呢?我怎样才能便利这个数组呢?啊,那一个字符串的形式你肯定没有办法遍历对吧?那所以说我们需要用的啥,需要用到杰森的解析工具对吧?那杰森的解析工具咱们在这儿呢,就不再引入其他的了啊have的eec当中其实引入了一个org的杰森,那咱们在这儿用它就可以了啊那具体怎么操作,首先我们需要先new一个杰森A啊A。Ay啊,然后呢,把谁放进来,把这个杰森瑞SR放进来,那这样一来我们就能得到一个杰森数组的对象了啊大家来看一下这个杰森数组的对象,它有什么样的方法点一下啊,那首先第一个是什么是lengths,也就是说我们能够获取数组的长度对吧?那除此之外呢,我还有一大堆的get的方法对吧?注意啊,Get的话是根据什么盖的,是不是就根据下标去盖它啊,或者根据索引去盖的对不对?那所以说我们这就可以怎样去便利数组元素了呀。
24:26
显然是for循环呀,对吧?那接下来咱们for循环去遍利一下这个数组,来咱们for I一下,好,那这里边我需要把杰森瑞的长度放在这个位置,那接下来咱就可以去遍历它了,没错吧,那具体怎么做呀,很简单,是不是就直接接算A瑞点get就行了,注意咱们get什么类型。啊,你看啊,你get什么,它的返回值是不是就是什么类型啊,对吧?那我们最终的返回值什么类型,是不是string类型,那就在这儿呢,我们应该get是不是get string啊对吧?然后把I放进来,那我们ctrl al的V,那你说我们得到的应该就是啥?是不是就是一个一个的杰森对象啊,当然是一个字符串啊,没错吧,那这个字符串实际上就是我们期望得到的谁是不是一个一个这样的一个字符串啊,对吧?啊,OK,那咱现在要做的是啥?这就把这样的一个一个的字符串给它输出出去,没错吧?那我们通过哪个方法输出,是不是通过调用forward方法输出啊,没错吧,那接下来咱们调用诶forward方法,那注意观察forward方法它的入参是什么类型,是object类型。
25:23
对吧,有同学在这儿呢,就会直接把这个杰森放进来输出出去,但是大家一定要注意啊,这样做是不对的啊,为什么不对啊,大家要注意啊。我们任何一个operator啊,它的输出的数据和输出的对象检查器,那必须得是一致的才可以啊,必须得是一致的才可以啊,OK,那大家来观察一下,咱上边输出的对象检查器是什么类型,是对象检查器对不对啊,那一个的结构体当中,那是不是会有多列对不对,会有多列,那多列每一列是不是都得有一个值,那它的数据呢,实际上可能会有多个对吧。
26:02
啊,那既然是多个,那我们去装数据的时候,是不是就得用一个数组或者是用一个集合去装啊,没错吧,那即便咱们这儿只有一列,那我也得用一个数组或者用一个集合去装才可以,那OK,所以说我们现在呢,哎,应该再拗一个,哎,一个数组吧,再用数组就行,那用一个什么,用一个死string类型的数组就可以,那当然数组当中咱们大家都知道啊,是不是只有一个元素对不对,那就是谁,那就是咱的这个杰森对吧?咱把它直接放进来就行了,然后我们ctrl al v,那这样一来我们就能得到一个re的一个最终结果了,那咱们要做的应该是把这个数组通过法输出,这样一来我们逻辑编OK法,不需任何逻辑OK,我们udtf函数的编码。
我来说两句