UNPKG

@opengis/bi

Version:

BI data visualization module

126 lines (102 loc) 4.5 kB
import { getFilterSQL, logger, pgClients, getMeta } from '@opengis/fastify-table/utils.js'; import { getWidget } from '../../../../utils.js'; import downloadClusterData from './utils/downloadClusterData.js'; const clusterExists = {}; export default async function cluster(req, reply) { const { query = {} } = req; const { widget, filter, dashboard, search } = query; if (!widget) { return { message: 'not enough params: widget', status: 400 }; } const { pg = req.pg || pgClients.client, data, style, controls } = await getWidget({ pg: req.pg, dashboard, widget }); const pkey = pg.pk?.[data?.table]; if (!pkey) { return { message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`, status: 400, }; } // data param const { table, query: where = '1=1', metrics = [], cluster, clusterTable = {}, } = data; if (!cluster) { return { message: `invalid ${widget ? 'widget' : 'dashboard'}: cluster column not specified`, status: 400, }; } if (!metrics.length) { return { message: `invalid ${widget ? 'widget' : 'dashboard'}: metric columns not found`, status: 400, }; } if (!clusterTable?.name) { Object.assign(clusterTable, { name: 'bi.cluster', title: 'title', query: `type='${cluster}'`, }); } try { if (cluster && !clusterExists[cluster]) { const res = await downloadClusterData({ pg, cluster }); if (res) return res; clusterExists[cluster] = 1; } if (clusterTable?.name && !pg.pk?.[clusterTable?.name]) { return { message: 'invalid widget params: clusterTable pkey not found', status: 404, }; } const { bounds, extentStr } = await pg.query(`select count(*), st_asgeojson(st_extent(geom))::json as bounds, replace(regexp_replace(st_extent(geom)::box2d::text,'BOX\\(|\\)','','g'),' ',',') as "extentStr" from ${table} where ${where || '1=1'}`).then((res) => res.rows?.[0] || {}); const extent = extentStr ? extentStr.split(',') : undefined; // get sql const { optimizedSQL } = filter || search ? await getFilterSQL({ pg, table, filter, search }) : {}; const { columns = [] } = await getMeta({ pg, table }); const columnList = columns.map(el => el.name); if (query.metric && typeof query.metric === 'string') { const checkInvalid = query.metric.split(',').find(el => !columnList.includes(el) && el !== 'count'); if (checkInvalid) { return reply.status(404).send(`invalid query metric value: ${checkInvalid}`); } } const multipleMetrics = query.metric ? query.metric.split(',').map(el => el === 'count' ? 'count(*)' : `sum(${el.replace(/'/g, "''")})::float as ${el}`).join(',') : null; const multipleMetricsOrder = query.metric ? query.metric.split(',').map(el => el === 'count' ? 'count(*)' : `sum(${el.replace(/'/g, "''")})::float`).join(',') : null; const metricFunc = multipleMetrics || `${clusterTable?.operator || 'sum'}("${metrics[0]}")::float`; const q = `select b.*, ${metricFunc} ${multipleMetrics ? '' : 'as metric'} from ${optimizedSQL ? `(${optimizedSQL})` : table} q left join lateral (select "${pg.pk?.[clusterTable?.name]}" as id, ${clusterTable?.column || cluster} as name, ${clusterTable?.title} as title from ${clusterTable?.name} where ${clusterTable?.codifierColumn || 'codifier'}=q."${clusterTable?.column || cluster}" limit 1)b on 1=1 where ${where} group by b.id, b.name, b.title order by ${multipleMetricsOrder || metricFunc} desc`; if (query.sql === '1') return q; // auto Index // autoIndex({ table, columns: (metrics || []).concat([cluster]) }); const { rows = [] } = await pg.query(q); const vals = rows.map((el) => el.metric - 0).sort((a, b) => a - b); const len = vals.length; const sizes = [ vals[0], vals[Math.floor(len / 4)], vals[Math.floor(len / 2)], vals[Math.floor(len * 0.75)], vals[len - 1], ]; return { sizes, style, controls, metrics, rows, columns: columns.map(({ name, title, dataTypeID }) => ({ name, title, type: pg.pgType[dataTypeID] })), bounds, extent, count: rows.length, total: rows?.reduce((acc, curr) => (curr.metric || 0) + acc, 0) }; } catch (err) { logger.file('bi/cluster/error', { error: err.toString(), query }); return { error: err.toString(), status: 500 }; } }