From ee02a4f35f79ffdec1f01febfffee0d50c239a9b Mon Sep 17 00:00:00 2001 From: Debjit Mandal <115482933+mystichronicle@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:43:18 +0530 Subject: [PATCH] fix(client-core): preserve zero values with fillWithValue in pivot (#10225) (#10226) The fillWithValue pivot config option was using the || (logical OR) operator, which treats 0 as falsy. This caused actual zero values in the data to be incorrectly replaced by fillWithValue, not just missing/undefined values. Changed the implementation to use the ?? (nullish coalescing) operator instead, which only falls through for null and undefined values, preserving actual 0 values in the data. Impact: Users can now use fillWithValue to display 'no data' indicators (like '-' or 'N/A') without also replacing legitimate zeros in their data. Fixes #10225 Co-authored-by: Debjit Mandal --- packages/cubejs-client-core/src/ResultSet.ts | 3 +- .../cubejs-client-core/test/ResultSet.test.ts | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-client-core/src/ResultSet.ts b/packages/cubejs-client-core/src/ResultSet.ts index 4056fb8e65691..5f3ff540d9976 100644 --- a/packages/cubejs-client-core/src/ResultSet.ts +++ b/packages/cubejs-client-core/src/ResultSet.ts @@ -535,8 +535,7 @@ export default class ResultSet = any> { const pivotImpl = (resultIndex = 0) => { let groupByXAxis = groupByToPairs<{ xValues: string[], row: Record }, string>(({ xValues }) => this.axisValuesString(xValues)); - - const measureValue = (row: Record, measure: string) => row[measure] || normalizedPivotConfig.fillWithValue || 0; + const measureValue = (row: Record, measure: string) => row[measure] ?? normalizedPivotConfig.fillWithValue ?? 0; if ( normalizedPivotConfig.fillMissingDates && diff --git a/packages/cubejs-client-core/test/ResultSet.test.ts b/packages/cubejs-client-core/test/ResultSet.test.ts index 7a79644dff45a..4aba7a92b3fac 100644 --- a/packages/cubejs-client-core/test/ResultSet.test.ts +++ b/packages/cubejs-client-core/test/ResultSet.test.ts @@ -1694,6 +1694,79 @@ describe('ResultSet', () => { ]); }); + test('fillWithValue should preserve actual zero values (issue #10225)', () => { + const resultSet = new ResultSet({ + query: { + measures: ['TestCube.value'], + dimensions: ['TestCube.category', 'TestCube.type'], + filters: [], + timezone: 'UTC' + }, + data: [ + { + 'TestCube.category': 'A', + 'TestCube.type': 'X', + 'TestCube.value': 10 + }, + { + 'TestCube.category': 'A', + 'TestCube.type': 'Y', + 'TestCube.value': 0 + }, + { + 'TestCube.category': 'B', + 'TestCube.type': 'X', + 'TestCube.value': 30 + } + ], + annotation: { + measures: { + 'TestCube.value': { + title: 'Value', + shortTitle: 'Value', + type: 'number' + } + }, + dimensions: { + 'TestCube.category': { + title: 'Category', + shortTitle: 'Category', + type: 'string' + }, + 'TestCube.type': { + title: 'Type', + shortTitle: 'Type', + type: 'string' + } + }, + segments: {}, + timeDimensions: {} + } + } as any); + + const pivotConfig = { + x: ['TestCube.category'], + y: ['TestCube.type', 'measures'], + fillWithValue: '-' + }; + + const result = resultSet.tablePivot(pivotConfig); + + // Actual zero values should be preserved, not replaced by fillWithValue + expect(result).toEqual([ + { + 'TestCube.category': 'A', + 'X,TestCube.value': 10, + 'Y,TestCube.value': 0 // Zero should be preserved, not replaced with '-' + }, + { + 'TestCube.category': 'B', + 'X,TestCube.value': 30, + 'Y,TestCube.value': '-' // Missing value should be replaced with '-' + } + ]); + }); + test('same dimension and time dimension without granularity', () => { const resultSet = new ResultSet({ query: {