Skip to content

Commit

Permalink
feat: Ungrouped query pre-aggregation support (#8058)
Browse files Browse the repository at this point in the history
  • Loading branch information
paveltiunov committed Mar 29, 2024
1 parent 19f6245 commit 2ca99de
Show file tree
Hide file tree
Showing 6 changed files with 436 additions and 14 deletions.
8 changes: 4 additions & 4 deletions packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ export class BaseQuery {
buildParamAnnotatedSql() {
let sql;
let preAggForQuery;
if (!this.options.preAggregationQuery && !this.ungrouped) {
if (!this.options.preAggregationQuery) {
preAggForQuery =
this.preAggregations.findPreAggregationForQuery();
if (this.options.disableExternalPreAggregations && preAggForQuery && preAggForQuery.preAggregation.external) {
Expand Down Expand Up @@ -539,7 +539,7 @@ export class BaseQuery {
}

externalPreAggregationQuery() {
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && !this.ungrouped && this.externalQueryClass) {
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && this.externalQueryClass) {
const preAggregationForQuery = this.preAggregations.findPreAggregationForQuery();
if (preAggregationForQuery && preAggregationForQuery.preAggregation.external) {
return true;
Expand All @@ -558,7 +558,7 @@ export class BaseQuery {
* @returns {Array<string>}
*/
buildSqlAndParams(exportAnnotatedSql) {
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && !this.ungrouped && this.externalQueryClass) {
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && this.externalQueryClass) {
if (this.externalPreAggregationQuery()) { // TODO performance
return this.externalQuery().buildSqlAndParams(exportAnnotatedSql);
}
Expand Down Expand Up @@ -1848,7 +1848,7 @@ export class BaseQuery {
primaryKeyNames(cubeName) {
const primaryKeys = this.cubeEvaluator.primaryKeys[cubeName];
if (!primaryKeys || !primaryKeys.length) {
throw new UserError(`One or more Primary key is required for '${cubeName}`);
throw new UserError(`One or more Primary key is required for '${cubeName}' cube`);
}
return primaryKeys.map((pk) => this.primaryKeyName(cubeName, pk));
}
Expand Down
40 changes: 34 additions & 6 deletions packages/cubejs-schema-compiler/src/adapter/PreAggregations.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,17 @@ export class PreAggregations {
});
}).map(d => query.newTimeDimension(d));

let sortedAllCubeNames;
let sortedUsedCubePrimaryKeys;

if (query.ungrouped) {
const { allCubeNames } = query;
sortedAllCubeNames = allCubeNames.concat([]);
sortedAllCubeNames.sort();
sortedUsedCubePrimaryKeys = query.allCubeNames.flatMap(c => query.primaryKeyNames(c));
sortedUsedCubePrimaryKeys.sort();
}

const measureToLeafMeasures = {};

const leafMeasurePaths =
Expand Down Expand Up @@ -398,7 +409,10 @@ export class PreAggregations {
ownedDimensions,
ownedTimeDimensionsWithRollupGranularity,
ownedTimeDimensionsAsIs,
allBackAliasMembers
allBackAliasMembers,
ungrouped: query.ungrouped,
sortedUsedCubePrimaryKeys,
sortedAllCubeNames
};
}

Expand Down Expand Up @@ -665,6 +679,20 @@ export class PreAggregations {
(references.sortedTimeDimensions || sortTimeDimensions(references.timeDimensions))
);

if (transformedQuery.ungrouped) {
const allReferenceCubes = R.pipe(R.map(m => (m.dimension || m).split('.')[0]), R.uniq, R.sortBy(R.identity))(
references.measures.concat(references.dimensions).concat(references.timeDimensions)
);
if (
!R.equals(transformedQuery.sortedAllCubeNames, allReferenceCubes) ||
!(
dimensionsMatch(transformedQuery.sortedUsedCubePrimaryKeys, true) || dimensionsMatch(transformedQuery.sortedUsedCubePrimaryKeys, false)
)
) {
return false;
}
}

const backAliasMeasures = backAlias(references.measures);
return ((
windowGranularityMatches(references)
Expand All @@ -687,9 +715,9 @@ export class PreAggregations {
* @returns {boolean}
*/
const canUseFn =
transformedQuery.leafMeasureAdditive &&
!transformedQuery.hasMultipliedMeasures
? (r) => canUsePreAggregationLeafMeasureAdditive(r) ||
(
transformedQuery.leafMeasureAdditive && !transformedQuery.hasMultipliedMeasures || transformedQuery.ungrouped
) ? (r) => canUsePreAggregationLeafMeasureAdditive(r) ||
canUsePreAggregationNotAdditive(r)
: canUsePreAggregationNotAdditive;

Expand Down Expand Up @@ -1293,12 +1321,12 @@ export class PreAggregations {
const measure = this.query.newMeasure(path);
return [
path,
this.query.aggregateOnGroupedColumn(
this.query.ungrouped ? measure.aliasName() : (this.query.aggregateOnGroupedColumn(
measure.measureDefinition(),
measure.aliasName(),
!this.query.safeEvaluateSymbolContext().overTimeSeriesAggregate,
path,
) || `sum(${measure.aliasName()})`,
) || `sum(${measure.aliasName()})`),
];
}),
R.fromPairs,
Expand Down
28 changes: 24 additions & 4 deletions packages/cubejs-testing-drivers/fixtures/_schemas.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@
{
"name": "category",
"type": "string",
"sql": "category"
"sql": "category",
"primary_key": true,
"shown": true
},
{
"name": "subCategory",
"type": "string",
"sql": "sub_category"
"sql": "sub_category",
"primary_key": true,
"shown": true
},
{
"name": "productName",
"type": "string",
"sql": "product_name"
"sql": "product_name",
"primary_key": true,
"shown": true
}
]
},
Expand Down Expand Up @@ -151,7 +157,6 @@
},
{
"name": "BigECommerce",
"extends": "Products",
"joins": [
{
"name": "Customers",
Expand All @@ -167,6 +172,21 @@
"primary_key": true,
"shown": true
},
{
"name": "category",
"type": "string",
"sql": "category"
},
{
"name": "subCategory",
"type": "string",
"sql": "sub_category"
},
{
"name": "productName",
"type": "string",
"sql": "product_name"
},
{
"name": "orderId",
"sql": "order_id",
Expand Down
12 changes: 12 additions & 0 deletions packages/cubejs-testing-drivers/src/tests/testQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1522,5 +1522,17 @@ from
`);
expect(res.rows).toMatchSnapshot('powerbi_min_max_ungrouped_flag');
});

executePg('SQL API: ungrouped pre-agg', async () => {
const res = await connection.query(`
select
"productName",
"totalSales"
from
"public"."BigECommerce" "$Table"
order by 2 desc, 1 asc
`);
expect(res.rows).toMatchSnapshot('ungrouped_pre_agg');
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,187 @@ Array [
]
`;

exports[`Queries with the @cubejs-backend/postgres-driver SQL API: ungrouped pre-agg: ungrouped_pre_agg 1`] = `
Array [
Object {
"productName": "Canon PC1080F Personal Copier",
"totalSales": 2399.96,
},
Object {
"productName": "Logitech diNovo Edge Keyboard",
"totalSales": 2249.91,
},
Object {
"productName": "Global Adaptabilites Bookcase, Cherry/Storm Gray Finish",
"totalSales": 2154.9,
},
Object {
"productName": "Google Nexus 5",
"totalSales": 1979.89,
},
Object {
"productName": "Global Adaptabilites Bookcase, Cherry/Storm Gray Finish",
"totalSales": 1292.94,
},
Object {
"productName": "Canon PC1080F Personal Copier",
"totalSales": 1199.98,
},
Object {
"productName": "Hewlett Packard 610 Color Digital Copier / Printer",
"totalSales": 899.982,
},
Object {
"productName": "Okidata C610n Printer",
"totalSales": 649,
},
Object {
"productName": "Lexmark 20R1285 X6650 Wireless All-in-One Printer",
"totalSales": 600,
},
Object {
"productName": "Google Nexus 6",
"totalSales": 539.97,
},
Object {
"productName": "Google Nexus 7",
"totalSales": 539.97,
},
Object {
"productName": "Harbour Creations 67200 Series Stacking Chairs",
"totalSales": 498.26,
},
Object {
"productName": "DMI Eclipse Executive Suite Bookcases",
"totalSales": 400.784,
},
Object {
"productName": "HTC One",
"totalSales": 239.976,
},
Object {
"productName": "Iceberg Nesting Folding Chair, 19w x 6d x 43h",
"totalSales": 232.88,
},
Object {
"productName": "Balt Solid Wood Rectangular Table",
"totalSales": 210.98,
},
Object {
"productName": "Tyvek Side-Opening Peel & Seel Expanding Envelopes",
"totalSales": 180.96,
},
Object {
"productName": "Panasonic KP-380BK Classic Electric Pencil Sharpener",
"totalSales": 179.9,
},
Object {
"productName": "Harbour Creations 67200 Series Stacking Chairs",
"totalSales": 128.124,
},
Object {
"productName": "Harbour Creations 67200 Series Stacking Chairs",
"totalSales": 113.888,
},
Object {
"productName": "Panasonic KP-380BK Classic Electric Pencil Sharpener",
"totalSales": 86.352,
},
Object {
"productName": "Kingston Digital DataTraveler 16GB USB 2.2",
"totalSales": 71.6,
},
Object {
"productName": "Linden 10 Round Wall Clock, Black",
"totalSales": 48.896,
},
Object {
"productName": "Vinyl Coated Wire Paper Clips in Organizer Box, 800/Box",
"totalSales": 45.92,
},
Object {
"productName": "Vinyl Coated Wire Paper Clips in Organizer Box, 800/Box",
"totalSales": 45.92,
},
Object {
"productName": "Kingston Digital DataTraveler 16GB USB 2.0",
"totalSales": 44.75,
},
Object {
"productName": "Kingston Digital DataTraveler 16GB USB 2.1",
"totalSales": 44.75,
},
Object {
"productName": "Recycled Eldon Regeneration Jumbo File",
"totalSales": 39.296,
},
Object {
"productName": "Linden 10 Round Wall Clock, Black",
"totalSales": 36.672,
},
Object {
"productName": "Linden 10 Round Wall Clock, Black",
"totalSales": 30.56,
},
Object {
"productName": "Linden 10 Round Wall Clock, Black",
"totalSales": 30.56,
},
Object {
"productName": "Anderson Hickey Conga Table Tops & Accessories",
"totalSales": 24.368,
},
Object {
"productName": "Plymouth Boxed Rubber Bands by Plymouth",
"totalSales": 23.55,
},
Object {
"productName": "Vinyl Coated Wire Paper Clips in Organizer Box, 800/Box",
"totalSales": 18.368,
},
Object {
"productName": "Wausau Papers Astrobrights Colored Envelopes",
"totalSales": 14.352,
},
Object {
"productName": "Plymouth Boxed Rubber Bands by Plymouth",
"totalSales": 14.13,
},
Object {
"productName": "Project Tote Personal File",
"totalSales": 14.03,
},
Object {
"productName": "Plymouth Boxed Rubber Bands by Plymouth",
"totalSales": 11.304,
},
Object {
"productName": "Magna Visual Magnetic Picture Hangers",
"totalSales": 9.64,
},
Object {
"productName": "OIC #2 Pencils, Medium Soft",
"totalSales": 9.4,
},
Object {
"productName": "Magna Visual Magnetic Picture Hangers",
"totalSales": 7.712,
},
Object {
"productName": "OIC #2 Pencils, Medium Soft",
"totalSales": 3.76,
},
Object {
"productName": "OIC #2 Pencils, Medium Soft",
"totalSales": 3.76,
},
Object {
"productName": "Lexmark 20R1285 X6650 Wireless All-in-One Printer",
"totalSales": null,
},
]
`;

exports[`Queries with the @cubejs-backend/postgres-driver filtering Customers: contains + dimensions, first 1`] = `
Array [
Object {
Expand Down

0 comments on commit 2ca99de

Please sign in to comment.