Apache Kylin的自定义函数实现

来源:互联网 发布:java高级工程师好考吗 编辑:程序博客网 时间:2024/05/20 06:51

Apache Kylin是一个开源的分布式分析引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由eBay开发并贡献至开源社区。Apache Kylin提供了自己的WEB界面,用户可以在上面进行相应的SQL语句查询。但是由于Apache Kylin使用的是Calcite进行了SQL解析,因此有很多函数都不支持。本文主要介绍如何在Apache Kylin中实现自定义函数。关于Calcite的简单使用可以参考:http://blog.csdn.net/yu616568/article/details/49915577,这里简单介绍下Calcite关于函数的定义:

{  name: 'MY_PLUS',  className: 'com.example.functions.MyPlusFunction',  methodName: 'apply',  path: []}

其中,name用来表示函数名,在调用自定义函数的时候使用;className表示实现函数的类;methodName表示在类中实现该函数的方法名。如果methodName是确定的,那么这个方法在对应的类中必须存在。方法可以是静态的或者非静态的,但是如果是非静态的,那么对应的类必须有一个不带参数的public构造函数。如果methodName是”*”,那么Calcite会为类中的每个方法都生成一个对应的函数,类中的每个方法名都必须大写,此时name将不再有意义,在调用的时候可以直接通过该方法名进行调用,后面将会结合例子进行介绍。如果以上关于函数的定义可以直接写在JSON中,关于Calcite JSON模型的更多介绍可以参考:https://calcite.apache.org/docs/model.html#custom-schema。
在Apache Kylin源码的org.apache.kylin.query目录下有一个ufd文件夹,我们可以在这个文件夹中定义自己的自定义函数类,当然也可以放在其他路径中,这里为了便于管理,最好将自定义的函数类都放在此路径下。这里我定义了一个名为KylinRound的类,用于实现自定义函数round,类的主要代码如下所示:

package org.apache.kylin.query.udf;import java.math.BigDecimal;public class KylinRound {    /**     * 返回第一个参数value小数点之后len位有效数字,并且四舍五入。     * @param value input value     * @param len given length     * @return     */    public static double ROUND(double value, int len) {        if (len < 0)            throw new IllegalArgumentException("Parameter(s) error!");        String valueS = String.valueOf(value);        valueS = valueS.substring(valueS.indexOf('.') + 1);        if (valueS.length() > len) {            BigDecimal input = new BigDecimal(value);            BigDecimal result =                 new BigDecimal(Math.round(input.movePointRight(len).doubleValue()));            return result.movePointLeft(len).doubleValue();        } else {            return value;        }    }    public static double ROUND(BigDecimal value, int len) {        double valueD = value.doubleValue();        return ROUND(valueD, len);    }    public static double ROUND(String value, int len) {        double valueD = Double.valueOf(value);        return ROUND(valueD, len);    }}

类中一共包含三个重载函数,可以接受三种不同类型的输入参数,分别是double、BigDecimal和String类型。为了在Kylin的SQL查询中支持round函数,还需要将该函数“注册”到Kylin中。前面提到Kylin在进行SQL解析的时候使用了Calcite,因此可以通过Calcite JSON模型的方法将自定义的round函数“注册”到Kylin上:

{  name: 'KylinROUND',  className: 'org.apache.kylin.query.udf.KylinRound',  methodName: '*',}

这里将methodName定义为‘*’,Kylin会为KylinRound类中的每一个方法都注册一个对应的函数(这些方法名都必须是大写)。此时name就没有什么意义了,用户可以在SQL语句中直接使用ROUND进行相应的处理。此方法不仅适用于重载函数,而且对于不同名的多个函数也是适用的。
在与udf同级的目录中有一个名为schema的文件夹,其中有一个OLAPSchemaFactory类,Kylin就是在这个类中,把自定义函数写到JSON中的。在这个类的末尾部分,我们将上面的“注册”代码加上:

for (String schemaName : schemaCounts.keySet()) {    out.write("        {\n");    out.write("            \"type\": \"custom\",\n");    out.write("            \"name\": \"" + schemaName + "\",\n");    out.write("            \"factory\": \"org.apache.kylin.query.schema.OLAPSchemaFactory\",\n");    out.write("            \"operand\": {\n");    out.write("                \"" + SCHEMA_PROJECT + "\": \"" + project + "\"\n");    out.write("            },\n");    out.write("            \"functions\": [\n");    out.write("               {\n");    out.write("                   name: 'MASSIN',\n");    out.write("                   className: 'org.apache.kylin.query.udf.MassInUDF'\n");    out.write("               },\n");    out.write("               {\n");    out.write("                   name: 'ROUND',\n");    out.write("                   className: 'org.apache.kylin.query.udf.KylinRound',\n");    out.write("                   methodName: '*'\n");    out.write("               }\n");    out.write("              ]\n");    out.write("        }\n");    if (++counter != schemaCounts.size()) {        out.write(",\n");    }}

可以看到Kylin自己也定义了一个自定义函数类MassInUDF,但是并没有指定methodName,因此根据Calcite的解析规则,会去MassInUDF类中寻找名为“eval”的方法。查看MassInUDF类,可以发现确实有一个eval方法:

public class MassInUDF {    public boolean eval(@Parameter(name = "col") Object col,         @Parameter(name = "filterTable") String filterTable) {        return true;    }}

至此,我们就完成Kylin的自定义函数编写。然后重新编译源码即可。对于已经部署了Kylin的情况,可以编译后新生成的query/target/kylin-query-*.jar拷贝到Kylin部署目录的/tomcat/webapps/kylin/WEB-INF/lib目录下,替换原先的jar包,然后再重启Kylin服务器,就可以在WEB页面使用编写的自定义函数了。

0 0
原创粉丝点击