@lightdash/common 0.1972.0 → 0.1973.1
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/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.js +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.js.map +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts.map +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.js +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.js.map +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolTableVizArgs.js +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolTableVizArgs.js.map +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.js +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.js.map +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolVerticalBarArgs.js +1 -1
- package/dist/cjs/ee/AiAgent/schemas/tools/toolVerticalBarArgs.js.map +1 -1
- package/dist/cjs/pivot/pivotQueryResults.d.ts +13 -0
- package/dist/cjs/pivot/pivotQueryResults.d.ts.map +1 -1
- package/dist/cjs/pivot/pivotQueryResults.js +507 -62
- package/dist/cjs/pivot/pivotQueryResults.js.map +1 -1
- package/dist/cjs/pivot/pivotQueryResults.mock.d.ts +14 -0
- package/dist/cjs/pivot/pivotQueryResults.mock.d.ts.map +1 -1
- package/dist/cjs/pivot/pivotQueryResults.mock.js +2343 -1
- package/dist/cjs/pivot/pivotQueryResults.mock.js.map +1 -1
- package/dist/cjs/pivot/pivotQueryResults.test.js +391 -0
- package/dist/cjs/pivot/pivotQueryResults.test.js.map +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.js +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.js.map +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts.map +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.js +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.js.map +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolTableVizArgs.js +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolTableVizArgs.js.map +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.js +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.js.map +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolVerticalBarArgs.js +1 -1
- package/dist/esm/ee/AiAgent/schemas/tools/toolVerticalBarArgs.js.map +1 -1
- package/dist/esm/pivot/pivotQueryResults.d.ts +13 -0
- package/dist/esm/pivot/pivotQueryResults.d.ts.map +1 -1
- package/dist/esm/pivot/pivotQueryResults.js +505 -61
- package/dist/esm/pivot/pivotQueryResults.js.map +1 -1
- package/dist/esm/pivot/pivotQueryResults.mock.d.ts +14 -0
- package/dist/esm/pivot/pivotQueryResults.mock.d.ts.map +1 -1
- package/dist/esm/pivot/pivotQueryResults.mock.js +2341 -0
- package/dist/esm/pivot/pivotQueryResults.mock.js.map +1 -1
- package/dist/esm/pivot/pivotQueryResults.test.js +393 -2
- package/dist/esm/pivot/pivotQueryResults.test.js.map +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts.map +1 -1
- package/dist/types/pivot/pivotQueryResults.d.ts +13 -0
- package/dist/types/pivot/pivotQueryResults.d.ts.map +1 -1
- package/dist/types/pivot/pivotQueryResults.mock.d.ts +14 -0
- package/dist/types/pivot/pivotQueryResults.mock.d.ts.map +1 -1
- package/package.json +1 -1
@@ -242,6 +242,77 @@ const combinedRetrofit = (data, getField, getFieldLabel) => {
|
|
242
242
|
data.retrofitData = { allCombinedData, pivotColumnInfo };
|
243
243
|
return data;
|
244
244
|
};
|
245
|
+
const getHeaderValueTypes = ({ metricsAsRows, pivotDimensionNames, }) => {
|
246
|
+
const headerDimensionValueTypes = pivotDimensionNames.map((fieldId) => ({
|
247
|
+
type: FieldType.DIMENSION,
|
248
|
+
fieldId,
|
249
|
+
}));
|
250
|
+
const headerMetricValueTypes = metricsAsRows
|
251
|
+
? []
|
252
|
+
: [{ type: FieldType.METRIC }];
|
253
|
+
return [...headerDimensionValueTypes, ...headerMetricValueTypes];
|
254
|
+
};
|
255
|
+
const getColumnTotals = ({ summableMetricFieldIds, rowIndices, totalColumns, dataColumns, dataValues, hasIndex, pivotConfig, indexValues, }) => {
|
256
|
+
let columnTotalFields;
|
257
|
+
let columnTotals;
|
258
|
+
const N_DATA_COLUMNS = dataColumns;
|
259
|
+
if (pivotConfig.columnTotals && hasIndex) {
|
260
|
+
if (pivotConfig.metricsAsRows) {
|
261
|
+
const N_TOTAL_ROWS = summableMetricFieldIds.length;
|
262
|
+
const N_TOTAL_COLS = totalColumns;
|
263
|
+
columnTotalFields = create2DArray(N_TOTAL_ROWS, N_TOTAL_COLS);
|
264
|
+
columnTotals = create2DArray(N_TOTAL_ROWS, N_DATA_COLUMNS);
|
265
|
+
summableMetricFieldIds.forEach((fieldId, metricIndex) => {
|
266
|
+
columnTotalFields[metricIndex][N_TOTAL_COLS - 1] = {
|
267
|
+
fieldId,
|
268
|
+
};
|
269
|
+
});
|
270
|
+
columnTotals = columnTotals.map((row, rowIndex) => row.map((_, totalColIndex) => {
|
271
|
+
const totalColFieldId = columnTotalFields[rowIndex][N_TOTAL_COLS - 1]?.fieldId;
|
272
|
+
const valueColIndices = totalColFieldId
|
273
|
+
? getAllIndicesForFieldId(rowIndices, totalColFieldId)
|
274
|
+
: [];
|
275
|
+
return dataValues
|
276
|
+
.filter((__, dataValueColIndex) => valueColIndices.includes(dataValueColIndex))
|
277
|
+
.reduce((acc, value) => acc + parseNumericValue(value[totalColIndex]), 0);
|
278
|
+
}));
|
279
|
+
}
|
280
|
+
else {
|
281
|
+
const N_TOTAL_COLS = indexValues[0].length;
|
282
|
+
const N_TOTAL_ROWS = 1;
|
283
|
+
columnTotalFields = create2DArray(N_TOTAL_ROWS, N_TOTAL_COLS);
|
284
|
+
columnTotals = create2DArray(N_TOTAL_ROWS, N_DATA_COLUMNS);
|
285
|
+
// set the last index cell as the "Total"
|
286
|
+
columnTotalFields[N_TOTAL_ROWS - 1][N_TOTAL_COLS - 1] = {
|
287
|
+
fieldId: undefined,
|
288
|
+
};
|
289
|
+
columnTotals = columnTotals.map((row, _totalRowIndex) => row.map((_col, colIndex) => dataValues
|
290
|
+
.map((dataRow) => dataRow[colIndex])
|
291
|
+
.reduce((acc, value) => acc + parseNumericValue(value), 0)));
|
292
|
+
}
|
293
|
+
}
|
294
|
+
return { columnTotalFields, columnTotals };
|
295
|
+
};
|
296
|
+
const getTitleFields = ({ hasHeader, hasIndex, headerValueTypes, indexValueTypes, }) => {
|
297
|
+
const titleFields = create2DArray(hasHeader ? headerValueTypes.length : 1, hasIndex ? indexValueTypes.length : 1);
|
298
|
+
headerValueTypes.forEach((headerValueType, headerIndex) => {
|
299
|
+
if (headerValueType.type === FieldType.DIMENSION) {
|
300
|
+
titleFields[headerIndex][hasIndex ? indexValueTypes.length - 1 : 0] = {
|
301
|
+
fieldId: headerValueType.fieldId,
|
302
|
+
direction: 'header',
|
303
|
+
};
|
304
|
+
}
|
305
|
+
});
|
306
|
+
indexValueTypes.forEach((indexValueType, indexIndex) => {
|
307
|
+
if (indexValueType.type === FieldType.DIMENSION) {
|
308
|
+
titleFields[hasHeader ? headerValueTypes.length - 1 : 0][indexIndex] = {
|
309
|
+
fieldId: indexValueType.fieldId,
|
310
|
+
direction: 'index',
|
311
|
+
};
|
312
|
+
}
|
313
|
+
});
|
314
|
+
return titleFields;
|
315
|
+
};
|
245
316
|
export const pivotQueryResults = ({ pivotConfig, metricQuery, rows, options, getField, getFieldLabel, groupedSubtotals, }) => {
|
246
317
|
if (rows.length === 0) {
|
247
318
|
throw new Error('Cannot pivot results with no rows');
|
@@ -260,15 +331,10 @@ export const pivotQueryResults = ({ pivotConfig, metricQuery, rows, options, get
|
|
260
331
|
const dimensions = [...metricQuery.dimensions];
|
261
332
|
// Headers (column index)
|
262
333
|
const headerDimensions = pivotConfig.pivotDimensions.filter((pivotDimension) => dimensions.includes(pivotDimension));
|
263
|
-
const
|
264
|
-
|
265
|
-
|
266
|
-
})
|
267
|
-
const headerMetricValueTypes = pivotConfig.metricsAsRows ? [] : [{ type: FieldType.METRIC }];
|
268
|
-
const headerValueTypes = [
|
269
|
-
...headerDimensionValueTypes,
|
270
|
-
...headerMetricValueTypes,
|
271
|
-
];
|
334
|
+
const headerValueTypes = getHeaderValueTypes({
|
335
|
+
metricsAsRows: pivotConfig.metricsAsRows,
|
336
|
+
pivotDimensionNames: headerDimensions,
|
337
|
+
});
|
272
338
|
// Indices (row index)
|
273
339
|
const indexDimensions = dimensions
|
274
340
|
.filter((d) => !pivotConfig.pivotDimensions.includes(d))
|
@@ -440,59 +506,21 @@ export const pivotQueryResults = ({ pivotConfig, metricQuery, rows, options, get
|
|
440
506
|
}));
|
441
507
|
}
|
442
508
|
}
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
columnTotalFields[metricIndex][N_TOTAL_COLS - 1] = {
|
453
|
-
fieldId,
|
454
|
-
};
|
455
|
-
});
|
456
|
-
columnTotals = columnTotals.map((row, rowIndex) => row.map((_, totalColIndex) => {
|
457
|
-
const totalColFieldId = columnTotalFields[rowIndex][N_TOTAL_COLS - 1]?.fieldId;
|
458
|
-
const valueColIndices = totalColFieldId
|
459
|
-
? getAllIndicesForFieldId(rowIndices, totalColFieldId)
|
460
|
-
: [];
|
461
|
-
return dataValues
|
462
|
-
.filter((__, dataValueColIndex) => valueColIndices.includes(dataValueColIndex))
|
463
|
-
.reduce((acc, value) => acc + parseNumericValue(value[totalColIndex]), 0);
|
464
|
-
}));
|
465
|
-
}
|
466
|
-
else {
|
467
|
-
const N_TOTAL_COLS = indexValues[0].length;
|
468
|
-
const N_TOTAL_ROWS = 1;
|
469
|
-
columnTotalFields = create2DArray(N_TOTAL_ROWS, N_TOTAL_COLS);
|
470
|
-
columnTotals = create2DArray(N_TOTAL_ROWS, N_DATA_COLUMNS);
|
471
|
-
// set the last index cell as the "Total"
|
472
|
-
columnTotalFields[N_TOTAL_ROWS - 1][N_TOTAL_COLS - 1] = {
|
473
|
-
fieldId: undefined,
|
474
|
-
};
|
475
|
-
columnTotals = columnTotals.map((row, _totalRowIndex) => row.map((_col, colIndex) => dataValues
|
476
|
-
.map((dataRow) => dataRow[colIndex])
|
477
|
-
.reduce((acc, value) => acc + parseNumericValue(value), 0)));
|
478
|
-
}
|
479
|
-
}
|
480
|
-
const titleFields = create2DArray(hasHeader ? headerValueTypes.length : 1, hasIndex ? indexValueTypes.length : 1);
|
481
|
-
headerValueTypes.forEach((headerValueType, headerIndex) => {
|
482
|
-
if (headerValueType.type === FieldType.DIMENSION) {
|
483
|
-
titleFields[headerIndex][hasIndex ? indexValueTypes.length - 1 : 0] = {
|
484
|
-
fieldId: headerValueType.fieldId,
|
485
|
-
direction: 'header',
|
486
|
-
};
|
487
|
-
}
|
509
|
+
const { columnTotalFields, columnTotals } = getColumnTotals({
|
510
|
+
summableMetricFieldIds,
|
511
|
+
totalColumns: indexValueTypes.length,
|
512
|
+
dataColumns: N_DATA_COLUMNS,
|
513
|
+
dataValues,
|
514
|
+
rowIndices,
|
515
|
+
pivotConfig,
|
516
|
+
indexValues,
|
517
|
+
hasIndex,
|
488
518
|
});
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
};
|
495
|
-
}
|
519
|
+
const titleFields = getTitleFields({
|
520
|
+
hasIndex,
|
521
|
+
hasHeader,
|
522
|
+
headerValueTypes,
|
523
|
+
indexValueTypes,
|
496
524
|
});
|
497
525
|
const cellsCount = (indexValueTypes.length === 0 ? titleFields[0].length : 0) +
|
498
526
|
indexValueTypes.length +
|
@@ -566,4 +594,420 @@ export const pivotResultsAsCsv = ({ pivotConfig, rows, itemMap, metricQuery, cus
|
|
566
594
|
});
|
567
595
|
return [...headers, ...pivotedRows];
|
568
596
|
};
|
597
|
+
/**
|
598
|
+
* Converts SQL-pivoted results to PivotData format
|
599
|
+
* This handles results that are already pivoted at the SQL level (e.g., payments_total_revenue_any_bank_transfer)
|
600
|
+
* and transforms them into the same PivotData structure as pivotQueryResults
|
601
|
+
*/
|
602
|
+
export const convertSqlPivotedRowsToPivotData = ({ rows, pivotDetails, pivotConfig, getField, getFieldLabel, }) => {
|
603
|
+
if (rows.length === 0) {
|
604
|
+
throw new Error('Cannot convert SQL pivoted results with no rows');
|
605
|
+
}
|
606
|
+
const hiddenMetricFieldIds = pivotConfig.hiddenMetricFieldIds || [];
|
607
|
+
// Extract information from pivot details metadata
|
608
|
+
let indexColumns;
|
609
|
+
if (pivotDetails.indexColumn) {
|
610
|
+
if (Array.isArray(pivotDetails.indexColumn)) {
|
611
|
+
indexColumns = pivotDetails.indexColumn.map((col) => col.reference);
|
612
|
+
}
|
613
|
+
else {
|
614
|
+
indexColumns = [pivotDetails.indexColumn.reference];
|
615
|
+
}
|
616
|
+
}
|
617
|
+
else {
|
618
|
+
indexColumns = [];
|
619
|
+
}
|
620
|
+
// Get unique base metrics from valuesColumns
|
621
|
+
const baseMetricsArray = Array.from(new Set(pivotDetails.valuesColumns.map((col) => col.referenceField)));
|
622
|
+
const headerValueTypes = getHeaderValueTypes({
|
623
|
+
metricsAsRows: pivotConfig.metricsAsRows,
|
624
|
+
pivotDimensionNames: (pivotDetails.groupByColumns ?? []).map(({ reference }) => reference),
|
625
|
+
});
|
626
|
+
const indexValueTypes = pivotConfig.metricsAsRows
|
627
|
+
? [
|
628
|
+
...indexColumns.map((col) => ({
|
629
|
+
type: FieldType.DIMENSION,
|
630
|
+
fieldId: col,
|
631
|
+
})),
|
632
|
+
{ type: FieldType.METRIC },
|
633
|
+
]
|
634
|
+
: indexColumns.map((col) => ({
|
635
|
+
type: FieldType.DIMENSION,
|
636
|
+
fieldId: col,
|
637
|
+
}));
|
638
|
+
// Build header values (pivot dimension values)
|
639
|
+
const headerValues = [];
|
640
|
+
pivotDetails.groupByColumns?.forEach(({ reference }, index) => {
|
641
|
+
headerValues.push([]);
|
642
|
+
let columns = pivotDetails.valuesColumns;
|
643
|
+
if (pivotConfig.metricsAsRows) {
|
644
|
+
// For metrics as rows, we only need unique combinations of pivot values, excluding per metric duplicates
|
645
|
+
columns = Array.from(new Map(columns.map((col) => [
|
646
|
+
col.pivotValues
|
647
|
+
.map(({ referenceField, value }) => `${referenceField}:${value}`)
|
648
|
+
.join('|'),
|
649
|
+
col,
|
650
|
+
])).values());
|
651
|
+
}
|
652
|
+
columns.forEach(({ pivotValues, pivotColumnName }) => {
|
653
|
+
const pivotValue = pivotValues.find(({ referenceField }) => referenceField === reference);
|
654
|
+
if (pivotValue) {
|
655
|
+
const field = getField(pivotValue.referenceField);
|
656
|
+
const formattedValue = field
|
657
|
+
? formatItemValue(field, pivotValue.value)
|
658
|
+
: String(pivotValue.value);
|
659
|
+
const allColumnsWithSamePivotValues = columns.filter((matchingColumn) => {
|
660
|
+
const referencesToMatch = (pivotDetails.groupByColumns || [])
|
661
|
+
.map((groupByColumn) => groupByColumn.reference)
|
662
|
+
.slice(0, index + 1);
|
663
|
+
// Match all pivot values
|
664
|
+
return referencesToMatch.every((referenceToMatch) => {
|
665
|
+
const pivotValueA = pivotValues.find(({ referenceField: referenceField3 }) => referenceField3 === referenceToMatch);
|
666
|
+
const pivotValueB = matchingColumn.pivotValues.find(({ referenceField: referenceField3 }) => referenceField3 === referenceToMatch);
|
667
|
+
return pivotValueA?.value === pivotValueB?.value;
|
668
|
+
});
|
669
|
+
});
|
670
|
+
const isFirstInGroup = allColumnsWithSamePivotValues[0].pivotColumnName ===
|
671
|
+
pivotColumnName;
|
672
|
+
headerValues[index].push({
|
673
|
+
type: 'value',
|
674
|
+
fieldId: reference,
|
675
|
+
value: {
|
676
|
+
raw: pivotValue.value,
|
677
|
+
formatted: formattedValue,
|
678
|
+
},
|
679
|
+
colSpan: isFirstInGroup
|
680
|
+
? allColumnsWithSamePivotValues.length
|
681
|
+
: 0,
|
682
|
+
});
|
683
|
+
}
|
684
|
+
});
|
685
|
+
});
|
686
|
+
// Add metric labels for columns if not metrics as rows
|
687
|
+
if (!pivotConfig.metricsAsRows && baseMetricsArray.length > 0) {
|
688
|
+
headerValues.push(pivotDetails.valuesColumns.map(({ referenceField }) => ({
|
689
|
+
type: 'label',
|
690
|
+
fieldId: referenceField,
|
691
|
+
})));
|
692
|
+
}
|
693
|
+
const filteredHeaderValues = headerValues.filter((row) => row.length > 0);
|
694
|
+
// Build index values (row identifiers)
|
695
|
+
let indexValues;
|
696
|
+
if (pivotConfig.metricsAsRows) {
|
697
|
+
indexValues = rows.reduce((acc, row) => {
|
698
|
+
// multiply rows per metric
|
699
|
+
baseMetricsArray.forEach((metric) => {
|
700
|
+
acc.push([
|
701
|
+
...indexColumns.map((col) => ({
|
702
|
+
type: 'value',
|
703
|
+
fieldId: col,
|
704
|
+
value: row[col].value,
|
705
|
+
colSpan: 1,
|
706
|
+
})),
|
707
|
+
{
|
708
|
+
type: 'label',
|
709
|
+
fieldId: metric,
|
710
|
+
},
|
711
|
+
]);
|
712
|
+
});
|
713
|
+
return acc;
|
714
|
+
}, []);
|
715
|
+
}
|
716
|
+
else {
|
717
|
+
indexValues = rows.map((row) => indexColumns.map((col) => ({
|
718
|
+
type: 'value',
|
719
|
+
fieldId: col,
|
720
|
+
value: row[col].value,
|
721
|
+
colSpan: 1,
|
722
|
+
})));
|
723
|
+
}
|
724
|
+
// Build data values (the actual pivot data)
|
725
|
+
let dataValues;
|
726
|
+
const rowIndices = {};
|
727
|
+
let rowCount = 0;
|
728
|
+
// Get unique columns
|
729
|
+
const uniqueColumns = pivotConfig.metricsAsRows
|
730
|
+
? Array.from(new Map(pivotDetails.valuesColumns.map((col) => [
|
731
|
+
col.pivotValues
|
732
|
+
.map(({ referenceField, value }) => `${referenceField}:${value}`)
|
733
|
+
.join('|'),
|
734
|
+
col,
|
735
|
+
])).values())
|
736
|
+
: pivotDetails.valuesColumns;
|
737
|
+
if (pivotConfig.metricsAsRows) {
|
738
|
+
// multiply rows per metric
|
739
|
+
dataValues = rows.reduce((acc, row) => {
|
740
|
+
baseMetricsArray.forEach((metric) => {
|
741
|
+
const indexRowValues = [
|
742
|
+
...indexColumns.map((fieldId) => String(row[fieldId].value.raw)),
|
743
|
+
metric,
|
744
|
+
];
|
745
|
+
// Build row data for this metric using unique columns
|
746
|
+
// For each unique column combination, find the corresponding value for this metric
|
747
|
+
const rowData = uniqueColumns.map((uniqueCol) => {
|
748
|
+
// Find the actual column in sortedValuesColumns that matches this unique combination and metric
|
749
|
+
const matchingColumn = pivotDetails.valuesColumns.find((col) => col.referenceField === metric &&
|
750
|
+
col.pivotValues.every((pv) => uniqueCol.pivotValues.some((upv) => upv.referenceField ===
|
751
|
+
pv.referenceField &&
|
752
|
+
upv.value === pv.value)));
|
753
|
+
return matchingColumn && row[matchingColumn.pivotColumnName]
|
754
|
+
? row[matchingColumn.pivotColumnName].value
|
755
|
+
: null;
|
756
|
+
});
|
757
|
+
acc.push(rowData);
|
758
|
+
setIndexByKey(rowIndices, indexRowValues, rowCount);
|
759
|
+
rowCount += 1;
|
760
|
+
});
|
761
|
+
return acc;
|
762
|
+
}, []);
|
763
|
+
}
|
764
|
+
else {
|
765
|
+
dataValues = rows.map((row, rowIndex) => {
|
766
|
+
const indexRowValues = indexColumns.map((fieldId) => String(row[fieldId].value.raw));
|
767
|
+
setIndexByKey(rowIndices, indexRowValues, rowIndex);
|
768
|
+
return pivotDetails.valuesColumns.map((valueCol) => row[valueCol.pivotColumnName]
|
769
|
+
? row[valueCol.pivotColumnName].value
|
770
|
+
: null);
|
771
|
+
});
|
772
|
+
rowCount = rows.length;
|
773
|
+
}
|
774
|
+
const fullPivotConfig = {
|
775
|
+
pivotDimensions: pivotDetails.groupByColumns?.map((col) => col.reference) || [],
|
776
|
+
metricsAsRows: pivotConfig.metricsAsRows || false,
|
777
|
+
columnOrder: pivotConfig.columnOrder,
|
778
|
+
hiddenMetricFieldIds: pivotConfig.hiddenMetricFieldIds || [],
|
779
|
+
columnTotals: pivotConfig.columnTotals,
|
780
|
+
rowTotals: pivotConfig.rowTotals,
|
781
|
+
};
|
782
|
+
// Compute row totals if requested
|
783
|
+
let rowTotalFields;
|
784
|
+
let rowTotals;
|
785
|
+
const hasHeader = headerValueTypes.length > 0;
|
786
|
+
const hasIndex = indexValueTypes.length > 0;
|
787
|
+
const N_DATA_ROWS = hasIndex ? dataValues.length : 1;
|
788
|
+
const N_DATA_COLUMNS = hasHeader ? uniqueColumns.length : 1;
|
789
|
+
// Build column indices using unique columns
|
790
|
+
const columnIndices = uniqueColumns.reduce((acc, valuesColumn, index) => {
|
791
|
+
const pivotValues = (pivotDetails.groupByColumns || []).map((col) => valuesColumn.pivotValues.find(({ referenceField }) => referenceField === col.reference)?.value);
|
792
|
+
// Create nested structure based on pivotValues
|
793
|
+
let current = acc;
|
794
|
+
for (let i = 0; i < pivotValues.length; i += 1) {
|
795
|
+
const pivotValue = String(pivotValues[i]);
|
796
|
+
if (!Object.prototype.hasOwnProperty.call(current, pivotValue)) {
|
797
|
+
Object.defineProperty(current, pivotValue, {
|
798
|
+
value: {},
|
799
|
+
writable: true,
|
800
|
+
enumerable: true,
|
801
|
+
configurable: true,
|
802
|
+
});
|
803
|
+
}
|
804
|
+
current = current[pivotValue];
|
805
|
+
}
|
806
|
+
// For metricsAsRows, don't include metric in column key
|
807
|
+
if (!pivotConfig.metricsAsRows) {
|
808
|
+
Object.defineProperty(current, valuesColumn.referenceField, {
|
809
|
+
value: index,
|
810
|
+
writable: true,
|
811
|
+
enumerable: true,
|
812
|
+
configurable: true,
|
813
|
+
});
|
814
|
+
}
|
815
|
+
else {
|
816
|
+
// Use a generic key since metrics are in rows, not columns
|
817
|
+
Object.defineProperty(current, 'value', {
|
818
|
+
value: index,
|
819
|
+
writable: true,
|
820
|
+
enumerable: true,
|
821
|
+
configurable: true,
|
822
|
+
});
|
823
|
+
}
|
824
|
+
return acc;
|
825
|
+
}, {});
|
826
|
+
const summableMetricFieldIds = baseMetricsArray.filter((metricId) => {
|
827
|
+
const field = getField(metricId);
|
828
|
+
// Skip if field is not found or is a dimension or is hidden
|
829
|
+
if (!field || isDimension(field))
|
830
|
+
return false;
|
831
|
+
if (hiddenMetricFieldIds.includes(metricId))
|
832
|
+
return false;
|
833
|
+
return isSummable(field);
|
834
|
+
});
|
835
|
+
if (fullPivotConfig.rowTotals && hasHeader) {
|
836
|
+
if (fullPivotConfig.metricsAsRows) {
|
837
|
+
const N_TOTAL_COLS = 1;
|
838
|
+
const N_TOTAL_ROWS = headerValues.length;
|
839
|
+
rowTotalFields = create2DArray(N_TOTAL_ROWS, N_TOTAL_COLS);
|
840
|
+
rowTotals = create2DArray(N_DATA_ROWS, N_TOTAL_COLS);
|
841
|
+
// set the last header cell as the "Total"
|
842
|
+
rowTotalFields[N_TOTAL_ROWS - 1][N_TOTAL_COLS - 1] = {
|
843
|
+
fieldId: undefined,
|
844
|
+
};
|
845
|
+
rowTotals = rowTotals.map((row, rowIndex) => row.map(() => dataValues[rowIndex].reduce((acc, value) => acc + parseNumericValue(value), 0)));
|
846
|
+
}
|
847
|
+
else {
|
848
|
+
const N_TOTAL_COLS = summableMetricFieldIds.length;
|
849
|
+
const N_TOTAL_ROWS = headerValues.length;
|
850
|
+
rowTotalFields = create2DArray(N_TOTAL_ROWS, N_TOTAL_COLS);
|
851
|
+
rowTotals = create2DArray(rows.length, N_TOTAL_COLS);
|
852
|
+
// Set the last header cell as the "Total"
|
853
|
+
summableMetricFieldIds.forEach((fieldId, metricIndex) => {
|
854
|
+
rowTotalFields[N_TOTAL_ROWS - 1][metricIndex] = {
|
855
|
+
fieldId,
|
856
|
+
};
|
857
|
+
});
|
858
|
+
rowTotals = rowTotals.map((row, rowIndex) => row.map((_, totalColIndex) => {
|
859
|
+
const totalColFieldId = rowTotalFields[N_TOTAL_ROWS - 1][totalColIndex]
|
860
|
+
?.fieldId;
|
861
|
+
const valueColIndices = totalColFieldId
|
862
|
+
? getAllIndicesForFieldId(columnIndices, totalColFieldId)
|
863
|
+
: [];
|
864
|
+
return dataValues[rowIndex]
|
865
|
+
.filter((__, dataValueColIndex) => valueColIndices.includes(dataValueColIndex))
|
866
|
+
.reduce((acc, value) => acc + parseNumericValue(value), 0);
|
867
|
+
}));
|
868
|
+
}
|
869
|
+
}
|
870
|
+
const { columnTotalFields, columnTotals } = getColumnTotals({
|
871
|
+
summableMetricFieldIds,
|
872
|
+
totalColumns: indexValueTypes.length,
|
873
|
+
dataColumns: N_DATA_COLUMNS,
|
874
|
+
dataValues,
|
875
|
+
rowIndices,
|
876
|
+
pivotConfig,
|
877
|
+
indexValues,
|
878
|
+
hasIndex,
|
879
|
+
});
|
880
|
+
// Build retrofit data for backwards compatibility
|
881
|
+
const allCombinedData = rows
|
882
|
+
.map((row) => {
|
883
|
+
const combinedRow = {};
|
884
|
+
// Add index columns
|
885
|
+
indexColumns.forEach((col) => {
|
886
|
+
combinedRow[col] = row[col];
|
887
|
+
});
|
888
|
+
if (pivotConfig.metricsAsRows) {
|
889
|
+
// Add metric label with correct index (after all index columns)
|
890
|
+
const labelIndex = indexColumns.length;
|
891
|
+
const metricLabel = getFieldLabel(baseMetricsArray[0]) || baseMetricsArray[0];
|
892
|
+
combinedRow[`label-${labelIndex}`] = {
|
893
|
+
value: {
|
894
|
+
raw: metricLabel,
|
895
|
+
formatted: metricLabel,
|
896
|
+
},
|
897
|
+
};
|
898
|
+
}
|
899
|
+
// Add data columns with generated field IDs using metadata
|
900
|
+
pivotDetails.valuesColumns.forEach((valueCol, colIndex) => {
|
901
|
+
const pivotDimensions = (pivotDetails.groupByColumns || []).map((col) => col.reference);
|
902
|
+
const fieldId = pivotConfig.metricsAsRows
|
903
|
+
? `${pivotDimensions.join('__')}__${colIndex}`
|
904
|
+
: `${pivotDimensions.join('__')}__${valueCol.referenceField}__${colIndex}`;
|
905
|
+
if (row[valueCol.pivotColumnName]) {
|
906
|
+
combinedRow[fieldId] = row[valueCol.pivotColumnName];
|
907
|
+
}
|
908
|
+
});
|
909
|
+
return combinedRow;
|
910
|
+
})
|
911
|
+
.map((combinedRow, rowIndex) => {
|
912
|
+
// Add row totals if enabled
|
913
|
+
if (fullPivotConfig.rowTotals && rowTotals) {
|
914
|
+
const rowTotalValue = rowTotals[rowIndex]?.[0];
|
915
|
+
if (rowTotalValue !== undefined) {
|
916
|
+
const field = getField(baseMetricsArray[0]);
|
917
|
+
const formattedValue = field
|
918
|
+
? formatItemValue(field, rowTotalValue)
|
919
|
+
: String(rowTotalValue);
|
920
|
+
return {
|
921
|
+
...combinedRow,
|
922
|
+
'row-total-0': {
|
923
|
+
value: {
|
924
|
+
raw: rowTotalValue,
|
925
|
+
formatted: formattedValue,
|
926
|
+
},
|
927
|
+
},
|
928
|
+
};
|
929
|
+
}
|
930
|
+
}
|
931
|
+
return combinedRow;
|
932
|
+
});
|
933
|
+
const pivotColumnInfo = [
|
934
|
+
...indexColumns.map((col) => ({
|
935
|
+
fieldId: col,
|
936
|
+
baseId: undefined,
|
937
|
+
underlyingId: undefined,
|
938
|
+
columnType: 'indexValue',
|
939
|
+
})),
|
940
|
+
...(pivotConfig.metricsAsRows
|
941
|
+
? [
|
942
|
+
{
|
943
|
+
fieldId: `label-${indexColumns.length}`,
|
944
|
+
baseId: undefined,
|
945
|
+
underlyingId: undefined,
|
946
|
+
columnType: 'label',
|
947
|
+
},
|
948
|
+
]
|
949
|
+
: []),
|
950
|
+
...pivotDetails.valuesColumns.map((valueCol, colIndex) => {
|
951
|
+
const pivotDimensions = (pivotDetails.groupByColumns || []).map((col) => col.reference);
|
952
|
+
const fieldId = pivotConfig.metricsAsRows
|
953
|
+
? `${pivotDimensions.join('__')}__${colIndex}`
|
954
|
+
: `${pivotDimensions.join('__')}__${valueCol.referenceField}__${colIndex}`;
|
955
|
+
return {
|
956
|
+
fieldId,
|
957
|
+
baseId: pivotConfig.metricsAsRows
|
958
|
+
? pivotDimensions[0]
|
959
|
+
: valueCol.referenceField,
|
960
|
+
underlyingId: undefined,
|
961
|
+
columnType: undefined,
|
962
|
+
};
|
963
|
+
}),
|
964
|
+
...(fullPivotConfig.rowTotals
|
965
|
+
? [
|
966
|
+
{
|
967
|
+
fieldId: 'row-total-0',
|
968
|
+
baseId: 'row-total-0',
|
969
|
+
underlyingId: undefined,
|
970
|
+
columnType: 'rowTotal',
|
971
|
+
},
|
972
|
+
]
|
973
|
+
: []),
|
974
|
+
];
|
975
|
+
const titleFields = getTitleFields({
|
976
|
+
hasIndex,
|
977
|
+
hasHeader,
|
978
|
+
headerValueTypes,
|
979
|
+
indexValueTypes,
|
980
|
+
});
|
981
|
+
const pivotData = {
|
982
|
+
titleFields,
|
983
|
+
headerValueTypes,
|
984
|
+
headerValues: filteredHeaderValues,
|
985
|
+
indexValueTypes,
|
986
|
+
indexValues,
|
987
|
+
dataColumnCount: N_DATA_COLUMNS,
|
988
|
+
dataValues,
|
989
|
+
rowTotalFields,
|
990
|
+
columnTotalFields,
|
991
|
+
rowTotals,
|
992
|
+
columnTotals,
|
993
|
+
cellsCount: pivotConfig.metricsAsRows
|
994
|
+
? indexColumns.length +
|
995
|
+
1 + // label column
|
996
|
+
uniqueColumns.length +
|
997
|
+
(rowTotals ? rowTotals[0].length : 0)
|
998
|
+
: indexColumns.length +
|
999
|
+
uniqueColumns.length +
|
1000
|
+
(rowTotals ? rowTotals[0].length : 0),
|
1001
|
+
rowsCount: pivotConfig.metricsAsRows
|
1002
|
+
? rows.length * baseMetricsArray.length
|
1003
|
+
: rows.length,
|
1004
|
+
pivotConfig: fullPivotConfig,
|
1005
|
+
retrofitData: {
|
1006
|
+
allCombinedData,
|
1007
|
+
pivotColumnInfo,
|
1008
|
+
},
|
1009
|
+
groupedSubtotals: undefined,
|
1010
|
+
};
|
1011
|
+
return combinedRetrofit(pivotData, getField, getFieldLabel);
|
1012
|
+
};
|
569
1013
|
//# sourceMappingURL=pivotQueryResults.js.map
|