Flink SQL UDF自定义函数collect_list实现及其泛型化

背景

Flink SQL1.10 没有collect_list函数,可以通过自定义函数的方式实现。
文章最后介绍自定义函数的泛型化。

实现CollectList函数

package com.test.realtime.dw.dtl.operator;   
import org.apache.flink.table.functions.AggregateFunction;  
import java.util.ArrayList;  
import java.util.List;  
  
public class CollectList extends AggregateFunction<String, List<String>> {  
  
    private static String separator = "_";  
    public CollectList(String separator) {  
        this.separator = separator;  
    }  
  
    public void retract(List acc , String column){  
        acc.remove(column);  
    }  
  
    public void accumulate(List acc, String column){  
        acc.add(column);  
    }  
  
    @Override  
    public String getValue(List<String> list) {  
        return String.join(this.separator, list);  
    }  
  
    @Override  
    public List<String> createAccumulator() {  
        List list = new ArrayList();  
        return list;  
    }  
  
    public void resetAccumulator(List list) {  
        list.clear();  
    }  
  
}

函数的功能:

  • 创建累加器, 对应代码createAccumulator方法
  • 接收字符串类型的数据,存到list中,对应代码中的accumulate方法
  • 把list中的元素转换为字符串输出,元素以"_"分隔,用户可以指定其它字符作为分隔符,对应代码中的getValue方法
  • 当发生回撤时,需删除回撤的元素,对应代码中的retract方法
  • 重置累加器,对应代码resetAccumulator方法

注册CollectList函数

在Flink环境中注册CollectList函数

tenv.registerFunction("collect_list", new CollectList("_"));

完整的测试代码

package com.test.realtime.dw.dtl.app;  
  
import com.test.realtime.dw.dtl.operator.CollectList;  
import org.apache.flink.api.java.tuple.Tuple3;  
import org.apache.flink.configuration.Configuration;  
import org.apache.flink.streaming.api.datastream.DataStream;  
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;  
import org.apache.flink.streaming.api.functions.source.SourceFunction;  
import org.apache.flink.table.api.EnvironmentSettings;  
import org.apache.flink.table.api.Table;  
import org.apache.flink.table.api.java.StreamTableEnvironment;  
import org.apache.flink.table.functions.AggregateFunction;  
import org.apache.flink.types.Row;  
  
  
public class TestSQLCustomUDF {  
    public static void main(String[] args) throws Exception {  
        Configuration configuration = new Configuration();  
        configuration.setString("rest.port","9091"); //指定 Flink Web UI 端口为9091  
        EnvironmentSettings environmentSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();  
        StreamTableEnvironment tenv = StreamTableEnvironment.create(env, environmentSettings);  
        env.enableCheckpointing(10000);  
        DataStream<Tuple3<String,Long,Long>> source = env.addSource(new SourceFunction<Tuple3<String,Long,Long>>() {  
            private boolean isRunning = true;  
  
            @Override  
            public void run(SourceContext<Tuple3<String, Long, Long>> sourceContext) throws Exception {  
                String city = "Guangzhou";  
                long code = 510000;  
                while (isRunning){  
                    code = code + 1;  
                    if (code >= 510003){  
                        code = 510000;  
                    }  
                    Thread.sleep(1000);  
                    sourceContext.collect(Tuple3.of(city, code, System.currentTimeMillis()));  
                }  
            }  
  
            @Override  
            public void cancel() {  
                isRunning = false;  
  
            }  
        });  
  
        source.print();  
  
  
        tenv.createTemporaryView("source", source, "city,code,create_time");  
        tenv.registerFunction("collect_list", new CollectList("_"));  
        Table table = tenv.sqlQuery("select \n" +  
                "    city,\n" +  
                "    collect_list(cast(code as String)) as code_list,\n" +  
                "    collect_list(cast(create_time as String)) as create_time_list\n" +  
                "from \n" +  
                "(\n" +  
                "    select \n" +  
                "    city,\n" +  
                "    code,\n" +  
                "    create_time,\n" +  
                "    row_number() over(partition by city order by create_time desc) as ranking\n" +  
                "    from \n" +  
                "    (\n" +  
                "        select \n" +  
                "        city,\n" +  
                "        code,\n" +  
                "        create_time,\n" +  
                "        row_number() over(partition by city,code order by create_time desc) as aranking\n" +  
                "        from source\n" +  
                "    ) r\n" +  
                "    where aranking = 1 \n" +  
                ")\n" +  
                "where ranking <= 3\n" +  
                "group by \n" +  
                "city\n");  
        tenv.toRetractStream(table, Row.class).print();  
        env.execute();  
    }  
  
}

执行结果:

在这里插入图片描述

5. 如何泛化CollectList函数

在本文第二节中实现的CollectList函数只能处理String类型的数据,要处理Integer,Long等类型时需要先转换为String类型才能执行。为了使CollectList函数能够处理更多数据类型的数据,需要实现泛型化,代码如下:

package com.test.bigdata.realtime.dw.udf;  
  
import org.apache.flink.api.common.typeinfo.TypeInformation;  
import org.apache.flink.api.common.typeinfo.Types;  
import org.apache.flink.table.functions.AggregateFunction;  
  
