DataWorks menyediakan serangkaian operasi API yang komprehensif untuk manajemen metadata. Panduan ini menjelaskan cara mengkueri tabel, mengambil detail tabel, dan mengelola metadata menggunakan API DataWorks.
Prasyarat
Sebelum memulai, tinjau sumber daya berikut:
1. Daftar tabel
Bagian ini menjelaskan cara menggunakan API untuk mengkueri daftar tabel di bawah proyek/skema MaxCompute dengan dukungan kueri halaman. Prosedur utamanya adalah sebagai berikut:
Backend: Gunakan MetaServiceProxy untuk mengkueri detail tabel
Tulis metode ListTables di MetaServiceProxy untuk memproses parameter frontend dan mengirim permintaan ke API ListTables guna mendapatkan informasi daftar tabel metadata.
Parameter Kueri yang Didukung: ID entitas induk, nama (pencocokan kabur), komentar (pencocokan kabur), tipe, parameter pengurutan, dan parameter halaman.
CatatanRujuk ke Deskripsi Konsep Terkait Entitas Metadata. ID entitas induk dapat berada dalam dua format.
ID entitas induk adalah ID Proyek MaxCompute, format:
maxcompute-project:${ID akun Alibaba Cloud}::{projectName}.ID entitas induk adalah ID Skema MaxCompute, format:
maxcompute-schema:${ID akun Alibaba Cloud}::{projectName}:{schemaName}.
Hasil Kueri Mencakup: Jumlah total tabel, informasi paginasi, serta untuk setiap tabel: ID, ID entitas induk, nama tabel, komentar, nama database, nama skema, tipe, daftar bidang partisi, waktu pembuatan, dan waktu modifikasi.
Dapatkan ID Akun Alibaba Cloud: Baik menggunakan Pengguna RAM atau Akun Alibaba Cloud, Anda dapat melihat ID akun di pojok kanan atas halaman.
Masuk ke Konsol DataWorks menggunakan Akun Alibaba Cloud atau Pengguna RAM Anda.
Arahkan mouse ke foto profil di pojok kanan atas untuk melihat ID akun Alibaba Cloud.
Akun Alibaba Cloud: Account ID adalah ID akun Alibaba Cloud yang perlu diperoleh.
Pengguna RAM: Anda dapat langsung melihat Alibaba Cloud Account ID.
Kode Contoh:
/** * @author dataworks demo */ @Service public class MetaServiceProxy { @Autowired private DataWorksOpenApiClient dataWorksOpenApiClient; /** * DataWorks OpenAPI : ListTables * * @param listTablesDto */ public ListTablesResponseBodyPagingInfo listTables(ListTablesDto listTablesDto) { try { Client client = dataWorksOpenApiClient.createClient(); ListTablesRequest listTablesRequest = new ListTablesRequest(); // ID entitas induk (lihat konsep entitas metadata) listTablesRequest.setParentMetaEntityId(listTablesDto.getParentMetaEntityId()); // Nama tabel, mendukung pencocokan kabur listTablesRequest.setName(listTablesDto.getName()); // Komentar tabel, mendukung pencocokan kabur listTablesRequest.setComment(listTablesDto.getComment()); // Jenis tabel, mengembalikan semua jenis secara default listTablesRequest.setTableTypes(listTablesDto.getTableTypes()); // Metode pengurutan, CreateTime (default) / ModifyTime / Name / TableType listTablesRequest.setSortBy(listTablesDto.getSortBy()); // Arah pengurutan, mendukung Asc (default) / Desc listTablesRequest.setOrder(listTablesDto.getOrder()); // Nomor halaman, default adalah 1 listTablesRequest.setPageNumber(listTablesDto.getPageNumber()); // Ukuran halaman, default adalah 10, maksimum 100 listTablesRequest.setPageSize(listTablesDto.getPageSize()); ListTablesResponse response = client.listTables(listTablesRequest); // Dapatkan jumlah total tabel yang memenuhi persyaratan System.out.println(response.getBody().getPagingInfo().getTotalCount()); for (Table table : response.getBody().getPagingInfo().getTables()) { // ID tabel System.out.println(table.getId()); // ID entitas induk System.out.println(table.getParentMetaEntityId()); // Nama tabel System.out.println(table.getName()); // Komentar tabel System.out.println(table.getComment()); // Nama Database/MaxCompute project System.out.println(table.getId().split(":")[3]); // Nama skema System.out.println(table.getId().split(":")[4]); // Jenis tabel System.out.println(table.getTableType()); // Waktu pembuatan tabel System.out.println(table.getCreateTime()); // Waktu modifikasi tabel System.out.println(table.getModifyTime()); // Daftar bidang partisi tabel System.out.println(table.getPartitionKeys()); } return response.getBody().getPagingInfo(); } catch (TeaException error) { // Hanya untuk demonstrasi. Tangani pengecualian dengan benar dalam kode produksi. // Pesan kesalahan System.out.println(error.getMessage()); // Alamat diagnostik System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); // Hanya untuk demonstrasi. Tangani pengecualian dengan benar dalam kode produksi. // Pesan kesalahan System.out.println(error.getMessage()); // Alamat diagnostik System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } return null; } }
Tambahkan metode
listTablesdi MetaRestController sebagai titik masuk untuk akses antarmuka depan./** * Platform metadata kustom menggunakan DataWorks API * * @author dataworks demo */ @RestController @RequestMapping("/meta") public class MetaRestController { @Autowired private MetaServiceProxy metaService; /** * Query data tabel per halaman * * @param listTablesDto * @return {@link ListTablesResponseBodyPagingInfo} */ @CrossOrigin(origins = "http://localhost:8080") @GetMapping("/listTables") public ListTablesResponseBodyPagingInfo listTables(ListTablesDto listTablesDto) { System.out.println("listTablesDto = " + listTablesDto); return metaService.listTables(listTablesDto); } }
Frontend: Menampilkan informasi metadata tabel
import React from "react";
import cn from "classnames";
import {
Table,
Form,
Field,
Input,
Button,
Pagination,
Dialog,
Card,
Grid,
Select,
} from "@alifd/next";
import type { TableListInput, TableEntity } from "../services/meta";
import * as services from "../services";
import DetailContent from "./detailContent";
import LineageContent from "./lineageContent";
import classes from "../styles/app.module.css";
import moment from "moment";
export interface Props {}
const { Column } = Table;
const { Item } = Form;
const { Header, Content } = Card;
const { Row, Col } = Grid;
const { Option } = Select;
const App: React.FunctionComponent<Props> = () => {
const field = Field.useField();
const [datasource, setDatasource] = React.useState<TableEntity[]>([]);
const [loading, setLoading] = React.useState<boolean>(false);
const [total, setTotal] = React.useState<number>(0);
const [current, setCurrent] = React.useState<number>(1);
const onSubmit = React.useCallback(
(pageNumber: number = 1) => {
field.validate(async (errors, values) => {
if (errors) {
return;
}
setLoading(true);
try {
const response = await services.meta.getTableList({
pageNumber,
...values,
} as TableListInput);
setDatasource(response.tables);
setCurrent(pageNumber);
setTotal(response.totalCount);
} catch (e) {
throw e;
} finally {
setLoading(false);
}
});
},
[field]
);
const onViewDetail = React.useCallback((record: TableEntity) => {
Dialog.show({
title: "Detail Tabel Data",
content: <DetailContent item={record} />,
shouldUpdatePosition: true,
footerActions: ["ok"],
});
}, []);
const onViewLineage = React.useCallback((record: TableEntity) => {
Dialog.show({
title: "Hubungan Lineage Tabel",
content: <LineageContent item={record} />,
shouldUpdatePosition: true,
footerActions: ["ok"],
});
}, []);
React.useEffect(() => {
field.setValue("dataSourceType", "odps");
}, []);
return (
<div className={cn(classes.appWrapper)}>
<Card free hasBorder={false}>
<Header title="Demo Skenario Manajemen Tabel Metadata (MaxCompute)" />
<Content style={{ marginTop: 24 }}>
<Form field={field} colon fullWidth>
<Row gutter="1">
<Col>
<Item
label="ID Proyek/Skema MaxCompute"
name="parentMetaEntityId"
required
help="ID"
>
<Input />
</Item>
</Col>
</Row>
<Row gutter="4">
<Col>
<Item label="Nama" name="name">
<Input placeholder="Masukkan nama tabel, pencocokan kabur" />
</Item>
</Col>
<Col>
<Item label="Komentar" name="comment">
<Input placeholder="Masukkan komentar tabel, pencocokan kabur" />
</Item>
</Col>
<Col>
<Item label="Tipe" name="tableTypes">
<Select mode="multiple" style={{ width: "100%" }}>
<Option value="TABLE">Tabel</Option>
<Option value="VIEW">Tampilan</Option>
<Option value="MATERIALIZED_VIEW">Tampilan Materialized</Option>
</Select>
</Item>
</Col>
<Col>
<Item label="Metode Pengurutan" name="sortBy">
<Select defaultValue="CreateTime" style={{ width: "100%" }}>
<Option value="CreateTime">Waktu Pembuatan</Option>
<Option value="ModifyTime">Waktu Modifikasi</Option>
<Option value="Name">Nama</Option>
<Option value="TableType">Tipe</Option>
</Select>
</Item>
</Col>
<Col>
<Item label="Arah Pengurutan" name="order">
<Select defaultValue="Asc" style={{ width: "100%" }}>
<Option value="Asc">Naik</Option>
<Option value="Desc">Turun</Option>
</Select>
</Item>
</Col>
</Row>
<div
className={cn(
classes.searchPanelButtonWrapper,
classes.buttonGroup
)}
>
<Button type="primary" onClick={() => onSubmit()}>
Kueri
</Button>
</div>
</Form>
<div>
<Table
dataSource={datasource}
loading={loading}
className={cn(classes.tableWrapper)}
emptyContent={
<div className={cn(classes.noDataWrapper)}>Tidak Ada Data</div>
}
>
<Column
title="Nama Proyek"
dataIndex="id"
cell={(value) => {
const parts = value.split(":");
return parts.length > 1 ? parts[parts.length - 3] : "";
}}
/>
<Column
title="skema"
dataIndex="id"
cell={(value) => {
const parts = value.split(":");
return parts.length > 1 ? parts[parts.length - 2] : "";
}}
/>
<Column title="Nama Tabel" dataIndex="name" />
<Column title="Komentar Tabel" dataIndex="comment" />
<Column title="Tipe Tabel" dataIndex="tableType" />
<Column
title="Status Partisi"
dataIndex="partitionKeys"
cell={(value) => {
return value != null && value.length > 0 ? "Ya" : "Tidak";
}}
/>
<Column
title="Waktu Pembuatan"
dataIndex="createTime"
cell={(value) => {
return moment(value).format("YYYY-MM-DD HH:mm:ss");
}}
/>
<Column
title="Waktu Modifikasi"
dataIndex="modifyTime"
cell={(value) => {
return moment(value).format("YYYY-MM-DD HH:mm:ss");
}}
/>
<Column
title="Operasi"
width={150}
cell={(value, index, record) => (
<div className={cn(classes.buttonGroup)}>
<Button
type="primary"
onClick={() => onViewDetail(record)}
text
>
Lihat Detail
</Button>
<Button
type="primary"
onClick={() => onViewLineage(record)}
text
>
Lihat Lineage Tabel
</Button>
</div>
)}
/>
</Table>
<Pagination
current={current}
total={total}
onChange={onSubmit}
showJump={false}
className={cn(classes.tablePaginationWrapper)}
/>
</div>
</Content>
</Card>
</div>
);
};
export default App;Setelah menyelesaikan pengembangan kode di atas, Anda dapat menerapkan dan menjalankan kode proyek. Masukkan ID Proyek MaxCompute dan parameter lainnya untuk melakukan kueri dan mendapatkan daftar semua tabel dalam ruang proyek, dengan dukungan untuk kueri berhalaman.
2. Kueri detail tabel
Praktik berikut menggabungkan tiga operasi API: GetTable, ListColumns, dan ListPartitions dalam metadata untuk mengkueri detail tabel. Gunakan API UpdateColumnBusinessMetadata untuk memperbarui deskripsi bisnis dari bidang. Alur kerja praktik adalah sebagai berikut:
Backend: Gunakan MetaServiceProxy untuk mengkueri detail tabel
Buat metode
getTable,listColumns,listPartitionsdi MetaServiceProxy untuk memanggil operasi mendapatkan informasi dasar tabel, informasi bidang tabel, dan informasi partisi tabel. Buat metodeupdateColumnBusinessMetadatauntuk memperbarui deskripsi bisnis dari bidang./** * @author dataworks demo */ @Service public class MetaServiceProxy { @Autowired private DataWorksOpenApiClient dataWorksOpenApiClient; /** * DataWorks OpenAPI : getTableDto * * @param getTableDto */ public Table getTable(GetTableDto getTableDto) { try { Client client = dataWorksOpenApiClient.createClient(); GetTableRequest getTableRequest = new GetTableRequest(); // Table ID getTableRequest.setId(getTableDto.getId()); // Whether to return business metadata getTableRequest.setIncludeBusinessMetadata(getTableDto.getIncludeBusinessMetadata()); GetTableResponse response = client.getTable(getTableRequest); // Corresponding data table Table table = response.getBody().getTable(); // Table ID System.out.println(table.getId()); // Parent entity ID System.out.println(table.getParentMetaEntityId()); // Table name System.out.println(table.getName()); // Table comment System.out.println(table.getComment()); // Database/MaxCompute project name System.out.println(table.getId().split(":")[3]); // Schema name System.out.println(table.getId().split(":")[4]); // Table type System.out.println(table.getTableType()); // Table creation time System.out.println(table.getCreateTime()); // Table modification time System.out.println(table.getModifyTime()); // Check if table is partitioned System.out.println(table.getPartitionKeys() != null && !table.getPartitionKeys().isEmpty()); // Technical metadata of the table TableTechnicalMetadata technicalMetadata = table.getTechnicalMetadata(); // Table owner (name) System.out.println(technicalMetadata.getOwner()); // Whether it is a compressed table System.out.println(technicalMetadata.getCompressed()); // Input format (HMS, DLF and other types support) System.out.println(technicalMetadata.getInputFormat()); // Output format (HMS, DLF and other types support) System.out.println(technicalMetadata.getOutputFormat()); // Class used for table serialization (HMS, DLF and other types support) System.out.println(technicalMetadata.getSerializationLibrary()); // Table storage location (HMS, DLF and other types support) System.out.println(technicalMetadata.getLocation()); // Other parameters Map<String, String> parameters = technicalMetadata.getParameters(); // Last DDL update time (millisecond-level timestamp), only MaxCompute type supports System.out.println(parameters.get("lastDDLTime")); // Lifecycle (unit: days), only MaxCompute type supports System.out.println(parameters.get("lifecycle")); // Storage size (unit: bytes), not real-time, only MaxCompute type supports System.out.println(parameters.get("dataSize")); // Business metadata of the table TableBusinessMetadata businessMetadata = table.getBusinessMetadata(); if (businessMetadata != null) { // Usage instructions System.out.println(businessMetadata.getReadme()); // Custom tag information List<TableBusinessMetadataTags> tags = businessMetadata.getTags(); if (tags != null && !tags.isEmpty()) { for (TableBusinessMetadataTags tag : tags) { System.out.println(tag.getKey() + ":" + tag.getValue()); } } // Upstream production tasks List<TableBusinessMetadataUpstreamTasks> upstreamTasks = businessMetadata.getUpstreamTasks(); if (upstreamTasks != null && !upstreamTasks.isEmpty()) { for (TableBusinessMetadataUpstreamTasks upstreamTask : upstreamTasks) { // Task ID and task name, task details can be obtained through GetTask API System.out.println(upstreamTask.getId() + ":" + upstreamTask.getName()); } } // Categories List<List<TableBusinessMetadataCategories>> categories = businessMetadata.getCategories(); if (categories != null && !categories.isEmpty()) { // Traverse multi-level categories for (List<TableBusinessMetadataCategories> category : categories) { // Output a single multi-level category System.out.println( category.stream() .map(TableBusinessMetadataCategories::getName) .collect(Collectors.joining("->")) ); } } // Extension information, only MaxCompute type supports TableBusinessMetadataExtension extension = businessMetadata.getExtension(); if (extension != null) { // Project ID System.out.println(extension.getProjectId()); // Environment (Prod: production / Dev: development) System.out.println(extension.getEnvType()); // Favorite count System.out.println(extension.getFavorCount()); // Read count System.out.println(extension.getReadCount()); // View count System.out.println(extension.getViewCount()); } } return table; } catch (TeaException error) { // For demonstration only. Handle exceptions properly in production code. // Error message System.out.println(error.getMessage()); // Diagnostic address System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); // For demonstration only. Handle exceptions properly in production code. // Error message System.out.println(error.getMessage()); // Diagnostic address System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } return null; } /** * DataWorks OpenAPI : ListColumns * * @param listColumnsDto */ public ListColumnsResponseBodyPagingInfo listColumns(ListColumnsDto listColumnsDto) { try { Client client = dataWorksOpenApiClient.createClient(); ListColumnsRequest listColumnsRequest = new ListColumnsRequest(); // Table ID (from ListTables API) listColumnsRequest.setTableId(listColumnsDto.getTableId()); // Field name, supports fuzzy matching listColumnsRequest.setName(listColumnsDto.getName()); // Field comment, supports word segmentation matching listColumnsRequest.setComment(listColumnsDto.getComment()); // Sort method, supports Name / Position (default) listColumnsRequest.setSortBy(listColumnsDto.getSortBy()); // Sort direction, supports Asc (default) / Desc listColumnsRequest.setOrder(listColumnsDto.getOrder()); // Page number, default is 1 listColumnsRequest.setPageNumber(listColumnsDto.getPageNumber()); // Page size, default is 10, maximum 100 listColumnsRequest.setPageSize(listColumnsDto.getPageSize()); ListColumnsResponse response = client.listColumns(listColumnsRequest); // Get the total number of fields that meet the requirements System.out.println(response.getBody().getRequestId()); System.out.println(response.getBody().getPagingInfo().getTotalCount()); for (Column column : response.getBody().getPagingInfo().getColumns()) { // Field ID System.out.println(column.getId()); // Field name System.out.println(column.getName()); // Field comment System.out.println(column.getComment()); // Field type System.out.println(column.getType()); // Field position System.out.println(column.getPosition()); // Whether it is a partition field System.out.println(column.getPartitionKey()); // Whether it is a primary key, only MaxCompute type supports System.out.println(column.getPrimaryKey()); // Whether it is a foreign key, only MaxCompute type supports System.out.println(column.getForeignKey()); // Business description for the field (supported by MaxCompute, HMS, and DLF) if (column.getBusinessMetadata() != null) { System.out.println(column.getBusinessMetadata().getDescription()); } } return response.getBody().getPagingInfo(); } catch (TeaException error) { // For demonstration only. Handle exceptions properly in production code. // Error message System.out.println(error.getMessage()); // Diagnostic address System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); // For demonstration only. Handle exceptions properly in production code. // Error message System.out.println(error.getMessage()); // Diagnostic address System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } return null; } /** * DataWorks OpenAPI : UpdateColumnBusinessMetadata * * @param updateColumnBusinessMetadataDto */ public boolean updateColumnBusinessMetadata(UpdateColumnBusinessMetadataDto updateColumnBusinessMetadataDto) { try { // Currently supports MaxCompute, DLF, HMS (EMR cluster) types Client client = dataWorksOpenApiClient.createClient(); UpdateColumnBusinessMetadataRequest updateColumnBusinessMetadataRequest = new UpdateColumnBusinessMetadataRequest(); // Field ID updateColumnBusinessMetadataRequest.setId(updateColumnBusinessMetadataDto.getId()); // Field business description updateColumnBusinessMetadataRequest.setDescription(updateColumnBusinessMetadataDto.getDescription()); UpdateColumnBusinessMetadataResponse response = client.updateColumnBusinessMetadata(updateColumnBusinessMetadataRequest); System.out.println(response.getBody().getRequestId()); // Whether the update is successful return response.getBody().getSuccess(); } catch (TeaException error) { // For demonstration only. Handle exceptions properly in production code. // Error message System.out.println(error.getMessage()); // Diagnostic address System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); // For demonstration only. Handle exceptions properly in production code. // Error message System.out.println(error.getMessage()); // Diagnostic address System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } return false; } /** * DataWorks OpenAPI : ListPartitions * * @param listPartitionsDto */ public ListPartitionsResponseBodyPagingInfo listPartitions(ListPartitionsDto listPartitionsDto) { try { // Currently supports MaxCompute type and HMS (EMR cluster) type Client client = dataWorksOpenApiClient.createClient(); ListPartitionsRequest listPartitionsRequest = new ListPartitionsRequest(); // Table ID (from ListTables API) listPartitionsRequest.setTableId(listPartitionsDto.getTableId()); // Partition name, supports fuzzy matching listPartitionsRequest.setName(listPartitionsDto.getName()); // Sort method, supports Name (HMS method default, MaxCompute type supports) / CreateTime (MaxCompute type default) / ModifyTime (MaxCompute type supports) / RecordCount (MaxCompute type supports) / DataSize (MaxCompute type supports) listPartitionsRequest.setSortBy(listPartitionsDto.getSortBy()); // Sort direction, supports Asc (default) / Desc listPartitionsRequest.setOrder(listPartitionsDto.getOrder()); // Page number, default is 1 listPartitionsRequest.setPageNumber(listPartitionsDto.getPageNumber()); // Page size, default is 10, maximum 100 listPartitionsRequest.setPageSize(listPartitionsDto.getPageSize()); ListPartitionsResponse response = client.listPartitions(listPartitionsRequest); // Get the total number of partitions that meet the requirements System.out.println(response.getBody().getPagingInfo().getTotalCount()); for (Partition partition : response.getBody().getPagingInfo().getPartitionList()) { // Table ID System.out.println(partition.getTableId()); // Partition name System.out.println(partition.getName()); // Creation time (millisecond-level timestamp) System.out.println(partition.getCreateTime()); // Modification time (millisecond-level timestamp) System.out.println(partition.getModifyTime()); // Partition record count, only MaxCompute type supports System.out.println(partition.getRecordCount()); // Partition storage size (unit: bytes), only MaxCompute type supports System.out.println(partition.getDataSize()); } return response.getBody().getPagingInfo(); } catch (TeaException error) { // For demonstration only. Handle exceptions properly in production code. // Error message System.out.println(error.getMessage()); // Diagnostic address System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); // For demonstration only. Handle exceptions properly in production code. // Error message System.out.println(error.getMessage()); // Diagnostic address System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } return null; } }Sediakan empat metode di MetaRestController:
getTable,listColumns,listPartitions, danupdateColumnBusinessMetadatauntuk pemanggilan frontend./** * Custom metadata platform menggunakan API DataWorks * * @author dataworks demo */ @RestController @RequestMapping("/meta") public class MetaRestController { @Autowired private MetaServiceProxy metaService; /** * Dapatkan detail tabel * * @param getTableDto * @return {@link Table} */ @CrossOrigin(origins = "http://localhost:8080") @GetMapping("/getTable") public Table getTable(GetTableDto getTableDto) { System.out.println("getTableDto = " + getTableDto); return metaService.getTable(getTableDto); } /** * Daftar kolom dengan halaman * * @param listColumnsDto * @return {@link ListColumnsResponseBodyPagingInfo} */ @CrossOrigin(origins = "http://localhost:8080") @GetMapping("/listColumns") public ListColumnsResponseBodyPagingInfo listColumns(ListColumnsDto listColumnsDto) { System.out.println("listColumnsDto = " + listColumnsDto); return metaService.listColumns(listColumnsDto); } /** * Perbarui deskripsi bisnis kolom * * @param updateColumnBusinessMetadataDto * @return {@link Boolean} */ @CrossOrigin(origins = "http://localhost:8080") @PostMapping("/updateColumnBusinessMetadata") public Boolean updateColumnBusinessMetadata(@RequestBody UpdateColumnBusinessMetadataDto updateColumnBusinessMetadataDto) { System.out.println("updateColumnBusinessMetadataDto = " + updateColumnBusinessMetadataDto); return metaService.updateColumnBusinessMetadata(updateColumnBusinessMetadataDto); } /** * Kueri data partisi berdasarkan halaman * * @param listPartitionsDto * @return {@link ListPartitionsResponseBodyPagingInfo} */ @CrossOrigin(origins = "http://localhost:8080") @GetMapping("/listPartitions") public ListPartitionsResponseBodyPagingInfo listPartitions(ListPartitionsDto listPartitionsDto) { System.out.println("listPartitionsDto = " + listPartitionsDto); return metaService.listPartitions(listPartitionsDto); } }
Frontend: Menampilkan informasi tabel dasar, informasi bidang, dan informasi partisi
import React from "react";
import moment from "moment";
import {
Form,
Grid,
Table,
Pagination,
Tag,
Field,
Input,
Button,
Select,
Dialog,
} from "@alifd/next";
import cn from "classnames";
import * as services from "../services";
import {
type ListColumnsInput,
type TableColumn,
type TableEntity,
type TablePartition,
} from "../services/meta";
import classes from "../styles/detailContent.module.css";
export interface Props {
item: TableEntity;
}
const formItemLayout = {
labelCol: {
fixedSpan: 6,
},
wrapperCol: {
span: 18,
},
labelTextAlign: "left" as const,
colon: true,
className: cn(classes.formItemWrapper),
};
const { Row, Col } = Grid;
const { Item } = Form;
const { Column } = Table;
const { Option } = Select;
const DetailContent: React.FunctionComponent<Props> = (props) => {
let columnsPageSize = 10;
let partitionsPageSize = 5;
const listColumnsField = Field.useField();
const listPartitionsField = Field.useField();
const labelAlign = "top";
const [detail, setDetail] = React.useState<Partial<TableEntity>>({});
const [columns, setColumns] = React.useState<Partial<TableColumn[]>>([]);
const [partitions, setPartitions] = React.useState<Partial<TablePartition[]>>(
[]
);
const [columnsTotal, setColumnsTotal] = React.useState<number>(0);
const [partitionTotal, setPartitionTotal] = React.useState<number>(0);
const [editingKey, setEditingKey] = React.useState<string>("");
const [editingDescription, setEditingDescription] =
React.useState<string>("");
const [updateColumnDialogVisible, setUpdateColumnDialogVisible] =
React.useState<boolean>(false);
const onUpdateColumnDialogOpen = () => {
setUpdateColumnDialogVisible(true);
};
const onUpdateColumnDialogOk = () => {
handleColumnDescriptionSave();
onUpdateColumnDialogClose();
};
const onUpdateColumnDialogClose = () => {
setUpdateColumnDialogVisible(false);
};
const handleEditColumnDescription = async (record: TableColumn) => {
setEditingKey(record.id);
setEditingDescription(record.businessMetadata?.description);
onUpdateColumnDialogOpen();
};
const handleColumnDesrciptionChange = async (e: string) => {
setEditingDescription(e);
};
const handleColumnDescriptionSave = async () => {
try {
const res = await services.meta.updateColumnDescription({
id: editingKey,
description: editingDescription,
});
if (!res) {
console.log("Permintaan pembaruan gagal");
}
} catch (e) {
console.error("Pembaruan gagal", e);
} finally {
listColumns();
setEditingKey("");
}
};
const getTableDetail = React.useCallback(async () => {
const response = await services.meta.getTableDetail({
id: props.item.id,
includeBusinessMetadata: true,
});
setDetail(response);
}, [props.item.id]);
const listColumns = React.useCallback(
async (pageNumber: number = 1) => {
listColumnsField.setValue("tableId", props.item.id);
listColumnsField.setValue("pageSize", columnsPageSize);
listColumnsField.validate(async (errors, values) => {
if (errors) {
return;
}
try {
const response = await services.meta.getMetaTableColumns({
pageNumber,
...values,
} as ListColumnsInput);
setColumns(response.columns);
setColumnsTotal(response.totalCount);
} catch (e) {
throw e;
} finally {
}
});
},
[listColumnsField]
);
const listPartitions = React.useCallback(
async (pageNumber: number = 1) => {
listPartitionsField.setValue("tableId", props.item.id);
listPartitionsField.setValue("pageSize", partitionsPageSize);
listPartitionsField.validate(async (errors, values) => {
if (errors) {
return;
}
try {
const response = await services.meta.getTablePartition({
pageNumber,
...values,
} as ListColumnsInput);
setPartitions(response.partitionList);
setPartitionTotal(response.totalCount);
} catch (e) {
throw e;
} finally {
}
});
},
[listPartitionsField]
);
React.useEffect(() => {
if (props.item.id) {
getTableDetail();
listColumns();
listPartitions();
}
}, [props.item.id]);
return (
<div className={cn(classes.detailContentWrapper)}>
<Dialog
v2
title="Perbarui Metadata Bisnis Bidang"
visible={updateColumnDialogVisible}
onOk={onUpdateColumnDialogOk}
onClose={onUpdateColumnDialogClose}
>
<Row>
<Col>
<Item
{...formItemLayout}
labelAlign={labelAlign}
label="Deskripsi Bisnis Bidang"
>
<Input
value={editingDescription}
onChange={(e) => handleColumnDesrciptionChange(e.toString())}
></Input>
</Item>
</Col>
</Row>
</Dialog>
<Form labelTextAlign="left">
<Row>
<Col>
<Item {...formItemLayout} label="ID Tabel">
{detail.id}
{detail?.id?.split(":").slice(-3, -2)[0]}
{detail?.id?.split(":").slice(-2, -1)[0]}
{detail.name}
{detail.comment}
{detail.tableType}
{detail.technicalMetadata?.compressed ? "Ya" : "Tidak"}
{detail.businessMetadata?.extension?.projectId}
{detail.businessMetadata?.extension?.envType === "Dev"
? "Pengembangan"
: "Produksi"}
{detail.technicalMetadata?.parameters["lifecycle"]
? `${detail.technicalMetadata?.parameters["lifecycle"]} hari`
: "Permanen`}
{`${detail.technicalMetadata?.parameters["dataSize"]} Bytes`}
{moment(
Number(detail.technicalMetadata?.parameters["lastDDLTime"])
).format("YYYY-MM-DD HH:mm:ss")}
{detail.partitionKeys != null && detail.partitionKeys.length > 0
? "Ya"
: "Tidak"}
<Tag type="primary" size="small">
{tag.key + (tag.value ? ":" + tag.value : "")}
</Tag>
{detail.businessMetadata?.categories?.map((category, index) => (
<span key={index} className={cn(classes.tag)}>
<Tag type="normal" color="gray" size="small">
{category.map((obj) => obj.name).join("->")}
</Tag>
{detail.businessMetadata?.upstreamTasks?.map((task, index) => (
<span key={index} className={cn(classes.tag)}>
<Tag type="primary" size="small">
{task.name + " (" + task.id + ")"}
</Tag>
<Tag type="primary" size="small">
{detail.businessMetadata?.extension?.viewCount}
</Tag>
<Tag type="primary" size="small">
{detail.businessMetadata?.extension?.readCount}
</Tag>
<Tag type="primary" size="small">
{detail.businessMetadata?.extension?.favorCount}
</Tag>
{moment(detail.createTime).format("YYYY-MM-DD HH:mm:ss")}
{moment(detail.modifyTime).format("YYYY-MM-DD HH:mm:ss")}
{value?.description}Tabel Non-partisiSetelah menyelesaikan pengembangan kode di atas, Anda dapat menerapkan dan menjalankan kode proyek secara lokal. Untuk operasi penerapan dan menjalankan, lihat Operasi Umum: Penerapan Lokal dan Menjalankan.
3. Kueri lineage tabel
Ketika definisi tabel berubah, Anda dapat menggunakan API DataWorks untuk melakukan analisis lineage tugas hulu dan hilir guna menemukan node yang sesuai dengan tabel.
Gunakan ListLineages untuk mengkueri lineage hulu dan hilir dari tabel data.
Backend: Gunakan MetaServiceProxy untuk mengkueri lineage tabel
Buat metode listTableLineages di MetaServiceProxy untuk memanggil API mendapatkan informasi entitas dan hubungan lineage hulu dan hilir dari tabel.
/** * @author dataworks demo */ @Service public class MetaServiceProxy { @Autowired private DataWorksOpenApiClient dataWorksOpenApiClient; /** * DataWorks OpenAPI : ListLineages * * @param listTableLineagesDto */ public ListLineagesResponseBodyPagingInfo listTableLineages(ListTableLineagesDto listTableLineagesDto) { try { Client client = dataWorksOpenApiClient.createClient(); ListLineagesRequest listLineagesRequest = new ListLineagesRequest(); // Arah kueri lineage, apakah hulu boolean needUpstream = "up".equalsIgnoreCase(listTableLineagesDto.getDirection()); // ID Tabel (dari ListTables API) // Nama partisi, mendukung pencocokan kabur if (needUpstream) { listLineagesRequest.setDstEntityId(listTableLineagesDto.getTableId()); listLineagesRequest.setSrcEntityName(listTableLineagesDto.getName()); } else { listLineagesRequest.setSrcEntityId(listTableLineagesDto.getTableId()); listLineagesRequest.setDstEntityName(listTableLineagesDto.getName()); } // Metode pengurutan, mendukung Nama listLineagesRequest.setSortBy(listTableLineagesDto.getSortBy()); // Arah pengurutan, mendukung Asc (default) / Desc listLineagesRequest.setOrder(listTableLineagesDto.getOrder()); // Nomor halaman, default adalah 1 listLineagesRequest.setPageNumber(listTableLineagesDto.getPageNumber()); // Ukuran halaman, default adalah 10, maksimum 100 listLineagesRequest.setPageSize(listTableLineagesDto.getPageSize()); // Apakah akan mengembalikan informasi hubungan lineage spesifik listLineagesRequest.setNeedAttachRelationship(true); ListLineagesResponse response = client.listLineages(listLineagesRequest); // Dapatkan jumlah total entitas lineage yang memenuhi persyaratan System.out.println(response.getBody().getPagingInfo().getTotalCount()); if (response.getBody().getPagingInfo().getLineages() == null) { response.getBody().getPagingInfo().setLineages(Collections.emptyList()); } if (response.getBody().getPagingInfo().getLineages() != null) { for (ListLineagesResponseBodyPagingInfoLineages lineage : response.getBody().getPagingInfo().getLineages()) { LineageEntity entity; // Dapatkan entitas lineage yang sesuai if (needUpstream) { entity = lineage.getSrcEntity(); } else { entity = lineage.getDstEntity(); } // ID Entitas System.out.println(entity.getId()); // Nama Entitas System.out.println(entity.getName()); // Informasi atribut entitas System.out.println(entity.getAttributes()); // Daftar hubungan lineage antar entitas List<LineageRelationship> relationships = lineage.getRelationships(); if (relationships != null) { relationships.forEach(relationship -> { // ID Hubungan Lineage System.out.println(relationship.getId()); // Waktu Pembuatan System.out.println(relationship.getCreateTime()); // Tugas yang sesuai dengan hubungan lineage LineageTask task = relationship.getTask(); if (task != null) { // ID Tugas, untuk tugas DataWorks, detail tugas dapat diperoleh melalui GetTask System.out.println(task.getId()); // Tipe Tugas System.out.println(task.getType()); // Informasi atribut tugas Map<String, String> attributes = task.getAttributes(); if (attributes != null) { // Untuk tugas DataWorks, informasi atribut tugas dan instans dapat diperoleh darinya // ID Proyek System.out.println(attributes.get("projectId")); // ID Tugas String taskId = attributes.containsKey("taskId") ? attributes.get("taskId") : attributes.get("nodeId"); System.out.println(taskId); // ID Instans Tugas String taskInstanceId = attributes.containsKey("taskInstanceId") ? attributes.get("taskInstanceId") : attributes.get("jobId"); System.out.println(taskInstanceId); } } }); } } return response.getBody().getPagingInfo(); } } catch (TeaException error) { // Hanya untuk demonstrasi. Tangani pengecualian dengan benar dalam kode produksi. // Pesan kesalahan System.out.println(error.getMessage()); // Alamat diagnostik System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception _error) { TeaException error = new TeaException(_error.getMessage(), _error); // Hanya untuk demonstrasi. Tangani pengecualian dengan benar dalam kode produksi. // Pesan kesalahan System.out.println(error.getMessage()); // Alamat diagnostik System.out.println(error.getData().get("Recommend")); com.aliyun.teautil.Common.assertAsString(error.message); } return null; } }Sediakan metode listTableLineages di MetaRestController untuk pemanggilan frontend.
/** * Custom metadata platform menggunakan API DataWorks * * @author dataworks demo */ @RestController @RequestMapping("/meta") public class MetaRestController { @Autowired private MetaServiceProxy metaService; /** * Kueri hubungan lineage tabel * * @param listTableLineagesDto * @return */ @CrossOrigin(origins = "http://localhost:8080") @GetMapping("/listTableLineages") public ListLineagesResponseBodyPagingInfo listTableLineages(ListTableLineagesDto listTableLineagesDto) { System.out.println("listTableLineagesDto = " + listTableLineagesDto); return metaService.listTableLineages(listTableLineagesDto); } }
Frontend: Menampilkan lineage tabel
import React from 'react';
import Graphin, { Behaviors } from '@antv/graphin';
import type { IUserNode, IUserEdge, GraphinData, GraphEvent } from '@antv/graphin';
import cn from 'classnames';
import * as services from '../services';
import type { TableEntity, ListTableLineageOutput, LineageEntity } from '../services/meta';
import classes from '../styles/lineageContent.module.css';
import '@antv/graphin/dist/index.css';
export interface Props {
item: TableEntity;
}
function transNode(entity: LineageEntity | TableEntity, direction?: string): IUserNode {
return {
id: entity.id,
label: entity.name,
data: {
...entity,
direction,
},
style: {
label: {
value: entity.name,
}
},
}
}
function transEdge(source: LineageEntity | TableEntity, target: LineageEntity | TableEntity): IUserEdge {
return {
source: source.id,
target: target.id,
style: {
keyshape: {
lineDash: [8, 4],
lineWidth: 2,
},
animate: {
type: 'line-dash',
repeat: true,
},
}
};
}
function parse(
source: LineageEntity | TableEntity,
data: ListTableLineageOutput,
direction: string,
): [IUserNode[], IUserEdge[]] {
const nodes: IUserNode[] = [];
const edges: IUserEdge[] = [];
data.lineages.forEach((lineageRelationship) => {
if (direction === 'down') {
nodes.push(transNode(lineageRelationship.dstEntity, direction));
edges.push(transEdge(source, lineageRelationship.dstEntity));
} else {
nodes.push(transNode(lineageRelationship.srcEntity, direction));
edges.push(transEdge(lineageRelationship.srcEntity, source));
}
});
return [nodes, edges];
}
function mergeNodes(prev: IUserNode[], next: IUserNode[]) {
const result: IUserNode[] = prev.slice();
next.forEach((item) => {
const hasValue = prev.findIndex(i => i.id === item.id) >= 0;
!hasValue && result.push(item);
});
return result;
}
function mergeEdges(source: IUserEdge[], target: IUserEdge[]) {
const result: IUserEdge[] = source.slice();
target.forEach((item) => {
const hasValue = source.findIndex(i => i.target === item.target && i.source === item.source) >= 0;
!hasValue && result.push(item);
});
return result;
}
const { ActivateRelations, DragNode, ZoomCanvas } = Behaviors;
const LineageContent: React.FunctionComponent = (props) => {
const ref = React.useRef();
const [data, setData] = React.useState({ nodes: [], edges: [] });
const getTableLineage = async (
collection: GraphinData,
target: TableEntity | LineageEntity,
direction: string,
) => {
if (!direction) {
return collection;
}
const response = await services.meta.getTableLineage({
direction,
tableId: target.id,
});
const [nodes, edges] = parse(target, response, direction);
collection.nodes = mergeNodes(collection.nodes!, nodes);
collection.edges = mergeEdges(collection.edges!, edges);
return collection;
};
const init = async () => {
let nextData = Object.assign({}, data, { nodes: [transNode(props.item)] });
nextData = await getTableLineage(nextData, props.item, 'up');
nextData = await getTableLineage(nextData, props.item, 'down');
setData(nextData);
ref.current!.graph.fitCenter();
};
React.useEffect(() => {
ref.current?.graph && init();
}, [
ref.current?.graph,
]);
React.useEffect(() => {
const graph = ref.current?.graph;
const event = async (event: GraphEvent) => {
const source = event.item?.get('model').data;
let nextData = Object.assign({}, data);
nextData = await getTableLineage(nextData, source, source.direction);
setData(nextData);
};
graph?.on('node:click', event);
return () => {
graph?.off('node:click', event);
};
}, [
data,
ref.current?.graph,
]);
return (
}
layout={{
type: 'dagre',
rankdir: 'LR',
align: 'DL',
nodesep: 10,
ranksep: 40,
}}
>
);
};
export default LineageContent;
Setelah menyelesaikan pengembangan kode di atas, Anda dapat menerapkan dan menjalankan kode proyek secara lokal. Untuk operasi penerapan dan menjalankan, lihat Operasi Umum: Penerapan Lokal dan Menjalankan.
4. Temukan tugas yang sesuai dengan tabel
Ketika definisi tabel berubah, Anda dapat melakukan analisis lineage tugas hilir melalui API DataWorks, OpenData, dan langganan pesan untuk menemukan node yang sesuai dengan tabel. Operasi spesifik adalah sebagai berikut:
Pesan saat ini mendukung perubahan tabel, perubahan tugas, dll. Pengguna Edisi Perusahaan dapat terhubung ke pesan perubahan tabel. Ketika Anda menerima pesan perubahan tabel, Anda dapat melihat hubungan lineage tabel tersebut.
Tugas lineage
Gunakan ListLineages untuk melihat lineage tabel, dan dapatkan ID Tugas DataWorks (dan ID Instans Tugas) dari Task dalam hubungan lineage.
Berdasarkan ID tugas yang diperoleh, gunakan GetTask untuk mendapatkan detail tugas.
Tugas produksi
Operasi umum: Penerapan lokal dan menjalankan
Persiapkan dependensi.
Anda perlu menyiapkan lingkungan dependensi berikut: Java 8 ke atas, alat pembuatan Maven, lingkungan Node, alat pnpm. Anda dapat menjalankan perintah berikut untuk menentukan apakah Anda memiliki lingkungan di atas:
npm -v // Jika instalasi berhasil, versi akan ditampilkan di output perintah. Jika instalasi gagal, kesalahan bahwa tidak ada perintah yang tersedia akan dilaporkan. java -version // Jika instalasi berhasil, versi akan ditampilkan di output perintah. Jika instalasi gagal, kesalahan bahwa tidak ada perintah yang tersedia akan dilaporkan. pnpm -v // Jika instalasi berhasil, versi akan ditampilkan di output perintah. Jika instalasi gagal, kesalahan bahwa tidak ada perintah yang tersedia akan dilaporkan.Unduh kode proyek dan jalankan perintah berikut.
Tautan unduhan kode proyek: meta-api-demo.zip.
pnpm iTemukan file application.properties di jalur backend/src/main/resources proyek contoh, dan modifikasi parameter inti dalam file tersebut.
api.access-key-id dan api.access-key-secret perlu dimodifikasi menjadi AccessKey ID dan AccessKey Secret dari akun Alibaba Cloud Anda.
CatatanAnda dapat merujuk ke Manajemen Pasangan AccessKey untuk mendapatkan informasi terkait AccessKey dan lainnya dari akun Alibaba Cloud Anda.
api.region-id dan api.endpoint perlu dimodifikasi menjadi informasi wilayah API yang akan dipanggil.
Parameter lainnya dapat dimodifikasi sesuai dengan situasi aktual. Contoh pengisian setelah dimodifikasi adalah sebagai berikut.

Jalankan perintah berikut di direktori root proyek untuk menjalankan kode contoh.
npm run dev