Skip to content

Commit 5d67018

Browse files
committed
Set alias for dimension columns in ClickHouse queries
1 parent 896af5e commit 5d67018

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,17 @@ export class ClickHouseQuery extends BaseQuery {
266266
return `ALTER TABLE ${tableName} ADD INDEX ${indexName} (${escapedColumns.join(', ')}) TYPE minmax GRANULARITY 1`;
267267
}
268268

269+
public dimensionColumns(cubeAlias) {
270+
// For the top-level SELECT statement, explicitly set the column alias.
271+
// Clickhouse sometimes includes the "q_0" prefix in the column name, and this
272+
// leads to errors during the result mapping.
273+
if (cubeAlias === 'q_0') {
274+
return this.dimensionAliasNames().map(alias => `${cubeAlias && `${cubeAlias}.` || ''}${alias} ${alias}`);
275+
} else {
276+
return super.dimensionColumns(cubeAlias);
277+
}
278+
}
279+
269280
public sqlTemplates() {
270281
const templates = super.sqlTemplates();
271282
templates.functions.DATETRUNC = 'DATE_TRUNC({{ args_concat }})';
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import { prepareJsCompiler } from '../../unit/PrepareCompiler';
2+
import { ClickHouseDbRunner } from './ClickHouseDbRunner';
3+
4+
describe('ClickHouse complex joins', () => {
5+
jest.setTimeout(20 * 1000);
6+
7+
const dbRunner = new ClickHouseDbRunner();
8+
9+
afterAll(async () => {
10+
await dbRunner.tearDown();
11+
});
12+
13+
const { compiler, joinGraph, cubeEvaluator } = prepareJsCompiler(`
14+
cube(\`Acube\`, {
15+
sql: \`SELECT * FROM (SELECT 1 as id, 'Category A' as category, 10 as value UNION ALL
16+
SELECT 2, 'Category B', 20 UNION ALL
17+
SELECT 3, 'Category C', 30) as t\`,
18+
19+
joins: {
20+
Bcube: {
21+
relationship: \`one_to_many\`,
22+
sql: \`\${Acube}.id = \${Bcube}.id\`
23+
},
24+
Ccube: {
25+
relationship: \`one_to_many\`,
26+
sql: \`\${Acube}.id = \${Ccube}.id\`
27+
},
28+
},
29+
30+
measures: {
31+
AcubeCount: {
32+
type: \`count\`,
33+
},
34+
AcubeTotalValue: {
35+
type: \`sum\`,
36+
sql: \`value\`
37+
},
38+
BcubeTotalValue: {
39+
sql: \`\${Bcube.totalValue}\`,
40+
type: \`number\`
41+
},
42+
CcubeTotalAmount: {
43+
sql: \`\${Ccube.totalAmount}\`,
44+
type: \`number\`
45+
},
46+
},
47+
48+
dimensions: {
49+
id: {
50+
sql: \`id\`,
51+
type: \`number\`,
52+
primaryKey: true,
53+
shown: true,
54+
title: \`id\`
55+
},
56+
category: {
57+
sql: \`category\`,
58+
type: \`string\`
59+
}
60+
}
61+
});
62+
63+
cube(\`Bcube\`, {
64+
sql: \`SELECT 1 as id, 'Bgroup1' as groupName, 50 as value UNION ALL
65+
SELECT 2, 'Bgroup2', 60 UNION ALL
66+
SELECT 3, 'Bgroup3', 70\`,
67+
68+
measures: {
69+
count: {
70+
type: \`count\`
71+
},
72+
totalValue: {
73+
type: \`sum\`,
74+
sql: \`value\`
75+
}
76+
},
77+
78+
dimensions: {
79+
id: {
80+
sql: \`id\`,
81+
type: \`number\`,
82+
primaryKey: true
83+
},
84+
groupName: {
85+
sql: \`groupName\`,
86+
type: \`string\`
87+
}
88+
}
89+
});
90+
91+
cube(\`Ccube\`, {
92+
sql: \`SELECT 1 as id, 'Ctype1' as type, 15 as amount UNION ALL
93+
SELECT 2, 'Ctype2', 25 UNION ALL
94+
SELECT 3, 'Ctype3', 35\`,
95+
96+
measures: {
97+
count: {
98+
type: \`count\`
99+
},
100+
totalAmount: {
101+
type: \`sum\`,
102+
sql: \`amount\`
103+
}
104+
},
105+
106+
dimensions: {
107+
id: {
108+
sql: \`id\`,
109+
type: \`number\`,
110+
primaryKey: true,
111+
shown: true
112+
},
113+
type: {
114+
sql: \`type\`,
115+
type: \`string\`
116+
}
117+
}
118+
});
119+
`);
120+
121+
it('query with 1 cube join', async () => dbRunner.runQueryTest(
122+
{
123+
dimensions: [
124+
'Acube.category',
125+
'Acube.id'
126+
],
127+
measures: [
128+
'Acube.AcubeCount',
129+
'Acube.AcubeTotalValue',
130+
'Acube.BcubeTotalValue'
131+
],
132+
order: [{
133+
id: 'Acube.id',
134+
desc: false
135+
}],
136+
queryType: 'multi'
137+
},
138+
[
139+
{
140+
acube__category: 'Category A',
141+
acube__id: '1',
142+
acube___acube_count: '1',
143+
acube___acube_total_value: '10',
144+
acube___bcube_total_value: '50',
145+
},
146+
{
147+
acube__category: 'Category B',
148+
acube__id: '2',
149+
acube___acube_count: '1',
150+
acube___acube_total_value: '20',
151+
acube___bcube_total_value: '60',
152+
},
153+
{
154+
acube__category: 'Category C',
155+
acube__id: '3',
156+
acube___acube_count: '1',
157+
acube___acube_total_value: '30',
158+
acube___bcube_total_value: '70',
159+
}
160+
],
161+
{ joinGraph, cubeEvaluator, compiler }
162+
));
163+
164+
it('query with 2 cube joins', async () => dbRunner.runQueryTest(
165+
{
166+
dimensions: [
167+
'Acube.category',
168+
'Acube.id'
169+
],
170+
measures: [
171+
'Acube.AcubeCount',
172+
'Acube.AcubeTotalValue',
173+
'Acube.BcubeTotalValue',
174+
'Acube.CcubeTotalAmount'
175+
],
176+
order: [{
177+
id: 'Acube.id',
178+
desc: false
179+
}],
180+
queryType: 'multi'
181+
},
182+
[
183+
{
184+
acube__category: 'Category A',
185+
acube__id: '1',
186+
acube___acube_count: '1',
187+
acube___acube_total_value: '10',
188+
acube___bcube_total_value: '50',
189+
acube___ccube_total_amount: '15',
190+
},
191+
{
192+
acube__category: 'Category B',
193+
acube__id: '2',
194+
acube___acube_count: '1',
195+
acube___acube_total_value: '20',
196+
acube___bcube_total_value: '60',
197+
acube___ccube_total_amount: '25',
198+
},
199+
{
200+
acube__category: 'Category C',
201+
acube__id: '3',
202+
acube___acube_count: '1',
203+
acube___acube_total_value: '30',
204+
acube___bcube_total_value: '70',
205+
acube___ccube_total_amount: '35',
206+
}
207+
],
208+
{ joinGraph, cubeEvaluator, compiler }
209+
));
210+
});

0 commit comments

Comments
 (0)