## 简介

商业智能（Business Intelligence）简称 BI，即通过数据挖掘与分析找到商业洞察，助力商业成功。

一个完整的 BI 链路包含数据采集、数据清洗、数据挖掘、数据展现，其本质是对数据进行多维分析。前端的主要工作在数据展现环节，由于展示方式繁多、分析模型复杂且数据量大，前端环节的复杂度很高。

在 BI 做前端非常有挑战，开发者需要充分理解数据概念，而本身复杂度较高的可视化建站也只是 BI 的基础能力，想要建设 BI 的上层能力，比如探索式分析和数据洞察，都需要在前后端引入更复杂的计算模型。

本文作为一个引子，简单介绍笔者做 BI 的经验，后面如果有机会再写一个系列文章对细节进行阐述。

## 精读

国内目前处于 BI 1.0 阶段，也就是报表阶段，因此笔者将阐述这个阶段 BI 的核心开发概念。

> BI 2.0 探索式分析阶段是国内数据分析最前沿领域，这部分等开发完成后再分享。

BI 1.0 阶段的核心概念包括 **数据集、渲染引擎、数据模型、可视化** 这四个技术模块。

### 数据集

数据集即数据的集合，在 BI 领域更多指一种标准化的数据结构。

任何数据都可以封装成数据集，比如 txt 文本、excel、mysql 数据库等等。

数据集的基本形态是二维表格，列头表示字段，每一行就是一份数据，数据展示就是通过对这些数据字段进行多维度分析。

#### 数据集导入

一般来说数据集导入有两种方式，分别是本地文件上传与数据库链接。本地文件上传又分为多种文件类型处理，比如对 excel 的解析，可能还包括数据清洗；数据库链接分析可视化导入与 SQL 输入。

可视化导入需要提前对数据库进行结构分析，绘制出表结构与字段结构，不用理解 SQL 也可以进行可视化操作。

