uban-bi
v0.1.0
Published
适用于 Vue2、eggjs 的 BI 数据处理函数库
Readme
这是一个基于 JS 适用于 Vue2、eggjs 的 npm 包。
命名与编码规范
- 每个基础 BI 函数采用大驼峰命名,并以
BI前缀,不超过 20 个字符 - 尽可能使用纯函数,函数需 JSDoc 注释参数及返回值
- 每类函数存放在不同文件,并在文件开头写明介绍
安装
npm install uban-bi依赖:moment、bignumber.js
目录结构
ubanBI/
├── package.json
├── readme.md
├── demo.js # 历史参考实现,不再维护
├── design/ # 设计脑图
│ ├── 表连接-合并.png
│ ├── 表连接-左右连接.png
│ ├── 数据清洗-分组汇总.png
│ └── 数据清洗-格式化.png
├── test/
│ └── test.js # 烟雾测试,覆盖所有公开函数
└── src/
├── index.js # 统一出口
├── utils/ # 内部工具
│ ├── dfsFuncs.js # DfsFuncs 类:IF/MAX/MIN/toFixed/toPercent/DateFormat…
│ ├── stringParser.js # fullMatchReplace, parseBIStr
│ ├── formula.js # FormulaFunc, GetValueByPath
│ ├── evalSafe.js # getEval, handleValByFuncs
│ └── filter.js # MergeFilter, filterDataByPlan
├── columns.js # 新增列类别
├── concat.js # 表连接类别
├── clean.js # 数据清洗类别
└── contrast.js # 数据对照数据结构约定
sourceData = {
data: [ { field1: v1, field2: v2, ... }, ... ], // 行数据
fields: [ { field: 'field1', name: '显示名', type: 'number|string|...' }, ... ]
}函数类别 - 新增列
公式列 BIFormulaFunc
按公式 token 数组逐行计算新列。
/**
* @param {Object} sourceData { data, fields }
* @param {Object} options
* @param {string[]} options.colFields 当前行参与公式的字段名(构建 customDev)
* @param {Array} options.formula 公式 token 数组,详见 utils/formula.js
* @param {Object} [options.globalDev] 全局参数
* @param {string} options.name 新列显示名
* @param {string} options.type 新列类型
* @param {string} options.uuid 新列字段 key
* @returns {Object[]} data
*/
BIFormulaFunc(sourceData, options)formula 中每个 token:
{type:'X', value:'a'}—— 取当前行a字段{type:'GlobalX', value:'g.x'}—— 取 globalDev 的g.x{type:'ValueX', value:'1'}—— 字面量- 字符串元素 —— 运算符(如
'+'、'*')
汇总列 BISumByCols(按列汇总生成新列)
每行将 colFields 多列累加写入新列;非数字按 0 处理。
BISumByCols(sourceData, { colFields, name, type, uuid })汇总行 BISumByRows(按行汇总生成新行)
对 colFields 中各列纵向累加,追加一行到 data 末尾;不在 colFields 中的列填 null,uuid 字段填 name 作为标识。
BISumByRows(sourceData, { colFields, name, type, uuid })赋值列 BIAddCol
复制 colFields[0] 到新列,源值为 null / undefined / '' 时赋 defaultValue。
BIAddCol(sourceData, { colFields, defaultValue, name, type, uuid })条件列 BIAddCondCol
按 filtersList 中第一个匹配的规则赋值,否则赋 defaultValue。
BIAddCondCol(sourceData, {
filtersList: [
{ filters: 查询方案, value: 命中时赋值, defaultValue?: 该组兜底 },
...
],
defaultValue, // 整体未命中时
name, type, uuid,
})查询方案结构:
[[rule, ...], ...],外层或、内层并。rule = { key, symbol, value },symbol支持$in | $eq | $ne | $nin | $exists。
函数类别 - 表连接
BIConcatData 上下连接(纵向)
按 mode 决定输出字段。
BIConcatData(sourceData, { concatArray, mode, fields })
// mode: 'main' | 'over' | 'merge' | 'custom'
// - main 仅保留主表字段
// - over 保留所有表都存在的字段(交集)
// - merge 保留所有表字段并集,缺失填 null(默认)
// - custom 仅保留 options.fields
// concatArray: [{ data, fields }, ...]
// 返回新的 { data, fields }BIConcatRow 左右连接(横向)
按 mode 决定连接方式,与 SQL JOIN 同义。
BIConcatRow(sourceData, { concatArray, mode, match })
// mode: 'left' | 'right' | 'inner' | 'outer'(默认 left)
// concatArray: { data, fields } 单表,或 [{ data, fields }] 数组(取首项)
// match: { rule: (l, r) => boolean } 或 { mapKeys: [leftKey, rightKey] }函数类别 - 数据清洗
文件:src/clean.js
分组汇总 BIGroupData
按 groupFields 分组,对 aggFields 执行聚合。
BIGroupData(sourceData, {
groupFields: ['region', 'product'],
aggFields: [
{ field: 'sales', agg: 'sum', alias: 'salesTotal' },
{ field: 'sales', agg: 'avg' },
{ field: 'id', agg: 'count', alias: 'cnt' },
{ field: 'sales', agg: 'min' },
{ field: 'sales', agg: 'max' },
{ field: 'code', agg: 'first', alias: 'firstCode' },
{ field: 'code', agg: 'last', alias: 'lastCode' },
],
})
// agg: 'sum' | 'avg' | 'count' | 'min' | 'max' | 'first' | 'last'
// - first / last 取分组内首条/末条记录的原值,不做数值转换,保留原类型
// 返回新的 { data, fields }格式转化 BIDataFormat
按 mode 处理字段值。
BIDataFormat(sourceData, {
mode, // 'date' | 'datetime' | 'number' | 'toFixed' | 'percent' | 'boolean'
field, // 处理的目标字段
alias, // 可选,输出字段名(默认覆盖原字段)
format, // mode 为 date/datetime 时使用,moment.format 支持的任意值
dot, // mode 为 toFixed/percent 时使用,小数位
trueSign, // mode 为 boolean 时使用,'是' 返回 是/否;'1' 返回 1/2
})
// - 'date' 默认 format 为 'YYYY-MM-DD'
// - 'datetime' 默认 format 为 'YYYY-MM-DD HH:mm:ss'
// - format 示例:'YYYY/MM' | 'YYYY-MM-DD HH:mm:ss' | 'YYYY年MM月DD日' 等数据筛选 BIDataFilter
按查询方案筛选。
BIDataFilter(sourceData, filters)
// filters: 查询方案(外层或、内层并)
// 返回新的 { data, fields }函数类别 - 数据对照
handleDataContrast
原始数据按规则与对照表匹配,将对照表字段映射到原始数据。
handleDataContrast(mainData, mapData, {
fills: [{ source: 'map字段', target: '结果字段' }],
mode: '11' | 'N1', // 默认 '11'(一对一);'N1' 为 N 对一
isAllowNull: false, // 未匹配是否允许为空(false 抛错)
uuid: '', // 方案 id,用于错误信息
match: {
rule: (origItem, mapItem) => boolean, // 优先使用
// 或
mapKeys: ['origKey', 'mapKey'],
},
})'11':原始数据每条只能匹配一条对照记录,且对照记录不可被重复匹配'N1':对照表可被多条原始数据匹配
aggregate(占位)
按多步配置对 mainData、auxData 聚合。
aggregate(mainData, auxData, params)内部工具
文件:src/utils/
| 函数/类 | 文件 | 说明 |
|---|---|---|
| DfsFuncs | dfsFuncs.js | toBoolean/toNumber/toFixed/toPercent/IF/MAX/MIN/DateStepChange/DateFormat |
| fullMatchReplace | stringParser.js | 全匹配替换,转义正则特殊字符 |
| parseBIStr | stringParser.js | 递归解析 ${MAX(1,2)} 模板,由内向外求值 |
| FormulaFunc | formula.js | 公式求值,支持 X/GlobalX/ValueX token |
| GetValueByPath | formula.js | 点分路径取值 |
| getEval | evalSafe.js | 受限 eval,禁 this/ctx/service/async |
| handleValByFuncs | evalSafe.js | 按函数链转换值 |
| MergeFilter | filter.js | 合并公共参数到自定义查询方案 |
| filterDataByPlan | filter.js | 按查询方案筛选数据 |
使用示例
const bi = require('uban-bi');
const sourceData = {
data: [
{ region: 'east', sales: 10, code: 'A' },
{ region: 'east', sales: 20, code: 'B' },
{ region: 'west', sales: 5, code: 'C' },
],
fields: [
{ field: 'region', name: '区域', type: 'string' },
{ field: 'sales', name: '销量', type: 'number' },
{ field: 'code', name: '编码', type: 'string' },
],
};
// 1. 公式列:sales + 10
bi.BIFormulaFunc(sourceData, {
colFields: ['sales'],
formula: [{ type: 'X', value: 'sales' }, '+', { type: 'ValueX', value: '10' }],
name: '调整后销量', type: 'number', uuid: 'salesAdj',
});
// 2. 按列汇总
bi.BISumByCols(sourceData, { colFields: ['sales', 'salesAdj'], name: '合计', type: 'number', uuid: 'sum' });
// 3. 分组汇总
const grouped = bi.BIGroupData(sourceData, {
groupFields: ['region'],
aggFields: [
{ field: 'sales', agg: 'sum', alias: 'salesTotal' },
{ field: 'code', agg: 'first', alias: 'firstCode' },
{ field: 'code', agg: 'last', alias: 'lastCode' },
],
});
// 4. 日期格式化
bi.BIDataFormat({ data: [{ d: '2024-01-02 03:04:05' }], fields: [] },
{ mode: 'datetime', field: 'd', format: 'YYYY/MM/DD HH:mm' });
// 5. 数据筛选
const filtered = bi.BIDataFilter(sourceData, [[{ key: 'region', symbol: '$eq', value: 'east' }]]);
// 6. 数据对照
const mapped = bi.handleDataContrast(
[{ code: 'A' }, { code: 'B' }],
[{ code: 'A', label: '甲' }, { code: 'B', label: '乙' }],
{
fills: [{ source: 'label', target: 'label' }],
match: { mapKeys: ['code', 'code'] },
},
);测试
npm test
# 或 node test/test.js当前覆盖 23 项用例:DfsFuncs、parseBIStr、FormulaFunc、filterDataByPlan、MergeFilter、 BIFormulaFunc、BISumByCols、BISumByRows、BIAddCol、BIAddCondCol、BIConcatData、BIConcatRow、 BIGroupData、BIDataFormat(含 date/datetime 多种 format)、BIDataFilter、handleDataContrast、 getEval、handleValByFuncs、fullMatchReplace。
