Skip to content

Commit

Permalink
fix(schema-compiler): fix FILTER_PARAMS propagation from view to cube…
Browse files Browse the repository at this point in the history
…'s SQL query (#8721)

* fix(schema-compiler): fix FILTER_PARAMS propagation from view to cube's SQL query

* Add tests for fix

* Remove console.log

* small code polish
  • Loading branch information
KSDaemon committed Sep 20, 2024
1 parent 7be9b44 commit ec2c2ec
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 10 deletions.
19 changes: 12 additions & 7 deletions packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)})`;
Expand Down Expand Up @@ -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)})`;
Expand Down
226 changes: 223 additions & 3 deletions packages/cubejs-schema-compiler/test/unit/base-query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
createCubeSchemaWithCustomGranularities,
createCubeSchemaYaml,
createJoinedCubesSchema,
createSchemaYaml
createSchemaYaml,
createSchemaYamlForGroupFilterParamsTests
} from './utils';
import { BigqueryQuery } from '../../src/adapter/BigqueryQuery';

Expand Down Expand Up @@ -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();
});
});

Expand Down Expand Up @@ -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();
});
});
});

Expand Down
39 changes: 39 additions & 0 deletions packages/cubejs-schema-compiler/test/unit/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit ec2c2ec

Please sign in to comment.