SQL 输入可以利用 [monaco-editor](https://github.com/microsoft/monaco-editor) 等 web 代码编辑器作为输入框，最好能结合智能提示提高 sql 编写效率。sql 智能提示可以参考往期精读 [精读《手写 SQL 编译器 - 智能提示》](https://github.com/dt-fe/weekly/blob/v2/085.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA%E3%80%8B.md)。

#### 数据集建模

数据集建模一般包含 **维度度量建模、字段配置、层系建模**。

维度度量建模需要智能分析出字段属于维度还是度量，一般会结合字段实际的值或者字段名来智能判断字段类型，如果数据库信息中已存储了字段类型，就可以 100% 准确归类。

字段配置即对字段进行增删或修改，还可以新增聚合字段或对比字段。

聚合字段是指将一个字段表达式封装为一个新字段，这里也会用到一个简单的 sql 编辑器，只需要支持四则运算、字段提示、以及一些基本函数的组合即可。

对比字段是指新增的字段是基于已有字段在某个时间周期内的对比，比如对 UV 字段的年同比就可以封装为一个对比字段。对比字段在前端技术上没有什么难度，仅需理解概念即可。

### 渲染引擎

渲染引擎包括了对报表进行编辑与渲染的引擎，理论上可以合二为一。

渲染引擎的重要模块包括：画布拖拽、组件编辑、事件中心。

画布拖拽其实包含了组件自定义开发流程，到 CDN 发布、CDN 加载、组件拖拽、画布排版等一系列技术点，每个点展开都有写不完的细节，但好在这套功能属于通用建站基础功能点，本文就不再赘述。

组件编辑中，基本属性的编辑与属于通用建站领域的表单模型范畴，一般通过 UISchema 来描述通用表单，这块也不再赘述。组件编辑的另一部分就是数据编辑，这部分在后面数据模型章节里详细讲。

事件中心是渲染引擎部分，此功能在编辑状态需要禁用。这个功能可以实现图表联动、上卷下钻等数据能力。一个通用事件中心一般包括 **事件触发** 与 **事件响应** 两部分，基本结构如下：

```typescript
interface Event {
  trigger:
    | {
        type: 'callback';
        callbackName: string;
      }
    | {
        type: 'listener';
        eventName: string;
      }
    | {
        type: 'system';
        name: string;
      };

  action:
    | {
        type: 'dispatch';
        eventName: string;
      }
    | {
        type: 'jumpUrl';
        url: string;
      }
}
```

`trigger` 即事件触发，包括基本的系统事件 `system`，比如定时器或者初始化自动触发；组件的回调 `callback` 比如当按钮被点击时；事件监听 `listener` 比如另一个事件被触发时，这个事件可能来自于 `action`。

`action` 即事件响应，包括基本的事件触发 `dispatch`，可以触发其他事件，可以构成一个事件链路；其他的 `action` 就是数据相关，可以用来做条件联动、字段联动、数据集联动等等，因为实现各异这里不做介绍。

事件机制还需要支持值传递，即事件触发源的值可以传递到事件响应方。值传递可以在触发源内部进行，比如当触发源是回调函数时，函数参数就自然作为值传递过去，触发源通过 `...args` 方式接收。

#### 数据钻取

配置了层系的字段都可以进行数据钻取。层系可以在数据集配置，也可以在报表编辑页配置，可以理解为一个顺序有关的文件夹，将文件夹作为字段使用时，默认生效的是第一个子元素，之后可以按照顺序分别进行下钻。

比如 “地区” 层系包含了国家、省、市、区，那么就可以按照这个层级进行数据上卷下钻。

如果一个字段是层系字段，图表需要有对应的操作区域进行上卷下钻，数据编辑区域也可以进行同样操作。数据钻取的计算过程不在图表内部处理，而是触发一个状态后，由渲染引擎将这个层系字段实例状态改为下钻到第 N 层，并且每下钻一次就多拿到一列的数据，由图表组件进行下钻展示。

一般来说下钻后数据仍是全量的，有时候为了避免数据量过大，比如在柱状图点击某个柱子进行下钻，只想看这个柱子下钻后的数据：比如 2017、2018、2019 年三年的数据，下钻到月后数据量是 3 x 12 = 36 条，但如果仅在 2019 年进行下钻，只想看 2019 年的 12 条数据，可以转化为下钻 + 筛选条件的模式：全局下钻展开后 36 条，在 2019 年上点击下钻后，增加一个筛选条件（年 = 2019），这样就达到了效果，整个流程对图表组件是无感知的。

### 数据模型

与通用表单模型 UISchema 相对应，数据模型笔者称之为 CubeSchema，因为 BI 领域对数据的多维处理模型成为 Cube 立方体，数据配置即表示如何对这个立方体进行查询，因此其配置表单成为 CubeSchema。

不管是探索式分析还是 BI 1.0 的报表阶段，数据模型的基本概念是通用的（探索式分析固定了行列，且增加了标记）：将字段放置到不同的区域，这些区域的划分方式可以按照功能：横轴、纵轴；按照概念：维度、度量；按照探索分析思路：固化为行、列等等。

这块可能涉及到的技术点有：拖拽、批量选择+拖拽、双击后按照维度度量自动添加、图表切换后区域字段自动迁移、对字段拖拽的系列配置：限制数量、限制类型、限制数据集、是否重复等等。

拖拽可以用 [react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd) 等库，与渲染引擎拖拽方案基本类似，遇到有层系的数据集还需支持嵌套层级的拖拽。

图表切换后字段迁移，可以将每个拖拽区域设置若干类型：

```json
{
  "dataType": ["dimension"]
}
```

这样在切换后，维度类型的字段可以自动迁移到维度类型区域，如果对应区域字段数量达到了 `limit` 限制，就继续填充到下一个区域，直到字段用尽或区域填充完为止。

如果在探索式分析场景里，需要提前对字段进行维度度量建模，在切换时按照图表情况进行相应的处理。比如折线图切换到表格的情况：折线图是天然一个维度（主轴） + N 个度量的场景，表格是天然两个维度（行、列）+ 1 个度量的场景（也可以支持多个，对单元格进行再切分即可），那么从折线图切换到表格时，度量就会落到标记的文本区域；如果从拥有行和列的表格切换到柱状图（之所以无法切换到折线图，是因为表格的度量值一般是离散的，而折线图度量值一般是连续的），表格的行与列的字段会落到柱状图的维度轴，表现效果是对维度轴进行下钻。

> [精读《Tableau 探索式模型》](https://github.com/dt-fe/weekly/blob/v2/117.%E7%B2%BE%E8%AF%BB%E3%80%8ATableau%20%E6%8E%A2%E7%B4%A2%E5%BC%8F%E6%A8%A1%E5%9E%8B%E3%80%8B.md) 了解更多探索式分析。

数据模型还包括数据分析相关配置，比如设置对比字段，或者均值线等分析功能。这些数据计算工作放在后端，前端需要将配置项整理到取数接口中，并按照数据驱动的方式展现。

对于对比字段等 “拓展字段” 的分析功能，可以拓展通用取数接口，图表组件无感知，相当于多添加了几个隐藏字段；去特殊值等对标准数据进行操作的情况图表组件也无需感知。

聚类、均值线等需要图表组件额外展示的部分抽象为一套固定的数据格式透传给图表组件，由图表组件自行处理。

可以看出来，都是取数 + 展示，普通的前端业务与 BI 业务开发的区别：

普通前端业务是以业务逻辑为核心的，根据业务需要确定接口格式；BI 业务是以数据为核心的，围绕数据计算模型确定一套固定的接口格式，取数不依赖组件，所有组件对标准数据都有对应的展现。

### 可视化

与普通可视化组件不同，BI 可视化组件需要对接 CubeSchema 模型，同时还要支持 **大数据性能优化、边界数据展示优化、交互响应**。

对接 CubeSchema 即统一对接二维表格的数据，大部分组件都是二维以上结构展示，因此对接起来并不困难，有一些一维数据结构的组件比如单指标块就要舍弃其中的某一维，需要确定一套规则。

二维以上部分是较为通用的，虽然计算模型是基于 Cube N 维的，但组件可以通过标准轴进行多维度展开，或者说下钻来实现类似效果。对于折线图来说，轴的含义有限，可以用分面的方式展示多维数据。当然也有一些组件只适合展示特定维度数量的数据。

#### 大数据性能优化

可视化组件特别需要关注性能优化，因为 BI 查询出的数据量可能非常大，特别是多层下钻或基于地理的数据。

技术手段包括 GPU 渲染、缓存 canvas、多线程运算等，业务手段包括数据抽样、按需渲染可视区域、限制数据条数等等。

#### 边界数据展示优化

永远不知道数据集会给出怎样的数据，因此 BI 边界情况特别多，可能点非常密集，也可能丢失一些数据导致渲染异常。图表组件需要利用避让算法将密集的数据打散或着色，目的是为了容易阅读，对于丢失的异常数据也要有保护性的补全机制。

#### 交互响应

包括上卷下钻、点选、圈选、高亮等交互操作，这些操作反馈到渲染引擎导致数据变化并将新的数据灌入图表组件。

业务逻辑上这些交互操作并不复杂，难点在使用的可视化库是否有这个能力，以及如何统一交互行为。

## 总结

BI 领域的四大方向：数据集、渲染引擎、数据模型与可视化都有许多可以做深的技术点，每一块都需要深入沉淀几年技术经验才能做好，需要大量优秀人才通力协作才有可能做好。

目前我们在阿里数据中台正在打造一款面向未来的优秀 BI 工具，如果 BI 领域让你觉得有挑战，随时欢迎你的加入，联系邮箱：ziyi.hzy@alibaba-inc.com

> 讨论地址是：[精读《前端与 BI》 · Issue #208 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/208)

**如果你想参与讨论，请 [点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**

> 关注 **前端精读微信公众号**

<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

> 版权声明：自由转载-非商用-非衍生-保持署名（[创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)）
