@opengis/bi 1.2.30 → 1.2.32

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.
Files changed (75) hide show
  1. package/README.md +92 -92
  2. package/dist/bi.js +1 -1
  3. package/dist/bi.umd.cjs +88 -86
  4. package/dist/{import-file-B4o9p2_2.js → import-file-D-ISqB7l.js} +1772 -1593
  5. package/dist/style.css +1 -1
  6. package/dist/{vs-funnel-bar-BOWwPnjW.js → vs-funnel-bar-aoZzvriV.js} +3 -3
  7. package/dist/{vs-list-D8jGusRT.js → vs-list-CBkyJSBj.js} +53 -32
  8. package/dist/{vs-map-BGplOwpB.js → vs-map-C3C11qmT.js} +102 -91
  9. package/dist/{vs-map-cluster-CHQJV2As.js → vs-map-cluster-BsPmHIMx.js} +91 -81
  10. package/dist/{vs-number-jPqxFQ6d.js → vs-number-d58ftpH5.js} +3 -3
  11. package/dist/{vs-table-BDgIvJbY.js → vs-table-BHa5Velm.js} +6 -6
  12. package/dist/{vs-text-DcHOffy9.js → vs-text-Bq87gMTx.js} +4 -4
  13. package/package.json +77 -75
  14. package/plugin.js +21 -22
  15. package/server/helpers/mdToHTML.js +17 -17
  16. package/server/migrations/bi.dataset.sql +46 -46
  17. package/server/migrations/bi.sql +115 -112
  18. package/server/plugins/docs.js +48 -48
  19. package/server/plugins/hook.js +89 -89
  20. package/server/plugins/vite.js +81 -81
  21. package/server/routes/dashboard/controllers/dashboard.import.js +103 -103
  22. package/server/routes/dashboard/controllers/dashboard.js +158 -157
  23. package/server/routes/dashboard/controllers/dashboard.list.js +60 -60
  24. package/server/routes/dashboard/controllers/utils/yaml.js +11 -11
  25. package/server/routes/dashboard/index.mjs +26 -26
  26. package/server/routes/data/controllers/data.js +230 -229
  27. package/server/routes/data/controllers/util/chartSQL.js +49 -49
  28. package/server/routes/data/controllers/util/normalizeData.js +65 -65
  29. package/server/routes/data/index.mjs +38 -38
  30. package/server/routes/dataset/controllers/bi.dataset.list.js +29 -29
  31. package/server/routes/dataset/controllers/bi.db.list.js +19 -19
  32. package/server/routes/dataset/controllers/comment.js +55 -55
  33. package/server/routes/dataset/controllers/createDatasetPost.js +134 -134
  34. package/server/routes/dataset/controllers/data.js +149 -149
  35. package/server/routes/dataset/controllers/dbTablePreview.js +58 -58
  36. package/server/routes/dataset/controllers/dbTables.js +34 -34
  37. package/server/routes/dataset/controllers/delete.js +40 -40
  38. package/server/routes/dataset/controllers/deleteDataset.js +52 -52
  39. package/server/routes/dataset/controllers/editDataset.js +90 -90
  40. package/server/routes/dataset/controllers/export.js +214 -214
  41. package/server/routes/dataset/controllers/form.js +99 -99
  42. package/server/routes/dataset/controllers/format.js +46 -46
  43. package/server/routes/dataset/controllers/insert.js +47 -47
  44. package/server/routes/dataset/controllers/table.js +68 -68
  45. package/server/routes/dataset/controllers/update.js +43 -43
  46. package/server/routes/dataset/index.mjs +132 -132
  47. package/server/routes/dataset/utils/convertJSONToCSV.js +17 -17
  48. package/server/routes/dataset/utils/convertJSONToXls.js +47 -47
  49. package/server/routes/dataset/utils/createTableQuery.js +59 -59
  50. package/server/routes/dataset/utils/datasetForms.js +1 -1
  51. package/server/routes/dataset/utils/descriptionList.js +45 -45
  52. package/server/routes/dataset/utils/downloadRemoteFile.js +58 -58
  53. package/server/routes/dataset/utils/executeQuery.js +46 -46
  54. package/server/routes/dataset/utils/getLayersData.js +106 -106
  55. package/server/routes/dataset/utils/getTableData.js +46 -46
  56. package/server/routes/dataset/utils/insertDataQuery.js +12 -12
  57. package/server/routes/dataset/utils/metaFormat.js +24 -24
  58. package/server/routes/edit/controllers/dashboard.add.js +36 -36
  59. package/server/routes/edit/controllers/dashboard.delete.js +39 -39
  60. package/server/routes/edit/controllers/dashboard.edit.js +61 -61
  61. package/server/routes/edit/controllers/widget.add.js +78 -78
  62. package/server/routes/edit/controllers/widget.del.js +58 -58
  63. package/server/routes/edit/controllers/widget.edit.js +115 -115
  64. package/server/routes/edit/index.mjs +33 -33
  65. package/server/routes/map/controllers/cluster.js +125 -125
  66. package/server/routes/map/controllers/clusterVtile.js +166 -166
  67. package/server/routes/map/controllers/geojson.js +127 -127
  68. package/server/routes/map/controllers/heatmap.js +118 -118
  69. package/server/routes/map/controllers/map.js +69 -69
  70. package/server/routes/map/controllers/utils/downloadClusterData.js +44 -44
  71. package/server/routes/map/controllers/vtile.js +183 -183
  72. package/server/routes/map/index.mjs +32 -32
  73. package/server/templates/page/login.html +58 -58
  74. package/server/utils/getWidget.js +118 -117
  75. package/utils.js +12 -12
