fix report table
This commit is contained in:
@@ -72,6 +72,58 @@ export function compute(
|
||||
(a, b) => new Date(a).getTime() - new Date(b).getTime(),
|
||||
);
|
||||
|
||||
// Calculate total_count for the formula using the same formula applied to input series' total_count values
|
||||
// total_count is constant across all dates for a breakdown group, so compute it once
|
||||
const totalCountScope: Record<string, number> = {};
|
||||
definitions.slice(0, formulaIndex).forEach((depDef, depIndex) => {
|
||||
const readableId = alphabetIds[depIndex];
|
||||
if (!readableId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the series for this dependency in the current breakdown group
|
||||
const depSeries = seriesByIndex.get(depIndex);
|
||||
if (depSeries) {
|
||||
// Get total_count from any data point (it's the same for all dates)
|
||||
const totalCount = depSeries.data.find(
|
||||
(d) => d.total_count != null,
|
||||
)?.total_count;
|
||||
totalCountScope[readableId] = totalCount ?? 0;
|
||||
} else {
|
||||
// Could be a formula from a previous breakdown group - find it in results
|
||||
const formulaSerie = results.find(
|
||||
(s) =>
|
||||
s.definitionIndex === depIndex &&
|
||||
'type' in s.definition &&
|
||||
s.definition.type === 'formula' &&
|
||||
s.name.slice(1).join(':::') === breakdownSignature,
|
||||
);
|
||||
if (formulaSerie) {
|
||||
const totalCount = formulaSerie.data.find(
|
||||
(d) => d.total_count != null,
|
||||
)?.total_count;
|
||||
totalCountScope[readableId] = totalCount ?? 0;
|
||||
} else {
|
||||
totalCountScope[readableId] = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Evaluate formula for total_count
|
||||
let formulaTotalCount: number | undefined;
|
||||
try {
|
||||
const result = mathjs
|
||||
.parse(formula.formula)
|
||||
.compile()
|
||||
.evaluate(totalCountScope) as number;
|
||||
formulaTotalCount =
|
||||
Number.isNaN(result) || !Number.isFinite(result)
|
||||
? undefined
|
||||
: round(result, 2);
|
||||
} catch (error) {
|
||||
formulaTotalCount = undefined;
|
||||
}
|
||||
|
||||
// Calculate formula for each date
|
||||
const formulaData = sortedDates.map((date) => {
|
||||
const scope: Record<string, number> = {};
|
||||
@@ -124,8 +176,7 @@ export function compute(
|
||||
Number.isNaN(count) || !Number.isFinite(count)
|
||||
? 0
|
||||
: round(count, 2),
|
||||
total_count: breakdownSeries[0]?.data.find((d) => d.date === date)
|
||||
?.total_count,
|
||||
total_count: formulaTotalCount,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -231,13 +231,60 @@ export function getChartSql({
|
||||
return sql;
|
||||
}
|
||||
|
||||
const totalUniqueSubquery = `(
|
||||
SELECT ${sb.select.count}
|
||||
// Build total_count calculation that accounts for breakdowns
|
||||
// When breakdowns exist, we need to calculate total_count per breakdown group
|
||||
if (breakdowns.length > 0) {
|
||||
// Create a subquery that calculates total_count per breakdown group (without date grouping)
|
||||
// Then reference it in the main query via JOIN
|
||||
const breakdownSelects = breakdowns
|
||||
.map((breakdown, index) => {
|
||||
const key = `label_${index + 1}`;
|
||||
const breakdownExpr = getSelectPropertyKey(breakdown.name);
|
||||
return `${breakdownExpr} as ${key}`;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
// GROUP BY needs to use the actual expressions, not aliases
|
||||
const breakdownGroupByExprs = breakdowns
|
||||
.map((breakdown) => getSelectPropertyKey(breakdown.name))
|
||||
.join(', ');
|
||||
|
||||
// Build the total_count subquery grouped only by breakdowns (no date)
|
||||
// Extract the count expression without the alias (remove "as count")
|
||||
const countExpression = sb.select.count.replace(/\s+as\s+count$/i, '');
|
||||
const totalCountSubquery = `(
|
||||
SELECT
|
||||
${breakdownSelects},
|
||||
${countExpression} as total_count
|
||||
FROM ${sb.from}
|
||||
${getJoins()}
|
||||
${getWhere()}
|
||||
)`;
|
||||
sb.select.total_unique_count = `${totalUniqueSubquery} as total_count`;
|
||||
GROUP BY ${breakdownGroupByExprs}
|
||||
) as total_counts`;
|
||||
|
||||
// Join the total_counts subquery to get total_count per breakdown
|
||||
// Match on the breakdown column values
|
||||
const joinConditions = breakdowns
|
||||
.map((_, index) => {
|
||||
const outerKey = `label_${index + 1}`;
|
||||
return `${outerKey} = total_counts.label_${index + 1}`;
|
||||
})
|
||||
.join(' AND ');
|
||||
|
||||
sb.joins.total_counts = `LEFT JOIN ${totalCountSubquery} ON ${joinConditions}`;
|
||||
// Use any() aggregate since total_count is the same for all rows in a breakdown group
|
||||
sb.select.total_unique_count =
|
||||
'any(total_counts.total_count) as total_count';
|
||||
} else {
|
||||
// No breakdowns - use a simple subquery for total count
|
||||
const totalUniqueSubquery = `(
|
||||
SELECT ${sb.select.count}
|
||||
FROM ${sb.from}
|
||||
${getJoins()}
|
||||
${getWhere()}
|
||||
)`;
|
||||
sb.select.total_unique_count = `${totalUniqueSubquery} as total_count`;
|
||||
}
|
||||
|
||||
const sql = `${getSelect()} ${getFrom()} ${getJoins()} ${getWhere()} ${getGroupBy()} ${getOrderBy()} ${getFill()}`;
|
||||
console.log('-- Report --');
|
||||
|
||||
Reference in New Issue
Block a user