diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 3bb6734aaddef..5e68f25be6871 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -3806,11 +3806,15 @@ export class BaseQuery { .map(v => (v.query ? v.query.allBackAliasMembersExceptSegments() : {})) .reduce((a, b) => ({ ...a, ...b }), {}) : {}; + // Filtering aliases that somehow relate to this group members + const aliasesForGroupMembers = Object.entries(aliases) + .filter(([key, value]) => groupMembers.includes(key)) + .map(([_key, value]) => value); const filter = BaseQuery.findAndSubTreeForFilterGroup( newGroupFilter({ operator: 'and', values: allFilters }), groupMembers, newGroupFilter, - Object.values(aliases) + aliasesForGroupMembers ); return `(${BaseQuery.renderFilterParams(filter, filterParamArgs, allocateParam, newGroupFilter, aliases)})`; @@ -3846,15 +3850,16 @@ export class BaseQuery { .map(v => (v.query ? v.query.allBackAliasMembersExceptSegments() : {})) .reduce((a, b) => ({ ...a, ...b }), {}) : {}; - // Filtering aliases that somehow relate to this cube - const filteredAliases = Object.entries(aliases) - .filter(([key, value]) => key.startsWith(cubeNameObj.cube) || value.startsWith(cubeNameObj.cube)) - .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); + // Filtering aliases that somehow relate to this group member + const groupMember = cubeEvaluator.pathFromArray([cubeNameObj.cube, propertyName]); + const aliasesForGroupMembers = Object.entries(aliases) + .filter(([key, _value]) => key === groupMember) + .map(([_key, value]) => value); const filter = BaseQuery.findAndSubTreeForFilterGroup( newGroupFilter({ operator: 'and', values: allFilters }), - [cubeEvaluator.pathFromArray([cubeNameObj.cube, propertyName])], + [groupMember], newGroupFilter, - Object.values(filteredAliases) + aliasesForGroupMembers ); return `(${BaseQuery.renderFilterParams(filter, [this], allocateParam, newGroupFilter, aliases)})`; diff --git a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts index 00f4efa2664c8..270065882a5df 100644 --- a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts @@ -6,7 +6,8 @@ import { createCubeSchemaWithCustomGranularities, createCubeSchemaYaml, createJoinedCubesSchema, - createSchemaYaml + createSchemaYaml, + createSchemaYamlForGroupFilterParamsTests } from './utils'; import { BigqueryQuery } from '../../src/adapter/BigqueryQuery'; @@ -1398,8 +1399,60 @@ describe('SQL Generation', () => { }, ], }); - const cubeSQL = query.cubeSql('Order'); - expect(cubeSQL).toContain('select * from order where ((type = $0$))'); + const queryAndParams = query.buildSqlAndParams(); + const queryString = queryAndParams[0]; + expect(queryString).toContain('select * from order where ((type = ?))'); + }); + + it('propagate filter params within cte from view into cube\'s query', async () => { + /** @type {Compilers} */ + const compiler = prepareYamlCompiler( + createSchemaYaml({ + cubes: [{ + name: 'Order', + sql: `WITH cte as (select * + from order + where {FILTER_PARAMS.Order.type.filter('type')} + ) + select * from cte`, + measures: [{ + name: 'count', + type: 'count', + }], + dimensions: [{ + name: 'type', + sql: 'type', + type: 'string' + }] + }], + views: [{ + name: 'orders_view', + cubes: [{ + join_path: 'Order', + prefix: true, + includes: [ + 'type', + 'count', + ] + }] + }] + }) + ); + + await compiler.compiler.compile(); + const query = new BaseQuery(compiler, { + measures: ['orders_view.Order_count'], + filters: [ + { + member: 'orders_view.Order_type', + operator: 'equals', + values: ['online'], + }, + ], + }); + const queryAndParams = query.buildSqlAndParams(); + const queryString = queryAndParams[0]; + expect(/select\s+\*\s+from\s+order\s+where\s+\(\(type\s=\s\?\)\)/.test(queryString)).toBeTruthy(); }); }); @@ -1527,6 +1580,173 @@ describe('SQL Generation', () => { const cubeSQL = query.cubeSql('Order'); expect(cubeSQL).toContain('where ((((dim0 = $0$) AND (dim1 = $1$)) OR ((dim0 = $2$) AND (dim1 = $3$))))'); }); + + it('propagate 1 filter param from view into cube\'s query', async () => { + /** @type {Compilers} */ + const compiler = prepareYamlCompiler( + createSchemaYamlForGroupFilterParamsTests( + `select * + from order + where {FILTER_GROUP( + FILTER_PARAMS.Order.dim0.filter('dim0') + , FILTER_PARAMS.Order.dim1.filter('dim1') + )}` + ) + ); + + await compiler.compiler.compile(); + const query = new PostgresQuery(compiler, { + measures: ['orders_view.Order_count'], + filters: [ + { + member: 'orders_view.Order_dim0', + operator: 'equals', + values: ['online'], + }, + ], + }); + const queryAndParams = query.buildSqlAndParams(); + const queryString = queryAndParams[0]; + expect(/select\s+\*\s+from\s+order\s+where\s+\(\(dim0\s=\s\$1\)\)/.test(queryString)).toBeTruthy(); + }); + + it('propagate 2 filter params from view into cube\'s query', async () => { + /** @type {Compilers} */ + const compiler = prepareYamlCompiler( + createSchemaYamlForGroupFilterParamsTests( + `select * + from order + where {FILTER_GROUP( + FILTER_PARAMS.Order.dim0.filter('dim0'), + FILTER_PARAMS.Order.dim1.filter('dim1') + )}` + ) + ); + + await compiler.compiler.compile(); + const query = new PostgresQuery(compiler, { + measures: ['orders_view.Order_count'], + filters: [ + { + member: 'orders_view.Order_dim0', + operator: 'equals', + values: ['online'], + }, + { + member: 'orders_view.Order_dim1', + operator: 'equals', + values: ['online'], + }, + ], + }); + const queryAndParams = query.buildSqlAndParams(); + const queryString = queryAndParams[0]; + expect(/select\s+\*\s+from\s+order\s+where\s+\(\(dim0\s=\s\$1\)\s+AND\s+\(dim1\s+=\s+\$2\)\)/.test(queryString)).toBeTruthy(); + }); + + it('propagate 1 filter param within cte from view into cube\'s query', async () => { + /** @type {Compilers} */ + const compiler = prepareYamlCompiler( + createSchemaYamlForGroupFilterParamsTests( + `with cte as ( + select * + from order + where + {FILTER_GROUP( + FILTER_PARAMS.Order.dim0.filter('dim0'), + FILTER_PARAMS.Order.dim1.filter('dim1') + )} + ) + select * from cte` + ) + ); + + await compiler.compiler.compile(); + const query = new PostgresQuery(compiler, { + measures: ['orders_view.Order_count'], + filters: [ + { + member: 'orders_view.Order_dim0', + operator: 'equals', + values: ['online'], + }, + ], + }); + const queryAndParams = query.buildSqlAndParams(); + const queryString = queryAndParams[0]; + expect(/select\s+\*\s+from\s+order\s+where\s+\(\(dim0\s=\s\$1\)\)/.test(queryString)).toBeTruthy(); + }); + + it('propagate 2 filter params within cte from view into cube\'s query', async () => { + /** @type {Compilers} */ + const compiler = prepareYamlCompiler( + createSchemaYamlForGroupFilterParamsTests( + `with cte as ( + select * + from order + where + {FILTER_GROUP( + FILTER_PARAMS.Order.dim0.filter('dim0'), + FILTER_PARAMS.Order.dim1.filter('dim1') + )} + ) + select * from cte` + ) + ); + + await compiler.compiler.compile(); + const query = new PostgresQuery(compiler, { + measures: ['orders_view.Order_count'], + filters: [ + { + member: 'orders_view.Order_dim0', + operator: 'equals', + values: ['online'], + }, + { + member: 'orders_view.Order_dim1', + operator: 'equals', + values: ['online'], + }, + ], + }); + const queryAndParams = query.buildSqlAndParams(); + const queryString = queryAndParams[0]; + expect(/select\s+\*\s+from\s+order\s+where\s+\(\(dim0\s=\s\$1\)\s+AND\s+\(dim1\s+=\s+\$2\)\)/.test(queryString)).toBeTruthy(); + }); + + it('propagate 1 filter param within cte (ref as cube and view dimensions)', async () => { + /** @type {Compilers} */ + const compiler = prepareYamlCompiler( + createSchemaYamlForGroupFilterParamsTests( + `with cte as ( + select * + from order + where + {FILTER_GROUP( + FILTER_PARAMS.Order.dim0.filter('dim0'), + FILTER_PARAMS.orders_view.dim0.filter('dim0') + )} + ) + select * from cte` + ) + ); + + await compiler.compiler.compile(); + const query = new PostgresQuery(compiler, { + measures: ['orders_view.Order_count'], + filters: [ + { + member: 'orders_view.Order_dim0', + operator: 'equals', + values: ['online'], + }, + ], + }); + const queryAndParams = query.buildSqlAndParams(); + const queryString = queryAndParams[0]; + expect(/select\s+\*\s+from\s+order\s+where\s+\(\(dim0\s=\s\$1\)\)/.test(queryString)).toBeTruthy(); + }); }); }); diff --git a/packages/cubejs-schema-compiler/test/unit/utils.ts b/packages/cubejs-schema-compiler/test/unit/utils.ts index af8ad21c6ae74..f366948adb128 100644 --- a/packages/cubejs-schema-compiler/test/unit/utils.ts +++ b/packages/cubejs-schema-compiler/test/unit/utils.ts @@ -226,6 +226,45 @@ export function createSchemaYaml(schema: CreateSchemaOptions): string { return YAML.dump(schema); } +export function createSchemaYamlForGroupFilterParamsTests(cubeDefSql: string): string { + return createSchemaYaml({ + cubes: [ + { + name: 'Order', + sql: cubeDefSql, + measures: [{ + name: 'count', + type: 'count', + }], + dimensions: [ + { + name: 'dim0', + sql: 'dim0', + type: 'string' + }, + { + name: 'dim1', + sql: 'dim1', + type: 'string' + } + ] + }, + ], + views: [{ + name: 'orders_view', + cubes: [{ + join_path: 'Order', + prefix: true, + includes: [ + 'count', + 'dim0', + 'dim1', + ] + }] + }] + }); +} + export function createCubeSchemaYaml({ name, sqlTable }: CreateCubeSchemaOptions): string { return ` cubes: