in src/dataViewBuilder/matrixBuilder.ts [447:571]
private readData(
rowHierarchyMetadata: HierarchyMetadata,
columnHierarchyMetadata: HierarchyMetadata,
measureMetadata: LevelMetadata | undefined,
table: DataTable): powerbi.DataViewMatrix {
const columnLevels = columnHierarchyMetadata.levels.map((level) => ({
sources: level.sources,
}));
// if (measureMetadata)
// columnLevels.push({ sources: measureMetadata.sources });
const matrix: powerbi.DataViewMatrix = {
columns: {
levels: columnLevels,
root: {}
},
rows: {
levels: rowHierarchyMetadata.levels.map((level) => ({
sources: level.sources,
})),
root: {},
},
valueSources: measureMetadata?.sources ?? [],
};
// Fill in empty children array for dimensions that don't have any grouping
if (matrix.columns.levels.length === 0)
matrix.columns.root.children = [];
if (matrix.rows.levels.length === 0)
matrix.rows.root.children = [];
// We do two passes over the data table,
// The first pass fills in the group instances of row & column hierarchies.
// The second pass fills in the measure values. We need to do this as a second pass
// because the index of the measure value will depend on the column hierarchy.
// See the doc for findValueInHierarchy
// Pass 1
table.forEachRow((row) => {
this.ensureValueInHierarchy(
columnHierarchyMetadata,
matrix.columns.root,
row,
true, // explicitly sort the column data
);
this.ensureValueInHierarchy(
rowHierarchyMetadata,
matrix.rows.root,
row,
false, // rows are sorted in the order they appear in the data
);
});
if (measureMetadata) {
// We ignore the last level of values, we only want to find a matching path up to, but not including, the measure nodes
const hierarchyDepth = columnLevels.length - 2;
// Pass 1.5, fill in intersections with null values
const leafCount = this.findValueInHierarchy(
(_node) => false,
matrix.columns.root,
hierarchyDepth,
);
this.forEachLeaf(
matrix.rows.root,
(leafNode) => {
leafNode.values =
range(0, leafCount)
.reduce((bag, i) => {
// @ts-ignore: No Implicit Any
bag[i] = <powerbi.DataViewMatrixNodeValue>{ value: null };
return bag;
},
{});
}
);
// Pass 2
table.forEachRow((row) => {
// Find the leaf node in the column heirarchy and calculate the index
const columnValues: any[][] = [];
for (const level of columnHierarchyMetadata.levels) {
columnValues.push(level.tableIndices.map((index) => row[index]));
}
const isMatch = (node: powerbi.DataViewMatrixNode, level: number) => {
return this.sequenceEqual(columnValues[level], node.levelValues, (value, levelValue) => value === levelValue.value);
};
const indexOfColumnLeaf = this.findValueInHierarchy(isMatch, matrix.columns.root, hierarchyDepth);
// debug.assert((leafCount === 0 && indexOfColumnLeaf === 0) || indexOfColumnLeaf < leafCount, 'could not find leaf ');
// Find the leaf node in the row hierarchy matching this data row
// TODO: find? we shouldn't be adding nodes here
const rowNode = this.ensureValueInHierarchy(
rowHierarchyMetadata,
matrix.rows.root,
row,
false,
);
if (rowNode.values == null)
rowNode.values = {};
for (let i = 0; i < measureMetadata.tableIndices.length; i++) {
const tableIdx = measureMetadata.tableIndices[i];
const measureValue = row[tableIdx];
let valueNode: powerbi.DataViewMatrixNodeValue = {
value: measureValue,
};
// We omit the valueSourceIndex when it is 0
if (i !== 0)
valueNode.valueSourceIndex = i;
rowNode.values[indexOfColumnLeaf + i] = valueNode;
}
});
}
return matrix;
}