Source: https://dev-docs.yueshu.com.cn/v5.3.2/clients/flink-connector/ Documentation version: v5.3.2 # Flink 连接器指南 [Flink 连接器](https://github.com/vesoft-inc/nebula-flink-connector/tree/v5.3.0)用于实现悦数图数据库开发版与 Apache Flink 间数据传输。本文介绍如何设置 Flink 连接器以实现数据传输。 ## 前提条件 - 已安装 Java 8 或更高版本。 - 悦数图数据库开发版服务组正在运行中。更多信息,请参见[快速开始](https://dev-docs.yueshu.com.cn/v5.3.2/get-started/deploy/)。 ## 安装 将 Flink 连接器导入到您的 Flink 项目中。例如,使用 Maven 导入时,需将以下依赖项添加到`pom.xml`文件: ```` com.vesoft nebula-flink-connector 5.3.0 ```` ## 通用配置 本文所有示例共享以下初始化代码。此处展示一次,后续示例中省略。 将``替换为登录账号的当前密码。若已修改密码,请使用修改后的密码。 ```` StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); ConnectionOptions connectionOptions = ConnectionOptions .builder() .withGraphAddress("127.0.0.1:9669") .withUser("root") .withPassword("") .build(); ```` 有关`ConnectionOptions`的完整参数说明,请参见 [Flink 连接器配置](https://dev-docs.yueshu.com.cn/v5.3.2/clients/flink-connector/flink-options/)。 ## 向悦数图数据库开发版写入数据 ### 写入点 将 Flink 中的数据写入悦数图数据库开发版作为特定点类型的点: ```` // ... env 和 connectionOptions 初始化代码省略 SinkNodeOptions sinkNodeOptions = SinkNodeOptions.builder() .withGraphName("flinkSink") .withNodeType("person") .withFlinkFields(Arrays.asList("c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12")) .withNebulaFields(Arrays.asList("col1", "col2", "col3", "col4", "col5", "col6", "col7", "col8", "col9", "col10", "col11", "col12", "col13")) .withWriteMode(WriteModeEnum.INSERTREPLACE) .withBatchSize(2) .build(); NebulaNodeBatchOutputFormat outputFormat = new NebulaNodeBatchOutputFormat(connectionOptions, sinkNodeOptions); NebulaSinkFunction nebulaSinkFunction = new NebulaSinkFunction<>(outputFormat); DataStream dataStream = playerSource.map(row -> { org.apache.flink.types.Row record = Row.withNames(); for (int i = 0; i < row.size(); i++) { record.setField("c" + i, row.get(i)); } return record; }); dataStream.addSink(nebulaSinkFunction); env.execute("Write Nebula"); ```` ### 写入边 将 Flink 中的数据写入悦数图数据库开发版作为特定边类型的边: ```` // ... env 和 connectionOptions 初始化代码省略 SinkEdgeOptions sinkEdgeOptions = SinkEdgeOptions.builder() .withGraphName("flinkSink") .withEdgeType("friend") .withFlinkSrcPkFields(Arrays.asList("c0")) .withNebulaSrcPks(Arrays.asList("col1")) .withFlinkDstPkFields(Arrays.asList("c1")) .withNebulaDstPks(Arrays.asList("col1")) .withFlinkFields(Arrays.asList("c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12", "c13", "c14")) .withNebulaFields(Arrays.asList("col1", "col2", "col3", "col4", "col5", "col6", "col7", "col8", "col9", "col10", "col11", "col12", "col13")) .withWriteMode(WriteModeEnum.INSERTIGNORE) .withBatchSize(2) .build(); NebulaEdgeBatchOutputFormat outputFormat = new NebulaEdgeBatchOutputFormat(connectionOptions, sinkEdgeOptions); NebulaSinkFunction nebulaSinkFunction = new NebulaSinkFunction<>(outputFormat); DataStream dataStream = friendSource.map(row -> { org.apache.flink.types.Row record = Row.withNames(); for (int i = 0; i < row.size(); i++) { record.setField("c" + i, row.get(i)); } return record; }); dataStream.addSink(nebulaSinkFunction); env.execute("Write Nebula Edge"); ```` ### 使用自定义 GQL 模板写入 默认情况下,连接器会根据配置选项自动生成 GQL 语句。对于进阶场景,可以使用`withGqlTemplate()`提供自定义 GQL 模板,从而完全控制生成的语句。 Tip 对于简单的字段到属性映射,推荐使用标准选项——它们性能更高,配置更简单。当需要复杂逻辑(如`CAST`类型转换、调用 UDF、或在插入前自定义`MATCH`逻辑)时,使用 GQL 模板。 自定义 GQL 模板是一个包含占位符的字符串,连接器会在运行时替换这些占位符。`{{ TABLE }}`占位符必填——连接器会将其替换为由当前批次 Flink 数据构建的`TABLE`子句。 生成的`TABLE`子句的表名固定为`t`。在后续语句中,可以通过`r.c0`、`r.c1`、`r.c2`等引用表中的列,其中数字为 Flink 数据行中列的从零开始的索引。对于边,起始/目标点主键列的命名为`r.src_0`、`r.dst_0`等。 模板中`{{ TABLE }}`之后可以是任意合法的 GQL 语句。例如,一个不引用表数据而插入固定值的最简模板: ```` {{ TABLE }} USE movie INSERT (:Movie{id:10000,name:"movie"}) ```` 可用占位符 | 占位符 | 描述 | `{{ TABLE }}` | 必填。批量数据的`TABLE`子句。连接器自动将 Flink 数据流中的数据构建为 TABLE 子句。 | `{{ GRAPH }}` | 图名称,即`withGraphName()`的值。 | `{{ TYPE }}` | 点类型或边类型名称。 #### 点示例 以下示例使用`CAST`在插入时显式转换数据类型: ```` // ... env 和 connectionOptions 初始化代码省略 String gqlTemplate = "{{ TABLE }} \n" + "USE `{{ GRAPH }}` \n" + "FOR r IN t \n" + "INSERT OR REPLACE (@`{{ TYPE }}`{" + "`col1`:r.c0,`col2`:r.c1,`col3`:CAST(r.c2 AS INT8),`col4`:CAST(r.c3 AS INT16)," + "`col5`:r.c4,`col6`:r.c5,`col7`:r.c6,`col8`:r.c7,`col9`:r.c8,`col10`:r.c9," + "`col11`:r.c10,`col12`:r.c11,`col13`:r.c12})"; SinkNodeOptions sinkNodeOptions = SinkNodeOptions.builder() .withGraphName("flinkSink") .withNodeType("person") .withFlinkFields(Arrays.asList("c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12")) .withNebulaFields(Arrays.asList("col1", "col2", "col3", "col4", "col5", "col6", "col7", "col8", "col9", "col10", "col11", "col12", "col13")) .withWriteMode(WriteModeEnum.INSERTREPLACE) .withGqlTemplate(gqlTemplate) // 使用自定义 GQL 模板 .withBatchSize(10) .build(); NebulaNodeBatchOutputFormat outputFormat = new NebulaNodeBatchOutputFormat(connectionOptions, sinkNodeOptions); NebulaSinkFunction nebulaSinkFunction = new NebulaSinkFunction<>(outputFormat); DataStream dataStream = playerSource.map(row -> { org.apache.flink.types.Row record = Row.withNames(); for (int i = 0; i < row.size(); i++) { record.setField("c" + i, row.get(i)); } return record; }); dataStream.addSink(nebulaSinkFunction); env.execute("Write Nebula with Template GQL"); ```` #### 边示例 对于边,`{{ TABLE }}`子句包含起始/目标点主键列(`src_0`、`dst_0`等)和边属性列(`c0`、`c1`等): ```` // ... env 和 connectionOptions 初始化代码省略 String gqlTemplate = "{{ TABLE }}\n" + "USE `{{ GRAPH }}` \n" + "FOR r IN t \n" + "OPTIONAL MATCH (n_src@`person`) WHERE n_src.`col1`=r.src_0 \n" + "OPTIONAL MATCH (n_dst@`person`) WHERE n_dst.`col1`=r.dst_0 \n" + "INSERT OR REPLACE (n_src)-[@`{{ TYPE }}`{" + "`col1`:r.c0,`col2`:r.c1,`col3`:CAST(r.c2 AS INT8),`col4`:CAST(r.c3 AS INT16)," + "`col5`:r.c4,`col6`:r.c5,`col7`:r.c6,`col8`:r.c7,`col9`:r.c8,`col10`:r.c9," + "`col11`:r.c10,`col12`:r.c11,`col13`:r.c12}]->(n_dst)"; SinkEdgeOptions sinkEdgeOptions = SinkEdgeOptions.builder() .withGraphName("flinkSink") .withEdgeType("friend") .withFlinkSrcPkFields(Arrays.asList("c0")) .withNebulaSrcPks(Arrays.asList("col1")) .withFlinkDstPkFields(Arrays.asList("c1")) .withNebulaDstPks(Arrays.asList("col1")) .withFlinkFields(Arrays.asList("c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "c10", "c11", "c12", "c13", "c14")) .withNebulaFields(Arrays.asList("col1", "col2", "col3", "col4", "col5", "col6", "col7", "col8", "col9", "col10", "col11", "col12", "col13")) .withWriteMode(WriteModeEnum.INSERTREPLACE) .withGqlTemplate(gqlTemplate) // 使用自定义 GQL 模板 .withBatchSize(10) .build(); NebulaEdgeBatchOutputFormat outputFormat = new NebulaEdgeBatchOutputFormat(connectionOptions, sinkEdgeOptions); NebulaSinkFunction nebulaSinkFunction = new NebulaSinkFunction<>(outputFormat); DataStream dataStream = friendSource.map(row -> { org.apache.flink.types.Row record = Row.withNames(); for (int i = 0; i < row.size(); i++) { record.setField("c" + i, row.get(i)); } return record; }); dataStream.addSink(nebulaSinkFunction); env.execute("Write Nebula Edge with Template GQL"); ```` ## 从悦数图数据库开发版读取数据 ### 读取点 读取悦数图数据库开发版中特定点类型的点并将其加载到 Flink 中: ```` // ... env 和 connectionOptions 初始化代码省略 SourceExecutionOptions nodeExecutionOptions = SourceNodeOptions.builder() .withGraphName("flinkSource") .withNodeType("person") .withReturnCols(null) .withBatchSize(10) .build(); NebulaInputRowFormat inputRowFormat = new NebulaInputRowFormat(connectionOptions, nodeExecutionOptions); DataSource rowDataSource = env.createInput(inputRowFormat); System.out.println("rowDataSource count: " + rowDataSource.count()); ```` ### 读取边 读取悦数图数据库开发版中特定边类型的边并将其加载到 Flink 中: ```` // ... env 和 connectionOptions 初始化代码省略 SourceExecutionOptions edgeExecutionOptions = SourceEdgeOptions.builder() .withGraphName("flinkSource") .withEdgeType("friend") .withReturnCols(null) .withBatchSize(10) .build(); NebulaInputRowFormat inputRowFormat = new NebulaInputRowFormat(connectionOptions, edgeExecutionOptions); DataSource rowDataSource = env.createInput(inputRowFormat); System.out.println("rowDataSource count: " + rowDataSource.count()); ```` ## 更多信息 - [Flink 连接器 GitHub 仓库](https://github.com/vesoft-inc/nebula-flink-connector/tree/v5.3.0) - [Flink 连接器配置](https://dev-docs.yueshu.com.cn/v5.3.2/clients/flink-connector/flink-options/)