Skip to content

Commit a1c89fb

Browse files
authored
chore(schema-compiler): Improve performance of CubeSymbols prepareIncludes() and membersFromCubes() methods (cube-js#9775)
1 parent 72e6059 commit a1c89fb

File tree

1 file changed

+105
-47
lines changed

1 file changed

+105
-47
lines changed

packages/cubejs-schema-compiler/src/compiler/CubeSymbols.ts

Lines changed: 105 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -448,15 +448,31 @@ export class CubeSymbols {
448448
const includeMembers = this.generateIncludeMembers(cubeIncludes, cube.name, type);
449449
this.applyIncludeMembers(includeMembers, cube, type, errorReporter);
450450

451-
cube.includedMembers = R.uniqWith(R.equals, [...(cube.includedMembers || []), ...Array.from(new Set(cubeIncludes.map((it: any) => {
452-
const split = it.member.split('.');
453-
const memberPath = this.pathFromArray([split[split.length - 2], split[split.length - 1]]);
454-
return {
455-
type,
456-
memberPath,
457-
name: it.name
458-
};
459-
})))]);
451+
const existing = cube.includedMembers ?? [];
452+
const seen = new Set(
453+
existing.map(({ type: t, memberPath, name }) => `${t}|${memberPath}|${name}`)
454+
);
455+
456+
const additions: {
457+
type: string;
458+
memberPath: string;
459+
name: string;
460+
}[] = [];
461+
462+
for (const { member, name } of cubeIncludes) {
463+
const parts = member.split('.');
464+
const memberPath = this.pathFromArray(parts.slice(-2));
465+
const key = `${type}|${memberPath}|${name}`;
466+
467+
if (!seen.has(key)) {
468+
seen.add(key);
469+
additions.push({ type, memberPath, name });
470+
}
471+
}
472+
473+
if (additions.length) {
474+
cube.includedMembers = [...existing, ...additions];
475+
}
460476
}
461477

462478
[...memberSets.allMembers].filter(it => !memberSets.resolvedMembers.has(it)).forEach(it => {
@@ -474,66 +490,98 @@ export class CubeSymbols {
474490
}
475491
}
476492

477-
protected membersFromCubes(parentCube: CubeDefinition, cubes: any[], type: string, errorReporter: ErrorReporter, splitViews: SplitViews, memberSets: any) {
478-
return R.uniqWith(R.equals, R.unnest(cubes.map(cubeInclude => {
479-
// TODO recheck `cubeInclude.joinPath` typing
480-
const fullPath = this.evaluateReferences(null, cubeInclude.joinPath as () => ToString, { collectJoinHints: true });
493+
protected membersFromCubes(
494+
parentCube: CubeDefinition,
495+
cubes: any[],
496+
type: string,
497+
errorReporter: ErrorReporter,
498+
splitViews: SplitViews,
499+
memberSets: any
500+
) {
501+
const result: any[] = [];
502+
const seen = new Set<string>();
503+
504+
for (const cubeInclude of cubes) {
505+
const fullPath = this.evaluateReferences(
506+
null,
507+
// TODO recheck `cubeInclude.joinPath` typing
508+
cubeInclude.joinPath as () => ToString,
509+
{ collectJoinHints: true }
510+
);
511+
481512
const split = fullPath.split('.');
482513
const cubeReference = split[split.length - 1];
483514
const cubeName = cubeInclude.alias || cubeReference;
484515

485-
let includes: any[];
486516
const fullMemberName = (memberName: string) => (cubeInclude.prefix ? `${cubeName}_${memberName}` : memberName);
487517

518+
let includes: any[];
519+
488520
if (cubeInclude.includes === '*') {
489521
const membersObj = this.symbols[cubeReference]?.cubeObj()?.[type] || {};
490-
includes = Object.keys(membersObj).map(memberName => ({ member: `${fullPath}.${memberName}`, name: fullMemberName(memberName) }));
522+
includes = Object.keys(membersObj).map((memberName) => ({
523+
member: `${fullPath}.${memberName}`,
524+
name: fullMemberName(memberName),
525+
}));
491526
} else {
492527
includes = cubeInclude.includes.map((include: any) => {
493528
const member = include.alias || include.name || include;
494529

495530
if (member.includes('.')) {
496-
errorReporter.error(`Paths aren't allowed in cube includes but '${member}' provided as include member`);
531+
errorReporter.error(
532+
`Paths aren't allowed in cube includes but '${member}' provided as include member`
533+
);
497534
}
498535

499536
const name = fullMemberName(member);
500537
memberSets.allMembers.add(name);
501538

502539
const includedMemberName = include.name || include;
503540

504-
const resolvedMember = this.getResolvedMember(type, cubeReference, includedMemberName) ? {
505-
member: `${fullPath}.${includedMemberName}`,
506-
name,
507-
...(include.title || include.description || include.format || include.meta) ? {
508-
override: {
509-
title: include.title,
510-
description: include.description,
511-
format: include.format,
512-
meta: include.meta,
513-
}
514-
} : {}
515-
} : undefined;
541+
const resolved = this.getResolvedMember(
542+
type,
543+
cubeReference,
544+
includedMemberName
545+
);
516546

517-
if (resolvedMember) {
518-
memberSets.resolvedMembers.add(name);
519-
}
547+
if (!resolved) return undefined;
548+
549+
memberSets.resolvedMembers.add(name);
520550

521-
return resolvedMember;
551+
const override = (include.title || include.description || include.format || include.meta)
552+
? {
553+
title: include.title,
554+
description: include.description,
555+
format: include.format,
556+
meta: include.meta,
557+
}
558+
: undefined;
559+
560+
return {
561+
member: `${fullPath}.${includedMemberName}`,
562+
name,
563+
...(override ? { override } : {}),
564+
};
522565
});
523566
}
524567

525-
const excludes = (cubeInclude.excludes || []).map((exclude: any) => {
526-
if (exclude.includes('.')) {
527-
errorReporter.error(`Paths aren't allowed in cube excludes but '${exclude}' provided as exclude member`);
528-
}
568+
const excludes = (cubeInclude.excludes || [])
569+
.map((exclude: any) => {
570+
if (exclude.includes('.')) {
571+
errorReporter.error(
572+
`Paths aren't allowed in cube excludes but '${exclude}' provided as exclude member`
573+
);
574+
}
529575

530-
const resolvedMember = this.getResolvedMember(type, cubeReference, exclude);
531-
return resolvedMember ? {
532-
member: `${fullPath}.${exclude}`
533-
} : undefined;
534-
});
576+
const resolved = this.getResolvedMember(type, cubeReference, exclude);
577+
return resolved ? { member: `${fullPath}.${exclude}` } : undefined;
578+
})
579+
.filter(Boolean);
535580

536-
const finalIncludes = this.diffByMember(includes.filter(Boolean), excludes.filter(Boolean));
581+
const finalIncludes = this.diffByMember(
582+
includes.filter(Boolean),
583+
excludes
584+
);
537585

538586
if (cubeInclude.split) {
539587
const viewName = `${parentCube.name}_${cubeName}`;
@@ -548,14 +596,24 @@ export class CubeSymbols {
548596
splitViewDef = splitViews[viewName];
549597
}
550598

551-
const includeMembers = this.generateIncludeMembers(finalIncludes, parentCube.name, type);
599+
const includeMembers = this.generateIncludeMembers(
600+
finalIncludes,
601+
parentCube.name,
602+
type
603+
);
552604
this.applyIncludeMembers(includeMembers, splitViewDef, type, errorReporter);
553-
554-
return [];
555605
} else {
556-
return finalIncludes;
606+
for (const member of finalIncludes) {
607+
const key = `${member.member}|${member.name}`;
608+
if (!seen.has(key)) {
609+
seen.add(key);
610+
result.push(member);
611+
}
612+
}
557613
}
558-
})));
614+
}
615+
616+
return result;
559617
}
560618

561619
protected diffByMember(includes: any[], excludes: any[]) {

0 commit comments

Comments
 (0)