基础数据结构用于统一各种服务端数据到可视化数据的转换流程。

数据转换流程

数据转换流程.png
从数据到 UI 的过程可以理解为一个管道,管道中每个环节的处理都是为了让下一个环节更简单,这样设计的好处是:

  • 具体到可视化需要作的数据处理逻辑会尽量简化。
  • 管道前面的环节可以共用,也可以做一些统一的优化。

整体流程包含三个部分:

  • transform,用于将具体的业务数据处理成表结构要求的数据。
  • createTable,将表结构数据创建为表对象,调用表对象上的数据处理方法作各种全局的处理。
  • 可视化,各个可视化组件依赖共同的表对象,针对各自的可视化需求可以在表对象上再次调用相关的处理方法,各个处理方法调用后会返回一个新的实例,最终通过 select 方法获取到各自需要的数据。

前端基础数据结构

数据转换方法用于将各种服务端 API 返回的数据转换成基础数据结构,不同的 API 需要有对应的转换方法。

基础数据结构就是一个表结构,比如事件分析 API 返回的数据会转换成两张表:

明细表

measures by_fields $country $province date value
CRM操作的总次数 中国,浙江省 中国 浙江省 2020-03-02 00:00:00 853
文档操作的总次数 中国,浙江省 中国 浙江省 2020-03-02 00:00:00 833
CRM操作的总次数 中国,浙江省 中国 浙江省 2020-03-03 00:00:00 857
文档操作的总次数 中国,浙江省 中国 浙江省 2020-03-03 00:00:00 757

合计表

measures by_fields $country $province date value
CRM操作的总次数 15667
文档操作的总次数 14889
CRM操作的总次数 韩国 韩国 9989
文档操作的总次数 韩国 韩国 9578
CRM操作的总次数 韩国,釜山广域市 韩国 釜山广域市 893
文档操作的总次数 韩国,釜山广域市 韩国 釜山广域市 852
CRM操作的总次数 韩国 韩国 2020-03-02 00:00:00 1474
文档操作的总次数 韩国 韩国 2020-03-02 00:00:00 1415
CRM操作的总次数 韩国 韩国 2020-03-03 00:00:00 1454
文档操作的总次数 韩国 韩国 2020-03-03 00:00:00 1414

转换方法

  1. // 分析模型数据转换方法
  2. import { transform } from '@sc/report-utils';
  3. // 将事件分析 API 返回的数据转为前端基础数据结构
  4. const { detail, rollup } = transform({
  5. type: 'segmentation',
  6. meta: {
  7. measures: [ '提交新密码的总次数', 'App 激活的总次数' ]
  8. },
  9. data
  10. });

JSON 数据结构如下:

  1. [
  2. {
  3. "measure": "CRM操作的总次数",
  4. "by_values": ["韩国"],
  5. "event.$Anything.$country": "韩国",
  6. "event.$Anything.$province": "",
  7. "date": "2020-03-02 00:00:00",
  8. "value": 9989
  9. },
  10. {
  11. "measure": "文档操作的总次数",
  12. "by_values": ["韩国"],
  13. "event.$Anything.$country": "韩国",
  14. "event.$Anything.$province": "",
  15. "date": "2020-03-02 00:00:00",
  16. "value": 9578
  17. },
  18. {
  19. "measure": "CRM操作的总次数",
  20. "by_values": ["韩国","釜山广域市"],
  21. "event.$Anything.$country": "韩国",
  22. "event.$Anything.$province": "釜山广域市",
  23. "date": "2020-03-03 00:00:00",
  24. "value": 893
  25. },
  26. {
  27. "measure": "文档操作的总次数",
  28. "by_values": ["韩国","釜山广域市"],
  29. "event.$Anything.$country": "韩国",
  30. "event.$Anything.$province": "釜山广域市",
  31. "date": "2020-03-03 00:00:00",
  32. "value": 852
  33. }
  34. ]

创建表对象

将符合表结构的数据创建为表对象,基于表对象提供的方法可以对数据进行各种操作,生成符合可视化组件的数据。

  1. // 分析模型数据转换方法
  2. import { transform } from '@sc/report-utils';
  3. // 表对象创建方法
  4. import { createTable } from '@sc/database';
  5. // 将事件分析 API 返回的数据转为前端基础数据结构
  6. const { detail, rollup } = transform({
  7. type: 'segmentation',
  8. meta: {
  9. measures: [ '提交新密码的总次数', 'App 激活的总次数' ]
  10. },
  11. data
  12. });
  13. // 创建明细表
  14. const detailTable = createTable(detail);
  15. // 创建合计表
  16. const rollupTable = createTable(rollup);