@@ -1,230 +1,231 @@
1
- import yaml from 'js-yaml';
2
-
3
- import {
4
- config,
5
- autoIndex,
6
- pgClients,
7
- getSelect,
8
- getSelectVal,
9
- getFilterSQL,
10
- getMeta,
11
- logger,
12
- } from '@opengis/fastify-table/utils.js';
13
-
14
- import chartSQL from './util/chartSQL.js';
15
- import normalizeData from './util/normalizeData.js';
16
-
17
- import { getWidget } from '../../../../utils.js';
18
-
19
- const maxLimit = 100;
20
-
21
- export default async function dataAPI(req, reply) {
22
- const time = Date.now();
23
-
24
- const { query = {}, user = {}, unittest } = req;
25
-
26
- const { dashboard, widget, filter, search, samples } = query;
27
-
28
- const widgetData = await getWidget({ pg: req.pg, dashboard, widget });
29
-
30
- query.metric = Array.isArray(query.metric) ? query.metric.pop() : query.metric || widgetData?.metric || widgetData?.data?.metrics?.[0]?.fx;
31
-
32
- if (widgetData.status) return widgetData;
33
-
34
- const { type, text, data = {}, controls, style, options } = widgetData;
35
-
36
- const pg = widgetData.pg || req.pg || pgClients.client;
37
-
38
- const error1 = {};
39
- const { fields: cols = [] } = await pg.query(
40
- `select * from ${data.table} t ${widgetData.tableSQL || data.tableSQL || ''} limit 0`
41
- ).catch(err => Object.assign(error1, { error: err.toString() })) || {};
42
- const columnTypes = cols?.map?.((el) => ({
43
- name: el.name,
44
- type: pg.pgType?.[el.dataTypeID],
45
- }));
46
-
47
- // data param
48
- const { x, cls, groupbyCls, metric, table, where, tableSQL, groupby, xName, yName, xType, yType, error = error1 } =
49
- normalizeData(widgetData, query, columnTypes);
50
-
51
- const limit = Math.min(query.limit || widgetData.limit || maxLimit, maxLimit);
52
- // if (error) { return reply.status(400).send(error); }
53
-
54
- // auto Index
55
- if (pg.pk?.[data.table]) {
56
- autoIndex({
57
- table: data.table,
58
- pg,
59
- columns: [data?.time]
60
- .concat([xName])
61
- .concat([groupby])
62
- .filter((el) => el),
63
- }).catch((err) => console.log(err));
64
- }
65
-
66
- const { pk, columns = [], view } = await getMeta({ pg, table: data.table });
67
-
68
- if (!view && !pk) {
69
- return { message: `table not found: ${data.table} (${pg.options?.database})`, status: 404 };
70
- }
71
-
72
- // const columnList = columns.map(col => col.name);
73
- const groupbyColumnNotExists = groupby?.split?.(',')?.filter?.(el => !columnTypes.map(el => el.name).includes(el.trim()));
74
-
75
- if (groupby && groupbyColumnNotExists?.length) {
76
- return { message: `groupby column not found: ${groupbyColumnNotExists} (${data.table}/${pg.options?.database})`, status: 404 };
77
- }
78
-
79
- // get group
80
- const groupData = groupby
81
- ? await pg
82
- .query(
83
- `select ${groupby} as name ,count(*) from ${tableSQL || table} group by ${groupby} order by count(*) desc limit ${limit}`
84
- )
85
- .then((el) => el.rows)
86
- : null;
87
-
88
- if (query.sql === '2') return { x, metric, table, tableSQL, data, groupData };
89
-
90
- const order = data.order || (type === 'listbar' && cols.find(el => el.name === 'metric') ? 'metric desc' : null);
91
-
92
- const fData =
93
- filter || search
94
- ? await getFilterSQL({
95
- pg,
96
- table,
97
- filter,
98
- search,
99
- filterList: widgetData.filters,
100
- })
101
- : {};
102
-
103
- const optimizedSQL = widgetData?.sql
104
- ? `${widgetData.sql} ${fData?.q && false ? fData?.q : ''} limit ${limit}`
105
- : (fData?.optimizedSQL || `select * from ${tableSQL || table}`);
106
-
107
- if (type?.includes('bar') && !metric?.length) {
108
- return { message: 'empty widget params: metrics', status: 400 };
109
- }
110
-
111
- const sql = widgetData.sql ? optimizedSQL : (chartSQL[type] || chartSQL.chart)({
112
- where: config.local && user?.user_type === 'superadmin' ? 'true' : where, // test
113
- metric,
114
- yType, // metric type
115
- columns: widgetData.columns,
116
- table: `(${optimizedSQL})q`,
117
- x,
118
- groupData,
119
- groupby,
120
- order,
121
- samples,
122
- limit,
123
- xType,
124
- fx: widgetData.fx,
125
- });
126
-
127
- if (query.sql) return sql;
128
-
129
- if (!sql || sql?.includes('undefined')) {
130
- return {
131
- message: {
132
- error: 'invalid sql',
133
- type,
134
- sql,
135
- where,
136
- metric,
137
- table: `(${optimizedSQL})q`,
138
- x,
139
- groupData,
140
- groupby,
141
- },
142
- status: 500,
143
- };
144
- }
145
-
146
- if (config.trace) console.log(sql, user?.uid);
147
-
148
- const { rows = [], fields = [], errorSql } = await pg.query(sql.replace('{{uid}}', user?.uid)).catch(err => {
149
- logger.file('bi/data', { error: err.toString(), sql });
150
- return { errorSql: err.toString() };
151
- }); // test with limit
152
-
153
- if (groupbyCls) {
154
- const { arr = [] } = await getSelect(groupbyCls, pg) || {};
155
- if (arr.length) {
156
- const ids = arr.map(el => el.id);
157
- const text = arr.reduce((acc, curr) => ({ ...acc, [curr.id]: curr.text }), {});
158
- rows.forEach(row => {
159
- ids.reduce((acc, curr) => {
160
- Object.assign(row, { [text[curr]]: row[curr] });
161
- delete row[curr];
162
- return acc;
163
- }, {});
164
- });
165
- }
166
- }
167
-
168
- if (cls) {
169
- const values = rows
170
- .map((row) => row[x])
171
- ?.filter((el, idx, arr) => el && arr.indexOf(el) === idx);
172
- const vals = await getSelectVal({ pg, name: cls, values });
173
- rows
174
- .filter((row) => row[x])
175
- .forEach((row) => {
176
- Object.assign(row, { [x]: vals?.[row[x]]?.text || vals?.[row[x]] || row[x] });
177
- });
178
- }
179
-
180
- const metaTitles = columns.reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.title || curr.ua }), {});
181
- const titles = Array.isArray(widgetData?.columns)
182
- ? widgetData.columns.reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.title || curr.ua }), {})
183
- : Object.keys(widgetData?.columns || {}).reduce((acc, curr) => Object.assign(acc, { [curr]: widgetData?.columns?.[curr] }), {});
184
-
185
- const rows1 = type === 'table' ? rows.map(row => Object.keys(row || {}).reduce((acc, curr) => Object.assign(acc, { [titles?.[curr] || metaTitles?.[curr] || curr]: row?.[curr] }), {})) : rows;
186
-
187
- const yml = widgetData.yml || yaml.dump(extractYml(widgetData));
188
- const dimensions = fields.map((el) => el.name);
189
-
190
- const res = {
191
- time: Date.now() - time,
192
- error: error || errorSql || (!widgetData.sql ? widgetData.error : undefined),
193
- dimensions,
194
- filter: xName,
195
- dimensionsType: [xType, yType].filter((el) => el)?.length
196
- ? [xType, yType].filter((el) => el)
197
- : fields.map((el) => pg.pgType?.[el.dataTypeID]),
198
- type,
199
-
200
- text: text || widgetData?.title || data.text,
201
- // data: query.format === 'data' ? dimensions.map(el => rows.map(r => r[el])) : undefined,
202
- source:
203
- query.format === 'array'
204
- ? dimensions.map((el) => rows1.map((r) => r[el]))
205
- : rows1,
206
- style,
207
- options,
208
- controls,
209
- yml,
210
- data: widgetData.data,
211
- id: query.widget,
212
- columns: columnTypes.map(el => Object.assign(el, { title: titles[el.name] || metaTitles?.[el.name] || el.name })),
213
- params: config?.local || unittest ? {
214
- x,
215
- cls,
216
- metric,
217
- table,
218
- tableSQL,
219
- where,
220
- groupby,
221
- sql,
222
- } : undefined,
223
- };
224
- return res;
225
- }
226
-
227
- function extractYml(sourceData) {
228
- const { title, description, type, data, style, controls } = sourceData;
229
- return { title, description, type, data, style, controls };
1
+ import yaml from 'js-yaml';
2
+
3
+ import {
4
+ config,
5
+ autoIndex,
6
+ pgClients,
7
+ getSelect,
8
+ getSelectVal,
9
+ getFilterSQL,
10
+ getMeta,
11
+ logger,
12
+ } from '@opengis/fastify-table/utils.js';
13
+
14
+ import chartSQL from './util/chartSQL.js';
15
+ import normalizeData from './util/normalizeData.js';
16
+
17
+ import { getWidget } from '../../../../utils.js';
18
+
19
+ const maxLimit = 100;
20
+
21
+ export default async function dataAPI(req, reply) {
22
+ const time = Date.now();
23
+
24
+ const { query = {}, user = {}, unittest } = req;
25
+
26
+ const { dashboard, widget, filter, search, samples } = query;
27
+
28
+ const widgetData = await getWidget({ pg: req.pg, dashboard, widget });
29
+
30
+ query.metric = Array.isArray(query.metric) ? query.metric.pop() : query.metric || widgetData?.metric || widgetData?.data?.metrics?.[0]?.fx;
31
+
32
+ if (widgetData.status) return widgetData;
33
+
34
+ const { type, text, data = {}, controls, style, options } = widgetData;
35
+
36
+ const pg = widgetData.pg || req.pg || pgClients.client;
37
+
38
+ const error1 = {};
39
+ const { fields: cols = [] } = await pg.query(
40
+ `select * from ${data.table} t ${widgetData.tableSQL || data.tableSQL || ''} limit 0`
41
+ ).catch(err => Object.assign(error1, { error: err.toString() })) || {};
42
+ const columnTypes = cols?.map?.((el) => ({
43
+ name: el.name,
44
+ type: pg.pgType?.[el.dataTypeID],
45
+ }));
46
+
47
+ // data param
48
+ const { x, cls, groupbyCls, metric, table, where, tableSQL, groupby, xName, yName, xType, yType, error = error1 } =
49
+ normalizeData(widgetData, query, columnTypes);
50
+
51
+ const limit = Math.min(query.limit || widgetData.limit || maxLimit, maxLimit);
52
+ // if (error) { return reply.status(400).send(error); }
53
+
54
+ // auto Index
55
+ if (pg.pk?.[data.table]) {
56
+ autoIndex({
57
+ table: data.table,
58
+ pg,
59
+ columns: [data?.time]
60
+ .concat([xName])
61
+ .concat([groupby])
62
+ .filter((el) => el),
63
+ }).catch((err) => console.log(err));
64
+ }
65
+
66
+ const { pk, columns = [], view } = await getMeta({ pg, table: data.table });
67
+
68
+ if (!view && !pk) {
69
+ return { message: `table not found: ${data.table} (${pg.options?.database})`, status: 404 };
70
+ }
71
+
72
+ // const columnList = columns.map(col => col.name);
73
+ const groupbyColumnNotExists = groupby?.split?.(',')?.filter?.(el => !columnTypes.map(el => el.name).includes(el.trim()));
74
+
75
+ if (groupby && groupbyColumnNotExists?.length) {
76
+ return { message: `groupby column not found: ${groupbyColumnNotExists} (${data.table}/${pg.options?.database})`, status: 404 };
77
+ }
78
+
79
+ // get group
80
+ const groupData = groupby
81
+ ? await pg
82
+ .query(
83
+ `select ${groupby} as name ,count(*) from ${tableSQL || table} group by ${groupby} order by count(*) desc limit ${limit}`
84
+ )
85
+ .then((el) => el.rows)
86
+ : null;
87
+
88
+ if (query.sql === '2') return { x, metric, table, tableSQL, data, groupData };
89
+
90
+ const order = data.order || (type === 'listbar' && cols.find(el => el.name === 'metric') ? 'metric desc' : null);
91
+
92
+ const fData =
93
+ filter || search
94
+ ? await getFilterSQL({
95
+ pg,
96
+ table,
97
+ filter,
98
+ search,
99
+ filterList: widgetData.filters,
100
+ })
101
+ : {};
102
+
103
+ const optimizedSQL = widgetData?.sql
104
+ ? `${widgetData.sql} ${fData?.q && false ? fData?.q : ''} limit ${limit}`
105
+ : (fData?.optimizedSQL || `select * from ${tableSQL || table}`);
106
+
107
+ if (type?.includes('bar') && !metric?.length) {
108
+ return { message: 'empty widget params: metrics', status: 400 };
109
+ }
110
+
111
+ const sql = widgetData.sql ? optimizedSQL : (chartSQL[type] || chartSQL.chart)({
112
+ where: config.local && user?.user_type === 'superadmin' ? 'true' : where, // test
113
+ metric,
114
+ yType, // metric type
115
+ columns: widgetData.columns,
116
+ table: `(${optimizedSQL})q`,
117
+ x,
118
+ groupData,
119
+ groupby,
120
+ order,
121
+ samples,
122
+ limit,
123
+ xType,
124
+ fx: widgetData.fx,
125
+ });
126
+
127
+ if (query.sql) return sql;
128
+
129
+ if (!sql || sql?.includes('undefined')) {
130
+ return {
131
+ message: {
132
+ error: 'invalid sql',
133
+ type,
134
+ sql,
135
+ where,
136
+ metric,
137
+ table: `(${optimizedSQL})q`,
138
+ x,
139
+ groupData,
140
+ groupby,
141
+ },
142
+ status: 500,
143
+ };
144
+ }
145
+
146
+ if (config.trace) console.log(sql, user?.uid);
147
+
148
+ const { rows = [], fields = [], errorSql } = await pg.query(sql.replace('{{uid}}', user?.uid)).catch(err => {
149
+ logger.file('bi/data', { error: err.toString(), sql });
150
+ return { errorSql: err.toString() };
151
+ }); // test with limit
152
+
153
+ if (groupbyCls) {
154
+ const { arr = [] } = await getSelect(groupbyCls, pg) || {};
155
+ if (arr.length) {
156
+ const ids = arr.map(el => el.id);
157
+ const text = arr.reduce((acc, curr) => ({ ...acc, [curr.id]: curr.text }), {});
158
+ rows.forEach(row => {
159
+ ids.reduce((acc, curr) => {
160
+ Object.assign(row, { [text[curr]]: row[curr] });
161
+ delete row[curr];
162
+ return acc;
163
+ }, {});
164
+ });
165
+ }
166
+ }
167
+
168
+ if (cls) {
169
+ const values = rows
170
+ .map((row) => row[x])
171
+ ?.filter((el, idx, arr) => el && arr.indexOf(el) === idx);
172
+ const vals = await getSelectVal({ pg, name: cls, values });
173
+ rows
174
+ .filter((row) => row[x])
175
+ .forEach((row) => {
176
+ Object.assign(row, { [x]: vals?.[row[x]]?.text || vals?.[row[x]] || row[x] });
177
+ });
178
+ }
179
+
180
+ const metaTitles = columns.reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.title || curr.ua }), {});
181
+ const titles = Array.isArray(widgetData?.columns)
182
+ ? widgetData.columns.reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.title || curr.ua }), {})
183
+ : Object.keys(widgetData?.columns || {}).reduce((acc, curr) => Object.assign(acc, { [curr]: widgetData?.columns?.[curr] }), {});
184
+
185
+ const rows1 = type === 'table' ? rows.map(row => Object.keys(row || {}).reduce((acc, curr) => Object.assign(acc, { [titles?.[curr] || metaTitles?.[curr] || curr]: row?.[curr] }), {})) : rows;
186
+
187
+ const yml = widgetData.yml || yaml.dump(extractYml(widgetData));
188
+ const dimensions = fields.map((el) => el.name);
189
+
190
+ const res = {
191
+ time: Date.now() - time,
192
+ last_update: widgetData?.last_update,
193
+ error: error || errorSql || (!widgetData.sql ? widgetData.error : undefined),
194
+ dimensions,
195
+ filter: xName,
196
+ dimensionsType: [xType, yType].filter((el) => el)?.length
197
+ ? [xType, yType].filter((el) => el)
198
+ : fields.map((el) => pg.pgType?.[el.dataTypeID]),
199
+ type,
200
+
201
+ text: text || widgetData?.title || data.text,
202
+ // data: query.format === 'data' ? dimensions.map(el => rows.map(r => r[el])) : undefined,
203
+ source:
204
+ query.format === 'array'
205
+ ? dimensions.map((el) => rows1.map((r) => r[el]))
206
+ : rows1,
207
+ style,
208
+ options,
209
+ controls,
210
+ yml,
211
+ data: widgetData.data,
212
+ id: query.widget,
213
+ columns: columnTypes.map(el => Object.assign(el, { title: titles[el.name] || metaTitles?.[el.name] || el.name })),
214
+ params: config?.local || unittest ? {
215
+ x,
216
+ cls,
217
+ metric,
218
+ table,
219
+ tableSQL,
220
+ where,
221
+ groupby,
222
+ sql,
223
+ } : undefined,
224
+ };
225
+ return res;
226
+ }
227
+
228
+ function extractYml(sourceData) {
229
+ const { title, description, type, data, style, controls } = sourceData;
230
+ return { title, description, type, data, style, controls };
230
231
  }
