Skip to content

Commit

Permalink
cached granularityHierarchies() for query timezone
Browse files Browse the repository at this point in the history
  • Loading branch information
KSDaemon committed Jan 31, 2025
1 parent 52e36a5 commit 905e873
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 33 deletions.
46 changes: 44 additions & 2 deletions packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { BaseTimeDimension } from './BaseTimeDimension';
import { ParamAllocator } from './ParamAllocator';
import { PreAggregations } from './PreAggregations';
import { SqlParser } from '../parser/SqlParser';
import { Granularity } from './Granularity';

const DEFAULT_PREAGGREGATIONS_SCHEMA = 'stb_pre_aggregations';

Expand Down Expand Up @@ -1379,7 +1380,47 @@ export class BaseQuery {
}

granularityHierarchies() {
return R.fromPairs(Object.keys(standardGranularitiesParents).map(k => [k, this.granularityParentHierarchy(k)]));
return this.cacheValue(
// If time dimension custom granularity in data model is defined without
// timezone information they are treated in query timezone.
// Because of that it's not possible to correctly precalculate
// granularities hierarchies on startup as they are specific for each timezone.
['granularityHierarchies', this.timezone],
() => R.reduce(
(hierarchies, cube) => R.reduce(
(acc, [tdName, td]) => {
const dimensionKey = `${cube}.${tdName}`;

// constructing standard granularities for time dimension
const standardEntries = R.fromPairs(
R.keys(standardGranularitiesParents).map(gr => [
`${dimensionKey}.${gr}`,
standardGranularitiesParents[gr],
]),
);

// If we have custom granularities in time dimension
const customEntries = td.granularities
? R.fromPairs(
R.keys(td.granularities).map(granularityName => {
const grObj = new Granularity(this, { dimension: dimensionKey, granularity: granularityName });
return [
`${dimensionKey}.${granularityName}`,
[granularityName, ...standardGranularitiesParents[grObj.minGranularity()]],
];
}),
)
: {};

return R.mergeRight(acc, standardEntries, customEntries);
},
hierarchies,
R.toPairs(this.cubeEvaluator.timeDimensionsForCube(cube)),
),
{},
R.keys(this.cubeEvaluator.evaluatedCubes),
),
);
}

