跳到主要内容

创建可视化插件

前言

Superset 中的可视化是用 JavaScript 或 TypeScript 实现的。Superset 预装了几种可视化类型(以下简称 “viz 插件”),可在 superset-frontend/plugins 目录下找到。可视化插件在 superset-frontend/src/visualizations/presets/MainPreset.js 中添加到应用程序中。

注意

注意:在开始下面的步骤前应确保你使用的是普通用户,使用root用户执行到yo @superset-ui/superset命令时会报错,目前还没有很好的解决办法。下面创建可视化插件的步骤可以跳过,直接下载可视化插件模板

创建一个简单的可视化插件

要开始使用,您需要 Superset Yeoman 生成器。建议使用您正在使用的 Superset 版本所附带的模板版本。安装方法如下:

npm i -g yo
cd superset-frontend/packages/generator-superset
npm i
npm link

之后,您就可以继续创建 viz 插件了。为您的 viz 插件创建一个新目录,前缀为 superset-plugin-chart,然后运行 Yeoman 生成器

mkdir /tmp/superset-plugin-chart-hello-world
cd /tmp/superset-plugin-chart-hello-world

初始化可视化插件:

yo @superset-ui/superset

之后,生成器会问几个问题(默认值应该没问题):

$ yo @superset-ui/superset
_-----_ ╭──────────────────────────╮
| | │ Welcome to the │
|--(o)--| │ generator-superset │
`---------´ │ generator!
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
? Package name: superset-plugin-chart-hello-world
? Description: Hello World
? What type of chart would you like? Time-series chart
create package.json
create .gitignore
create babel.config.js
create jest.config.js
create README.md
create tsconfig.json
create src/index.ts
create src/plugin/buildQuery.ts
create src/plugin/controlPanel.ts
create src/plugin/index.ts
create src/plugin/transformProps.ts
create src/types.ts
create src/SupersetPluginChartHelloWorld.tsx
create test/index.test.ts
create test/__mocks__/mockExportString.js
create test/plugin/buildQuery.test.ts
create test/plugin/transformProps.test.ts
create types/external.d.ts
create src/images/thumbnail.png

要构建可视化插件,请运行以下命令:

npm i --force
npm run build

或者,要在开发模式下运行可视化插件(每当进行更改时重建),请使用以下命令启动开发服务器:

npm run dev

要将软件包添加到 Superset,请进入 Superset 源文件夹中的 superset-frontend 子目录,运行以下命令:

npm i -S /tmp/superset-plugin-chart-hello-world

如果您将包发布到 npm,您自然也可以直接从那里安装。之后编辑 superset-frontend/src/visualizations/presets/MainPreset.js 并进行以下更改:

superset-frontend/src/visualizations/presets/MainPreset.js
import { SupersetPluginChartHelloWorld } from 'superset-plugin-chart-hello-world';

导入 viz 插件,然后将以下内容添加到传递给 plugins 属性的数组中:

superset-frontend/src/visualizations/presets/MainPreset.js
new SupersetPluginChartHelloWorld().configure({ key: 'ext-hello-world' }),

之后,在运行 Superset(例如开发服务器)时,就会显示 viz 插件:

superset/superset-frontend/
npm run dev-server

可视化插件源码讲解

上面的内容来自官方文档翻译,经过上面的步骤我们完成了可视化插件的创建以及如何添加到superset中,接下来将详细讲解可视化插件文件夹下包含哪些内容,逐步揭开superset的面纱,如何开发自己的可视化插件。

以echart pie 饼图为例,进入到可视化插件目录superset-frontend/plugins/plugin-chart-echarts/src/Pie,我们可以看到这样的目录结构:

.
├── buildQuery.ts
├── controlPanel.tsx
├── EchartsPie.tsx
├── images
│ ├── Pie1.jpg
│ ├── Pie2.jpg
│ ├── Pie3.jpg
│ ├── Pie4.jpg
│ └── thumbnail.png
├── index.ts
├── transformProps.ts
└── types.ts

EchartsPie.tsx

这是一个react 方法组件,接收一个由transformProps.ts返回的props,包含了图表组件所需的数据以及配置。图表组件的展示内容由你制定。

controlPanel.tsx

该文件主要包括一个对象,用于定义superset创建图表时左侧的控制面板,,其中包含可视化插件中使用的每个设置的数组。它将设置传递给主组件,由主组件创建数据可视化查询。

控制面板分为两个选项卡: “查询 “和 ”图表选项"。定义图表数据请求输入(如列和度量)的控件通常位于 “查询 ”部分, 而影响图表视觉外观或功能的控件则位于 “图表选项 ”部分。

控件的类型

这里有两种控件: 预定义控件和自定义控件

下面有几种预定义控件可以使用:

  • groupby: 按列分组(转换为 GROUP BY 语句)
  • series: 与 groupby 相同,但只有一个选择。
  • metrics: 多个度量(转换为聚合表达式:Sum、Avg、Max、Min等)
  • metric: 与 metrics 相同,但只有一个选择。
  • adhoc_filters:过滤器(根据过滤器类型转换为 WHERE 或 HAVING)
  • row_limit: 最大记录数(转换为 LIMIT 语句)

如果控制面板上同时有seriesgroupby控件,而用户选择col1作为series控件的值,并且col2col3 作为groupby控件的值。 结果查询将包含三个groupby列。 这是因为我们认为 series 控件是一个 groupby 查询字段,其值将在生成查询时自动追加到 groupby 字段。

所有现有预定义的控件列表可在以下文件中找到:superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
可以看到有这些控件:

superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx
export default {
metrics: dndAdhocMetricsControl,
metric: dndAdhocMetricControl,
datasource: datasourceControl,
viz_type,
color_picker,
metric_2: dndAdhocMetricControl2,
linear_color_scheme,
secondary_metric: dndSecondaryMetricControl,
groupby: dndGroupByControl,
columns: dndColumnsControl,
granularity,
granularity_sqla: dndGranularitySqlaControl,
time_grain_sqla,
time_range,
row_limit,
limit,
timeseries_limit_metric: dndSortByControl,
orderby: dndSortByControl,
order_desc,
series: dndSeriesControl,
entity: dndEntityControl,
x: dndXControl,
y: dndYControl,
size: dndSizeControl,
y_axis_format,
x_axis_time_format,
adhoc_filters: dndAdhocFilterControl,
color_scheme,
time_shift_color,
series_columns: dndColumnsControl,
series_limit,
series_limit_metric: dndSortByControl,
legacy_order_by: dndSortByControl,
truncate_metric,
x_axis: dndXAxisControl,
show_empty_columns,
temporal_columns_lookup,
currency_format,
sort_by_metric,
};

自定义控件

可以通过导入必要的依赖关系和覆盖默认参数来定义自定义控件,然后将其放置在 “查询 ”部分的controlSetRows部分,而不是预定义控件中。
重写预定义控件:

controlPanel.tsx
const config: ControlPanelConfig = {
controlPanelSections: [
{
label: t('Query'),
expanded: true,
controlSetRows: [
['groupby'],
['metric'],
['adhoc_filters'],
['row_limit'],
[
{
name: 'sort_by_metric',
config: {
...sharedControls.sort_by_metric,
default: true,
},
},
],
],
},
]
}

上面代码中导入了一个sort_by_metric组件,并且重写了default属性为true。

除了基本的下拉控件外,还有几种预定义的控件类型(可通过 type 属性设置)可以使用。一些常用的示例如下:

  • SelectControl: 用于选择单个或多个值的下拉菜单,通常为列
  • MetricsControl: 下拉选择度量标准,触发模态 来定义指标详情
  • AdhocFilterControl: 控制选择过滤器
  • CheckboxControl: 用于选择真/假值的复选框
  • SliderControl: 带有最小/最大值的滑块
  • TextControl: 文本输入控件

要了解更多控制输入类型,请查看以下文件: superset-frontend/src/explore/components/controls/index.js

自定义控件:

{
name: 'show_labels_threshold',
config: {
type: 'TextControl',
label: t('Percentage threshold'),
renderTrigger: true, // 如果值改变,会触发图表的渲染
isFloat: true,
default: 5,
description: t(
'Minimum threshold in percentage points for showing labels.',
),
},
}

show_labels_threshold是你的字段名称,输入的值会保存在你定义的这个字段中。 有关config的更多配置参数,建议查看superset中的现有控件,并比较相关控制面板的编辑方式。

为确保正确填写所有控件,提供了以下验证器,来自 @superset-ui/core/lib/validator:

  • validateNonEmpty: 必须且至少有一个值
  • validateInteger: 必须是整数值
  • validateNumber: 必须是整数或小数值

有关控制输入类型,请参阅: superset-frontend/src/explore/components/controls/index.js

我们可从官方源码中看到控件是如何定义的,例如行限制控件:

superset-frontend/src/explore/components/controls/index.js
const row_limit: SharedControlConfig<'SelectControl'> = {
type: 'SelectControl',
freeForm: true,
label: t('Row limit'),
clearable: false,
mapStateToProps: state => ({ maxValue: state?.common?.conf?.SQL_MAX_ROW }),
validators: [
legacyValidateInteger,
(v, state) => validateMaxValue(v, state?.maxValue || DEFAULT_MAX_ROW),
],
default: 10000,
choices: formatSelectOptions(ROW_LIMIT_OPTIONS),
description: t(
'Limits the number of the rows that are computed in the query that is the source of the data used for this chart.',
),
};

所以,只需使用共享控件中的预设选项,你就能建立自己的控制面板。选择你需要的控件名称,然后像这样设置即可:

const config: ControlPanelConfig = {
controlPanelSections: [
{
label: t('Query'),
expanded: true,
controlSetRows: [
['entity'],
['adhoc_filters'],
['row_limit'],
//...
//其他的控件
//...
],
},
...
]
}
你可能会好奇,我们在定义的控件的值是存储在哪的?这些值又该如何获取呢?
  1. 这些值Superset会为你保存到一个formData的对象中,其中key是你定义的控件名称,value是用户在界面输入数据,这个formData会在你保存图表时存储在图表的元数据中。
  2. superset会将formData传递给transformProps.ts文件中定义的transformPropsprops,你能够通过props.formData解包出你想要的控件值。

buildQuery.ts

定义查询数据的方式。每次打开图表时,图表会向服务器发起请求http://example.com/api/v1/chart/data请求图表的数据,可以看到请求负载为:

buildQuery 函数用于创建一个 QueryContext 实例,并将其发送到图表数据端点. 除了包含要使用的数据源信息外,它还指定了返回的类型(如完整有效载荷:full、样本:samples、查询:query)和格式(如 CSV 或 JSON), 以及是否从数据源强制刷新数据,而不是使用缓存的数据副本。想要知道更多返回类型,请查看:superset/common/query_actions.py:193

你也可以看到你定义的控件的值存放在form_data中,当你保存图表时,这些值最终会为你存放在服务器中,你下次去请求获取图表时返回这些数据。

更重要的是,QueryContext 包含一个属性 queries,它是一个数组,其中包含QueryObjects 的数组。 一个 QueryObject 可以指定列、度量和过滤器等。通常只需在 baseQueryObject 的基础上指定一个查询即可, 但对于一些更高级的用例,可以在 QueryObject 中定义后处理操作,或者在 viz 需要多个不同结果集时定义多个查询。

transformProps.ts

从图表数据端点收到成功响应后调用此函数,用于在将传入数据发送到可视化之前对其进行转换。

transformProps 函数向数据可视化组件返回额外的或修改的Props时非常有用。你也可以从 EchartsPie.tsx 文件访问直接formData,但在这里提供自定义props通常便于集成依赖特定props的第三方库。
chartProps 中属性的描述:

  • height, width: 图表所在 DOM 元素的高度/宽度
  • formData: 发送到后端的图表数据请求有效载荷。
  • queriesData: 接收到的图表数据响应的有效载荷 从后端获取。queriesData` 的一些重要属性:
    • data: 是一个包含数据的数组,每一行都包含一个将列/别名映射到其值的对象。
      例如: [{ col1: 'abc', metric1: 10 }, { col1: 'xyz', metric1: 20 }]
    • rowcount: data中的行数
    • query: 发出的查询。
注意

请注意:应用程序加载时会缓存 transformProps 函数。在开发过程中通过热加载对 transformProps 函数进行更改时,只有重新启动开发服器才能看到更改。

index.ts

定义图表插件,配置插件的元数据。

这些元数据我们能够在superset新建图表中看到:

images

这个目录用于存放图表的缩略图。

总结

相信从上面阅读下来对可视化插件的整个架构有了大致的了解,有了这部分基础,在接下来的章节中,会使用3个小练习来带大家熟悉如何二次开发可视化插件。在实际情况中,superset已经提供了大部分常用的图表类型,这些图表类型也能够满足我们的开发需求,有些图表可能在某些功能上不满足我们的需求,我们可以在原有图表的基础上进行二次开发、改造来达到我们的需求,而完全自定义自己的图表组件的情况还是比较少的。所以了解如何在原有的图表插件上扩展就够了。(个人的体会)