import java.util.ArrayList;  
import java.util.List;  
  
public class CollectList<OUT,IN> extends AggregateFunction<OUT, List<IN>> {  
  
    private static String separator = null;  
    public CollectList(String separator) {  
        this.separator = separator;  
  
    }  
  
    public void retract(List acc , IN column){  
        acc.remove(column);  
    }  
  
    public void accumulate(List acc, IN column){  
        acc.add(column);  
    }  
  
    @Override  
    public OUT getValue(List<IN> list) {  
        List<String> result = new ArrayList<>();  
        for(IN item:list){  
            result.add(String.valueOf(item));  
        }  
        return (OUT) String.join(this.separator, result);  
    }  
  
    @Override  
    public List<IN> createAccumulator() {  
        List list = new ArrayList();  
        return list;  
    }  
  
    public void resetAccumulator(List list) {  
        list.clear();  
    }  
  
    @Override  
    public TypeInformation<OUT> getResultType() {  
        return (TypeInformation<OUT>) Types.STRING;  
    }  
  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 您可以尝试以下代码: public class CollectListUDF extends AggregateFunction<Integer,List<Integer>, List<Integer>> { public List<Integer> createAccumulator() { return new ArrayList<Integer>(); } public List<Integer> add(Integer value, List<Integer> accumulator) { accumulator.add(value); return accumulator; } public List<Integer> getResult(List<Integer> accumulator) { return accumulator; } public List<Integer> merge(List<Integer> a, List<Integer> b) { a.addAll(b); return a; } } ### 回答2: 要实现collect_list方法的Flink UDF,可以按照以下步骤进行: 1.创建一个继承自`org.apache.flink.api.common.functions.MapFunction`接口的类,并指定输入类型和输出类型。假设输入类型为T,输出类型为List<T>。 2.实现`MapFunction`接口的`map`方法。该方法用于对输入元素进行处理,并返回转换后的输出结果。 3.在`map`方法中,创建一个ArrayList对象,用于存储转换后的元素列表。 4.对于每个输入元素,将其添加到ArrayList中。 5.最后,返回ArrayList作为输出结果。 以下是一个示例实现: ```java import org.apache.flink.api.common.functions.MapFunction; import java.util.ArrayList; import java.util.List; public class ListCollector<T> implements MapFunction<T, List<T>> { @Override public List<T> map(T value) throws Exception { List<T> resultList = new ArrayList<>(); resultList.add(value); return resultList; } } ``` 注意,上述示例实现只是一个简单的示例,仅将输入元素添加到一个ArrayList中。如果需要实现更复杂的collect_list方法,可以根据需求自行修改`map`方法的实现逻辑。 ### 回答3: 在 Flink 中,可以使用自定义的 UDF(用户定义函数)来实现 collect_list 方法,将数据流中的元素按照指定的 Key 进行分组,并将每个分组下的元素以 List 的形式返回。 下面是一个示例的 Flink UDF 代码实现: ```java import org.apache.flink.api.common.functions.GroupReduceFunction; import org.apache.flink.util.Collector; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class CollectListUDF implements GroupReduceFunction<Tuple2<String, Integer>, Tuple2<String, List<Integer>>> { @Override public void reduce(Iterable<Tuple2<String, Integer>> iterable, Collector<Tuple2<String, List<Integer>>> collector) throws Exception { Iterator<Tuple2<String, Integer>> iterator = iterable.iterator(); List<Integer> list = new ArrayList<>(); String key = null; while (iterator.hasNext()) { Tuple2<String, Integer> next = iterator.next(); key = next.f0; list.add(next.f1); } collector.collect(new Tuple2<>(key, list)); } } ``` 上述代码中,`Tuple2<String, Integer>` 表示数据流中的元素类型,假设第一个字段为 Key,第二个字段为 Value。`CollectListUDF` 实现Flink 的 `GroupReduceFunction` 接口,重写了其中的 `reduce` 方法。通过迭代器将数据流中的元素取出,将对应的 Value 值添加到一个 List 中,并将结果作为 Tuple2 类型通过 `collector.collect()` 方法进行输出。 然后,可以在 Flink 的数据流处理程序中使用该 UDF 进行操作,示例代码如下: ```java DataStream<Tuple2<String, Integer>> inputDataStream = ... DataStream<Tuple2<String, List<Integer>>> resultDataStream = inputDataStream .groupBy(0) // 按第一个字段进行分组 .reduceGroup(new CollectListUDF()); resultDataStream.print(); ``` 上述代码中,`inputDataStream` 是待处理的数据流,可以从 Kafka、Socket 或其他数据源中获取。通过 `groupBy(0)` 方法按照第一个字段进行分组,在结果数据流中每个分组下的元素将按照 Key 的值以 List 的形式返回。 最后,通过 `resultDataStream.print()` 方法将结果数据流输出到控制台。 需要注意的是,实际使用中需要根据数据源的类型和数据结构进行相应的调整,如将 `Tuple2<String, Integer>` 替换为正确的数据类型,并根据需要调整分组的字段以及聚合函数的实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修破立生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值