@quillsql/react 2.13.37 → 2.13.39
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/dist/cjs/Chart.d.ts +2 -1
- package/dist/cjs/Chart.d.ts.map +1 -1
- package/dist/cjs/Chart.js +1 -2
- package/dist/cjs/ChartBuilder.d.ts.map +1 -1
- package/dist/cjs/ChartBuilder.js +68 -57
- package/dist/cjs/ReportBuilder.d.ts.map +1 -1
- package/dist/cjs/ReportBuilder.js +69 -51
- package/dist/cjs/components/Chart/ChartTooltip.d.ts +1 -1
- package/dist/cjs/components/Chart/ChartTooltip.d.ts.map +1 -1
- package/dist/cjs/components/Chart/ChartTooltip.js +3 -3
- package/dist/cjs/components/Chart/InternalChart.d.ts.map +1 -1
- package/dist/cjs/components/Chart/InternalChart.js +7 -1
- package/dist/cjs/components/Chart/LineChart.d.ts.map +1 -1
- package/dist/cjs/components/Chart/LineChart.js +4 -4
- package/dist/cjs/components/Dashboard/MetricComponent.d.ts.map +1 -1
- package/dist/cjs/components/Dashboard/MetricComponent.js +3 -3
- package/dist/cjs/components/ReportBuilder/convert.d.ts +1 -1
- package/dist/cjs/components/ReportBuilder/convert.d.ts.map +1 -1
- package/dist/cjs/components/ReportBuilder/convert.js +73 -21
- package/dist/cjs/components/UiComponents.d.ts +2 -1
- package/dist/cjs/components/UiComponents.d.ts.map +1 -1
- package/dist/cjs/components/UiComponents.js +11 -4
- package/dist/cjs/hooks/useExport.d.ts.map +1 -1
- package/dist/cjs/hooks/useExport.js +4 -5
- package/dist/cjs/internals/ReportBuilder/PivotForm.d.ts +19 -11
- package/dist/cjs/internals/ReportBuilder/PivotForm.d.ts.map +1 -1
- package/dist/cjs/internals/ReportBuilder/PivotForm.js +62 -48
- package/dist/cjs/internals/ReportBuilder/PivotList.js +5 -4
- package/dist/cjs/internals/ReportBuilder/PivotModal.d.ts +28 -31
- package/dist/cjs/internals/ReportBuilder/PivotModal.d.ts.map +1 -1
- package/dist/cjs/internals/ReportBuilder/PivotModal.js +315 -633
- package/dist/cjs/models/Pivot.d.ts +27 -7
- package/dist/cjs/models/Pivot.d.ts.map +1 -1
- package/dist/cjs/utils/astProcessing.js +2 -2
- package/dist/cjs/utils/dashboard.d.ts.map +1 -1
- package/dist/cjs/utils/dashboard.js +36 -11
- package/dist/cjs/utils/merge.d.ts.map +1 -1
- package/dist/cjs/utils/merge.js +2 -1
- package/dist/cjs/utils/pivotConstructor.d.ts +1 -0
- package/dist/cjs/utils/pivotConstructor.d.ts.map +1 -1
- package/dist/cjs/utils/pivotConstructor.js +37 -7
- package/dist/cjs/utils/pivotProcessing.d.ts.map +1 -1
- package/dist/cjs/utils/pivotProcessing.js +10 -14
- package/dist/cjs/utils/queryConstructor.d.ts.map +1 -1
- package/dist/cjs/utils/queryConstructor.js +421 -134
- package/dist/cjs/utils/report.d.ts.map +1 -1
- package/dist/cjs/utils/report.js +2 -2
- package/dist/cjs/utils/textProcessing.d.ts +1 -1
- package/dist/cjs/utils/textProcessing.d.ts.map +1 -1
- package/dist/cjs/utils/textProcessing.js +3 -0
- package/dist/esm/Chart.d.ts +2 -1
- package/dist/esm/Chart.d.ts.map +1 -1
- package/dist/esm/Chart.js +1 -2
- package/dist/esm/ChartBuilder.d.ts.map +1 -1
- package/dist/esm/ChartBuilder.js +68 -57
- package/dist/esm/ReportBuilder.d.ts.map +1 -1
- package/dist/esm/ReportBuilder.js +69 -51
- package/dist/esm/components/Chart/ChartTooltip.d.ts +1 -1
- package/dist/esm/components/Chart/ChartTooltip.d.ts.map +1 -1
- package/dist/esm/components/Chart/ChartTooltip.js +3 -3
- package/dist/esm/components/Chart/InternalChart.d.ts.map +1 -1
- package/dist/esm/components/Chart/InternalChart.js +7 -1
- package/dist/esm/components/Chart/LineChart.d.ts.map +1 -1
- package/dist/esm/components/Chart/LineChart.js +4 -4
- package/dist/esm/components/Dashboard/MetricComponent.d.ts.map +1 -1
- package/dist/esm/components/Dashboard/MetricComponent.js +3 -3
- package/dist/esm/components/ReportBuilder/convert.d.ts +1 -1
- package/dist/esm/components/ReportBuilder/convert.d.ts.map +1 -1
- package/dist/esm/components/ReportBuilder/convert.js +74 -22
- package/dist/esm/components/UiComponents.d.ts +2 -1
- package/dist/esm/components/UiComponents.d.ts.map +1 -1
- package/dist/esm/components/UiComponents.js +11 -4
- package/dist/esm/hooks/useExport.d.ts.map +1 -1
- package/dist/esm/hooks/useExport.js +4 -5
- package/dist/esm/internals/ReportBuilder/PivotForm.d.ts +19 -11
- package/dist/esm/internals/ReportBuilder/PivotForm.d.ts.map +1 -1
- package/dist/esm/internals/ReportBuilder/PivotForm.js +63 -49
- package/dist/esm/internals/ReportBuilder/PivotList.js +5 -4
- package/dist/esm/internals/ReportBuilder/PivotModal.d.ts +28 -31
- package/dist/esm/internals/ReportBuilder/PivotModal.d.ts.map +1 -1
- package/dist/esm/internals/ReportBuilder/PivotModal.js +327 -635
- package/dist/esm/models/Pivot.d.ts +27 -7
- package/dist/esm/models/Pivot.d.ts.map +1 -1
- package/dist/esm/utils/astProcessing.js +2 -2
- package/dist/esm/utils/dashboard.d.ts.map +1 -1
- package/dist/esm/utils/dashboard.js +36 -11
- package/dist/esm/utils/merge.d.ts.map +1 -1
- package/dist/esm/utils/merge.js +2 -1
- package/dist/esm/utils/pivotConstructor.d.ts +1 -0
- package/dist/esm/utils/pivotConstructor.d.ts.map +1 -1
- package/dist/esm/utils/pivotConstructor.js +37 -8
- package/dist/esm/utils/pivotProcessing.d.ts.map +1 -1
- package/dist/esm/utils/pivotProcessing.js +10 -14
- package/dist/esm/utils/queryConstructor.d.ts.map +1 -1
- package/dist/esm/utils/queryConstructor.js +421 -134
- package/dist/esm/utils/report.d.ts.map +1 -1
- package/dist/esm/utils/report.js +2 -2
- package/dist/esm/utils/textProcessing.d.ts +1 -1
- package/dist/esm/utils/textProcessing.d.ts.map +1 -1
- package/dist/esm/utils/textProcessing.js +3 -0
- package/package.json +1 -1
|
@@ -212,7 +212,7 @@ function generateDistinctQueryMySQL(stringFields, query) {
|
|
|
212
212
|
}
|
|
213
213
|
function generateDistinctQueryPostgres(stringFields, query) {
|
|
214
214
|
const distinctQueries = stringFields.map((field) => {
|
|
215
|
-
return `SELECT '${field}' AS ${processColumnReference('field', 'postgresql')}, to_json(ARRAY_AGG(DISTINCT ${field})) AS ${processColumnReference('string_values', 'postgresql')} FROM querytable`;
|
|
215
|
+
return `SELECT '${field}' AS ${processColumnReference('field', 'postgresql')}, to_json(ARRAY_AGG(DISTINCT "${field}")) AS ${processColumnReference('string_values', 'postgresql')} FROM querytable`;
|
|
216
216
|
});
|
|
217
217
|
const distinctQuery = distinctQueries.join(' UNION ALL ');
|
|
218
218
|
return `WITH querytable AS (${query.replace(';', '')}) ` + distinctQuery;
|
|
@@ -283,53 +283,115 @@ function create2DPivotQuery(pivot, itemQuery, databaseType, columnFieldValues, d
|
|
|
283
283
|
if (!pivot || !pivot.columnField) {
|
|
284
284
|
return undefined;
|
|
285
285
|
}
|
|
286
|
-
if (isStringType(pivot.rowFieldType
|
|
286
|
+
if (isStringType(pivot.rowFieldType ?? '') || !pivot.rowFieldType) {
|
|
287
287
|
return create2DStringPivotQuery(pivot, itemQuery, columnFieldValues, databaseType);
|
|
288
288
|
}
|
|
289
289
|
return create2DDatePivotQuery(pivot, itemQuery, columnFieldValues, databaseType, dateBucket);
|
|
290
290
|
}
|
|
291
291
|
function create2DStringPivotQuery(pivot, itemQuery, columnFieldValues, databaseType) {
|
|
292
292
|
const isValidBaseQuery = itemQuery.match(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/);
|
|
293
|
-
if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField)
|
|
293
|
+
if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField)
|
|
294
294
|
return undefined;
|
|
295
|
-
}
|
|
296
295
|
const rowField = pivot.rowField;
|
|
297
|
-
|
|
296
|
+
if (!pivot.aggregations?.[0]?.valueField && !pivot.valueField)
|
|
297
|
+
throw new Error('No value field provided for pivot');
|
|
298
|
+
if (!pivot.aggregations?.[0]?.aggregationType && !pivot.aggregationType)
|
|
299
|
+
throw new Error('No aggregation type provided for pivot');
|
|
298
300
|
const columnField = pivot.columnField;
|
|
299
301
|
const rowFieldAlias = processColumnReference(rowField, databaseType, undefined, false, true);
|
|
300
|
-
const valueFieldAlias = processColumnReference(valueField ?? rowField, databaseType, undefined, false, true);
|
|
301
302
|
const columnFieldAlias = processColumnReference(columnField, databaseType, undefined, false, true);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (pivot.aggregationType === 'percentage' && pivot.valueField2) {
|
|
310
|
-
const valueField2Alias = processColumnReference(pivot.valueField2, databaseType, undefined, false, true);
|
|
311
|
-
if (pivot.valueField === pivot.valueField2) {
|
|
312
|
-
caseWhens = columnFieldValues.map((column) => {
|
|
313
|
-
return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / GREATEST(sum(${processColumnReference(pivot.valueField2, databaseType)}), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
|
|
314
|
-
});
|
|
303
|
+
let caseWhens = [];
|
|
304
|
+
let valueAliases = [];
|
|
305
|
+
const seenAggs = {};
|
|
306
|
+
pivot.aggregations?.forEach((currentAgg) => {
|
|
307
|
+
// Track duplicate aggregation combos for disambiguation.
|
|
308
|
+
if (seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']) {
|
|
309
|
+
seenAggs[currentAgg.aggregationType ?? ''][currentAgg.valueField ?? ''] += 1;
|
|
315
310
|
}
|
|
316
311
|
else {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
312
|
+
seenAggs[currentAgg.aggregationType ?? ''] = {
|
|
313
|
+
...seenAggs[currentAgg.aggregationType ?? ''],
|
|
314
|
+
[currentAgg.valueField ?? '']: 1,
|
|
315
|
+
};
|
|
321
316
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
317
|
+
let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']?.toString();
|
|
318
|
+
if (disambiguationIndex === '1')
|
|
319
|
+
disambiguationIndex = '';
|
|
320
|
+
const valueFieldAlias = processColumnReference((currentAgg.valueField || rowField || 'count'), databaseType, undefined, false, true);
|
|
321
|
+
const valueAliasSubstring = currentAgg.valueField
|
|
322
|
+
? `${processColumnReference(currentAgg.valueField, databaseType, undefined, true)} AS ${valueFieldAlias}`
|
|
323
|
+
: '';
|
|
324
|
+
let value2AliasSubstring = '';
|
|
325
|
+
const disambiguationField = Object.values(seenAggs[currentAgg.aggregationType ?? ''] ?? {}).reduce((acc, v) => acc + v) > 1
|
|
326
|
+
? `_${currentAgg.valueField}${disambiguationIndex}`
|
|
327
|
+
: '';
|
|
328
|
+
const disambiguation = pivot.aggregations?.length > 1
|
|
329
|
+
? `${disambiguationField}_${disambiguationField ? matchCasing(currentAgg.aggregationType, currentAgg.valueField) : currentAgg.aggregationType}`
|
|
330
|
+
: '';
|
|
331
|
+
// Wrap the value field in CASE WHEN if its type is bool.
|
|
332
|
+
const valueExpr = currentAgg.valueFieldType === 'bool'
|
|
333
|
+
? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`
|
|
334
|
+
: processValueField(currentAgg.aggregationType, databaseType, valueFieldAlias);
|
|
335
|
+
if (currentAgg.aggregationType === 'percentage') {
|
|
336
|
+
if (!currentAgg.valueField)
|
|
337
|
+
throw new Error('No value field provided for pivot');
|
|
338
|
+
const valueField2Alias = processColumnReference(currentAgg.valueField2 ?? currentAgg.valueField, databaseType, undefined, false, true);
|
|
339
|
+
// Wrap the second value field if its type is bool.
|
|
340
|
+
const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === 'bool'
|
|
341
|
+
? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`
|
|
342
|
+
: valueField2Alias;
|
|
343
|
+
if (currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2) {
|
|
344
|
+
caseWhens = columnFieldValues.map((column) => {
|
|
345
|
+
return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${valueExpr} END) / GREATEST(sum(${value2Expr}), 1) AS ${processColumnReference(column + disambiguation, databaseType, '_', true)}`;
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
value2AliasSubstring = `${processColumnReference(currentAgg.valueField2 ?? currentAgg.valueField, databaseType, undefined, true)} AS ${valueField2Alias}`;
|
|
350
|
+
caseWhens = columnFieldValues.map((column) => {
|
|
351
|
+
return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${valueExpr} END) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${value2Expr} END), 1) AS ${processColumnReference(column + disambiguation, databaseType, '_', true)}`;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
caseWhens = [
|
|
357
|
+
...caseWhens,
|
|
358
|
+
...columnFieldValues.map((column) => {
|
|
359
|
+
return `${processAggType(currentAgg.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${valueExpr} END) AS ${processColumnReference(column + disambiguation, databaseType, '_', true)}`;
|
|
360
|
+
}),
|
|
361
|
+
];
|
|
362
|
+
}
|
|
363
|
+
if (valueAliasSubstring)
|
|
364
|
+
valueAliases.push(valueAliasSubstring);
|
|
365
|
+
if (value2AliasSubstring)
|
|
366
|
+
valueAliases.push(value2AliasSubstring);
|
|
367
|
+
});
|
|
368
|
+
valueAliases = [
|
|
369
|
+
`${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}`,
|
|
370
|
+
...valueAliases,
|
|
371
|
+
`${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias}`,
|
|
372
|
+
];
|
|
373
|
+
valueAliases = Array.from(new Set(valueAliases));
|
|
374
|
+
const sortQuery = pivot.sort && pivot.sortField && pivot.rowLimit
|
|
375
|
+
? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} `
|
|
376
|
+
: '';
|
|
325
377
|
const pivotQuery = `
|
|
326
|
-
,quill_alias AS (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
378
|
+
,quill_alias AS (
|
|
379
|
+
SELECT ${valueAliases.length > 0 ? `${valueAliases.join(', ')}` : ''}
|
|
380
|
+
FROM quill_base_table
|
|
381
|
+
),
|
|
382
|
+
quill_qt_cw AS (
|
|
383
|
+
SELECT ${rowFieldAlias}
|
|
384
|
+
${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''}
|
|
385
|
+
FROM quill_alias
|
|
386
|
+
GROUP BY ${rowFieldAlias}
|
|
387
|
+
),
|
|
388
|
+
quill_base_pivot AS (
|
|
389
|
+
SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} *
|
|
390
|
+
FROM quill_qt_cw qt
|
|
391
|
+
${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''}
|
|
392
|
+
)
|
|
331
393
|
SELECT * FROM quill_base_pivot
|
|
332
|
-
|
|
394
|
+
`
|
|
333
395
|
.replace(/\s+/g, ' ')
|
|
334
396
|
.trim();
|
|
335
397
|
return itemQuery.replace(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/, pivotQuery);
|
|
@@ -339,38 +401,95 @@ function create2DDatePivotQuery(pivot, itemQuery, columnFieldValues, databaseTyp
|
|
|
339
401
|
if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField) {
|
|
340
402
|
return undefined;
|
|
341
403
|
}
|
|
404
|
+
if (!pivot.aggregations?.[0]?.valueField && !pivot.valueField)
|
|
405
|
+
throw new Error('No value field provided for pivot');
|
|
406
|
+
if (!pivot.aggregations?.[0]?.aggregationType && !pivot.aggregationType)
|
|
407
|
+
throw new Error('No aggregation type provided for pivot');
|
|
408
|
+
// const aggregationType = (pivot.aggregations?.[0]?.aggregationType ?? pivot.aggregationType) as any;
|
|
342
409
|
const rowField = pivot.rowField;
|
|
343
410
|
const columnField = pivot.columnField;
|
|
344
411
|
const rowFieldAlias = processColumnReference(rowField, databaseType, undefined, false, true);
|
|
345
|
-
const valueFieldAlias = processColumnReference(pivot.valueField ?? rowField, databaseType, undefined, false, true);
|
|
346
412
|
const columnFieldAlias = processColumnReference(columnField, databaseType, undefined, false, true);
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (pivot.aggregationType === 'percentage' && pivot.valueField2) {
|
|
355
|
-
const valueField2Alias = processColumnReference(pivot.valueField2, databaseType, undefined, false, true);
|
|
356
|
-
// edge case. if the user picks amount and amount, we assume they want a pie chart like breakdown of amount. so the summation of valueField2 has to be moved outside of the case when
|
|
357
|
-
if (pivot.valueField === pivot.valueField2) {
|
|
358
|
-
caseWhens = columnFieldValues.map((column) => {
|
|
359
|
-
return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / GREATEST(sum(${processColumnReference(valueField2Alias, databaseType)}), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
|
|
360
|
-
});
|
|
413
|
+
let caseWhens = [];
|
|
414
|
+
let valueFieldAliases = [];
|
|
415
|
+
const seenAggs = {};
|
|
416
|
+
pivot.aggregations?.forEach((currentAgg) => {
|
|
417
|
+
// if the aggregation combo has been seen before, increment the count, else add it to the seenAggs array
|
|
418
|
+
if (seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']) {
|
|
419
|
+
seenAggs[currentAgg.aggregationType ?? ''][currentAgg.valueField ?? ''] += 1;
|
|
361
420
|
}
|
|
362
421
|
else {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
422
|
+
seenAggs[currentAgg.aggregationType ?? ''] = {
|
|
423
|
+
...seenAggs[currentAgg.aggregationType ?? ''],
|
|
424
|
+
[currentAgg.valueField ?? '']: 1,
|
|
425
|
+
};
|
|
367
426
|
}
|
|
368
|
-
|
|
427
|
+
let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']?.toString();
|
|
428
|
+
if (disambiguationIndex === '1')
|
|
429
|
+
disambiguationIndex = '';
|
|
430
|
+
const valueFieldAlias = processColumnReference((currentAgg.valueField ?? rowField), databaseType, undefined, false, true);
|
|
431
|
+
const valueAliasSubstring = currentAgg.valueField
|
|
432
|
+
? `${processColumnReference(currentAgg.valueField, databaseType, undefined, true)} AS ${valueFieldAlias}`
|
|
433
|
+
: '';
|
|
434
|
+
let value2AliasSubstring = '';
|
|
435
|
+
const disambiguationField = Object.values(seenAggs[currentAgg.aggregationType ?? ''] ?? {}).reduce((acc, v) => acc + v) > 1 ? `_${currentAgg.valueField}${disambiguationIndex}` : '';
|
|
436
|
+
const disambiguation = pivot.aggregations?.length > 1
|
|
437
|
+
? `${disambiguationField}_${disambiguationField ? matchCasing(currentAgg.aggregationType, currentAgg.valueField) : currentAgg.aggregationType}`
|
|
438
|
+
: '';
|
|
439
|
+
// Wrap the value field in CASE WHEN if its type is bool.
|
|
440
|
+
const valueExpr = currentAgg.valueFieldType === 'bool'
|
|
441
|
+
? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`
|
|
442
|
+
: processValueField(currentAgg.aggregationType, databaseType, valueFieldAlias);
|
|
443
|
+
if (currentAgg.aggregationType === 'percentage') {
|
|
444
|
+
if (!currentAgg.valueField)
|
|
445
|
+
throw new Error('No value field provided for pivot');
|
|
446
|
+
const valueField2Alias = processColumnReference(currentAgg.valueField2 ?? currentAgg.valueField, databaseType, undefined, false, true);
|
|
447
|
+
// Wrap the second value field if its type is bool.
|
|
448
|
+
const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === 'bool'
|
|
449
|
+
? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`
|
|
450
|
+
: valueField2Alias;
|
|
451
|
+
// edge case. if the user picks amount and amount, we assume they want a pie chart like breakdown of amount. so the summation of valueField2 has to be moved outside of the case whe
|
|
452
|
+
if (currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2) {
|
|
453
|
+
caseWhens = [
|
|
454
|
+
...caseWhens,
|
|
455
|
+
...columnFieldValues.map((column) => {
|
|
456
|
+
return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${valueExpr} END) / GREATEST(sum(${value2Expr}), 1) AS ${processColumnReference(column + disambiguation, databaseType, '_', true)}`;
|
|
457
|
+
}),
|
|
458
|
+
];
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
value2AliasSubstring = `${processColumnReference(currentAgg.valueField2 ?? currentAgg.valueField, databaseType, undefined, true)} AS ${valueField2Alias}`;
|
|
462
|
+
caseWhens = [
|
|
463
|
+
...caseWhens,
|
|
464
|
+
...columnFieldValues.map((column) => {
|
|
465
|
+
return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${valueExpr} END) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${value2Expr} END), 1) AS ${processColumnReference(column + disambiguation, databaseType, '_', true)}`;
|
|
466
|
+
}),
|
|
467
|
+
];
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
caseWhens = [
|
|
472
|
+
...caseWhens,
|
|
473
|
+
...columnFieldValues.map((column) => {
|
|
474
|
+
return `${processAggType(currentAgg.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${valueExpr} END) AS ${processColumnReference(column + disambiguation, databaseType, '_', true)}`;
|
|
475
|
+
}),
|
|
476
|
+
];
|
|
477
|
+
}
|
|
478
|
+
if (valueAliasSubstring)
|
|
479
|
+
valueFieldAliases.push(valueAliasSubstring);
|
|
480
|
+
if (value2AliasSubstring)
|
|
481
|
+
valueFieldAliases.push(value2AliasSubstring);
|
|
482
|
+
});
|
|
483
|
+
valueFieldAliases = [
|
|
484
|
+
`${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}`,
|
|
485
|
+
...valueFieldAliases,
|
|
486
|
+
`${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias}`,
|
|
487
|
+
];
|
|
488
|
+
valueFieldAliases = Array.from(new Set(valueFieldAliases));
|
|
369
489
|
// pivot sort matters in the base query when there is a rowLimit. In mssql, an orderby must be accompanied by a limit in a subquery and not allowed in a cte
|
|
370
490
|
const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} ` : ''}`;
|
|
371
491
|
const pivotQuery = `
|
|
372
|
-
, quill_alias AS (SELECT ${
|
|
373
|
-
${processColumnReference(columnField, databaseType, undefined, true, true)} AS ${columnFieldAlias} FROM quill_base_table),
|
|
492
|
+
, quill_alias AS (SELECT ${valueFieldAliases.length > 0 ? `${valueFieldAliases.join(', ')}` : ''} FROM quill_base_table),
|
|
374
493
|
quill_qt_agg AS (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)}),
|
|
375
494
|
quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} * FROM quill_qt_agg qt
|
|
376
495
|
${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''})
|
|
@@ -391,37 +510,91 @@ function create1DPivotQuery(pivot, itemQuery, dateBucket = 'month', databaseType
|
|
|
391
510
|
// their own with statements and withs with the same name will conflict
|
|
392
511
|
function create1DStringPivotQuery(pivot, itemQuery, databaseType) {
|
|
393
512
|
const isValidBaseQuery = itemQuery.match(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/);
|
|
394
|
-
if (!isValidBaseQuery)
|
|
513
|
+
if (!isValidBaseQuery)
|
|
395
514
|
return undefined;
|
|
396
|
-
}
|
|
397
515
|
const rowField = pivot.rowField;
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const value2Alias = processColumnReference(pivot.valueField2, databaseType, undefined, false, true);
|
|
409
|
-
if (pivot.valueField !== pivot.valueField2) {
|
|
410
|
-
value2AliasSubstring =
|
|
411
|
-
valueField && valueField !== pivot.valueField2
|
|
412
|
-
? `, ${processColumnReference(`${pivot.valueField2}`, databaseType, undefined, true)} AS ${value2Alias}`
|
|
413
|
-
: '';
|
|
516
|
+
const rowAlias = processColumnReference(rowField, databaseType, undefined, true);
|
|
517
|
+
let quillAggSelects = [rowAlias];
|
|
518
|
+
let valueFieldAliases = [];
|
|
519
|
+
const seenAggs = {};
|
|
520
|
+
pivot.aggregations?.forEach((currentAgg) => {
|
|
521
|
+
if (!currentAgg.valueField)
|
|
522
|
+
currentAgg.valueField = undefined;
|
|
523
|
+
// Track duplicates to disambiguate names
|
|
524
|
+
if (seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']) {
|
|
525
|
+
seenAggs[currentAgg.aggregationType ?? ''][currentAgg.valueField ?? ''] += 1;
|
|
414
526
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
527
|
+
else {
|
|
528
|
+
seenAggs[currentAgg.aggregationType ?? ''] = {
|
|
529
|
+
...seenAggs[currentAgg.aggregationType ?? ''],
|
|
530
|
+
[currentAgg.valueField ?? '']: 1,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']?.toString() ?? '';
|
|
534
|
+
if (disambiguationIndex === '1')
|
|
535
|
+
disambiguationIndex = '';
|
|
536
|
+
// This is the alias (from quill_alias CTE) for the field
|
|
537
|
+
const valueFieldAlias = processColumnReference((currentAgg.valueField || rowField || 'count'), databaseType, undefined, false, true);
|
|
538
|
+
// In the base query, we select the raw column.
|
|
539
|
+
const valueAliasSubstring = currentAgg.valueField
|
|
540
|
+
? `${processColumnReference(currentAgg.valueField, databaseType, undefined, true)} AS ${valueFieldAlias}`
|
|
541
|
+
: '';
|
|
542
|
+
let value2AliasSubstring = '';
|
|
543
|
+
const disambiguation = pivot.aggregations?.length > 1
|
|
544
|
+
? `${disambiguationIndex}_${currentAgg.aggregationType}`
|
|
545
|
+
: '';
|
|
546
|
+
// Wrap the field in a CASE WHEN if it's boolean
|
|
547
|
+
let valueExpr = !currentAgg.valueField ? '*' : valueFieldAlias;
|
|
548
|
+
if (currentAgg.valueFieldType === 'bool') {
|
|
549
|
+
valueExpr = `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`;
|
|
550
|
+
}
|
|
551
|
+
if (currentAgg.aggregationType === 'percentage') {
|
|
552
|
+
if (!currentAgg.valueField) {
|
|
553
|
+
throw new Error('No value field provided for percentage aggregation');
|
|
554
|
+
}
|
|
555
|
+
const valueField2Alias = processColumnReference(currentAgg.valueField2 ?? currentAgg.valueField, databaseType, undefined, false, true);
|
|
556
|
+
let value2Expr = valueField2Alias;
|
|
557
|
+
if ((currentAgg.valueField2Type ?? currentAgg.valueFieldType) === 'bool') {
|
|
558
|
+
value2Expr = `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`;
|
|
559
|
+
}
|
|
560
|
+
value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2
|
|
561
|
+
? `${processColumnReference(currentAgg.valueField2, databaseType, undefined, true)} AS ${valueField2Alias}`
|
|
562
|
+
: '';
|
|
563
|
+
const percentageExpr = currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2
|
|
564
|
+
? `sum(${valueExpr}) / ${(currentAgg.valueField2Type ?? currentAgg.valueFieldType) === 'bool' ? 'COUNT(*)' : 'SUM(sum(' + valueExpr + ')) OVER ()'}`
|
|
565
|
+
: `sum(${valueExpr}) / GREATEST(sum(${value2Expr}), 1)`;
|
|
566
|
+
quillAggSelects = [
|
|
567
|
+
...quillAggSelects,
|
|
568
|
+
`${percentageExpr} as ${processColumnReference(`${currentAgg.valueField ?? ''}${disambiguation}`, databaseType, undefined, false, true)}`,
|
|
569
|
+
];
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
quillAggSelects = [
|
|
573
|
+
...quillAggSelects,
|
|
574
|
+
`${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference((currentAgg.valueField || 'count') + disambiguation, databaseType)}`,
|
|
575
|
+
];
|
|
576
|
+
}
|
|
577
|
+
if (valueAliasSubstring)
|
|
578
|
+
valueFieldAliases.push(valueAliasSubstring);
|
|
579
|
+
if (value2AliasSubstring)
|
|
580
|
+
valueFieldAliases.push(value2AliasSubstring);
|
|
581
|
+
});
|
|
582
|
+
valueFieldAliases = Array.from(new Set(valueFieldAliases));
|
|
583
|
+
const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit
|
|
584
|
+
? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} `
|
|
585
|
+
: ''}`;
|
|
586
|
+
const pivotQuery = `, quill_alias AS (
|
|
587
|
+
SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowAlias}${valueFieldAliases.length > 0 ? `, ${valueFieldAliases.join(', ')}` : ''}
|
|
588
|
+
FROM quill_base_table
|
|
589
|
+
),
|
|
590
|
+
quill_qt_cw AS (
|
|
591
|
+
SELECT ${quillAggSelects.join(', ')} FROM quill_alias GROUP BY ${rowAlias}
|
|
592
|
+
),
|
|
593
|
+
quill_base_pivot AS (
|
|
594
|
+
SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} *
|
|
595
|
+
FROM quill_qt_cw qt ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''}
|
|
596
|
+
)
|
|
597
|
+
SELECT * FROM quill_base_pivot`
|
|
425
598
|
.replace(/\s+/g, ' ')
|
|
426
599
|
.trim();
|
|
427
600
|
return itemQuery.replace(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/, pivotQuery);
|
|
@@ -432,33 +605,76 @@ function create1DDatePivotQuery(pivot, itemQuery, dateBucket = 'month', database
|
|
|
432
605
|
return undefined;
|
|
433
606
|
}
|
|
434
607
|
const rowField = pivot.rowField || '';
|
|
435
|
-
const valueField = pivot.valueField;
|
|
436
608
|
const rowFieldAlias = processColumnReference(rowField, databaseType, undefined);
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
609
|
+
let quillAggSelects = [`${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(rowField, databaseType)}`];
|
|
610
|
+
let valueFieldAliases = [];
|
|
611
|
+
const seenAggs = {};
|
|
612
|
+
pivot.aggregations?.forEach((currentAgg) => {
|
|
613
|
+
if (!currentAgg.valueField)
|
|
614
|
+
currentAgg.valueField = undefined;
|
|
615
|
+
// if the aggregation combo has been seen before, increment the count, else add it to the seenAggs array
|
|
616
|
+
if (seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']) {
|
|
617
|
+
seenAggs[currentAgg.aggregationType ?? ''][currentAgg.valueField ?? ''] += 1;
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
seenAggs[currentAgg.aggregationType ?? ''] = {
|
|
621
|
+
...seenAggs[currentAgg.aggregationType ?? ''],
|
|
622
|
+
[currentAgg.valueField ?? '']: 1,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']?.toString() ?? '';
|
|
626
|
+
if (disambiguationIndex === '1')
|
|
627
|
+
disambiguationIndex = '';
|
|
628
|
+
const valueFieldAlias = processColumnReference((currentAgg.valueField || rowField || 'count'), databaseType, undefined, false, true);
|
|
629
|
+
const valueAliasSubstring = currentAgg.valueField
|
|
630
|
+
? `${processColumnReference(currentAgg.valueField, databaseType, undefined, true)} AS ${valueFieldAlias}`
|
|
631
|
+
: '';
|
|
632
|
+
let value2AliasSubstring = '';
|
|
633
|
+
const disambiguation = pivot.aggregations?.length > 1
|
|
634
|
+
? `${disambiguationIndex}_${currentAgg.aggregationType}`
|
|
635
|
+
: '';
|
|
636
|
+
// Wrap the field in a CASE WHEN if it's boolean
|
|
637
|
+
let valueExpr = !currentAgg.valueField ? '*' : valueFieldAlias;
|
|
638
|
+
if (currentAgg.valueFieldType === 'bool') {
|
|
639
|
+
valueExpr = `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`;
|
|
640
|
+
}
|
|
641
|
+
if (currentAgg.aggregationType === 'percentage') {
|
|
642
|
+
if (!currentAgg.valueField) {
|
|
643
|
+
throw new Error('No value field provided for percentage aggregation');
|
|
644
|
+
}
|
|
645
|
+
const valueField2Alias = processColumnReference(currentAgg.valueField2 ?? currentAgg.valueField, databaseType, undefined, false, true);
|
|
646
|
+
value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2
|
|
647
|
+
? `${processColumnReference(currentAgg.valueField2, databaseType, undefined, true)} AS ${valueField2Alias}`
|
|
648
|
+
: '';
|
|
649
|
+
let value2Expr = valueField2Alias;
|
|
650
|
+
if ((currentAgg.valueField2Type ?? currentAgg.valueFieldType) === 'bool') {
|
|
651
|
+
value2Expr = `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`;
|
|
652
|
+
}
|
|
653
|
+
const percentageExpr = currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2
|
|
654
|
+
? `sum(${valueExpr}) / ${(currentAgg.valueField2Type ?? currentAgg.valueFieldType) === 'bool' ? 'COUNT(*)' : 'SUM(sum(' + valueExpr + ')) OVER ()'}`
|
|
655
|
+
: `sum(${valueExpr}) / GREATEST(sum(${value2Expr}), 1)`;
|
|
656
|
+
quillAggSelects = [
|
|
657
|
+
...quillAggSelects,
|
|
658
|
+
`${percentageExpr} as ${processColumnReference(`${currentAgg.valueField}${disambiguation}`, databaseType, undefined, false, true)}`,
|
|
659
|
+
];
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
quillAggSelects = [
|
|
663
|
+
...quillAggSelects,
|
|
664
|
+
`${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference((currentAgg.valueField || 'count') + disambiguation, databaseType)}`,
|
|
665
|
+
];
|
|
666
|
+
}
|
|
667
|
+
if (valueAliasSubstring)
|
|
668
|
+
valueFieldAliases.push(valueAliasSubstring);
|
|
669
|
+
if (value2AliasSubstring)
|
|
670
|
+
valueFieldAliases.push(value2AliasSubstring);
|
|
671
|
+
});
|
|
672
|
+
valueFieldAliases = Array.from(new Set(valueFieldAliases));
|
|
456
673
|
// pivot sort matters in the base query when there is a rowLimit. In mssql, an orderby must be accompanied by a limit in a subquery and not allowed in a cte
|
|
457
674
|
const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} ` : ''}`;
|
|
458
|
-
const pivotQuery = `, quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}${
|
|
459
|
-
${value2AliasSubstring}
|
|
675
|
+
const pivotQuery = `, quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}${valueFieldAliases.length > 0 ? `, ${valueFieldAliases.join(', ')}` : ''}
|
|
460
676
|
FROM quill_base_table),
|
|
461
|
-
quill_qt_agg AS (SELECT ${
|
|
677
|
+
quill_qt_agg AS (SELECT ${quillAggSelects.join(', ')}
|
|
462
678
|
FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)}),
|
|
463
679
|
quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} * FROM quill_qt_agg qt
|
|
464
680
|
${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''})
|
|
@@ -470,27 +686,89 @@ function create1DDatePivotQuery(pivot, itemQuery, dateBucket = 'month', database
|
|
|
470
686
|
}
|
|
471
687
|
function createAggregationValuePivot(pivot, itemQuery, databaseType) {
|
|
472
688
|
const isValidBaseQuery = itemQuery.match(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/);
|
|
473
|
-
if (!isValidBaseQuery
|
|
689
|
+
if (!isValidBaseQuery)
|
|
474
690
|
return undefined;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
691
|
+
let quillAggSelects = [];
|
|
692
|
+
let valueFieldAliases = [];
|
|
693
|
+
const seenAggs = {};
|
|
694
|
+
pivot.aggregations?.forEach((currentAgg) => {
|
|
695
|
+
if (!currentAgg.valueField)
|
|
696
|
+
currentAgg.valueField = undefined;
|
|
697
|
+
// Track duplicate aggregation combos for disambiguation.
|
|
698
|
+
if (seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']) {
|
|
699
|
+
seenAggs[currentAgg.aggregationType ?? ''][currentAgg.valueField ?? ''] += 1;
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
seenAggs[currentAgg.aggregationType ?? ''] = {
|
|
703
|
+
...seenAggs[currentAgg.aggregationType ?? ''],
|
|
704
|
+
[currentAgg.valueField ?? '']: 1,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
let disambiguationIndex = seenAggs[currentAgg.aggregationType ?? '']?.[currentAgg.valueField ?? '']?.toString() ?? '';
|
|
708
|
+
if (disambiguationIndex === '1')
|
|
709
|
+
disambiguationIndex = '';
|
|
710
|
+
const valueFieldAlias = processColumnReference((currentAgg.valueField || 'count'), databaseType, undefined, false, true);
|
|
711
|
+
const valueAliasSubstring = currentAgg.valueField
|
|
712
|
+
? `${processColumnReference(currentAgg.valueField, databaseType, undefined, true)} AS ${valueFieldAlias}`
|
|
713
|
+
: '';
|
|
714
|
+
let value2AliasSubstring = '';
|
|
715
|
+
const disambiguation = pivot.aggregations?.length > 1
|
|
716
|
+
? `${disambiguationIndex}_${currentAgg.aggregationType}`
|
|
717
|
+
: '';
|
|
718
|
+
// If the field type is bool, wrap it in a CASE WHEN
|
|
719
|
+
let valueExpr = !currentAgg.valueField ? '*' : valueFieldAlias;
|
|
720
|
+
valueExpr = currentAgg.valueFieldType === 'bool'
|
|
721
|
+
? `CASE WHEN ${valueFieldAlias} THEN 1 ELSE 0 END`
|
|
722
|
+
: valueExpr;
|
|
723
|
+
if (currentAgg.aggregationType === 'percentage') {
|
|
724
|
+
if (!currentAgg.valueField) {
|
|
725
|
+
throw new Error('No value field provided for percentage aggregation');
|
|
726
|
+
}
|
|
727
|
+
const valueField2Alias = processColumnReference(currentAgg.valueField2 ?? currentAgg.valueField, databaseType, undefined, false, true);
|
|
728
|
+
const value2Expr = (currentAgg.valueField2Type ?? currentAgg.valueFieldType) === 'bool'
|
|
729
|
+
? `CASE WHEN ${valueField2Alias} THEN 1 ELSE 0 END`
|
|
730
|
+
: valueField2Alias;
|
|
731
|
+
value2AliasSubstring = currentAgg.valueField2 && currentAgg.valueField !== currentAgg.valueField2
|
|
732
|
+
? `${processColumnReference(currentAgg.valueField2, databaseType, undefined, true)} AS ${valueField2Alias}`
|
|
733
|
+
: '';
|
|
734
|
+
const percentageExpr = currentAgg.valueField === currentAgg.valueField2 || !currentAgg.valueField2
|
|
735
|
+
? `sum(${valueExpr}) / ${(currentAgg.valueField2Type ?? currentAgg.valueFieldType) === 'bool' ? 'COUNT(*)' : 'SUM(sum(' + valueExpr + ')) OVER ()'}`
|
|
736
|
+
: `sum(${valueExpr}) / GREATEST(sum(${value2Expr}), 1)`;
|
|
737
|
+
quillAggSelects = [
|
|
738
|
+
...quillAggSelects,
|
|
739
|
+
`${percentageExpr} as ${processColumnReference(`${currentAgg.valueField ?? ''}${disambiguation}`, databaseType, undefined, false, true)}`,
|
|
740
|
+
];
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
quillAggSelects = [
|
|
744
|
+
...quillAggSelects,
|
|
745
|
+
`${processAggType(currentAgg.aggregationType)}(${valueExpr}) AS ${processColumnReference((currentAgg.valueField || 'count') + disambiguation, databaseType)}`,
|
|
746
|
+
];
|
|
747
|
+
}
|
|
748
|
+
if (valueAliasSubstring)
|
|
749
|
+
valueFieldAliases.push(valueAliasSubstring);
|
|
750
|
+
if (value2AliasSubstring)
|
|
751
|
+
valueFieldAliases.push(value2AliasSubstring);
|
|
752
|
+
});
|
|
753
|
+
valueFieldAliases = Array.from(new Set(valueFieldAliases));
|
|
754
|
+
const sortQuery = pivot.sort && pivot.sortField && pivot.rowLimit
|
|
755
|
+
? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} `
|
|
756
|
+
: '';
|
|
757
|
+
const pivotQuery = `, quill_alias AS (
|
|
758
|
+
SELECT ${valueFieldAliases.join(', ')} FROM quill_base_table
|
|
759
|
+
),
|
|
760
|
+
quill_qt_agg AS (
|
|
761
|
+
SELECT ${quillAggSelects.join(', ')} FROM quill_alias
|
|
762
|
+
),
|
|
763
|
+
quill_base_pivot AS (
|
|
764
|
+
SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql'
|
|
765
|
+
? `TOP ${pivot.rowLimit}`
|
|
766
|
+
: ''} * FROM quill_qt_agg qt
|
|
767
|
+
${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql'
|
|
768
|
+
? ` LIMIT ${pivot.rowLimit}`
|
|
769
|
+
: ''}
|
|
770
|
+
)
|
|
771
|
+
SELECT * FROM quill_base_pivot`
|
|
494
772
|
.replace(/\s+/g, ' ')
|
|
495
773
|
.trim();
|
|
496
774
|
return itemQuery.replace(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/, pivotQuery);
|
|
@@ -502,19 +780,19 @@ export function additionalProcessingOnPivotQuery(pivot, query, additionalProcess
|
|
|
502
780
|
if (!isValidBaseQuery) {
|
|
503
781
|
return undefined;
|
|
504
782
|
}
|
|
783
|
+
if (!pivot.aggregations || pivot.aggregations.length === 0) {
|
|
784
|
+
if (pivot.aggregationType) {
|
|
785
|
+
pivot.aggregations = [{ aggregationType: pivot.aggregationType, valueField: pivot.valueField, valueField2: pivot.valueField2 }];
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
throw new Error('No aggregations provided for pivot');
|
|
789
|
+
}
|
|
790
|
+
}
|
|
505
791
|
let rowsPerPage = 0;
|
|
506
792
|
let currentInterval = 0;
|
|
507
793
|
let offset = 0;
|
|
508
794
|
let limit = 1000;
|
|
509
|
-
let sortQuery =
|
|
510
|
-
? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, undefined, true)} ${pivot.sortDirection || ''}`
|
|
511
|
-
: pivot.rowField
|
|
512
|
-
? `ORDER BY ${processColumnReference(`${pivot.rowField}`, databaseType)}`
|
|
513
|
-
: pivot.valueField
|
|
514
|
-
? pivot.aggregationType === 'percentage'
|
|
515
|
-
? `ORDER BY ${processColumnReference(`${pivot.valueField}_${matchCasing('percentage', pivot.valueField)}`, databaseType, undefined, false, true)}`
|
|
516
|
-
: `ORDER BY ${processColumnReference(pivot.valueField, databaseType, undefined, false, true)}`
|
|
517
|
-
: ''}`;
|
|
795
|
+
let sortQuery = '';
|
|
518
796
|
if (additionalProcessing.page) {
|
|
519
797
|
const page = additionalProcessing.page.page || 0;
|
|
520
798
|
if (additionalProcessing.page.rowsPerRequest) {
|
|
@@ -524,9 +802,18 @@ export function additionalProcessingOnPivotQuery(pivot, query, additionalProcess
|
|
|
524
802
|
currentInterval = page ? Math.floor(page / (limit / rowsPerPage)) : 0;
|
|
525
803
|
offset = currentInterval * limit;
|
|
526
804
|
}
|
|
805
|
+
const disambiguation = pivot.aggregations.length > 1
|
|
806
|
+
? `_${matchCasing(pivot.aggregations?.[0]?.aggregationType, pivot.aggregations?.[0]?.valueField)}`
|
|
807
|
+
: '';
|
|
527
808
|
if (additionalProcessing.sort) {
|
|
528
809
|
sortQuery = `ORDER BY ${processColumnReference(additionalProcessing.sort.field, databaseType)} ${additionalProcessing.sort.direction || ''}`;
|
|
529
810
|
}
|
|
811
|
+
else {
|
|
812
|
+
const valueFieldAlias = processColumnReference((pivot.aggregations?.[0]?.valueField ?? '') + disambiguation, databaseType, undefined, false, true);
|
|
813
|
+
const defaultSortField = pivot.sortField || pivot.rowField || valueFieldAlias;
|
|
814
|
+
const defaultSortDirection = pivot.sortDirection || '';
|
|
815
|
+
sortQuery = `ORDER BY ${processColumnReference(defaultSortField, databaseType)} ${defaultSortDirection}`;
|
|
816
|
+
}
|
|
530
817
|
const additionalProcessingQuery = `
|
|
531
818
|
SELECT *
|
|
532
819
|
FROM quill_base_pivot ${sortQuery}${databaseType === 'mssql' ? ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY` : ` LIMIT ${limit} OFFSET ${offset}`}
|