Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/cubejs-api-gateway/openspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ components:
$ref: "#/components/schemas/V1CubeMetaFormat"
order:
$ref: "#/components/schemas/V1CubeMetaDimensionOrder"
key:
type: "string"
description: "Key reference for the dimension"
V1CubeMetaDimensionOrder:
type: "string"
enum:
Expand Down
4 changes: 2 additions & 2 deletions packages/cubejs-client-core/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Meta from './Meta';
import { TimeDimensionGranularity } from './time';
import { TransportOptions } from './HttpTransport';

export type QueryOrder = 'asc' | 'desc' | 'none';

Expand All @@ -27,7 +26,7 @@ export type Annotation = {
shortTitle: string;
type: string;
meta?: any;
format?: DimensionFormat;
format?: DimensionFormat | MeasureFormat;
drillMembers?: any[];
drillMembersGrouped?: any;
granularity?: GranularityAnnotation;
Expand Down Expand Up @@ -396,6 +395,7 @@ export type BaseCubeDimension = BaseCubeMember & {
primaryKey?: boolean;
suggestFilterValues: boolean;
format?: DimensionFormat;
key?: string;
};

export type CubeTimeDimension = BaseCubeDimension &
Expand Down
2 changes: 2 additions & 0 deletions packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export type DimensionDefinition = {
multiStage?: boolean;
shiftInterval?: string;
order?: 'asc' | 'desc';
key?: (...args: any[]) => ToString;
keyReference?: string;
};

export type TimeShiftDefinition = {
Expand Down
79 changes: 79 additions & 0 deletions packages/cubejs-schema-compiler/src/compiler/CubeSymbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export type CubeSymbolDefinition = {
timeShift?: TimeshiftDefinition[];
format?: string;
order?: 'asc' | 'desc';
key?: (...args: any[]) => ToString;
keyReference?: string;
};

export type HierarchyDefinition = {
Expand Down Expand Up @@ -275,9 +277,16 @@ export class CubeSymbols implements TranspilerSymbolResolver, CompilerInterface
const sortedByDependency = R.pipe(
R.sortBy((c: CubeDefinition) => !!c.isView),
)(cubes);

for (const cube of sortedByDependency) {
const splitViews: SplitViews = {};

this.symbols[cube.name] = this.transform(cube.name, errorReporter.inContext(`${cube.name} cube`), splitViews);

if (!cube.isView) {
this.evaluateDimensionKeys(this.getCubeDefinition(cube.name), errorReporter.inContext(`${cube.name} cube`));
}

for (const viewName of Object.keys(splitViews)) {
// TODO can we define it when cubeList is defined?
this.cubeList.push(splitViews[viewName]);
Expand Down Expand Up @@ -542,6 +551,45 @@ export class CubeSymbols implements TranspilerSymbolResolver, CompilerInterface
});
}

private evaluateDimensionKeys(cube: CubeDefinition, errorReporter: ErrorReporter) {
const dimensions = cube.dimensions || {};

// eslint-disable-next-line no-restricted-syntax
for (const [dimensionName, dimension] of Object.entries(dimensions)) {
if (dimension.key) {
const keyReference = this.evaluateReference(
cube.name,
dimension.key,
`Dimension '${cube.name}.${dimensionName}' key`
);

const [refCubeName, refDimensionName] = keyReference.split('.');

if (refCubeName !== cube.name) {
errorReporter.error(
`Dimension '${cube.name}.${dimensionName}' has a key that references dimension '${keyReference}' ` +
'from a different cube. Key must reference a dimension within the same cube.'
);
} else if (!dimensions[refDimensionName]) {
errorReporter.error(
`Dimension '${cube.name}.${dimensionName}' references key dimension '${refDimensionName}' ` +
`which does not exist in cube '${cube.name}'.`
);
} else {
const referencedDimension = dimensions[refDimensionName];
if (referencedDimension.key) {
errorReporter.error(
`Dimension '${cube.name}.${dimensionName}' references '${keyReference}' as its key, ` +
`but '${keyReference}' already defines its own key. Nested keys are not allowed.`
);
} else {
dimension.keyReference = keyReference;
}
}
}
}
}

protected transformPreAggregations(preAggregations: Object) {
// eslint-disable-next-line no-restricted-syntax
for (const preAggregation of Object.values(preAggregations)) {
Expand Down Expand Up @@ -836,6 +884,23 @@ export class CubeSymbols implements TranspilerSymbolResolver, CompilerInterface
return this.symbols[cubeName]?.cubeObj()?.[type]?.[memberName];
}

protected processKeyReferenceForView(
keyReference: string,
viewName: string,
viewAllMembers: ViewResolvedMember[],
dimensionName: string
): { keyReference: string } {
const viewKeyMember = viewAllMembers.find(v => v.member === keyReference);

if (!viewKeyMember) {
throw new UserError(
`Dimension '${dimensionName}' has key '${keyReference}' but the key dimension is not included in view '${viewName}'`
);
}

return { keyReference: `${viewName}.${viewKeyMember.name}` };
}

protected generateIncludeMembers(members: any[], type: string, targetCube: CubeDefinitionExtended, viewAllMembers: ViewResolvedMember[]) {
return members.map(memberRef => {
const path = memberRef.member.split('.');
Expand Down Expand Up @@ -900,6 +965,7 @@ export class CubeSymbols implements TranspilerSymbolResolver, CompilerInterface
format: memberRef.override?.format || resolvedMember.format,
...(resolvedMember.granularities ? { granularities: resolvedMember.granularities } : {}),
...(resolvedMember.multiStage && { multiStage: resolvedMember.multiStage }),
...(resolvedMember.keyReference && this.processKeyReferenceForView(resolvedMember.keyReference, targetCube.name, viewAllMembers, memberRef.member)),
};
} else if (type === 'segments') {
memberDefinition = {
Expand Down Expand Up @@ -994,6 +1060,19 @@ export class CubeSymbols implements TranspilerSymbolResolver, CompilerInterface
return options.originalSorting ? references : R.sortBy(R.identity, references) as any;
}

public evaluateReference(
cube: string,
referencesFn: (...args: Array<unknown>) => ToString,
context: string
): string {
const result = this.evaluateReferences(cube, referencesFn);
if (Array.isArray(result)) {
throw new UserError(`${context} must be a single reference, not an array`);
}

return result;
}

public pathFromArray(array: string[]): string {
return array.join('.');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface ExtendedCubeSymbolDefinition extends CubeSymbolDefinition {
drillMemberReferences?: any;
cumulative?: boolean;
aggType?: string;
keyReference?: string;
}

interface ExtendedCubeDefinition extends CubeDefinitionExtended {
Expand Down Expand Up @@ -98,6 +99,7 @@ export type DimensionConfig = {
aliasMember?: string;
granularities?: GranularityDefinition[];
order?: 'asc' | 'desc';
key?: string;
};

export type SegmentConfig = {
Expand Down Expand Up @@ -276,6 +278,7 @@ export class CubeToMetaTransformer implements CompilerInterface {
}))
: undefined,
order: extendedDimDef.order,
key: extendedDimDef.keyReference,
};
}),
segments: Object.entries(extendedCube.segments || {}).map((nameToSegment: [string, any]) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ const BaseDimensionWithoutSubQuery = {
}),
meta: Joi.any(),
order: Joi.string().valid('asc', 'desc'),
key: Joi.func(),
keyReference: Joi.string(),
values: Joi.when('type', {
is: 'switch',
then: Joi.array().items(Joi.string()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const transpiledFieldsPatterns: Array<RegExp> = [
/^measures\.[_a-zA-Z][_a-zA-Z0-9]*\.(timeShift|time_shift)\.[0-9]+\.(timeDimension|time_dimension)$/,
/^measures\.[_a-zA-Z][_a-zA-Z0-9]*\.(reduceBy|reduce_by|groupBy|group_by|addGroupBy|add_group_by)$/,
/^(measures|dimensions)\.[_a-zA-Z][_a-zA-Z0-9]*\.case\.switch$/,
/^dimensions\.[_a-zA-Z][_a-zA-Z0-9]*\.(reduceBy|reduce_by|groupBy|group_by|addGroupBy|add_group_by)$/,
/^dimensions\.[_a-zA-Z][_a-zA-Z0-9]*\.(reduceBy|reduce_by|groupBy|group_by|addGroupBy|add_group_by|key)$/,
/^(preAggregations|pre_aggregations)\.[_a-zA-Z][_a-zA-Z0-9]*\.indexes\.[_a-zA-Z][_a-zA-Z0-9]*\.columns$/,
/^(preAggregations|pre_aggregations)\.[_a-zA-Z][_a-zA-Z0-9]*\.(timeDimensionReference|timeDimension|time_dimension|segments|dimensions|measures|rollups|segmentReferences|dimensionReferences|measureReferences|rollupReferences)$/,
/^(preAggregations|pre_aggregations)\.[_a-zA-Z][_a-zA-Z0-9]*\.(timeDimensions|time_dimensions)\.\d+\.dimension$/,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Object {
"format": "imageUrl",
"granularities": undefined,
"isVisible": true,
"key": undefined,
"meta": Object {
"key": "Meta.key for CubeA.id",
},
Expand All @@ -29,6 +30,7 @@ Object {
"format": "imageUrl",
"granularities": undefined,
"isVisible": true,
"key": undefined,
"meta": Object {
"key": "Meta.key for CubeB.other_id",
},
Expand Down Expand Up @@ -112,6 +114,7 @@ Object {
"format": "imageUrl",
"granularities": undefined,
"isVisible": true,
"key": undefined,
"meta": Object {
"key": "Meta.key for CubeB.other_id",
},
Expand Down Expand Up @@ -195,6 +198,7 @@ Object {
"format": "imageUrl",
"granularities": undefined,
"isVisible": true,
"key": undefined,
"meta": Object {
"key": "Meta.key for CubeA.id",
},
Expand All @@ -213,6 +217,7 @@ Object {
"format": "imageUrl",
"granularities": undefined,
"isVisible": true,
"key": undefined,
"meta": Object {
"key": "Meta.key for CubeB.id",
},
Expand All @@ -231,6 +236,7 @@ Object {
"format": "imageUrl",
"granularities": undefined,
"isVisible": true,
"key": undefined,
"meta": Object {
"key": "Meta.key for CubeB.other_id",
},
Expand Down Expand Up @@ -314,6 +320,7 @@ Object {
"format": "imageUrl",
"granularities": undefined,
"isVisible": true,
"key": undefined,
"meta": Object {
"key": "Meta.key for CubeB.other_id",
},
Expand Down
Loading
Loading