@opengis/bi 1.0.15 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -50
- package/config.js +12 -12
- package/dist/bi.js +1 -1
- package/dist/bi.umd.cjs +63 -63
- package/dist/{import-file-CRC0sYYT.js → import-file-BqdbrKVj.js} +7591 -7587
- package/dist/{map-component-mixin-BCtWEvzv.js → map-component-mixin-BeW3TYyl.js} +2115 -3794
- package/dist/style.css +1 -1
- package/dist/{vs-calendar-5ot79n0N.js → vs-calendar-CnosX1Ss.js} +1 -1
- package/dist/{vs-funnel-bar-CLo6gXI_.js → vs-funnel-bar-CcXr5oIQ.js} +1 -1
- package/dist/{vs-heatmap-DHGA8dRk.js → vs-heatmap-DvOx7wot.js} +1 -1
- package/dist/{vs-map-pIn5wS4G.js → vs-map-C2KEv_W6.js} +2 -2
- package/dist/{vs-map-cluster-CNgX6JVF.js → vs-map-cluster-DJpEG5n8.js} +2 -2
- package/dist/{vs-number-DYfok8VU.js → vs-number-C23hqXxA.js} +1 -1
- package/dist/{vs-text-Dckykz09.js → vs-text-COR-T0yn.js} +2 -2
- package/package.json +107 -107
- package/plugin.js +14 -14
- package/server/migrations/bi.dataset.sql +26 -26
- package/server/migrations/bi.sql +93 -93
- package/server/plugins/docs.js +48 -48
- package/server/plugins/hook.js +89 -89
- package/server/plugins/vite.js +69 -69
- package/server/routes/dashboard/controllers/dashboard.delete.js +38 -38
- package/server/routes/dashboard/controllers/dashboard.js +118 -118
- package/server/routes/dashboard/controllers/dashboard.list.js +30 -30
- package/server/routes/dashboard/controllers/utils/yaml.js +11 -11
- package/server/routes/dashboard/index.mjs +25 -25
- package/server/routes/data/controllers/data.js +167 -167
- package/server/routes/data/controllers/util/chartSQL.js +42 -42
- package/server/routes/data/controllers/util/normalizeData.js +59 -59
- package/server/routes/data/index.mjs +29 -29
- package/server/routes/dataset/index.mjs +19 -19
- package/server/routes/db/controllers/dbTablePreview.js +63 -63
- package/server/routes/db/controllers/dbTables.js +36 -36
- package/server/routes/db/index.mjs +17 -17
- package/server/routes/edit/controllers/dashboard.add.js +26 -26
- package/server/routes/edit/controllers/dashboard.edit.js +46 -46
- package/server/routes/edit/controllers/widget.add.js +75 -75
- package/server/routes/edit/controllers/widget.del.js +69 -69
- package/server/routes/edit/controllers/widget.edit.js +52 -52
- package/server/routes/edit/index.mjs +31 -31
- package/server/routes/map/controllers/cluster.js +109 -109
- package/server/routes/map/controllers/clusterVtile.js +166 -166
- package/server/routes/map/controllers/geojson.js +127 -127
- package/server/routes/map/controllers/map.js +60 -60
- package/server/routes/map/controllers/utils/downloadClusterData.js +42 -42
- package/server/routes/map/controllers/vtile.js +183 -183
- package/server/routes/map/index.mjs +25 -25
- package/server/utils/getWidget.js +85 -85
- package/utils.js +12 -12
|
@@ -1,168 +1,168 @@
|
|
|
1
|
-
import yaml from 'js-yaml';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
config,
|
|
5
|
-
getPGAsync,
|
|
6
|
-
autoIndex,
|
|
7
|
-
pgClients,
|
|
8
|
-
getSelectVal,
|
|
9
|
-
getFilterSQL,
|
|
10
|
-
} from '@opengis/fastify-table/utils.js';
|
|
11
|
-
|
|
12
|
-
import chartSQL from './util/chartSQL.js';
|
|
13
|
-
import normalizeData from './util/normalizeData.js';
|
|
14
|
-
|
|
15
|
-
import { getWidget } from '../../../../utils.js';
|
|
16
|
-
|
|
17
|
-
export default async function dataAPI({
|
|
18
|
-
const time = Date.now();
|
|
19
|
-
|
|
20
|
-
query.metric = Array.isArray(query.metric) ? query.metric.pop() : query.metric;
|
|
21
|
-
|
|
22
|
-
const { dashboard, widget, filter, search, samples } = query;
|
|
23
|
-
|
|
24
|
-
const widgetData = await getWidget({ dashboard, widget });
|
|
25
|
-
|
|
26
|
-
if (widgetData.status) return widgetData;
|
|
27
|
-
|
|
28
|
-
const { type, text, data = {}, controls, style, options } = widgetData;
|
|
29
|
-
|
|
30
|
-
const pg = data.db ? await getPGAsync(data.db) : pgClients.client;
|
|
31
|
-
|
|
32
|
-
const { fields: cols } = await pg.query(
|
|
33
|
-
`select * from ${data.table} limit 0`
|
|
34
|
-
);
|
|
35
|
-
const columnTypes = cols.map((el) => ({
|
|
36
|
-
name: el.name,
|
|
37
|
-
type: pg.pgType?.[el.dataTypeID],
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
// data param
|
|
41
|
-
const { x, cls, metric, table, where, tableSQL, groupby, xName, yName, xType, yType } =
|
|
42
|
-
normalizeData(widgetData, query, columnTypes);
|
|
43
|
-
|
|
44
|
-
// auto Index
|
|
45
|
-
if (pg.pk?.[data.table]) {
|
|
46
|
-
autoIndex({
|
|
47
|
-
table: data.table,
|
|
48
|
-
pg,
|
|
49
|
-
columns: [data?.time]
|
|
50
|
-
.concat([xName])
|
|
51
|
-
.concat([groupby])
|
|
52
|
-
.filter((el) => el),
|
|
53
|
-
}).catch((err) => console.log(err));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// get group
|
|
57
|
-
const groupData = groupby
|
|
58
|
-
? await pg
|
|
59
|
-
.query(
|
|
60
|
-
`select ${groupby} as name ,count(*) from ${tableSQL || table} group by ${groupby} order by count(*) desc limit 20`
|
|
61
|
-
)
|
|
62
|
-
.then((el) => el.rows)
|
|
63
|
-
: null;
|
|
64
|
-
|
|
65
|
-
if (query.sql === '2') return { x, metric, table, tableSQL, data, groupData };
|
|
66
|
-
|
|
67
|
-
const order = data.order || (type === 'listbar' ? 'metric desc' : null);
|
|
68
|
-
|
|
69
|
-
const { optimizedSQL = `select * from ${tableSQL || table}` } =
|
|
70
|
-
filter || search
|
|
71
|
-
? await getFilterSQL({
|
|
72
|
-
pg,
|
|
73
|
-
table,
|
|
74
|
-
filter,
|
|
75
|
-
search,
|
|
76
|
-
})
|
|
77
|
-
: {};
|
|
78
|
-
|
|
79
|
-
if (type?.includes('bar') && !metric?.length) {
|
|
80
|
-
return { message: 'empty widget params: metrics', status: 400 };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const sql = (chartSQL[type] || chartSQL.chart)({
|
|
84
|
-
where,
|
|
85
|
-
metric,
|
|
86
|
-
table: `(${optimizedSQL} ${samples ? 'limit 10' : ''})q`,
|
|
87
|
-
x,
|
|
88
|
-
groupData,
|
|
89
|
-
groupby,
|
|
90
|
-
order,
|
|
91
|
-
samples,
|
|
92
|
-
xType,
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
if (query.sql) return sql;
|
|
96
|
-
|
|
97
|
-
if (!sql || sql?.includes('undefined')) {
|
|
98
|
-
return {
|
|
99
|
-
message: {
|
|
100
|
-
error: 'invalid sql',
|
|
101
|
-
type,
|
|
102
|
-
sql,
|
|
103
|
-
where,
|
|
104
|
-
metric,
|
|
105
|
-
table: `(${optimizedSQL})q`,
|
|
106
|
-
x,
|
|
107
|
-
groupData,
|
|
108
|
-
groupby,
|
|
109
|
-
},
|
|
110
|
-
status: 500,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const { rows, fields } = await pg.query(sql); // test with limit
|
|
115
|
-
|
|
116
|
-
if (cls) {
|
|
117
|
-
const values = rows
|
|
118
|
-
.map((row) => row[x])
|
|
119
|
-
?.filter((el, idx, arr) => el && arr.indexOf(el) === idx);
|
|
120
|
-
const vals = await getSelectVal({ pg, name: cls, values });
|
|
121
|
-
rows
|
|
122
|
-
.filter((row) => row[x])
|
|
123
|
-
.forEach((row) => {
|
|
124
|
-
Object.assign(row, { [x]: vals?.[row[x]] || row[x] });
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const yml = widgetData.yml || yaml.dump(extractYml(widgetData));
|
|
129
|
-
const dimensions = fields.map((el) => el.name);
|
|
130
|
-
|
|
131
|
-
const res = {
|
|
132
|
-
time: Date.now() - time,
|
|
133
|
-
dimensions,
|
|
134
|
-
filter: xName,
|
|
135
|
-
dimensionsType: [xType, yType].filter((el) => el)?.length
|
|
136
|
-
? [xType, yType].filter((el) => el)
|
|
137
|
-
: fields.map((el) => pg.pgType?.[el.dataTypeID]),
|
|
138
|
-
type,
|
|
139
|
-
|
|
140
|
-
text: text || widgetData?.title || data.text,
|
|
141
|
-
// data: query.format === 'data' ? dimensions.map(el => rows.map(r => r[el])) : undefined,
|
|
142
|
-
source:
|
|
143
|
-
query.format === 'array'
|
|
144
|
-
? dimensions.map((el) => rows.map((r) => r[el]))
|
|
145
|
-
: rows,
|
|
146
|
-
style,
|
|
147
|
-
options,
|
|
148
|
-
controls,
|
|
149
|
-
yml,
|
|
150
|
-
params: config?.local ? {
|
|
151
|
-
x,
|
|
152
|
-
cls,
|
|
153
|
-
metric,
|
|
154
|
-
table,
|
|
155
|
-
tableSQL,
|
|
156
|
-
where,
|
|
157
|
-
groupby,
|
|
158
|
-
sql,
|
|
159
|
-
} : undefined,
|
|
160
|
-
columnTypes,
|
|
161
|
-
};
|
|
162
|
-
return res;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function extractYml(sourceData) {
|
|
166
|
-
const { title, description, type, data, style, controls } = sourceData;
|
|
167
|
-
return { title, description, type, data, style, controls };
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
config,
|
|
5
|
+
getPGAsync,
|
|
6
|
+
autoIndex,
|
|
7
|
+
pgClients,
|
|
8
|
+
getSelectVal,
|
|
9
|
+
getFilterSQL,
|
|
10
|
+
} from '@opengis/fastify-table/utils.js';
|
|
11
|
+
|
|
12
|
+
import chartSQL from './util/chartSQL.js';
|
|
13
|
+
import normalizeData from './util/normalizeData.js';
|
|
14
|
+
|
|
15
|
+
import { getWidget } from '../../../../utils.js';
|
|
16
|
+
|
|
17
|
+
export default async function dataAPI({ query = {} }) {
|
|
18
|
+
const time = Date.now();
|
|
19
|
+
|
|
20
|
+
query.metric = Array.isArray(query.metric) ? query.metric.pop() : query.metric;
|
|
21
|
+
|
|
22
|
+
const { dashboard, widget, filter, search, samples } = query;
|
|
23
|
+
|
|
24
|
+
const widgetData = await getWidget({ dashboard, widget });
|
|
25
|
+
|
|
26
|
+
if (widgetData.status) return widgetData;
|
|
27
|
+
|
|
28
|
+
const { type, text, data = {}, controls, style, options } = widgetData;
|
|
29
|
+
|
|
30
|
+
const pg = data.db ? await getPGAsync(data.db) : pgClients.client;
|
|
31
|
+
|
|
32
|
+
const { fields: cols } = await pg.query(
|
|
33
|
+
`select * from ${data.table} limit 0`
|
|
34
|
+
);
|
|
35
|
+
const columnTypes = cols.map((el) => ({
|
|
36
|
+
name: el.name,
|
|
37
|
+
type: pg.pgType?.[el.dataTypeID],
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
// data param
|
|
41
|
+
const { x, cls, metric, table, where, tableSQL, groupby, xName, yName, xType, yType } =
|
|
42
|
+
normalizeData(widgetData, query, columnTypes);
|
|
43
|
+
|
|
44
|
+
// auto Index
|
|
45
|
+
if (pg.pk?.[data.table]) {
|
|
46
|
+
autoIndex({
|
|
47
|
+
table: data.table,
|
|
48
|
+
pg,
|
|
49
|
+
columns: [data?.time]
|
|
50
|
+
.concat([xName])
|
|
51
|
+
.concat([groupby])
|
|
52
|
+
.filter((el) => el),
|
|
53
|
+
}).catch((err) => console.log(err));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// get group
|
|
57
|
+
const groupData = groupby
|
|
58
|
+
? await pg
|
|
59
|
+
.query(
|
|
60
|
+
`select ${groupby} as name ,count(*) from ${tableSQL || table} group by ${groupby} order by count(*) desc limit 20`
|
|
61
|
+
)
|
|
62
|
+
.then((el) => el.rows)
|
|
63
|
+
: null;
|
|
64
|
+
|
|
65
|
+
if (query.sql === '2') return { x, metric, table, tableSQL, data, groupData };
|
|
66
|
+
|
|
67
|
+
const order = data.order || (type === 'listbar' ? 'metric desc' : null);
|
|
68
|
+
|
|
69
|
+
const { optimizedSQL = `select * from ${tableSQL || table}` } =
|
|
70
|
+
filter || search
|
|
71
|
+
? await getFilterSQL({
|
|
72
|
+
pg,
|
|
73
|
+
table,
|
|
74
|
+
filter,
|
|
75
|
+
search,
|
|
76
|
+
})
|
|
77
|
+
: {};
|
|
78
|
+
|
|
79
|
+
if (type?.includes('bar') && !metric?.length) {
|
|
80
|
+
return { message: 'empty widget params: metrics', status: 400 };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const sql = (chartSQL[type] || chartSQL.chart)({
|
|
84
|
+
where,
|
|
85
|
+
metric,
|
|
86
|
+
table: `(${optimizedSQL} ${samples ? 'limit 10' : ''})q`,
|
|
87
|
+
x,
|
|
88
|
+
groupData,
|
|
89
|
+
groupby,
|
|
90
|
+
order,
|
|
91
|
+
samples,
|
|
92
|
+
xType,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (query.sql) return sql;
|
|
96
|
+
|
|
97
|
+
if (!sql || sql?.includes('undefined')) {
|
|
98
|
+
return {
|
|
99
|
+
message: {
|
|
100
|
+
error: 'invalid sql',
|
|
101
|
+
type,
|
|
102
|
+
sql,
|
|
103
|
+
where,
|
|
104
|
+
metric,
|
|
105
|
+
table: `(${optimizedSQL})q`,
|
|
106
|
+
x,
|
|
107
|
+
groupData,
|
|
108
|
+
groupby,
|
|
109
|
+
},
|
|
110
|
+
status: 500,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const { rows, fields } = await pg.query(sql); // test with limit
|
|
115
|
+
|
|
116
|
+
if (cls) {
|
|
117
|
+
const values = rows
|
|
118
|
+
.map((row) => row[x])
|
|
119
|
+
?.filter((el, idx, arr) => el && arr.indexOf(el) === idx);
|
|
120
|
+
const vals = await getSelectVal({ pg, name: cls, values });
|
|
121
|
+
rows
|
|
122
|
+
.filter((row) => row[x])
|
|
123
|
+
.forEach((row) => {
|
|
124
|
+
Object.assign(row, { [x]: vals?.[row[x]] || row[x] });
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const yml = widgetData.yml || yaml.dump(extractYml(widgetData));
|
|
129
|
+
const dimensions = fields.map((el) => el.name);
|
|
130
|
+
|
|
131
|
+
const res = {
|
|
132
|
+
time: Date.now() - time,
|
|
133
|
+
dimensions,
|
|
134
|
+
filter: xName,
|
|
135
|
+
dimensionsType: [xType, yType].filter((el) => el)?.length
|
|
136
|
+
? [xType, yType].filter((el) => el)
|
|
137
|
+
: fields.map((el) => pg.pgType?.[el.dataTypeID]),
|
|
138
|
+
type,
|
|
139
|
+
|
|
140
|
+
text: text || widgetData?.title || data.text,
|
|
141
|
+
// data: query.format === 'data' ? dimensions.map(el => rows.map(r => r[el])) : undefined,
|
|
142
|
+
source:
|
|
143
|
+
query.format === 'array'
|
|
144
|
+
? dimensions.map((el) => rows.map((r) => r[el]))
|
|
145
|
+
: rows,
|
|
146
|
+
style,
|
|
147
|
+
options,
|
|
148
|
+
controls,
|
|
149
|
+
yml,
|
|
150
|
+
params: config?.local ? {
|
|
151
|
+
x,
|
|
152
|
+
cls,
|
|
153
|
+
metric,
|
|
154
|
+
table,
|
|
155
|
+
tableSQL,
|
|
156
|
+
where,
|
|
157
|
+
groupby,
|
|
158
|
+
sql,
|
|
159
|
+
} : undefined,
|
|
160
|
+
columnTypes,
|
|
161
|
+
};
|
|
162
|
+
return res;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function extractYml(sourceData) {
|
|
166
|
+
const { title, description, type, data, style, controls } = sourceData;
|
|
167
|
+
return { title, description, type, data, style, controls };
|
|
168
168
|
}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
function number({ metric, where, table, samples }) {
|
|
2
|
-
const sql = `select ${metric} from ${table} where ${where} ${samples ? 'limit 10' : ''}`;
|
|
3
|
-
return sql;
|
|
4
|
-
}
|
|
5
|
-
function table({ columns, table, where, samples }) {
|
|
6
|
-
return `select ${columns.map((el) => el.name || el)}::text from ${table} where ${where} ${samples ? 'limit 10' : 'limit 20'} `;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function chart({
|
|
10
|
-
metric,
|
|
11
|
-
where,
|
|
12
|
-
table,
|
|
13
|
-
x,
|
|
14
|
-
groupby,
|
|
15
|
-
groupData,
|
|
16
|
-
order,
|
|
17
|
-
samples,
|
|
18
|
-
xType,
|
|
19
|
-
}) {
|
|
20
|
-
const xCol = x && xType?.includes('[]') ? `unnest(${x})` : x;
|
|
21
|
-
|
|
22
|
-
const metricData =
|
|
23
|
-
groupData
|
|
24
|
-
?.map(
|
|
25
|
-
(el) =>
|
|
26
|
-
`${metric} filter (where ${groupby}='${el.name}') as "${el.name}"`
|
|
27
|
-
)
|
|
28
|
-
.join(',') || `${metric} as metric`;
|
|
29
|
-
const sql = `select ${xCol}, ${metricData}
|
|
30
|
-
from ${table}
|
|
31
|
-
where ${where}
|
|
32
|
-
${xCol ? `group by ${xCol}` : ''}
|
|
33
|
-
${order || xCol ? `order by ${order || xCol}` : ''}
|
|
34
|
-
${samples ? 'limit 10' : 'limit 100'}`;
|
|
35
|
-
return sql;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function text() {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export default { number, chart };
|
|
1
|
+
function number({ metric, where, table, samples }) {
|
|
2
|
+
const sql = `select ${metric} from ${table} where ${where} ${samples ? 'limit 10' : ''}`;
|
|
3
|
+
return sql;
|
|
4
|
+
}
|
|
5
|
+
function table({ columns, table, where, samples }) {
|
|
6
|
+
return `select ${columns.map((el) => el.name || el)}::text from ${table} where ${where} ${samples ? 'limit 10' : 'limit 20'} `;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function chart({
|
|
10
|
+
metric,
|
|
11
|
+
where,
|
|
12
|
+
table,
|
|
13
|
+
x,
|
|
14
|
+
groupby,
|
|
15
|
+
groupData,
|
|
16
|
+
order,
|
|
17
|
+
samples,
|
|
18
|
+
xType,
|
|
19
|
+
}) {
|
|
20
|
+
const xCol = x && xType?.includes('[]') ? `unnest(${x})` : x;
|
|
21
|
+
|
|
22
|
+
const metricData =
|
|
23
|
+
groupData
|
|
24
|
+
?.map(
|
|
25
|
+
(el) =>
|
|
26
|
+
`${metric} filter (where ${groupby}='${el.name}') as "${el.name}"`
|
|
27
|
+
)
|
|
28
|
+
.join(',') || `${metric} as metric`;
|
|
29
|
+
const sql = `select ${xCol}, ${metricData}
|
|
30
|
+
from ${table}
|
|
31
|
+
where ${where}
|
|
32
|
+
${xCol ? `group by ${xCol}` : ''}
|
|
33
|
+
${order || xCol ? `order by ${order || xCol}` : ''}
|
|
34
|
+
${samples ? 'limit 10' : 'limit 100'}`;
|
|
35
|
+
return sql;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function text() {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default { number, chart };
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
function normalizeData(data, query = {}, columnTypes = []) {
|
|
2
|
-
['x', 'groupby', 'granularity'].forEach((el) => {
|
|
3
|
-
// console.log(el, query[el], columnTypes.find(col => col.name == query[el]))
|
|
4
|
-
if (!columnTypes.find((col) => col.name == query[el])) {
|
|
5
|
-
delete query[el];
|
|
6
|
-
}
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
if (
|
|
10
|
-
!columnTypes.find(
|
|
11
|
-
(col) => col.type === 'numeric' && col.name == query.metric
|
|
12
|
-
)
|
|
13
|
-
) {
|
|
14
|
-
delete query.metric;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const xName = query.x || (Array.isArray(data.x) ? data.x[0] : data.x);
|
|
18
|
-
const xType = columnTypes.find((el) => el.name == xName)?.type;
|
|
19
|
-
|
|
20
|
-
const granularity =
|
|
21
|
-
xType === 'date' || xType?.includes('timestamp')
|
|
22
|
-
? query.granularity || data.granularity || 'year'
|
|
23
|
-
: null;
|
|
24
|
-
|
|
25
|
-
const x =
|
|
26
|
-
(granularity
|
|
27
|
-
? `date_trunc('${granularity}',${xName})::date::text`
|
|
28
|
-
: null) || xName;
|
|
29
|
-
|
|
30
|
-
const metrics = Array.isArray(data.metrics) ? data.metrics : [data.metrics];
|
|
31
|
-
const metric =
|
|
32
|
-
(query.metric ? `sum(${query.metric})` : null) ||
|
|
33
|
-
(metrics.length
|
|
34
|
-
? (metrics
|
|
35
|
-
?.filter((el) => el && columnTypes.find((col) => col.name == (el?.name || el)))
|
|
36
|
-
?.map((el) => el.fx || `${el.operator || 'sum'}(${el.name || el})`)?.join(',') || 'count(*)')
|
|
37
|
-
: 'count(*)');
|
|
38
|
-
|
|
39
|
-
const yName = metrics?.[0]?.name || metrics?.[0];
|
|
40
|
-
const yType = columnTypes.find((el) => el.name == yName)?.type;
|
|
41
|
-
|
|
42
|
-
const { cls, table, filterCustom } = data;
|
|
43
|
-
const groupby = query.groupby || data.groupby;
|
|
44
|
-
// const orderby = query.orderby || data.orderby || 'count(*)';
|
|
45
|
-
|
|
46
|
-
const custom = query?.filterCustom
|
|
47
|
-
?.split(',')
|
|
48
|
-
?.map((el) => filterCustom?.find((item) => item?.name === el)?.sql)
|
|
49
|
-
?.filter((el) => el)
|
|
50
|
-
?.join(' and ');
|
|
51
|
-
const where = `${data.query || '1=1'} and ${custom || 'true'}`;
|
|
52
|
-
|
|
53
|
-
const tableSQL = data.tableSQL?.length
|
|
54
|
-
? `(select * from ${data?.table} t ${data.tableSQL.join(' \n ')} where ${where})q`
|
|
55
|
-
: undefined;
|
|
56
|
-
|
|
57
|
-
return { x, cls, metric, table, where, tableSQL, groupby, xName, xType, yName, yType };
|
|
58
|
-
}
|
|
59
|
-
export default normalizeData;
|
|
1
|
+
function normalizeData(data, query = {}, columnTypes = []) {
|
|
2
|
+
['x', 'groupby', 'granularity'].forEach((el) => {
|
|
3
|
+
// console.log(el, query[el], columnTypes.find(col => col.name == query[el]))
|
|
4
|
+
if (!columnTypes.find((col) => col.name == query[el])) {
|
|
5
|
+
delete query[el];
|
|
6
|
+
}
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
if (
|
|
10
|
+
!columnTypes.find(
|
|
11
|
+
(col) => col.type === 'numeric' && col.name == query.metric
|
|
12
|
+
)
|
|
13
|
+
) {
|
|
14
|
+
delete query.metric;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const xName = query.x || (Array.isArray(data.x) ? data.x[0] : data.x);
|
|
18
|
+
const xType = columnTypes.find((el) => el.name == xName)?.type;
|
|
19
|
+
|
|
20
|
+
const granularity =
|
|
21
|
+
xType === 'date' || xType?.includes('timestamp')
|
|
22
|
+
? query.granularity || data.granularity || 'year'
|
|
23
|
+
: null;
|
|
24
|
+
|
|
25
|
+
const x =
|
|
26
|
+
(granularity
|
|
27
|
+
? `date_trunc('${granularity}',${xName})::date::text`
|
|
28
|
+
: null) || xName;
|
|
29
|
+
|
|
30
|
+
const metrics = Array.isArray(data.metrics) ? data.metrics : [data.metrics];
|
|
31
|
+
const metric =
|
|
32
|
+
(query.metric ? `sum(${query.metric})` : null) ||
|
|
33
|
+
(metrics.length
|
|
34
|
+
? (metrics
|
|
35
|
+
?.filter((el) => el && columnTypes.find((col) => col.name == (el?.name || el)))
|
|
36
|
+
?.map((el) => el.fx || `${el.operator || 'sum'}(${el.name || el})`)?.join(',') || 'count(*)')
|
|
37
|
+
: 'count(*)');
|
|
38
|
+
|
|
39
|
+
const yName = metrics?.[0]?.name || metrics?.[0];
|
|
40
|
+
const yType = columnTypes.find((el) => el.name == yName)?.type;
|
|
41
|
+
|
|
42
|
+
const { cls, table, filterCustom } = data;
|
|
43
|
+
const groupby = query.groupby || data.groupby;
|
|
44
|
+
// const orderby = query.orderby || data.orderby || 'count(*)';
|
|
45
|
+
|
|
46
|
+
const custom = query?.filterCustom
|
|
47
|
+
?.split(',')
|
|
48
|
+
?.map((el) => filterCustom?.find((item) => item?.name === el)?.sql)
|
|
49
|
+
?.filter((el) => el)
|
|
50
|
+
?.join(' and ');
|
|
51
|
+
const where = `${data.query || '1=1'} and ${custom || 'true'}`;
|
|
52
|
+
|
|
53
|
+
const tableSQL = data.tableSQL?.length
|
|
54
|
+
? `(select * from ${data?.table} t ${data.tableSQL.join(' \n ')} where ${where})q`
|
|
55
|
+
: undefined;
|
|
56
|
+
|
|
57
|
+
return { x, cls, metric, table, where, tableSQL, groupby, xName, xType, yName, yType };
|
|
58
|
+
}
|
|
59
|
+
export default normalizeData;
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import config from '../../../config.js';
|
|
2
|
-
|
|
3
|
-
import data from './controllers/data.js';
|
|
4
|
-
|
|
5
|
-
const biSchema = {
|
|
6
|
-
querystring: {
|
|
7
|
-
widget: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
8
|
-
dashboard: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
9
|
-
sql: { type: 'string', pattern: '^([\\d])$' },
|
|
10
|
-
// metric: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
11
|
-
x: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
12
|
-
granularity: { type: 'string', pattern: '^(week|month|quarter|year)$' },
|
|
13
|
-
groupby: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
14
|
-
filterCustom: { type: 'string', pattern: '^([\\d\\w_,]+)$' },
|
|
15
|
-
},
|
|
16
|
-
params: {
|
|
17
|
-
id: { type: 'string', pattern: '^([\\d\\w]+)$' },
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export default async function route(fastify, opts) {
|
|
22
|
-
const prefix = opts?.prefix || config.prefix || '/api';
|
|
23
|
-
fastify.route({
|
|
24
|
-
method: 'GET',
|
|
25
|
-
url: '/bi-data',
|
|
26
|
-
schema: biSchema,
|
|
27
|
-
handler: data,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
1
|
+
import config from '../../../config.js';
|
|
2
|
+
|
|
3
|
+
import data from './controllers/data.js';
|
|
4
|
+
|
|
5
|
+
const biSchema = {
|
|
6
|
+
querystring: {
|
|
7
|
+
widget: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
8
|
+
dashboard: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
9
|
+
sql: { type: 'string', pattern: '^([\\d])$' },
|
|
10
|
+
// metric: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
11
|
+
x: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
12
|
+
granularity: { type: 'string', pattern: '^(week|month|quarter|year)$' },
|
|
13
|
+
groupby: { type: 'string', pattern: '^([\\d\\w_]+)$' },
|
|
14
|
+
filterCustom: { type: 'string', pattern: '^([\\d\\w_,]+)$' },
|
|
15
|
+
},
|
|
16
|
+
params: {
|
|
17
|
+
id: { type: 'string', pattern: '^([\\d\\w]+)$' },
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default async function route(fastify, opts) {
|
|
22
|
+
const prefix = opts?.prefix || config.prefix || '/api';
|
|
23
|
+
fastify.route({
|
|
24
|
+
method: 'GET',
|
|
25
|
+
url: '/bi-data',
|
|
26
|
+
schema: biSchema,
|
|
27
|
+
handler: data,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import biDatasetPost from './controllers/bi.dataset.import.js';
|
|
2
|
-
import biDatasetDemoAdd from './controllers/bi.dataset.demo.add.js';
|
|
3
|
-
|
|
4
|
-
const biSchema = {};
|
|
5
|
-
|
|
6
|
-
export default async function route(fastify, opts) {
|
|
7
|
-
fastify.route({
|
|
8
|
-
method: 'POST',
|
|
9
|
-
url: '/bi-dataset/:id',
|
|
10
|
-
schema: biSchema,
|
|
11
|
-
handler: biDatasetPost,
|
|
12
|
-
});
|
|
13
|
-
fastify.route({
|
|
14
|
-
method: 'GET',
|
|
15
|
-
url: '/bi-dataset-demo',
|
|
16
|
-
schema: biSchema,
|
|
17
|
-
handler: biDatasetDemoAdd,
|
|
18
|
-
});
|
|
19
|
-
}
|
|
1
|
+
import biDatasetPost from './controllers/bi.dataset.import.js';
|
|
2
|
+
import biDatasetDemoAdd from './controllers/bi.dataset.demo.add.js';
|
|
3
|
+
|
|
4
|
+
const biSchema = {};
|
|
5
|
+
|
|
6
|
+
export default async function route(fastify, opts) {
|
|
7
|
+
fastify.route({
|
|
8
|
+
method: 'POST',
|
|
9
|
+
url: '/bi-dataset/:id',
|
|
10
|
+
schema: biSchema,
|
|
11
|
+
handler: biDatasetPost,
|
|
12
|
+
});
|
|
13
|
+
fastify.route({
|
|
14
|
+
method: 'GET',
|
|
15
|
+
url: '/bi-dataset-demo',
|
|
16
|
+
schema: biSchema,
|
|
17
|
+
handler: biDatasetDemoAdd,
|
|
18
|
+
});
|
|
19
|
+
}
|