表对象 API

表对象上有一系列操作数据的方法。

select()

选取表中的数据,除 select 方法外,其他方法在执行后均会返回一个新的表对象。

add(key, value)

在每条数据中新增字段。

参数 类型 描述
key string 新增的字段名
value string or function 字段值,类型为函数则可以根据每条数据生成返回字段值,传入函数的第 1 个参数为当前数据项,第 2 个参数为元素的索引

filter(callback)

对数据进行过滤。

参数 类型 描述
callback function 测试表中的每条数据,返回 true 保留数据,false 不保留数据,传入函数的第 1 个参数为当前数据项,第 2 个参数为元素的索引

orderBy(key, compareFunction)

根据指定的列对数据进行排序。

参数 类型 描述
key string 字段名
compareFunction function= 比较函数,与数组 sort 方法的使用一致,接收两条比较数据的字段值

uniqBy(key)

根据指定的列对数据进行去重。

参数 类型 描述
key string 字段名

groupBy(key)

根据指定的列对数据进行分组。

参数 类型 描述
key string 字段名

分组后数据结构如下:

  1. [
  2. {
  3. "key": "CRM操作的总次数",
  4. "items": [
  5. {
  6. "measures": "CRM操作的总次数",
  7. "event.$Anything.$country": "韩国",
  8. "event.$Anything.$province": "",
  9. "date": "",
  10. "value": 9989
  11. },
  12. {
  13. "measures": "CRM操作的总次数",
  14. "event.$Anything.$country": "韩国",
  15. "event.$Anything.$province": "釜山广域市",
  16. "date": "",
  17. "value": 893
  18. }
  19. ]
  20. },
  21. {
  22. "key": "文档操作的总次数",
  23. "items": [
  24. {
  25. "measures": "文档操作的总次数",
  26. "event.$Anything.$country": "韩国",
  27. "event.$Anything.$province": "",
  28. "date": "",
  29. "value": 9578
  30. },
  31. {
  32. "measures": "文档操作的总次数",
  33. "event.$Anything.$country": "韩国",
  34. "event.$Anything.$province": "釜山广域市",
  35. "date": "",
  36. "value": 852
  37. }
  38. ]
  39. }
  40. ]

示例

获取指标合计值

  1. rollupTable
  2. // 合计表中没有分组值和时间值的数据
  3. .filter(item => !item.by_fields && !item.date)
  4. .select();

生成图例数据

  1. detailTable
  2. // 按分组名去重
  3. .uniqBy('by_fields')
  4. // 按指标名分组
  5. .groupBy('measures')
  6. .select();

生成线图数据

  1. chart.data(detailTable
  2. // 增加格式化后的日期字段
  3. .add('dateText', item => moment(item.date).format('MM-DD'))
  4. .select()
  5. );
  6. chart
  7. .line()
  8. .position('dateText*value')
  9. .color('measures')
  10. .shape('smooth');
  11. chart
  12. .point()
  13. .position('dateText*value')
  14. .color('measures')
  15. .shape('circle');

生成分层表格数据

  1. const tableData = [];
  2. const map = {};
  3. // 处理成分层表格的数据结构
  4. rollup
  5. .map(item => {
  6. // 以指标名为 key 添加值
  7. item[item.measures] = item.value;
  8. Reflect.deleteProperty(item, 'measures');
  9. Reflect.deleteProperty(item, 'value');
  10. })
  11. .forEach(item => {
  12. // 以国家和省份作为每条数据的标识
  13. const id = `${item.$country}${item.$province}`;
  14. if (id) {
  15. // 整合 id 相同的数据
  16. if (map[id]) {
  17. // 补充新的指标值
  18. map[id][item.measures] = item.value;
  19. } else {
  20. map[id] = item;
  21. tableData.push(item);
  22. }
  23. }
  24. });
  25. // 创建表对象
  26. const table = createTable(tableData);
  27. table
  28. // 按国家分组
  29. .groupBy('$country')
  30. .select();

皮成,2020.03.22