@opengis/bi
Version:
BI data visualization module
126 lines (102 loc) • 4.5 kB
JavaScript
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 };
}
}