From 59fe0c6dc6e1b4a2dfcf0a009ceacdb53123b93b Mon Sep 17 00:00:00 2001 From: Eric Gan Date: Fri, 10 Oct 2025 13:29:08 -0700 Subject: [PATCH 1/2] add column projection in resolution --- meerkat-browser/package.json | 2 +- .../browser-cube-to-sql-with-resolution.ts | 8 +++- meerkat-core/package.json | 2 +- .../src/resolution/resolution.spec.ts | 48 ++++++++++++++++++- meerkat-core/src/resolution/resolution.ts | 46 +++++++++++------- meerkat-node/package.json | 2 +- meerkat-node/src/__tests__/resolution.spec.ts | 48 +++++++++++++++++++ .../cube-to-sql-with-resolution.ts | 8 +++- 8 files changed, 140 insertions(+), 24 deletions(-) diff --git a/meerkat-browser/package.json b/meerkat-browser/package.json index e230b0b2..2b817f4b 100644 --- a/meerkat-browser/package.json +++ b/meerkat-browser/package.json @@ -1,6 +1,6 @@ { "name": "@devrev/meerkat-browser", - "version": "0.0.102", + "version": "0.0.103", "dependencies": { "tslib": "^2.3.0", "@devrev/meerkat-core": "*", diff --git a/meerkat-browser/src/browser-cube-to-sql-with-resolution/browser-cube-to-sql-with-resolution.ts b/meerkat-browser/src/browser-cube-to-sql-with-resolution/browser-cube-to-sql-with-resolution.ts index 2b9bb6c4..914f1c60 100644 --- a/meerkat-browser/src/browser-cube-to-sql-with-resolution/browser-cube-to-sql-with-resolution.ts +++ b/meerkat-browser/src/browser-cube-to-sql-with-resolution/browser-cube-to-sql-with-resolution.ts @@ -19,6 +19,7 @@ export interface CubeQueryToSQLWithResolutionParams { query: Query; tableSchemas: TableSchema[]; resolutionConfig: ResolutionConfig; + columnProjections: string[]; contextParams?: ContextParams; } @@ -27,6 +28,7 @@ export const cubeQueryToSQLWithResolution = async ({ query, tableSchemas, resolutionConfig, + columnProjections, contextParams, }: CubeQueryToSQLWithResolutionParams) => { const baseSql = await cubeQueryToSQL({ @@ -59,7 +61,11 @@ export const cubeQueryToSQLWithResolution = async ({ connection: connection, query: { measures: [], - dimensions: generateResolvedDimensions(query, resolutionConfig), + dimensions: generateResolvedDimensions( + query, + resolutionConfig, + columnProjections + ), joinPaths: generateResolutionJoinPaths(resolutionConfig, tableSchemas), }, tableSchemas: [baseTable, ...resolutionSchemas], diff --git a/meerkat-core/package.json b/meerkat-core/package.json index 0bd4e49b..238cf2d7 100644 --- a/meerkat-core/package.json +++ b/meerkat-core/package.json @@ -1,6 +1,6 @@ { "name": "@devrev/meerkat-core", - "version": "0.0.102", + "version": "0.0.103", "dependencies": { "tslib": "^2.3.0" }, diff --git a/meerkat-core/src/resolution/resolution.spec.ts b/meerkat-core/src/resolution/resolution.spec.ts index 83e06b9a..93586301 100644 --- a/meerkat-core/src/resolution/resolution.spec.ts +++ b/meerkat-core/src/resolution/resolution.spec.ts @@ -602,7 +602,8 @@ describe('Generate resolved dimensions', () => { const resolvedDimensions = generateResolvedDimensions( query, - resolutionConfig + resolutionConfig, + [] ); expect(resolvedDimensions).toEqual([ @@ -630,7 +631,8 @@ describe('Generate resolved dimensions', () => { const resolvedDimensions = generateResolvedDimensions( query, - resolutionConfig + resolutionConfig, + [] ); expect(resolvedDimensions).toEqual([ @@ -638,6 +640,48 @@ describe('Generate resolved dimensions', () => { '__base_query.base_table__column1', ]); }); + + it('only include projected columns', () => { + const query = { + measures: ['base_table.count', 'base_table.total'], + dimensions: ['base_table.column1', 'base_table.column2'], + }; + const resolutionConfig = { + columnConfigs: [ + { + name: 'base_table.column1', + source: 'resolution_table', + joinColumn: 'id', + resolutionColumns: ['display_id'], + }, + { + name: 'base_table.column2', + source: 'resolution_table', + joinColumn: 'id', + resolutionColumns: ['id', 'display_name'], + }, + ], + tableSchemas: [], + }; + const projections = [ + 'base_table.count', + 'base_table.column2', + 'base_table.total', + ]; + + const resolvedDimensions = generateResolvedDimensions( + query, + resolutionConfig, + projections + ); + + expect(resolvedDimensions).toEqual([ + '__base_query.base_table__count', + 'base_table__column2.base_table__column2__id', + 'base_table__column2.base_table__column2__display_name', + '__base_query.base_table__total', + ]); + }); }); describe('Generate resolution join paths', () => { diff --git a/meerkat-core/src/resolution/resolution.ts b/meerkat-core/src/resolution/resolution.ts index f24b2b1e..daa6e2b7 100644 --- a/meerkat-core/src/resolution/resolution.ts +++ b/meerkat-core/src/resolution/resolution.ts @@ -127,27 +127,39 @@ export const generateResolutionSchemas = ( export const generateResolvedDimensions = ( query: Query, - config: ResolutionConfig + config: ResolutionConfig, + columnProjections: string[] ): Member[] => { - const resolvedDimensions: Member[] = [ - ...query.measures, - ...(query.dimensions || []), - ].flatMap((dimension) => { - const columnConfig = config.columnConfigs.find((c) => c.name === dimension); + // If column projections are provided, use those. + // Otherwise, use all measures and dimensions from the original query. + const aggregatedDimensions = + columnProjections && columnProjections.length > 0 + ? columnProjections + : [...query.measures, ...(query.dimensions || [])]; - if (!columnConfig) { - return [ - getNamespacedKey(BASE_DATA_SOURCE_NAME, memberKeyToSafeKey(dimension)), - ]; - } else { - return columnConfig.resolutionColumns.map((col) => - getNamespacedKey( - memberKeyToSafeKey(dimension), - memberKeyToSafeKey(getNamespacedKey(columnConfig.name, col)) - ) + const resolvedDimensions: Member[] = aggregatedDimensions.flatMap( + (dimension) => { + const columnConfig = config.columnConfigs.find( + (c) => c.name === dimension ); + + if (!columnConfig) { + return [ + getNamespacedKey( + BASE_DATA_SOURCE_NAME, + memberKeyToSafeKey(dimension) + ), + ]; + } else { + return columnConfig.resolutionColumns.map((col) => + getNamespacedKey( + memberKeyToSafeKey(dimension), + memberKeyToSafeKey(getNamespacedKey(columnConfig.name, col)) + ) + ); + } } - }); + ); return resolvedDimensions; }; diff --git a/meerkat-node/package.json b/meerkat-node/package.json index eabbee17..dc2a8961 100644 --- a/meerkat-node/package.json +++ b/meerkat-node/package.json @@ -1,6 +1,6 @@ { "name": "@devrev/meerkat-node", - "version": "0.0.102", + "version": "0.0.103", "dependencies": { "@swc/helpers": "~0.5.0", "@devrev/meerkat-core": "*", diff --git a/meerkat-node/src/__tests__/resolution.spec.ts b/meerkat-node/src/__tests__/resolution.spec.ts index da65aa73..33ff664f 100644 --- a/meerkat-node/src/__tests__/resolution.spec.ts +++ b/meerkat-node/src/__tests__/resolution.spec.ts @@ -147,6 +147,7 @@ describe('Resolution Tests', () => { columnConfigs: [], tableSchemas: [], }, + columnProjections: [], }); console.info(`SQL: `, sql); const expectedSQL = ` @@ -198,6 +199,7 @@ describe('Resolution Tests', () => { ], tableSchemas: [], }, + columnProjections: [], }) ).rejects.toThrow('Table schema not found for dim_part'); }); @@ -239,6 +241,7 @@ describe('Resolution Tests', () => { ], tableSchemas: [DIM_PART_SCHEMA, DIM_WORK_SCHEMA], }, + columnProjections: [], }); console.info(`SQL: `, sql); const expectedSQL = ` @@ -282,6 +285,7 @@ describe('Resolution Tests', () => { ], tableSchemas: [DIM_PART_SCHEMA], }, + columnProjections: [], }); console.info(`SQL: `, sql); const expectedSQL = ` @@ -329,6 +333,7 @@ describe('Resolution Tests', () => { ], tableSchemas: [DIM_PART_SCHEMA_WITH_ALIASES], }, + columnProjections: [], }); console.info(`SQL: `, sql); const expectedSQL = ` @@ -348,4 +353,47 @@ describe('Resolution Tests', () => { expectedSQL.replace(/\s+/g, ' ').trim() ); }); + + it('Resolution With Column Projections', async () => { + const query = { + measures: [], + dimensions: [ + 'base_table.part_id_1', + 'base_table.random_column', + 'base_table.work_id', + 'base_table.part_id_2', + ], + }; + + const sql = await cubeQueryToSQLWithResolution({ + query, + tableSchemas: [BASE_TABLE_SCHEMA], + resolutionConfig: { + columnConfigs: [ + { + name: 'base_table.part_id_1', + source: 'dim_part', + joinColumn: 'id', + resolutionColumns: ['display_id'], + }, + ], + tableSchemas: [DIM_PART_SCHEMA, DIM_WORK_SCHEMA], + }, + columnProjections: ['base_table.random_column', 'base_table.part_id_1'], + }); + console.info(`SQL: `, sql); + const expectedSQL = ` + SELECT + "base_table__random_column", + "base_table__part_id_1 - display_id" + FROM + (SELECT __base_query.base_table__random_column AS "base_table__random_column", * FROM (SELECT base_table__part_id_1, base_table__random_column, base_table__work_id, base_table__part_id_2 FROM (SELECT base_table.part_id_1 AS base_table__part_id_1, base_table.random_column AS base_table__random_column, base_table.work_id AS base_table__work_id, base_table.part_id_2 AS base_table__part_id_2, * FROM (select * from base_table) AS base_table) AS base_table) AS __base_query + LEFT JOIN (SELECT base_table__part_id_1.display_id AS "base_table__part_id_1 - display_id", * FROM (select id, display_id from system.dim_feature UNION ALL select id, display_id from system.dim_product) AS base_table__part_id_1) AS base_table__part_id_1 + ON __base_query.base_table__part_id_1 = base_table__part_id_1.id) + AS MEERKAT_GENERATED_TABLE + `; + expect(sql.replace(/\s+/g, ' ').trim()).toBe( + expectedSQL.replace(/\s+/g, ' ').trim() + ); + }); }); diff --git a/meerkat-node/src/cube-to-sql-with-resolution/cube-to-sql-with-resolution.ts b/meerkat-node/src/cube-to-sql-with-resolution/cube-to-sql-with-resolution.ts index b7f75f4f..7d1d5707 100644 --- a/meerkat-node/src/cube-to-sql-with-resolution/cube-to-sql-with-resolution.ts +++ b/meerkat-node/src/cube-to-sql-with-resolution/cube-to-sql-with-resolution.ts @@ -17,6 +17,7 @@ export interface CubeQueryToSQLWithResolutionParams { query: Query; tableSchemas: TableSchema[]; resolutionConfig: ResolutionConfig; + columnProjections: string[]; contextParams?: ContextParams; } @@ -24,6 +25,7 @@ export const cubeQueryToSQLWithResolution = async ({ query, tableSchemas, resolutionConfig, + columnProjections, contextParams, }: CubeQueryToSQLWithResolutionParams) => { const baseSql = await cubeQueryToSQL({ @@ -54,7 +56,11 @@ export const cubeQueryToSQLWithResolution = async ({ const resolveParams: CubeQueryToSQLParams = { query: { measures: [], - dimensions: generateResolvedDimensions(query, resolutionConfig), + dimensions: generateResolvedDimensions( + query, + resolutionConfig, + columnProjections + ), joinPaths: generateResolutionJoinPaths(resolutionConfig, tableSchemas), }, tableSchemas: [baseTable, ...resolutionSchemas], From 12efa5cbb7b406a2f30ef4a513b4c7e4f60c16ea Mon Sep 17 00:00:00 2001 From: Eric Gan Date: Sun, 12 Oct 2025 15:34:48 -0700 Subject: [PATCH 2/2] review comments --- .../browser-cube-to-sql-with-resolution.ts | 2 +- meerkat-core/src/resolution/resolution.spec.ts | 6 ++---- meerkat-core/src/resolution/resolution.ts | 9 ++++----- meerkat-node/src/__tests__/resolution.spec.ts | 5 ----- .../cube-to-sql-with-resolution.ts | 2 +- 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/meerkat-browser/src/browser-cube-to-sql-with-resolution/browser-cube-to-sql-with-resolution.ts b/meerkat-browser/src/browser-cube-to-sql-with-resolution/browser-cube-to-sql-with-resolution.ts index 914f1c60..c06d8a75 100644 --- a/meerkat-browser/src/browser-cube-to-sql-with-resolution/browser-cube-to-sql-with-resolution.ts +++ b/meerkat-browser/src/browser-cube-to-sql-with-resolution/browser-cube-to-sql-with-resolution.ts @@ -19,7 +19,7 @@ export interface CubeQueryToSQLWithResolutionParams { query: Query; tableSchemas: TableSchema[]; resolutionConfig: ResolutionConfig; - columnProjections: string[]; + columnProjections?: string[]; contextParams?: ContextParams; } diff --git a/meerkat-core/src/resolution/resolution.spec.ts b/meerkat-core/src/resolution/resolution.spec.ts index 93586301..45170199 100644 --- a/meerkat-core/src/resolution/resolution.spec.ts +++ b/meerkat-core/src/resolution/resolution.spec.ts @@ -602,8 +602,7 @@ describe('Generate resolved dimensions', () => { const resolvedDimensions = generateResolvedDimensions( query, - resolutionConfig, - [] + resolutionConfig ); expect(resolvedDimensions).toEqual([ @@ -631,8 +630,7 @@ describe('Generate resolved dimensions', () => { const resolvedDimensions = generateResolvedDimensions( query, - resolutionConfig, - [] + resolutionConfig ); expect(resolvedDimensions).toEqual([ diff --git a/meerkat-core/src/resolution/resolution.ts b/meerkat-core/src/resolution/resolution.ts index daa6e2b7..3afe29ce 100644 --- a/meerkat-core/src/resolution/resolution.ts +++ b/meerkat-core/src/resolution/resolution.ts @@ -128,14 +128,13 @@ export const generateResolutionSchemas = ( export const generateResolvedDimensions = ( query: Query, config: ResolutionConfig, - columnProjections: string[] + columnProjections?: string[] ): Member[] => { // If column projections are provided, use those. // Otherwise, use all measures and dimensions from the original query. - const aggregatedDimensions = - columnProjections && columnProjections.length > 0 - ? columnProjections - : [...query.measures, ...(query.dimensions || [])]; + const aggregatedDimensions = columnProjections + ? columnProjections + : [...query.measures, ...(query.dimensions || [])]; const resolvedDimensions: Member[] = aggregatedDimensions.flatMap( (dimension) => { diff --git a/meerkat-node/src/__tests__/resolution.spec.ts b/meerkat-node/src/__tests__/resolution.spec.ts index 33ff664f..90bde754 100644 --- a/meerkat-node/src/__tests__/resolution.spec.ts +++ b/meerkat-node/src/__tests__/resolution.spec.ts @@ -147,7 +147,6 @@ describe('Resolution Tests', () => { columnConfigs: [], tableSchemas: [], }, - columnProjections: [], }); console.info(`SQL: `, sql); const expectedSQL = ` @@ -199,7 +198,6 @@ describe('Resolution Tests', () => { ], tableSchemas: [], }, - columnProjections: [], }) ).rejects.toThrow('Table schema not found for dim_part'); }); @@ -241,7 +239,6 @@ describe('Resolution Tests', () => { ], tableSchemas: [DIM_PART_SCHEMA, DIM_WORK_SCHEMA], }, - columnProjections: [], }); console.info(`SQL: `, sql); const expectedSQL = ` @@ -285,7 +282,6 @@ describe('Resolution Tests', () => { ], tableSchemas: [DIM_PART_SCHEMA], }, - columnProjections: [], }); console.info(`SQL: `, sql); const expectedSQL = ` @@ -333,7 +329,6 @@ describe('Resolution Tests', () => { ], tableSchemas: [DIM_PART_SCHEMA_WITH_ALIASES], }, - columnProjections: [], }); console.info(`SQL: `, sql); const expectedSQL = ` diff --git a/meerkat-node/src/cube-to-sql-with-resolution/cube-to-sql-with-resolution.ts b/meerkat-node/src/cube-to-sql-with-resolution/cube-to-sql-with-resolution.ts index 7d1d5707..8ef3de8e 100644 --- a/meerkat-node/src/cube-to-sql-with-resolution/cube-to-sql-with-resolution.ts +++ b/meerkat-node/src/cube-to-sql-with-resolution/cube-to-sql-with-resolution.ts @@ -17,7 +17,7 @@ export interface CubeQueryToSQLWithResolutionParams { query: Query; tableSchemas: TableSchema[]; resolutionConfig: ResolutionConfig; - columnProjections: string[]; + columnProjections?: string[]; contextParams?: ContextParams; }