@@ -1,49 +1,49 @@
1
- function number({ metric, where, table, samples, fx }) {
2
- const sql = `select ${fx || metric} from ${table} where ${where} ${samples ? 'limit 10' : ''}`;
3
- return sql;
4
- }
5
- function table({ columns = [], table, where, samples }) {
6
- const cols = Array.isArray(columns)
7
- ? columns.map((el) => `"${(el.name || el).replace(/'/g, "''")}"`).join(',')
8
- : Object.keys(columns).map(key => `"${key.replace(/'/g, "''")}"`).join(',');
9
- return `select ${cols || '*'} from ${table} where ${where} ${samples ? 'limit 10' : 'limit 20'} `;
10
- }
11
-
12
- function chart({
13
- metric,
14
- yType, // metric type
15
- where,
16
- table,
17
- x,
18
- groupby,
19
- groupData,
20
- order,
21
- samples,
22
- limit = 100,
23
- xType,
24
- fx, // agg function
25
- }) {
26
- const xCol = x && xType?.includes('[]') ? `unnest(${x})` : x;
27
-
28
- const metricData =
29
- groupData
30
- ?.filter(el => el.name)
31
- ?.map(
32
- (el) =>
33
- `${metric} filter (where '${el.name.toString().replace(/'/g, "''")}'=${yType?.includes('[]') ? `any(${groupby.replace(/'/g, "''")}::text[])` : groupby.replace(/'/g, "''")}) as "${el.name.toString().replace(/'/g, "''")}"`
34
- )
35
- .join(',') || `${fx || metric} as metric`;
36
- const sql = `select ${xCol} ${x && xType?.includes('[]') ? `as ${x}` : ''}, ${metricData}
37
- from ${table}
38
- where ${where}
39
- ${xCol ? `group by ${xCol}` : ''}
40
- ${order || xCol ? `order by ${order || xCol}` : ''}
41
- ${samples ? 'limit 10' : `limit ${limit}`}`;
42
- return sql;
43
- }
44
-
45
- function text() {
46
- return undefined;
47
- }
48
-
49
- export default { number, chart, table };
1
+ function number({ metric, where, table, samples, fx }) {
2
+ const sql = `select ${fx || metric} from ${table} where ${where} ${samples ? 'limit 10' : ''}`;
3
+ return sql;
4
+ }
5
+ function table({ columns = [], table, where, samples }) {
6
+ const cols = Array.isArray(columns)
7
+ ? columns.map((el) => `"${(el.name || el).replace(/'/g, "''")}"`).join(',')
8
+ : Object.keys(columns).map(key => `"${key.replace(/'/g, "''")}"`).join(',');
9
+ return `select ${cols || '*'} from ${table} where ${where} ${samples ? 'limit 10' : 'limit 20'} `;
10
+ }
11
+
12
+ function chart({
13
+ metric,
14
+ yType, // metric type
15
+ where,
16
+ table,
17
+ x,
18
+ groupby,
19
+ groupData,
20
+ order,
21
+ samples,
22
+ limit = 100,
23
+ xType,
24
+ fx, // agg function
25
+ }) {
26
+ const xCol = x && xType?.includes('[]') ? `unnest(${x})` : x;
27
+
28
+ const metricData =
29
+ groupData
30
+ ?.filter(el => el.name)
31
+ ?.map(
32
+ (el) =>
33
+ `${metric} filter (where '${el.name.toString().replace(/'/g, "''")}'=${yType?.includes('[]') ? `any(${groupby.replace(/'/g, "''")}::text[])` : groupby.replace(/'/g, "''")}) as "${el.name.toString().replace(/'/g, "''")}"`
34
+ )
35
+ .join(',') || `${fx || metric} as metric`;
36
+ const sql = `select ${xCol} ${x && xType?.includes('[]') ? `as ${x}` : ''}, ${metricData}
37
+ from ${table}
38
+ where ${where}
39
+ ${xCol ? `group by ${xCol}` : ''}
40
+ ${order || xCol ? `order by ${order || xCol}` : ''}
41
+ ${samples ? 'limit 10' : `limit ${limit}`}`;
42
+ return sql;
43
+ }
44
+
45
+ function text() {
46
+ return undefined;
47
+ }
48
+
49
+ export default { number, chart, table };