@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.
Files changed (49) hide show
  1. package/dist/cjs/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.js +1 -1
  2. package/dist/cjs/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.js.map +1 -1
  3. package/dist/cjs/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts.map +1 -1
  4. package/dist/cjs/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.js +1 -1
  5. package/dist/cjs/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.js.map +1 -1
  6. package/dist/cjs/ee/AiAgent/schemas/tools/toolTableVizArgs.js +1 -1
  7. package/dist/cjs/ee/AiAgent/schemas/tools/toolTableVizArgs.js.map +1 -1
  8. package/dist/cjs/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.js +1 -1
  9. package/dist/cjs/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.js.map +1 -1
  10. package/dist/cjs/ee/AiAgent/schemas/tools/toolVerticalBarArgs.js +1 -1
  11. package/dist/cjs/ee/AiAgent/schemas/tools/toolVerticalBarArgs.js.map +1 -1
  12. package/dist/cjs/pivot/pivotQueryResults.d.ts +13 -0
  13. package/dist/cjs/pivot/pivotQueryResults.d.ts.map +1 -1
  14. package/dist/cjs/pivot/pivotQueryResults.js +507 -62
  15. package/dist/cjs/pivot/pivotQueryResults.js.map +1 -1
  16. package/dist/cjs/pivot/pivotQueryResults.mock.d.ts +14 -0
  17. package/dist/cjs/pivot/pivotQueryResults.mock.d.ts.map +1 -1
  18. package/dist/cjs/pivot/pivotQueryResults.mock.js +2343 -1
  19. package/dist/cjs/pivot/pivotQueryResults.mock.js.map +1 -1
  20. package/dist/cjs/pivot/pivotQueryResults.test.js +391 -0
  21. package/dist/cjs/pivot/pivotQueryResults.test.js.map +1 -1
  22. package/dist/esm/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.js +1 -1
  23. package/dist/esm/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.js.map +1 -1
  24. package/dist/esm/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts.map +1 -1
  25. package/dist/esm/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.js +1 -1
  26. package/dist/esm/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.js.map +1 -1
  27. package/dist/esm/ee/AiAgent/schemas/tools/toolTableVizArgs.js +1 -1
  28. package/dist/esm/ee/AiAgent/schemas/tools/toolTableVizArgs.js.map +1 -1
  29. package/dist/esm/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.js +1 -1
  30. package/dist/esm/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.js.map +1 -1
  31. package/dist/esm/ee/AiAgent/schemas/tools/toolVerticalBarArgs.js +1 -1
  32. package/dist/esm/ee/AiAgent/schemas/tools/toolVerticalBarArgs.js.map +1 -1
  33. package/dist/esm/pivot/pivotQueryResults.d.ts +13 -0
  34. package/dist/esm/pivot/pivotQueryResults.d.ts.map +1 -1
  35. package/dist/esm/pivot/pivotQueryResults.js +505 -61
  36. package/dist/esm/pivot/pivotQueryResults.js.map +1 -1
  37. package/dist/esm/pivot/pivotQueryResults.mock.d.ts +14 -0
  38. package/dist/esm/pivot/pivotQueryResults.mock.d.ts.map +1 -1
  39. package/dist/esm/pivot/pivotQueryResults.mock.js +2341 -0
  40. package/dist/esm/pivot/pivotQueryResults.mock.js.map +1 -1
  41. package/dist/esm/pivot/pivotQueryResults.test.js +393 -2
  42. package/dist/esm/pivot/pivotQueryResults.test.js.map +1 -1
  43. package/dist/tsconfig.types.tsbuildinfo +1 -1
  44. package/dist/types/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts.map +1 -1
  45. package/dist/types/pivot/pivotQueryResults.d.ts +13 -0
  46. package/dist/types/pivot/pivotQueryResults.d.ts.map +1 -1
  47. package/dist/types/pivot/pivotQueryResults.mock.d.ts +14 -0
  48. package/dist/types/pivot/pivotQueryResults.mock.d.ts.map +1 -1
  49. 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 headerDimensionValueTypes = headerDimensions.map((d) => ({
264
- type: FieldType.DIMENSION,
265
- fieldId: d,
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
- let columnTotalFields;
444
- let columnTotals;
445
- if (pivotConfig.columnTotals && hasIndex) {
446
- if (pivotConfig.metricsAsRows) {
447
- const N_TOTAL_ROWS = summableMetricFieldIds.length;
448
- const N_TOTAL_COLS = indexValueTypes.length;
449
- columnTotalFields = create2DArray(N_TOTAL_ROWS, N_TOTAL_COLS);
450
- columnTotals = create2DArray(N_TOTAL_ROWS, N_DATA_COLUMNS);
451
- summableMetricFieldIds.forEach((fieldId, metricIndex) => {
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
- indexValueTypes.forEach((indexValueType, indexIndex) => {
490
- if (indexValueType.type === FieldType.DIMENSION) {
491
- titleFields[hasHeader ? headerValueTypes.length - 1 : 0][indexIndex] = {
492
- fieldId: indexValueType.fieldId,
493
- direction: 'index',
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