granularityParentHierarchy(granularity) {
Expand Down Expand Up @@ -1646,7 +1687,8 @@ export class BaseQuery {

/**
*
* @param {{sql: string, on: {cubeName: string, expression: Function}, joinType: 'LEFT' | 'INNER', alias: string}} customJoin
* @param {{sql: string, on: {cubeName: string, expression: Function}, joinType: 'LEFT' | 'INNER', alias: string}}
* customJoin
* @returns {JoinItem}
*/
customSubQueryJoin(customJoin) {
Expand Down
39 changes: 11 additions & 28 deletions packages/cubejs-schema-compiler/src/adapter/PreAggregations.js
Original file line number Diff line number Diff line change
Expand Up @@ -439,14 +439,14 @@ export class PreAggregations {
static sortTimeDimensionsWithRollupGranularity(timeDimensions) {
return timeDimensions && R.sortBy(
R.prop(0),
timeDimensions.map(d => [d.expressionPath(), d.rollupGranularity(), d.granularityObj?.minGranularity()])
timeDimensions.map(d => [d.expressionPath(), d.rollupGranularity()])
) || [];
}

static timeDimensionsAsIs(timeDimensions) {
return timeDimensions && R.sortBy(
R.prop(0),
timeDimensions.map(d => [d.expressionPath(), d.resolvedGranularity(), d.granularityObj?.minGranularity()]),
timeDimensions.map(d => [d.expressionPath(), d.resolvedGranularity()]),
) || [];
}

Expand Down Expand Up @@ -536,9 +536,7 @@ export class PreAggregations {
backAlias(references.sortedTimeDimensions || sortTimeDimensions(references.timeDimensions));
const qryTimeDimensions = references.allowNonStrictDateRangeMatch
? transformedQuery.timeDimensions
: transformedQuery.sortedTimeDimensions.map(t => t.slice(0, 2));
// slices above/below are used to exclude granularity on 3rd position returned from sortTimeDimensionsWithRollupGranularity()
const qryDimensions = transformedQuery.timeDimensions.map(t => t.slice(0, 2));
: transformedQuery.sortedTimeDimensions;
const backAliasMeasures = backAlias(references.measures);
const backAliasSortedDimensions = backAlias(references.sortedDimensions || references.dimensions);
const backAliasDimensions = backAlias(references.dimensions);
Expand All @@ -550,7 +548,7 @@ export class PreAggregations {
R.equals(qryTimeDimensions, refTimeDimensions)
) && (
transformedQuery.isAdditive ||
R.equals(qryDimensions, refTimeDimensions)
R.equals(transformedQuery.timeDimensions, refTimeDimensions)
) && (
filterDimensionsSingleValueEqual &&
references.dimensions.length === filterDimensionsSingleValueEqual.size &&
Expand All @@ -566,28 +564,13 @@ export class PreAggregations {

/**
* Expand granularity into array of granularity hierarchy.
* resolvedGranularity might be a custom granularity name, in this case
* we insert it into all expanded hierarchies using its minimal granularity passed
* as minGranularity param
* @param {string} resolvedGranularity
* @param {string} minGranularity
* @param {string} granularity Granularity full path in the form of `cube.timeDimension.granularity`
* @returns {Array<string>}
*/
const expandGranularity = (resolvedGranularity, minGranularity) => {
if (!resolvedGranularity) {
return [];
}

let predefinedHierarchy = transformedQuery.granularityHierarchies[resolvedGranularity];

if (predefinedHierarchy) {
return predefinedHierarchy;
}

predefinedHierarchy = transformedQuery.granularityHierarchies[minGranularity];
// Custom granularity should be the first in list for exact match hit
return [resolvedGranularity, ...predefinedHierarchy];
};
const expandGranularity = (granularity) => (
transformedQuery.granularityHierarchies[granularity] ||
[granularity]
);

/**
* Determine whether time dimensions match to the window granularity or not.
Expand Down Expand Up @@ -619,8 +602,8 @@ export class PreAggregations {
* @returns {Array<Array<string>>}
*/
const expandTimeDimension = (timeDimension) => {
const [dimension, resolvedGranularity, minGranularity] = timeDimension;
return expandGranularity(resolvedGranularity, minGranularity)
const [dimension, resolvedGranularity] = timeDimension;
return expandGranularity(`${dimension}.${resolvedGranularity}`)
.map((newGranularity) => [dimension, newGranularity]);
};

Expand Down
13 changes: 10 additions & 3 deletions packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,8 @@ export class CubeEvaluator extends CubeSymbols {

public timeDimensionPathsForCube(cube: any) {
return R.compose(
R.map(nameToDefinition => `${cube}.${nameToDefinition[0]}`),
R.toPairs,
R.map(dimName => `${cube}.${dimName}`),
R.keys,
// @ts-ignore
R.filter((d: any) => d.type === 'time')
// @ts-ignore
Expand All @@ -460,12 +460,19 @@ export class CubeEvaluator extends CubeSymbols {
return this.cubeFromPath(cube).measures || {};
}

public timeDimensionsForCube(cube) {
return R.filter(
(d: any) => d.type === 'time',
this.cubeFromPath(cube).dimensions || {}
);
}

public preAggregationsForCube(path: string) {
return this.cubeFromPath(path).preAggregations || {};
}

/**
* Returns pre-aggregations filtered by the spcified selector.
* Returns pre-aggregations filtered by the specified selector.
* @param {{
* scheduled: boolean,
* dataSource: Array<string>,
Expand Down

0 comments on commit 905e873

Please sign in to comment.