From d6f53556e17d60902d67a5f7c93c62f5dc6b9d04 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 23 Jul 2024 14:32:47 +0300 Subject: [PATCH 01/84] remove unneeded filter for granularities --- packages/cubejs-api-gateway/src/gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-api-gateway/src/gateway.ts b/packages/cubejs-api-gateway/src/gateway.ts index e690658987550..a067bcc9d95ed 100644 --- a/packages/cubejs-api-gateway/src/gateway.ts +++ b/packages/cubejs-api-gateway/src/gateway.ts @@ -1231,7 +1231,7 @@ class ApiGateway { if (queryGranularity.length > 1) { throw new UserError('Data blending query granularities must match'); } - if (queryGranularity.filter(Boolean).length === 0) { + if (queryGranularity.length === 0) { throw new UserError('Data blending query without granularity is not supported'); } } From 3b86c537092ec43755c552ffeb5bcb4301fc7a02 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 1 Aug 2024 14:28:43 +0300 Subject: [PATCH 02/84] dedup month in standardGranularitiesParents --- packages/cubejs-schema-compiler/src/adapter/BaseQuery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 9a7ca0fff16f0..b799fd722f0cc 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -31,7 +31,7 @@ import { SqlParser } from '../parser/SqlParser'; const DEFAULT_PREAGGREGATIONS_SCHEMA = 'stb_pre_aggregations'; const standardGranularitiesParents = { - year: ['year', 'quarter', 'month', 'month', 'day', 'hour', 'minute', 'second'], + year: ['year', 'quarter', 'month', 'day', 'hour', 'minute', 'second'], quarter: ['quarter', 'month', 'day', 'hour', 'minute', 'second'], month: ['month', 'day', 'hour', 'minute', 'second'], week: ['week', 'day', 'hour', 'minute', 'second'], From ecd648e742e813f39555dc0c41ad3e0b102ae0c1 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 29 Jul 2024 17:25:05 +0300 Subject: [PATCH 03/84] feat(schema-compiler): update cube's schema to allow granularities for time dimensions --- packages/cubejs-api-gateway/src/query.js | 3 ++- .../src/compiler/CubeValidator.ts | 14 ++++++++++++-- .../test/unit/cube-validator.test.ts | 11 +++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/cubejs-api-gateway/src/query.js b/packages/cubejs-api-gateway/src/query.js index b31ed2661ec47..7b14d6b6bad96 100644 --- a/packages/cubejs-api-gateway/src/query.js +++ b/packages/cubejs-api-gateway/src/query.js @@ -103,7 +103,8 @@ const querySchema = Joi.object().keys({ filters: Joi.array().items(oneFilter, oneCondition), timeDimensions: Joi.array().items(Joi.object().keys({ dimension: id.required(), - granularity: Joi.valid('quarter', 'day', 'month', 'year', 'week', 'hour', 'minute', 'second', null), + // granularity: Joi.valid('quarter', 'day', 'month', 'year', 'week', 'hour', 'minute', 'second', null), + granularity: Joi.string(), // To support custom granularities dateRange: [ Joi.array().items(Joi.string()).min(1).max(2), Joi.string() diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts index 3e79893ea4f8a..8573387eeeb98 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts @@ -102,6 +102,15 @@ const BaseDimensionWithoutSubQuery = { }) ]), meta: Joi.any(), + granularities: Joi.when('type', { + is: 'time', + then: Joi.object().pattern(identifierRegex, + Joi.object().keys({ + sql: Joi.func().required(), + baseGranularity: Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year').optional(), + })).optional(), + otherwise: Joi.forbidden() + }) }; const BaseDimension = Object.assign({ @@ -116,7 +125,8 @@ const FixedRollingWindow = { offset: Joi.any().valid('start', 'end') }; -const GranularitySchema = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year').required(); +// const GranularitySchema = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year').required(); +const GranularitySchema = Joi.string().required(); // To support custom granularities const YearToDate = { type: Joi.string().valid('year_to_date'), @@ -201,7 +211,7 @@ const PreAggregationRefreshKeySchema = condition( (s) => defined(s.sql), Joi.object().keys({ sql: Joi.func().required(), - // We dont support timezone for this, because it's useless + // We don't support timezone for this, because it's useless // We cannot support cron interval every: everyInterval, }), diff --git a/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts b/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts index 24e8cf8bd6e87..8ea645aa4f4a8 100644 --- a/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts @@ -338,7 +338,7 @@ describe('Cube Validation', () => { expect(validationResult.error).toBeTruthy(); }); - it('preAggregations alternatives', async () => { + it('preAggregations custom granularities', async () => { const cubeValidator = new CubeValidator(new CubeSymbols()); const cube = { name: 'name', @@ -350,7 +350,7 @@ describe('Cube Validation', () => { measureReferences: () => '', dimensionReferences: () => '', partitionGranularity: 'month', - granularity: 'days', + granularity: 'custom_granularity_name', timeDimensionReference: () => '', external: true, refreshKey: { @@ -364,13 +364,12 @@ describe('Cube Validation', () => { const validationResult = cubeValidator.validate(cube, { error: (message, e) => { - console.log(message); - expect(message).toContain('must be one of'); - expect(message).not.toContain('rollup) must be'); + // this callback should not be invoked + expect(true).toBeFalsy(); } } as any); - expect(validationResult.error).toBeTruthy(); + expect(validationResult.error).toBeFalsy(); }); it('preAggregations type unknown', async () => { From 747da066259ec6ae068a6bbbac6c597eddb00179 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 30 Jul 2024 15:47:58 +0300 Subject: [PATCH 04/84] fix custom granularity processing in time dimension --- packages/cubejs-backend-shared/src/time.ts | 2 ++ .../src/adapter/BaseTimeDimension.ts | 23 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-backend-shared/src/time.ts b/packages/cubejs-backend-shared/src/time.ts index e14e37231b28c..628e12316c71f 100644 --- a/packages/cubejs-backend-shared/src/time.ts +++ b/packages/cubejs-backend-shared/src/time.ts @@ -46,6 +46,8 @@ export const timeSeries = (granularity: string, dateRange: QueryDateRange, optio return TIME_SERIES[granularity](range, options.timestampPrecision); }; +export const isPredefinedGranularity = (granularity: string): boolean => !!TIME_SERIES[granularity]; + export const FROM_PARTITION_RANGE = '__FROM_PARTITION_RANGE'; export const TO_PARTITION_RANGE = '__TO_PARTITION_RANGE'; diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index 6511b4212bf5c..d6b10df27ed2e 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -1,5 +1,5 @@ import moment from 'moment-timezone'; -import { timeSeries, FROM_PARTITION_RANGE, TO_PARTITION_RANGE, BUILD_RANGE_START_LOCAL, BUILD_RANGE_END_LOCAL } from '@cubejs-backend/shared'; +import { timeSeries, isPredefinedGranularity, FROM_PARTITION_RANGE, TO_PARTITION_RANGE, BUILD_RANGE_START_LOCAL, BUILD_RANGE_END_LOCAL } from '@cubejs-backend/shared'; import { BaseFilter } from './BaseFilter'; import { UserError } from '../compiler/UserError'; @@ -11,6 +11,10 @@ export class BaseTimeDimension extends BaseFilter { public readonly granularity: string; + public readonly isPredefined: boolean; + + public readonly baseGranularity: string; + public readonly boundaryDateRange: any; public readonly shiftInterval: string; @@ -26,6 +30,10 @@ export class BaseTimeDimension extends BaseFilter { }); this.dateRange = timeDimension.dateRange; this.granularity = timeDimension.granularity; + this.isPredefined = isPredefinedGranularity(this.granularity); + this.baseGranularity = this.query.cubeEvaluator + .byPath('dimensions', timeDimension.dimension) + .granularities?.[this.granularity]?.baseGranularity; this.boundaryDateRange = timeDimension.boundaryDateRange; this.shiftInterval = timeDimension.shiftInterval; } @@ -210,7 +218,18 @@ export class BaseTimeDimension extends BaseFilter { this.rollupGranularityValue = this.query.cacheValue( ['rollupGranularity', this.granularity].concat(this.dateRange), - () => this.query.minGranularity(this.granularity, this.dateRangeGranularity()) + () => { + if (this.isPredefined) { + return this.query.minGranularity(this.granularity, this.dateRangeGranularity()); + } + + if (this.baseGranularity) { + return this.query.minGranularity(this.baseGranularity, this.dateRangeGranularity()); + } + + // Trying to get granularity from the date range if it was provided + return this.dateRangeGranularity(); + } ); } From 306309fd32474c6e69d0baaad03dd3687a565e2b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 2 Aug 2024 16:04:16 +0300 Subject: [PATCH 05/84] update yaml schema compiler to process custom granularities --- .../src/compiler/YamlCompiler.ts | 22 ++++++++++++------- .../src/compiler/utils.ts | 9 +++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts index cd87a39eb4547..2cbf2d049e31b 100644 --- a/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts +++ b/packages/cubejs-schema-compiler/src/compiler/YamlCompiler.ts @@ -294,19 +294,25 @@ export class YamlCompiler { return {}; } - const remapped = yamlArray.map(({ name, indexes, ...rest }) => { + const remapped = yamlArray.map(({ name, indexes, granularities, ...rest }) => { + if (!name) { + errorsReport.error(`name isn't defined for ${memberType}: ${JSON.stringify(rest)}`); + return {}; + } + + const res = { [name]: {} }; if (memberType === 'preAggregation' && indexes) { indexes = this.yamlArrayToObj(indexes || [], `${memberType}.index`, errorsReport); + res[name] = { indexes, ...res[name] }; } - if (!name) { - errorsReport.error(`name isn't defined for ${memberType}: ${JSON.stringify(rest)}`); - return {}; - } else if (indexes) { - return { [name]: { indexes, ...rest } }; - } else { - return { [name]: rest }; + if (memberType === 'dimension' && granularities) { + granularities = this.yamlArrayToObj(granularities || [], `${memberType}.granularity`, errorsReport); + res[name] = { granularities, ...res[name] }; } + + res[name] = { ...res[name], ...rest }; + return res; }); return remapped.reduce((a, b) => ({ ...a, ...b }), {}); diff --git a/packages/cubejs-schema-compiler/src/compiler/utils.ts b/packages/cubejs-schema-compiler/src/compiler/utils.ts index d1b39007d3563..64380629e7883 100644 --- a/packages/cubejs-schema-compiler/src/compiler/utils.ts +++ b/packages/cubejs-schema-compiler/src/compiler/utils.ts @@ -1,5 +1,12 @@ import { camelize } from 'inflection'; +// It's a map where key - is a level and value - is a map of properties on this level to ignore camelization +const IGNORE_CAMELIZE = { + 1: { + granularities: true, + } +}; + function camelizeObjectPart(obj: unknown, camelizeKeys: boolean, level = 0): unknown { if (!obj) { return obj; @@ -12,7 +19,7 @@ function camelizeObjectPart(obj: unknown, camelizeKeys: boolean, level = 0): unk } else if (typeof obj === 'object') { for (const key of Object.keys(obj)) { if (!(level === 1 && key === 'meta')) { - obj[key] = camelizeObjectPart(obj[key], true, level + 1); + obj[key] = camelizeObjectPart(obj[key], !IGNORE_CAMELIZE[level]?.[key], level + 1); } if (camelizeKeys) { From 719391132c1b32146c32478a6978b3d101aa0dd8 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 5 Aug 2024 18:08:55 +0300 Subject: [PATCH 06/84] =?UTF-8?q?Add=20custom=20granularities=20support=20?= =?UTF-8?q?to=C2=A0time=20dimension=20and=C2=A0base=20query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/adapter/BaseQuery.js | 20 +++++++++-- .../src/adapter/BaseTimeDimension.ts | 35 +++++++++++++++---- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index b799fd722f0cc..963786d5a143f 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -2116,6 +2116,10 @@ export class BaseQuery { return this.evaluateSymbolSql(dimension.path()[0], dimension.path()[1], dimension.dimensionDefinition()); } + dimensionGranularitySql(dimension) { + return this.evaluateSymbolSql(dimension.path()[0], dimension.path()[1], dimension.dimensionDefinition(), null, ['granularities', dimension.granularity]); + } + segmentSql(segment) { return this.evaluateSymbolSql(segment.path()[0], segment.path()[1], segment.segmentDefinition()); } @@ -2172,12 +2176,12 @@ export class BaseQuery { return this.evaluateSymbolContext || {}; } - evaluateSymbolSql(cubeName, name, symbol, memberExpressionType) { + evaluateSymbolSql(cubeName, name, symbol, memberExpressionType, childPropPathArray) { const isMemberExpr = !!memberExpressionType; if (!memberExpressionType) { this.pushMemberNameForCollectionIfNecessary(cubeName, name); } - const memberPathArray = [cubeName, name]; + const memberPathArray = [cubeName, name].concat(childPropPathArray ?? []); const memberPath = this.cubeEvaluator.pathFromArray(memberPathArray); let type = memberExpressionType; if (!type) { @@ -2283,7 +2287,17 @@ export class BaseQuery { this.autoPrefixAndEvaluateSql(cubeName, symbol.longitude.sql, isMemberExpr) ]); } else { - let res = this.autoPrefixAndEvaluateSql(cubeName, symbol.sql, isMemberExpr); + let res; + + // TODO Temporarily placed it here, but maybe this should be moved level up and processed in + // more generalized way to support nested properties not only on dimension level + if (childPropPathArray && childPropPathArray.length > 0) { + const childProperty = this.cubeEvaluator.byPath('dimensions', memberPathArray); + res = this.autoPrefixAndEvaluateSql(cubeName, childProperty.sql, isMemberExpr); + } else { + res = this.autoPrefixAndEvaluateSql(cubeName, symbol.sql, isMemberExpr); + } + if (symbol.shiftInterval) { res = `(${this.addTimestampInterval(res, symbol.shiftInterval)})`; } diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index d6b10df27ed2e..ebd045f8a7986 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -13,7 +13,9 @@ export class BaseTimeDimension extends BaseFilter { public readonly isPredefined: boolean; - public readonly baseGranularity: string; + public readonly baseGranularity: string | undefined; + + public readonly granularitySql: Function | undefined; public readonly boundaryDateRange: any; @@ -31,9 +33,14 @@ export class BaseTimeDimension extends BaseFilter { this.dateRange = timeDimension.dateRange; this.granularity = timeDimension.granularity; this.isPredefined = isPredefinedGranularity(this.granularity); - this.baseGranularity = this.query.cubeEvaluator - .byPath('dimensions', timeDimension.dimension) - .granularities?.[this.granularity]?.baseGranularity; + if (!this.isPredefined) { + const customGranularity = this.query.cubeEvaluator + .byPath('dimensions', timeDimension.dimension) + .granularities?.[this.granularity]; + + this.baseGranularity = customGranularity?.baseGranularity; + this.granularitySql = customGranularity?.sql; + } this.boundaryDateRange = timeDimension.boundaryDateRange; this.shiftInterval = timeDimension.shiftInterval; } @@ -95,12 +102,22 @@ export class BaseTimeDimension extends BaseFilter { if (context.rollupGranularity === this.granularity) { return super.dimensionSql(); } - return this.query.timeGroupedColumn(granularity, this.query.dimensionSql(this)); + if (this.isPredefined || !this.granularity) { + return this.query.timeGroupedColumn(granularity, this.query.dimensionSql(this)); + } else { + return this.query.dimensionGranularitySql(this); + } } + if (context.ungrouped) { return this.convertedToTz(); } - return this.query.timeGroupedColumn(granularity, this.convertedToTz()); + + if (this.isPredefined) { + return this.query.timeGroupedColumn(granularity, this.convertedToTz()); + } else { + return this.granularityConvertedToTz(); + } } public dimensionDefinition(): DimensionDefinition | SegmentDefinition { @@ -115,7 +132,11 @@ export class BaseTimeDimension extends BaseFilter { } public convertedToTz() { - return this.query.convertTz(this.query.dimensionSql(this)); + return this.query.convertTz(`${this.query.dimensionSql(this)}`); + } + + public granularityConvertedToTz() { + return this.query.convertTz(`(${this.query.dimensionGranularitySql(this)})`); } public filterToWhere() { From 868c0ee17b2b576f15ca32dbb92559cf51c629b5 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 Aug 2024 15:06:36 +0300 Subject: [PATCH 07/84] feat(schema-compiler): update cube's schema to allow granularities for time dimensions (simple syntax) --- .../src/compiler/CubeValidator.ts | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts index 8573387eeeb98..e1922bac583a1 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts @@ -42,11 +42,14 @@ function formatStatePath(state: Joi.State): string { return ''; } +const BaseGranularity = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'); +const GranularityStep = Joi.string().pattern(/^(\d+\s+)?(second|minute|hour|day|week|month|year)s?(\s\d+\s+(second|minute|hour|day|week|month|year)s?){0,7}$/, 'granularity step'); + const regexTimeInterval = Joi.string().custom((value, helper) => { - if (value.match(/^(-?\d+) (minute|hour|day|week|month|quarter|year)$/)) { + if (value.match(/^(-?\d+) (minute|hour|day|week|month|quarter|year)s?$/)) { return value; } else { - return helper.message({ custom: `(${formatStatePath(helper.state)} = ${value}) does not match regexp: /^(-?\\d+) (minute|hour|day|week|month|quarter|year)$/` }); + return helper.message({ custom: `(${formatStatePath(helper.state)} = ${value}) does not match regexp: /^(-?\\d+) (minute|hour|day|week|month|quarter|year)s?$/` }); } }); @@ -105,10 +108,19 @@ const BaseDimensionWithoutSubQuery = { granularities: Joi.when('type', { is: 'time', then: Joi.object().pattern(identifierRegex, - Joi.object().keys({ - sql: Joi.func().required(), - baseGranularity: Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year').optional(), - })).optional(), + Joi.alternatives([ + Joi.object().keys({ + sql: Joi.func().required(), + baseGranularity: BaseGranularity.optional(), + }), + Joi.object().keys({ + leading: GranularityStep, + trailing: GranularityStep, + bin: GranularityStep.required(), + baseGranularity: BaseGranularity.optional(), + }) + .oxor('leading', 'trailing') + ])).optional(), otherwise: Joi.forbidden() }) }; From db95c27eafe4a64b2ffe093c743e03ad30b2278b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 15 Aug 2024 19:56:38 +0300 Subject: [PATCH 08/84] =?UTF-8?q?Add=20custom=20granularities=20simple=20s?= =?UTF-8?q?yntax=20support=20to=C2=A0the=20time=20dimension=20and=C2=A0bas?= =?UTF-8?q?e=20query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/adapter/BaseMeasure.ts | 2 +- .../src/adapter/BaseQuery.js | 41 ++++++++- .../src/adapter/BaseTimeDimension.ts | 84 ++++++++++++++++++- .../src/adapter/PostgresQuery.ts | 8 ++ 4 files changed, 131 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts b/packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts index 9a050a3c9c6ab..9638a5c0e28f5 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts @@ -176,7 +176,7 @@ export class BaseMeasure { return this.query.minGranularity(granularityA, granularityB); } - public granularityFromInterval(interval) { + public granularityFromInterval(interval: string) { if (!interval) { return undefined; } diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 963786d5a143f..add339a761151 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -16,7 +16,6 @@ import { FROM_PARTITION_RANGE, inDbTimeZone, MAX_SOURCE_ROW_LIMIT, QueryAlias, g import { buildSqlAndParams as nativeBuildSqlAndParams, } from '@cubejs-backend/native'; -import { eventNames } from 'process'; import { UserError } from '../compiler/UserError'; import { BaseMeasure } from './BaseMeasure'; import { BaseDimension } from './BaseDimension'; @@ -716,22 +715,54 @@ export class BaseQuery { ); } + /** + * @param {string} date + * @param {string} interval + * @returns {string} + */ subtractInterval(date, interval) { return `${date} - interval '${interval}'`; } + /** + * @param {string} date + * @param {string} interval + * @returns {string} + */ addInterval(date, interval) { return `${date} + interval '${interval}'`; } + /** + * @param {string} timestamp + * @param {string} interval + * @returns {string} + */ addTimestampInterval(timestamp, interval) { return this.addInterval(timestamp, interval); } + /** + * @param {string} timestamp + * @param {string} interval + * @returns {string} + */ subtractTimestampInterval(timestamp, interval) { return this.subtractInterval(timestamp, interval); } + /** + * @param {string} stride (a value expression of type interval) + * @param {string} source (a value expression of type timestamp/date) + * @param {string} origin (a value expression of type timestamp/date) + * @returns {string} + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + dateBin(stride, source, origin) { + throw new Error('Not implemented'); + // Different syntax possible in different DBs + } + cumulativeMeasures() { return this.measures.filter(m => m.isCumulative()); } @@ -2870,6 +2901,14 @@ export class BaseQuery { return 'NOW()'; } + /** + * @returns {string} + */ + startOfTheYearTimestampSql() { + // different DBs have different way of getting this + throw new Error('Not implemented'); + } + unixTimestampSql() { return `EXTRACT(EPOCH FROM ${this.nowTimestampSql()})`; } diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index ebd045f8a7986..9d2cf6607562a 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -15,6 +15,12 @@ export class BaseTimeDimension extends BaseFilter { public readonly baseGranularity: string | undefined; + public readonly leadingWindowPart: string | undefined; + + public readonly trailingWindowPart: string | undefined; + + public readonly binStride: { num: number, type: string } | undefined; + public readonly granularitySql: Function | undefined; public readonly boundaryDateRange: any; @@ -40,6 +46,18 @@ export class BaseTimeDimension extends BaseFilter { this.baseGranularity = customGranularity?.baseGranularity; this.granularitySql = customGranularity?.sql; + this.leadingWindowPart = customGranularity?.leading; + this.trailingWindowPart = customGranularity?.trailing; + if (customGranularity?.bin) { + // XXX: Potentially to use BaseQuery#parseInterval? + // But it's protected and expects duration to be specified, so no "bin: year" will be possible + const v = customGranularity.bin.trim().split(' '); + if (v.length === 1) { + this.binStride = { num: 1, type: v[0] }; + } else { + this.binStride = { num: parseInt(v[0], 10), type: v[1] }; + } + } } this.boundaryDateRange = timeDimension.boundaryDateRange; this.shiftInterval = timeDimension.shiftInterval; @@ -105,7 +123,7 @@ export class BaseTimeDimension extends BaseFilter { if (this.isPredefined || !this.granularity) { return this.query.timeGroupedColumn(granularity, this.query.dimensionSql(this)); } else { - return this.query.dimensionGranularitySql(this); + return this.dimensionGranularitySql(); } } @@ -136,7 +154,69 @@ export class BaseTimeDimension extends BaseFilter { } public granularityConvertedToTz() { - return this.query.convertTz(`(${this.query.dimensionGranularitySql(this)})`); + return this.query.convertTz(`(${this.dimensionGranularitySql()})`); + } + + public dimensionGranularitySql() { + if (this.granularitySql) { // Need to evaluate symbol's SQL + return this.query.dimensionGranularitySql(this); + } + + let dtDate = this.query.dimensionSql(this); + + // Need to construct SQL + if (this.binStride?.num === 1) { // range is aligned with natural calendar, so we can use DATE_TRUNC + if (this.leadingWindowPart) { + // Example: DATE_TRUNC('granularity', dimension - INTERVAL 'xxxx') + INTERVAL 'xxxx' + dtDate = this.query.subtractInterval(dtDate, this.leadingWindowPart); + dtDate = this.query.timeGroupedColumn(this.granularityFromInterval(this.binStride.type), dtDate); + dtDate = this.query.addInterval(dtDate, this.leadingWindowPart); + + return dtDate; + } else if (this.trailingWindowPart) { + // Example: DATE_TRUNC('granularity', dimension + INTERVAL 'xxxx') - INTERVAL 'xxxx' + dtDate = this.query.addInterval(dtDate, this.trailingWindowPart); + dtDate = this.query.timeGroupedColumn(this.granularityFromInterval(this.binStride.type), dtDate); + dtDate = this.query.subtractInterval(dtDate, this.trailingWindowPart); + + return dtDate; + } + + // No window offsets + return this.query.timeGroupedColumn(this.granularityFromInterval(this.binStride.type), dtDate); + } + + // need to use DATE_BIN + let origin = this.query.startOfTheYearTimestampSql(); + if (this.leadingWindowPart) { + origin = this.query.addInterval(origin, this.leadingWindowPart); + } else if (this.trailingWindowPart) { + origin = this.query.subtractInterval(origin, this.trailingWindowPart); + } + + return this.query.dateBin(`${this.binStride?.num} ${this.binStride?.type}`, dtDate, origin); + } + + public granularityFromInterval(interval: string) { + if (!interval) { + return undefined; + } + if (interval.match(/day/)) { + return 'day'; + } else if (interval.match(/month/)) { + return 'month'; + } else if (interval.match(/year/)) { + return 'year'; + } else if (interval.match(/week/)) { + return 'week'; + } else if (interval.match(/hour/)) { + return 'hour'; + } else if (interval.match(/minute/)) { + return 'minute'; + } else if (interval.match(/second/)) { + return 'second'; + } + return undefined; } public filterToWhere() { diff --git a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts index 98997a6b53634..8491ec2216e1c 100644 --- a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts @@ -31,6 +31,14 @@ export class PostgresQuery extends BaseQuery { return `date_trunc('${GRANULARITY_TO_INTERVAL[granularity]}', ${dimension})`; } + public dateBin(stride: string, source: string, origin: string): string { + return `date_bin('${stride}', ${source}, ${origin})`; + } + + public startOfTheYearTimestampSql() { + return 'date_trunc(\'year\', CURRENT_TIMESTAMP)'; + } + public hllInit(sql) { return `hll_add_agg(hll_hash_any(${sql}))`; } From 5e41356c28a990041b46f5da90af36f013f5de5e Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 Aug 2024 15:15:28 +0300 Subject: [PATCH 09/84] =?UTF-8?q?Rename=20baseGranularity=20=E2=86=92=20ro?= =?UTF-8?q?llupGranularity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/adapter/BaseTimeDimension.ts | 8 ++++---- .../cubejs-schema-compiler/src/compiler/CubeValidator.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index 9d2cf6607562a..88e46060fac34 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -13,7 +13,7 @@ export class BaseTimeDimension extends BaseFilter { public readonly isPredefined: boolean; - public readonly baseGranularity: string | undefined; + public readonly baseRollupGranularity: string | undefined; public readonly leadingWindowPart: string | undefined; @@ -44,7 +44,7 @@ export class BaseTimeDimension extends BaseFilter { .byPath('dimensions', timeDimension.dimension) .granularities?.[this.granularity]; - this.baseGranularity = customGranularity?.baseGranularity; + this.baseRollupGranularity = customGranularity?.baseGranularity; this.granularitySql = customGranularity?.sql; this.leadingWindowPart = customGranularity?.leading; this.trailingWindowPart = customGranularity?.trailing; @@ -324,8 +324,8 @@ export class BaseTimeDimension extends BaseFilter { return this.query.minGranularity(this.granularity, this.dateRangeGranularity()); } - if (this.baseGranularity) { - return this.query.minGranularity(this.baseGranularity, this.dateRangeGranularity()); + if (this.baseRollupGranularity) { + return this.query.minGranularity(this.baseRollupGranularity, this.dateRangeGranularity()); } // Trying to get granularity from the date range if it was provided diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts index e1922bac583a1..b5d0448afc8e0 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts @@ -42,7 +42,7 @@ function formatStatePath(state: Joi.State): string { return ''; } -const BaseGranularity = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'); +const RollupGranularity = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'); const GranularityStep = Joi.string().pattern(/^(\d+\s+)?(second|minute|hour|day|week|month|year)s?(\s\d+\s+(second|minute|hour|day|week|month|year)s?){0,7}$/, 'granularity step'); const regexTimeInterval = Joi.string().custom((value, helper) => { @@ -111,13 +111,13 @@ const BaseDimensionWithoutSubQuery = { Joi.alternatives([ Joi.object().keys({ sql: Joi.func().required(), - baseGranularity: BaseGranularity.optional(), + rollupGranularity: RollupGranularity.optional(), }), Joi.object().keys({ leading: GranularityStep, trailing: GranularityStep, bin: GranularityStep.required(), - baseGranularity: BaseGranularity.optional(), + rollupGranularity: RollupGranularity.optional(), }) .oxor('leading', 'trailing') ])).optional(), From 57db063969477d51ab965cae8f216f40590bd75d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 Aug 2024 16:22:10 +0300 Subject: [PATCH 10/84] fix schema validation --- .../src/compiler/CubeValidator.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts index b5d0448afc8e0..f13018cb1effe 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts @@ -42,9 +42,6 @@ function formatStatePath(state: Joi.State): string { return ''; } -const RollupGranularity = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'); -const GranularityStep = Joi.string().pattern(/^(\d+\s+)?(second|minute|hour|day|week|month|year)s?(\s\d+\s+(second|minute|hour|day|week|month|year)s?){0,7}$/, 'granularity step'); - const regexTimeInterval = Joi.string().custom((value, helper) => { if (value.match(/^(-?\d+) (minute|hour|day|week|month|quarter|year)s?$/)) { return value; @@ -85,6 +82,11 @@ const everyCronTimeZone = Joi.string().custom((value, helper) => { } }); +const RollupGranularity = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'); +const GranularityInterval = Joi.string().pattern(/^\d+\s+(second|minute|hour|day|week|month|year)s?(\s\d+\s+(second|minute|hour|day|week|month|year)s?){0,7}$/, 'granularity interval'); +// Do not allow negative intervals for granularities, while offsets could be negative +const GranularityOffset = Joi.string().pattern(/^-?(\d+\s+)(second|minute|hour|day|week|month|year)s?(\s-?\d+\s+(second|minute|hour|day|week|month|year)s?){0,7}$/, 'granularity offset'); + const BaseDimensionWithoutSubQuery = { aliases: Joi.array().items(Joi.string()), type: Joi.any().valid('string', 'number', 'boolean', 'time', 'geo').required(), @@ -111,15 +113,13 @@ const BaseDimensionWithoutSubQuery = { Joi.alternatives([ Joi.object().keys({ sql: Joi.func().required(), - rollupGranularity: RollupGranularity.optional(), + rollupGranularity: RollupGranularity.required(), + interval: GranularityInterval.required(), }), Joi.object().keys({ - leading: GranularityStep, - trailing: GranularityStep, - bin: GranularityStep.required(), - rollupGranularity: RollupGranularity.optional(), + offset: GranularityOffset.optional(), + interval: GranularityInterval.required(), }) - .oxor('leading', 'trailing') ])).optional(), otherwise: Joi.forbidden() }) From acbf61ada4448746a0d221482be6957e691e7139 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 16 Aug 2024 16:22:48 +0300 Subject: [PATCH 11/84] =?UTF-8?q?fix=20custom=20granularity=20processing?= =?UTF-8?q?=20in=C2=A0BaseTimeDimension?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/adapter/BaseQuery.js | 5 ++ .../src/adapter/BaseTimeDimension.ts | 63 +++++++------------ 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index add339a761151..cd353f479c998 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -2758,6 +2758,11 @@ export class BaseQuery { throw new Error('Not implemented'); } + /** + * @param {string} granularity + * @param {string} dimension + * @return {string} + */ // eslint-disable-next-line @typescript-eslint/no-unused-vars timeGroupedColumn(granularity, dimension) { throw new Error('Not implemented'); diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index 88e46060fac34..787f011c4117a 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -15,11 +15,9 @@ export class BaseTimeDimension extends BaseFilter { public readonly baseRollupGranularity: string | undefined; - public readonly leadingWindowPart: string | undefined; + public readonly granularityInterval: string[]; - public readonly trailingWindowPart: string | undefined; - - public readonly binStride: { num: number, type: string } | undefined; + public readonly granularityOffset: string | undefined; public readonly granularitySql: Function | undefined; @@ -39,25 +37,16 @@ export class BaseTimeDimension extends BaseFilter { this.dateRange = timeDimension.dateRange; this.granularity = timeDimension.granularity; this.isPredefined = isPredefinedGranularity(this.granularity); + this.granularityInterval = []; if (!this.isPredefined) { const customGranularity = this.query.cubeEvaluator .byPath('dimensions', timeDimension.dimension) .granularities?.[this.granularity]; - this.baseRollupGranularity = customGranularity?.baseGranularity; + this.baseRollupGranularity = customGranularity?.rollupGranularity; this.granularitySql = customGranularity?.sql; - this.leadingWindowPart = customGranularity?.leading; - this.trailingWindowPart = customGranularity?.trailing; - if (customGranularity?.bin) { - // XXX: Potentially to use BaseQuery#parseInterval? - // But it's protected and expects duration to be specified, so no "bin: year" will be possible - const v = customGranularity.bin.trim().split(' '); - if (v.length === 1) { - this.binStride = { num: 1, type: v[0] }; - } else { - this.binStride = { num: parseInt(v[0], 10), type: v[1] }; - } - } + this.granularityInterval = customGranularity?.interval.split(' '); + this.granularityOffset = customGranularity?.offset; } this.boundaryDateRange = timeDimension.boundaryDateRange; this.shiftInterval = timeDimension.shiftInterval; @@ -165,42 +154,37 @@ export class BaseTimeDimension extends BaseFilter { let dtDate = this.query.dimensionSql(this); // Need to construct SQL - if (this.binStride?.num === 1) { // range is aligned with natural calendar, so we can use DATE_TRUNC - if (this.leadingWindowPart) { + // range is aligned with natural calendar, so we can use DATE_TRUNC + if (this.isGranularityNaturalAligned()) { + if (this.granularityOffset) { // Example: DATE_TRUNC('granularity', dimension - INTERVAL 'xxxx') + INTERVAL 'xxxx' - dtDate = this.query.subtractInterval(dtDate, this.leadingWindowPart); - dtDate = this.query.timeGroupedColumn(this.granularityFromInterval(this.binStride.type), dtDate); - dtDate = this.query.addInterval(dtDate, this.leadingWindowPart); - - return dtDate; - } else if (this.trailingWindowPart) { - // Example: DATE_TRUNC('granularity', dimension + INTERVAL 'xxxx') - INTERVAL 'xxxx' - dtDate = this.query.addInterval(dtDate, this.trailingWindowPart); - dtDate = this.query.timeGroupedColumn(this.granularityFromInterval(this.binStride.type), dtDate); - dtDate = this.query.subtractInterval(dtDate, this.trailingWindowPart); + dtDate = this.query.subtractInterval(dtDate, this.granularityOffset); + dtDate = this.query.timeGroupedColumn(this.granularityFromInterval(this.granularityInterval[1]), dtDate); + dtDate = this.query.addInterval(dtDate, this.granularityOffset); return dtDate; } // No window offsets - return this.query.timeGroupedColumn(this.granularityFromInterval(this.binStride.type), dtDate); + return this.query.timeGroupedColumn(this.granularityFromInterval(this.granularityInterval[1]), dtDate); } // need to use DATE_BIN let origin = this.query.startOfTheYearTimestampSql(); - if (this.leadingWindowPart) { - origin = this.query.addInterval(origin, this.leadingWindowPart); - } else if (this.trailingWindowPart) { - origin = this.query.subtractInterval(origin, this.trailingWindowPart); + if (this.granularityOffset) { + origin = this.query.addInterval(origin, this.granularityOffset); } - return this.query.dateBin(`${this.binStride?.num} ${this.binStride?.type}`, dtDate, origin); + return this.query.dateBin(`${this.granularityInterval.join(' ')}`, dtDate, origin); + } + + private isGranularityNaturalAligned(): boolean { + if (!this.granularityInterval) { return false; } + + return !(this.granularityInterval.length !== 2 || this.granularityInterval[0] !== '1'); } public granularityFromInterval(interval: string) { - if (!interval) { - return undefined; - } if (interval.match(/day/)) { return 'day'; } else if (interval.match(/month/)) { @@ -213,10 +197,9 @@ export class BaseTimeDimension extends BaseFilter { return 'hour'; } else if (interval.match(/minute/)) { return 'minute'; - } else if (interval.match(/second/)) { + } else /* if (interval.match(/second/)) */ { return 'second'; } - return undefined; } public filterToWhere() { From 22295cb1e29ebe639044a0387ceb2cc8305a75fa Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 19 Aug 2024 20:13:56 +0300 Subject: [PATCH 12/84] =?UTF-8?q?Moved=20custom=20granularity=20processing?= =?UTF-8?q?=20from=20time=20dimension=20to=C2=A0base=20query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/adapter/BaseQuery.js | 85 ++++++++++---- .../src/adapter/BaseTimeDimension.ts | 109 +++++------------- .../src/adapter/PostgresQuery.ts | 29 ++++- 3 files changed, 118 insertions(+), 105 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index cd353f479c998..2d91f721e60ff 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -751,18 +751,6 @@ export class BaseQuery { return this.subtractInterval(timestamp, interval); } - /** - * @param {string} stride (a value expression of type interval) - * @param {string} source (a value expression of type timestamp/date) - * @param {string} origin (a value expression of type timestamp/date) - * @returns {string} - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - dateBin(stride, source, origin) { - throw new Error('Not implemented'); - // Different syntax possible in different DBs - } - cumulativeMeasures() { return this.measures.filter(m => m.isCumulative()); } @@ -2768,6 +2756,71 @@ export class BaseQuery { throw new Error('Not implemented'); } + /** + * @param {string} dimension + * @param {string} interval + * @param {string | undefined} offset + * @return {string} + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + dimensionTimeGroupedColumn(dimension, interval, offset) { + let dtDate; + + // Interval is aligned with natural calendar, so we can use DATE_TRUNC + if (this.isGranularityNaturalAligned(interval)) { + if (offset) { + // Example: DATE_TRUNC(interval, dimension - INTERVAL 'offset') + INTERVAL 'offset' + dtDate = this.subtractInterval(dimension, offset); + dtDate = this.timeGroupedColumn(this.granularityFromInterval(interval), dtDate); + dtDate = this.addInterval(dtDate, offset); + + return dtDate; + } + + return this.timeGroupedColumn(this.granularityFromInterval(interval), dimension); + } + + throw new Error('Complex time grouped queries with arbitrary interval is not implemented'); + } + + /** + * @protected + * @param {string} interval + * @returns {boolean} + */ + isGranularityNaturalAligned(interval) { + const intParsed = interval.split(' '); + + return !(intParsed.length !== 2 || intParsed[0] !== '1'); + } + + /** + * Returns the smallest granularity for the provided interval string + * Interval may be presented as `1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds + * It is important to bubble up from the smallest, as this is used e.g. for minimum rollup granularity + * @param {string} interval + * @returns {string} + */ + granularityFromInterval(interval) { + if (interval.match(/second/)) { + return 'second'; + } else if (interval.match(/minute/)) { + return 'minute'; + } else if (interval.match(/hour/)) { + return 'hour'; + } else if (interval.match(/day/)) { + return 'day'; + } else if (interval.match(/week/)) { + return 'week'; + } else if (interval.match(/month/)) { + return 'month'; + } else if (interval.match(/quarter/)) { + return 'quarter'; + } else /* if (interval.match(/year/)) */ { + return 'year'; + } + } + /** * Evaluate alias for specific cube's property. * @param {string} name Property name. @@ -2906,14 +2959,6 @@ export class BaseQuery { return 'NOW()'; } - /** - * @returns {string} - */ - startOfTheYearTimestampSql() { - // different DBs have different way of getting this - throw new Error('Not implemented'); - } - unixTimestampSql() { return `EXTRACT(EPOCH FROM ${this.nowTimestampSql()})`; } diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index 787f011c4117a..d4c0568398b44 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -15,7 +15,7 @@ export class BaseTimeDimension extends BaseFilter { public readonly baseRollupGranularity: string | undefined; - public readonly granularityInterval: string[]; + public readonly granularityInterval: string; public readonly granularityOffset: string | undefined; @@ -37,16 +37,19 @@ export class BaseTimeDimension extends BaseFilter { this.dateRange = timeDimension.dateRange; this.granularity = timeDimension.granularity; this.isPredefined = isPredefinedGranularity(this.granularity); - this.granularityInterval = []; - if (!this.isPredefined) { + if (this.granularity && !this.isPredefined) { const customGranularity = this.query.cubeEvaluator .byPath('dimensions', timeDimension.dimension) .granularities?.[this.granularity]; this.baseRollupGranularity = customGranularity?.rollupGranularity; this.granularitySql = customGranularity?.sql; - this.granularityInterval = customGranularity?.interval.split(' '); + this.granularityInterval = customGranularity?.interval; this.granularityOffset = customGranularity?.offset; + } else if (this.granularity) { + this.granularityInterval = `1 ${this.granularity}`; + } else { + this.granularityInterval = '1 hour'; } this.boundaryDateRange = timeDimension.boundaryDateRange; this.shiftInterval = timeDimension.shiftInterval; @@ -101,6 +104,7 @@ export class BaseTimeDimension extends BaseFilter { const context = this.query.safeEvaluateSymbolContext(); const granularity = context.granularityOverride || this.granularity; const path = granularity ? `${this.expressionPath()}.${granularity}` : this.expressionPath(); + const granularityInterval = isPredefinedGranularity(granularity) ? `1 ${granularity}` : this.granularityInterval; if ((context.renderedReference || {})[path]) { return context.renderedReference[path]; } @@ -109,22 +113,15 @@ export class BaseTimeDimension extends BaseFilter { if (context.rollupGranularity === this.granularity) { return super.dimensionSql(); } - if (this.isPredefined || !this.granularity) { - return this.query.timeGroupedColumn(granularity, this.query.dimensionSql(this)); - } else { - return this.dimensionGranularitySql(); - } + + return this.query.dimensionTimeGroupedColumn(this.query.dimensionSql(this), granularityInterval, this.granularityOffset); } if (context.ungrouped) { return this.convertedToTz(); } - if (this.isPredefined) { - return this.query.timeGroupedColumn(granularity, this.convertedToTz()); - } else { - return this.granularityConvertedToTz(); - } + return this.query.dimensionTimeGroupedColumn(this.convertedToTz(), granularityInterval, this.granularityOffset); } public dimensionDefinition(): DimensionDefinition | SegmentDefinition { @@ -139,67 +136,7 @@ export class BaseTimeDimension extends BaseFilter { } public convertedToTz() { - return this.query.convertTz(`${this.query.dimensionSql(this)}`); - } - - public granularityConvertedToTz() { - return this.query.convertTz(`(${this.dimensionGranularitySql()})`); - } - - public dimensionGranularitySql() { - if (this.granularitySql) { // Need to evaluate symbol's SQL - return this.query.dimensionGranularitySql(this); - } - - let dtDate = this.query.dimensionSql(this); - - // Need to construct SQL - // range is aligned with natural calendar, so we can use DATE_TRUNC - if (this.isGranularityNaturalAligned()) { - if (this.granularityOffset) { - // Example: DATE_TRUNC('granularity', dimension - INTERVAL 'xxxx') + INTERVAL 'xxxx' - dtDate = this.query.subtractInterval(dtDate, this.granularityOffset); - dtDate = this.query.timeGroupedColumn(this.granularityFromInterval(this.granularityInterval[1]), dtDate); - dtDate = this.query.addInterval(dtDate, this.granularityOffset); - - return dtDate; - } - - // No window offsets - return this.query.timeGroupedColumn(this.granularityFromInterval(this.granularityInterval[1]), dtDate); - } - - // need to use DATE_BIN - let origin = this.query.startOfTheYearTimestampSql(); - if (this.granularityOffset) { - origin = this.query.addInterval(origin, this.granularityOffset); - } - - return this.query.dateBin(`${this.granularityInterval.join(' ')}`, dtDate, origin); - } - - private isGranularityNaturalAligned(): boolean { - if (!this.granularityInterval) { return false; } - - return !(this.granularityInterval.length !== 2 || this.granularityInterval[0] !== '1'); - } - - public granularityFromInterval(interval: string) { - if (interval.match(/day/)) { - return 'day'; - } else if (interval.match(/month/)) { - return 'month'; - } else if (interval.match(/year/)) { - return 'year'; - } else if (interval.match(/week/)) { - return 'week'; - } else if (interval.match(/hour/)) { - return 'hour'; - } else if (interval.match(/minute/)) { - return 'minute'; - } else /* if (interval.match(/second/)) */ { - return 'second'; - } + return this.query.convertTz(this.query.dimensionSql(this)); } public filterToWhere() { @@ -303,15 +240,25 @@ export class BaseTimeDimension extends BaseFilter { this.query.cacheValue( ['rollupGranularity', this.granularity].concat(this.dateRange), () => { - if (this.isPredefined) { + if (!this.granularity) { + return this.dateRangeGranularity(); + } else if (this.isPredefined) { return this.query.minGranularity(this.granularity, this.dateRangeGranularity()); + } else if (this.granularityInterval && !this.granularityOffset) { + return this.query.minGranularity( + this.query.granularityFromInterval(this.granularityInterval), + this.dateRangeGranularity() + ); + } else if (this.granularityInterval && this.granularityOffset) { // There is offset too + return this.query.minGranularity( + this.query.minGranularity( + this.query.granularityFromInterval(this.granularityInterval), + this.query.granularityFromInterval(this.granularityOffset) + ), + this.dateRangeGranularity() + ); } - if (this.baseRollupGranularity) { - return this.query.minGranularity(this.baseRollupGranularity, this.dateRangeGranularity()); - } - - // Trying to get granularity from the date range if it was provided return this.dateRangeGranularity(); } ); diff --git a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts index 8491ec2216e1c..0261772703bdf 100644 --- a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts @@ -23,16 +23,37 @@ export class PostgresQuery extends BaseQuery { return new PostgresParamAllocator(expressionParams); } - public convertTz(field) { + public convertTz(field: string): string { return `(${field}::timestamptz AT TIME ZONE '${this.timezone}')`; } - public timeGroupedColumn(granularity, dimension) { + public timeGroupedColumn(granularity: string, dimension: string): string { return `date_trunc('${GRANULARITY_TO_INTERVAL[granularity]}', ${dimension})`; } - public dateBin(stride: string, source: string, origin: string): string { - return `date_bin('${stride}', ${source}, ${origin})`; + public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { + if (this.isGranularityNaturalAligned(interval)) { + return super.dimensionTimeGroupedColumn(dimension, interval, offset); + } + + // Formula: + // SELECT ((DATE_TRUNC('year', dimension) + offset?) + + // FLOOR( + // EXTRACT(EPOCH FROM (dimension - (DATE_TRUNC('year', dimension) + offset?))) / + // EXTRACT(EPOCH FROM interval) + // ) * interval) + // + // Should also work for AWS RedShift + + let dtDate = this.timeGroupedColumn('year', dimension); + if (offset) { + dtDate = this.addInterval(dtDate, offset); + } + + return `${dtDate} + FLOOR( + EXTRACT(EPOCH FROM (${dimension} - (${dtDate}))) / + EXTRACT(EPOCH FROM INTERVAL '${interval}') + ) * INTERVAL '${interval}'`; } public startOfTheYearTimestampSql() { From 68b2cf9e99441045f5886fc5f132c3561697e37f Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 20 Aug 2024 18:35:23 +0300 Subject: [PATCH 13/84] Revert: deep evaluateSymbolSql processing --- .../src/adapter/BaseQuery.js | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 2d91f721e60ff..4842fe9c3845f 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -2135,10 +2135,6 @@ export class BaseQuery { return this.evaluateSymbolSql(dimension.path()[0], dimension.path()[1], dimension.dimensionDefinition()); } - dimensionGranularitySql(dimension) { - return this.evaluateSymbolSql(dimension.path()[0], dimension.path()[1], dimension.dimensionDefinition(), null, ['granularities', dimension.granularity]); - } - segmentSql(segment) { return this.evaluateSymbolSql(segment.path()[0], segment.path()[1], segment.segmentDefinition()); } @@ -2195,12 +2191,12 @@ export class BaseQuery { return this.evaluateSymbolContext || {}; } - evaluateSymbolSql(cubeName, name, symbol, memberExpressionType, childPropPathArray) { + evaluateSymbolSql(cubeName, name, symbol, memberExpressionType) { const isMemberExpr = !!memberExpressionType; if (!memberExpressionType) { this.pushMemberNameForCollectionIfNecessary(cubeName, name); } - const memberPathArray = [cubeName, name].concat(childPropPathArray ?? []); + const memberPathArray = [cubeName, name]; const memberPath = this.cubeEvaluator.pathFromArray(memberPathArray); let type = memberExpressionType; if (!type) { @@ -2306,17 +2302,7 @@ export class BaseQuery { this.autoPrefixAndEvaluateSql(cubeName, symbol.longitude.sql, isMemberExpr) ]); } else { - let res; - - // TODO Temporarily placed it here, but maybe this should be moved level up and processed in - // more generalized way to support nested properties not only on dimension level - if (childPropPathArray && childPropPathArray.length > 0) { - const childProperty = this.cubeEvaluator.byPath('dimensions', memberPathArray); - res = this.autoPrefixAndEvaluateSql(cubeName, childProperty.sql, isMemberExpr); - } else { - res = this.autoPrefixAndEvaluateSql(cubeName, symbol.sql, isMemberExpr); - } - + let res = this.autoPrefixAndEvaluateSql(cubeName, symbol.sql, isMemberExpr); if (symbol.shiftInterval) { res = `(${this.addTimestampInterval(res, symbol.shiftInterval)})`; } From 747a04f00dfef2ef31110d66dcff2fbf2202f46b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 20 Aug 2024 21:54:34 +0300 Subject: [PATCH 14/84] Implement dimensionTimeGroupedColumn for Snowflake --- .../src/adapter/SnowflakeQuery.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts b/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts index 7d35585774b54..dd0b938d59807 100644 --- a/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts @@ -42,6 +42,55 @@ export class SnowflakeQuery extends BaseQuery { return `date_trunc('${GRANULARITY_TO_INTERVAL[granularity]}', ${dimension})`; } + public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { + if (offset) { + offset = this.formatInterval(offset); + } + + if (this.isGranularityNaturalAligned(interval)) { + return super.dimensionTimeGroupedColumn(dimension, interval, offset); + } + + // Formula: + // SELECT DATEADD(second, + // FLOOR( + // DATEDIFF(seconds, DATE_TRUNC('year', dimension) + offset?, dimension) / + // DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)) + // ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)), + // DATE_TRUNC('year', dimension) + offset?) + // + // The formula operates with seconds so it won't produce dates aligned with offset date parts, like: + // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add + // exact number of seconds in the "6 months 3 days" without aligning with natural calendar. + + let dtDate = this.timeGroupedColumn('year', dimension); + if (offset) { + dtDate = this.addInterval(dtDate, offset); + } + + interval = this.formatInterval(interval); + + return `DATEADD(second, + FLOOR( + DATEDIFF(seconds, ${dtDate}, CURRENT_TIMESTAMP) / + DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${interval}')) + ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${interval}')), + ${dtDate})`; + } + + /** + * The input interval in format "2 years 3 months 4 weeks 5 days...." + * will be converted to Snowflake dialect "2 years, 3 months, 4 weeks, 5 days...." + */ + private formatInterval(interval: string): string { + return interval.split(' ').map((word, index, arr) => { + if (index % 2 !== 0 && index < arr.length - 1) { + return `${word},`; + } + return word; + }).join(' '); + } + public timeStampCast(value) { return `${value}::timestamp_tz`; } From ad78b11e505309b44fdad7f834312d24c36a1971 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 21 Aug 2024 00:38:03 +0300 Subject: [PATCH 15/84] =?UTF-8?q?Implement=20dimensionTimeGroupedColumn=20?= =?UTF-8?q?for=C2=A0MySQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/adapter/MysqlQuery.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts b/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts index 0d44fa34d828f..f270ca0f0a1eb 100644 --- a/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts @@ -60,6 +60,38 @@ export class MysqlQuery extends BaseQuery { return `CAST(${GRANULARITY_TO_INTERVAL[granularity](dimension)} AS DATETIME)`; } + public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { + if (this.isGranularityNaturalAligned(interval)) { + return super.dimensionTimeGroupedColumn(dimension, interval, offset); + } + + // Formula: + // SELECT TIMESTAMPADD( + // SECOND, + // FLOOR(TIMESTAMPDIFF(SECOND, DATE_ADD(DATE_FORMAT(dimension, '%Y-01-01 00:00:00'), INTERVAL offset), + // dimension) / + // TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL interval)) + // * TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL interval), + // DATE_ADD(DATE_FORMAT(dimension, '%Y-01-01 00:00:00'), INTERVAL offset) + // ) + // + // The formula operates with seconds so it won't produce dates aligned with offset date parts, like: + // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add + // exact number of seconds in the "6 months 3 days" without aligning with natural calendar. + + let dtDate = this.timeGroupedColumn('year', dimension); + if (offset) { + dtDate = this.addInterval(dtDate, offset); + } + + return `TIMESTAMPADD( + SECOND, + FLOOR(TIMESTAMPDIFF(SECOND, ${dtDate}, ${dimension}) / + TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${interval})) + * TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${interval}), + ${dtDate})`; + } + public escapeColumnName(name) { return `\`${name}\``; } From 5146ce4f512747d6c5f56ac8fa8b681a67d8f091 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 22 Aug 2024 14:48:10 +0300 Subject: [PATCH 16/84] Add custom granularity time intervals generation (used in rollups) --- packages/cubejs-backend-shared/src/time.ts | 104 +++++++++++++++++- .../src/adapter/BaseTimeDimension.ts | 20 +++- 2 files changed, 118 insertions(+), 6 deletions(-) diff --git a/packages/cubejs-backend-shared/src/time.ts b/packages/cubejs-backend-shared/src/time.ts index 628e12316c71f..7f1132bc3551f 100644 --- a/packages/cubejs-backend-shared/src/time.ts +++ b/packages/cubejs-backend-shared/src/time.ts @@ -6,6 +6,11 @@ const Moment = require('moment-timezone'); const moment = extendMoment(Moment); type QueryDateRange = [string, string]; +type SqlInterval = string; +type TimeSeriesOptions = { + timestampPrecision: number +}; +type ParsedInterval = Partial>; export const TIME_SERIES: Record QueryDateRange[]> = { day: (range: DateRange, digits) => Array.from(range.snapTo('day').by('day')) @@ -26,8 +31,103 @@ export const TIME_SERIES: Record [d.format(`YYYY-MM-DDT00:00:00.${'0'.repeat(digits)}`), d.endOf('quarter').format(`YYYY-MM-DDT23:59:59.${'9'.repeat(digits)}`)]), }; -type TimeSeriesOptions = { - timestampPrecision: number +/** + * Parse PostgreSQL-like interval string into object + * E.g. '2 years 15 months 100 weeks 99 hours 15 seconds' + * Negative units are also supported + * E.g. '-2 months 5 days -10 hours' + */ +function parseSqlInterval(intervalStr: SqlInterval): ParsedInterval { + const regex = /(-?\d+)\s+(year|quarter|month|week|day|hour|minute|second)s?/g; + const interval: ParsedInterval = {}; + + for (const match of intervalStr.matchAll(regex)) { + const value = parseInt(match[1], 10); + const unit = match[2] as unitOfTime.DurationConstructor; + interval[unit] = value; + } + + return interval; +} + +function addInterval(date: moment.Moment, interval: ParsedInterval): moment.Moment { + const res = date.clone(); + + Object.entries(interval).forEach(([key, value]) => { + res.add(value, key as unitOfTime.DurationConstructor); + }); + + return res; +} + +function subtractInterval(date: moment.Moment, interval: ParsedInterval): moment.Moment { + const res = date.clone(); + + Object.entries(interval).forEach(([key, value]) => { + res.subtract(value, key as unitOfTime.DurationConstructor); + }); + + return res; +} + +/** + * Returns the closest date prior to date parameter aligned with the offset and interval + * If no offset provided, the beginning of the year will be taken as pivot point + */ +function alignToOffset(date: moment.Moment, interval: ParsedInterval, offset?: ParsedInterval): moment.Moment { + let alignedDate = date.clone(); + let intervalOp; + + const startOfYear = moment().year(date.year()).startOf('year'); + let offsetDate = offset ? addInterval(startOfYear, offset) : startOfYear; + + if (date.isBefore(offsetDate)) { + intervalOp = offsetDate.isBefore(startOfYear) ? addInterval : subtractInterval; + + while (date.isBefore(offsetDate)) { + offsetDate = intervalOp(offsetDate, interval); + } + alignedDate = offsetDate; + } else if (offsetDate.isBefore(startOfYear)) { + intervalOp = offsetDate.isBefore(startOfYear) ? addInterval : subtractInterval; + + while (date.isAfter(offsetDate)) { + alignedDate = offsetDate.clone(); + offsetDate = intervalOp(offsetDate, interval); + } + } else { + intervalOp = offsetDate.isBefore(startOfYear) ? subtractInterval : addInterval; + + while (date.isAfter(offsetDate)) { + alignedDate = offsetDate.clone(); + offsetDate = intervalOp(offsetDate, interval); + } + } + + return alignedDate; +} + +export const timeSeriesFromCustomInterval = (intervalStr: string, [startStr, endStr]: QueryDateRange, offsetStr?: string, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] => { + const intervalParsed = parseSqlInterval(intervalStr); + const offsetParsed = offsetStr ? parseSqlInterval(offsetStr) : undefined; + const start = moment(startStr); + const end = moment(endStr); + let alignedStart = alignToOffset(start, intervalParsed, offsetParsed); + + const dates: QueryDateRange[] = []; + + while (alignedStart.isBefore(end)) { + const s = alignedStart.clone(); + alignedStart = addInterval(alignedStart, intervalParsed); + dates.push([ + s.format(`YYYY-MM-DDTHH:mm:ss.${'0'.repeat(options.timestampPrecision)}`), + alignedStart.clone() + .subtract(1, 'second') + .format(`YYYY-MM-DDTHH:mm:ss.${'9'.repeat(options.timestampPrecision)}`) + ]); + } + + return dates; }; export const timeSeries = (granularity: string, dateRange: QueryDateRange, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] => { diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index d4c0568398b44..d9c74864d09ca 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -1,5 +1,13 @@ import moment from 'moment-timezone'; -import { timeSeries, isPredefinedGranularity, FROM_PARTITION_RANGE, TO_PARTITION_RANGE, BUILD_RANGE_START_LOCAL, BUILD_RANGE_END_LOCAL } from '@cubejs-backend/shared'; +import { + timeSeries, + isPredefinedGranularity, + timeSeriesFromCustomInterval, + FROM_PARTITION_RANGE, + TO_PARTITION_RANGE, + BUILD_RANGE_START_LOCAL, + BUILD_RANGE_END_LOCAL +} from '@cubejs-backend/shared'; import { BaseFilter } from './BaseFilter'; import { UserError } from '../compiler/UserError'; @@ -278,9 +286,13 @@ export class BaseTimeDimension extends BaseFilter { ]; } - return timeSeries(this.granularity, [this.dateFromFormatted(), this.dateToFormatted()], { - timestampPrecision: this.query.timestampPrecision(), - }); + if (this.isPredefined) { + return timeSeries(this.granularity, [this.dateFromFormatted(), this.dateToFormatted()], { + timestampPrecision: this.query.timestampPrecision(), + }); + } + + return timeSeriesFromCustomInterval(this.granularityInterval, [this.dateFromFormatted(), this.dateToFormatted()], this.granularityOffset, { timestampPrecision: this.query.timestampPrecision() }); } public wildcardRange() { From 26bb1ad4860eb01dd282031615dbacf390c8c37d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 22 Aug 2024 16:37:50 +0300 Subject: [PATCH 17/84] Rewrite parseSqlInterval from regex to split --- packages/cubejs-backend-shared/src/time.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/cubejs-backend-shared/src/time.ts b/packages/cubejs-backend-shared/src/time.ts index 7f1132bc3551f..d3be5382c4357 100644 --- a/packages/cubejs-backend-shared/src/time.ts +++ b/packages/cubejs-backend-shared/src/time.ts @@ -38,13 +38,16 @@ export const TIME_SERIES: Record 'day') + const singularUnit = (unit.endsWith('s') ? unit.slice(0, -1) : unit) as unitOfTime.DurationConstructor; + interval[singularUnit] = value; } return interval; From 340ee72a01ea2dc6ab21f04bcfec707119567b87 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 22 Aug 2024 16:40:13 +0300 Subject: [PATCH 18/84] Add tests for timeSeriesFromCustomInterval --- .../cubejs-backend-shared/test/time.test.ts | 117 +++++++++++++++++- 1 file changed, 113 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-backend-shared/test/time.test.ts b/packages/cubejs-backend-shared/test/time.test.ts index 8ef25535b0a6f..a8ea61afdecae 100644 --- a/packages/cubejs-backend-shared/test/time.test.ts +++ b/packages/cubejs-backend-shared/test/time.test.ts @@ -1,4 +1,4 @@ -import { inDbTimeZone, timeSeries } from '../src'; +import { inDbTimeZone, timeSeries, isPredefinedGranularity, timeSeriesFromCustomInterval } from '../src'; describe('time', () => { it('time series - day', () => { @@ -29,13 +29,122 @@ describe('time', () => { ]); }); + it('time series - custom: interval - 1 year, no offset', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2023-12-31'])).toEqual([ + ['2021-01-01T00:00:00.000', '2021-12-31T23:59:59.999'], + ['2022-01-01T00:00:00.000', '2022-12-31T23:59:59.999'], + ['2023-01-01T00:00:00.000', '2023-12-31T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 1 year, offset - 2 month', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2022-12-31'], '2 month')).toEqual([ + ['2020-03-01T00:00:00.000', '2021-02-28T23:59:59.999'], + ['2021-03-01T00:00:00.000', '2022-02-28T23:59:59.999'], + ['2022-03-01T00:00:00.000', '2023-02-28T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 1 year, offset - 2 month 14 days', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2022-12-31'], '2 month 14 days')).toEqual([ + ['2020-03-15T00:00:00.000', '2021-03-14T23:59:59.999'], + ['2021-03-15T00:00:00.000', '2022-03-14T23:59:59.999'], + ['2022-03-15T00:00:00.000', '2023-03-14T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 2 months, no offset', () => { + expect(timeSeriesFromCustomInterval('2 months', ['2021-01-01', '2021-12-31'])).toEqual([ + ['2021-01-01T00:00:00.000', '2021-02-28T23:59:59.999'], + ['2021-03-01T00:00:00.000', '2021-04-30T23:59:59.999'], + ['2021-05-01T00:00:00.000', '2021-06-30T23:59:59.999'], + ['2021-07-01T00:00:00.000', '2021-08-31T23:59:59.999'], + ['2021-09-01T00:00:00.000', '2021-10-31T23:59:59.999'], + ['2021-11-01T00:00:00.000', '2021-12-31T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 2 months, offset - 2 month 14 days', () => { + expect(timeSeriesFromCustomInterval('2 months', ['2021-01-01', '2021-12-31'], '2 month 14 days')).toEqual([ + ['2020-11-15T00:00:00.000', '2021-01-14T23:59:59.999'], + ['2021-01-15T00:00:00.000', '2021-03-14T23:59:59.999'], + ['2021-03-15T00:00:00.000', '2021-05-14T23:59:59.999'], + ['2021-05-15T00:00:00.000', '2021-07-14T23:59:59.999'], + ['2021-07-15T00:00:00.000', '2021-09-14T23:59:59.999'], + ['2021-09-15T00:00:00.000', '2021-11-14T23:59:59.999'], + ['2021-11-15T00:00:00.000', '2022-01-14T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 1 months 2 weeks 3 days, offset - 3 weeks 4 days', () => { + expect(timeSeriesFromCustomInterval('1 months 2 weeks 3 days', ['2021-01-01', '2021-12-31'], '3 weeks 4 days')).toEqual([ + ['2020-12-09T00:00:00.000', '2021-01-25T23:59:59.999'], + ['2021-01-26T00:00:00.000', '2021-03-14T23:59:59.999'], + ['2021-03-15T00:00:00.000', '2021-05-01T23:59:59.999'], + ['2021-05-02T00:00:00.000', '2021-06-18T23:59:59.999'], + ['2021-06-19T00:00:00.000', '2021-08-04T23:59:59.999'], + ['2021-08-05T00:00:00.000', '2021-09-21T23:59:59.999'], + ['2021-09-22T00:00:00.000', '2021-11-07T23:59:59.999'], + ['2021-11-08T00:00:00.000', '2021-12-24T23:59:59.999'], + ['2021-12-25T00:00:00.000', '2022-02-10T23:59:59.999'], + ]); + }); + + it('time series - custom: interval - 3 weeks, negative offset - -3 weeks 4 days', () => { + expect(timeSeriesFromCustomInterval('3 weeks', ['2021-01-01', '2021-03-01'], '-3 weeks 4 days')).toEqual([ + ['2020-12-15T00:00:00.000', '2021-01-04T23:59:59.999'], + ['2021-01-05T00:00:00.000', '2021-01-25T23:59:59.999'], + ['2021-01-26T00:00:00.000', '2021-02-15T23:59:59.999'], + ['2021-02-16T00:00:00.000', '2021-03-08T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 3 weeks, big offset - 9 months', () => { + expect(timeSeriesFromCustomInterval('3 weeks', ['2021-01-01', '2021-03-01'], '9 months')).toEqual([ + ['2021-01-01T00:00:00.000', '2021-01-21T23:59:59.999'], + ['2021-01-22T00:00:00.000', '2021-02-11T23:59:59.999'], + ['2021-02-12T00:00:00.000', '2021-03-04T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 3 weeks, big offset - 10 months', () => { + expect(timeSeriesFromCustomInterval('3 weeks', ['2021-01-01', '2021-03-01'], '10 months')).toEqual([ + ['2020-12-21T00:00:00.000', '2021-01-10T23:59:59.999'], + ['2021-01-11T00:00:00.000', '2021-01-31T23:59:59.999'], + ['2021-02-01T00:00:00.000', '2021-02-21T23:59:59.999'], + ['2021-02-22T00:00:00.000', '2021-03-14T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds, no offset', () => { + expect(timeSeriesFromCustomInterval('2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', ['2021-01-01', '2021-12-31'])).toEqual([ + ['2021-01-01T00:00:00.000', '2021-03-26T05:06:06.999'], + ['2021-03-26T05:06:07.000', '2021-06-20T10:12:13.999'], + ['2021-06-20T10:12:14.000', '2021-09-14T15:18:20.999'], + ['2021-09-14T15:18:21.000', '2021-12-09T20:24:27.999'], + ['2021-12-09T20:24:28.000', '2022-03-07T01:30:34.999'] + ]); + }); + + it('time series - custom: interval - 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds, offset - 2 months 3 weeks 4 days 5 hours 6 minutes 8 seconds', () => { + expect(timeSeriesFromCustomInterval('2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', ['2021-01-01', '2021-12-31'], '2 months 3 weeks 4 days 5 hours 6 minutes 8 seconds')).toEqual([ + ['2020-10-06T18:53:54.000', '2021-01-01T00:00:00.999'], + ['2021-01-01T00:00:01.000', '2021-03-26T05:06:07.999'], + ['2021-03-26T05:06:08.000', '2021-06-20T10:12:14.999'], + ['2021-06-20T10:12:15.000', '2021-09-14T15:18:21.999'], + ['2021-09-14T15:18:22.000', '2021-12-09T20:24:28.999'], + ['2021-12-09T20:24:29.000', '2022-03-07T01:30:35.999'] + ]); + }); + it('inDbTimeZone', () => { expect(inDbTimeZone('UTC', 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', '2020-01-01T00:00:00.000000')).toEqual( '2020-01-01T00:00:00.000000Z' ); + }); - expect(inDbTimeZone('UTC', 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', '2020-01-31T23:59:59.999999')).toEqual( - '2020-01-31T23:59:59.999999Z' - ); + it('isPredefinedGranularity', () => { + expect(isPredefinedGranularity('day')).toBeTruthy(); + expect(isPredefinedGranularity('fiscal_year_by_1st_feb')).toBeFalsy(); }); }); From f0bc42e98d871607ce02e9c0dfa2428ae3d5ca19 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 22 Aug 2024 17:16:46 +0300 Subject: [PATCH 19/84] Add tests for yaml schema compiler --- .../test/unit/yaml-schema.test.ts | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts b/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts index 710f8cdf9531b..baf681d336f42 100644 --- a/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts @@ -128,7 +128,7 @@ describe('Yaml Schema Testing', () => { } }); - it('unammed measure', async () => { + it('unnamed measure', async () => { const { compiler } = prepareYamlCompiler( `cubes: - name: Users @@ -222,4 +222,93 @@ describe('Yaml Schema Testing', () => { expect(segments.length).toBeGreaterThan(0); expect(segments.find((segment) => segment.name === 'CubeA.sfUsers').description).toBe('SF users segment from createCubeSchema'); }); + + describe('Custom dimension granularities: ', () => { + it('no granularity name', async () => { + const { compiler } = prepareYamlCompiler( + ` + cubes: + - name: Orders + sql: "select * from tbl" + dimensions: + - name: created_at + sql: created_at + type: time + granularities: + - interval: 6 months + - name: status + sql: status + type: string + measures: + - name: count + type: count + ` + ); + + try { + await compiler.compile(); + throw new Error('compile must return an error'); + } catch (e: any) { + expect(e.message).toContain('name isn\'t defined for dimension.granularity'); + } + }); + + it('incorrect granularity name', async () => { + const { compiler } = prepareYamlCompiler( + ` + cubes: + - name: Orders + sql: "select * from tbl" + dimensions: + - name: created_at + sql: created_at + type: time + granularities: + - name: 6_months + - name: status + sql: status + type: string + measures: + - name: count + type: count + ` + ); + + try { + await compiler.compile(); + throw new Error('compile must return an error'); + } catch (e: any) { + expect(e.message).toContain('(dimensions.created_at.granularities.6_months = [object Object]) is not allowed'); + } + }); + + it('granularities as object ', async () => { + const { compiler } = prepareYamlCompiler( + ` + cubes: + - name: Orders + sql: "select * from tbl" + dimensions: + - name: created_at + sql: created_at + type: time + granularities: + name: half_year + - name: status + sql: status + type: string + measures: + - name: count + type: count + ` + ); + + try { + await compiler.compile(); + throw new Error('compile must return an error'); + } catch (e: any) { + expect(e.message).toContain('dimension.granularitys must be defined as array'); + } + }); + }); }); From 06eea5ef0fea0ed353e20fad6090d35532ec307f Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 22 Aug 2024 18:13:49 +0300 Subject: [PATCH 20/84] Add tests for cube validator --- .../test/unit/cube-validator.test.ts | 273 ++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts b/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts index 8ea645aa4f4a8..bf9e1683e6ecb 100644 --- a/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts @@ -544,4 +544,277 @@ describe('Cube Validation', () => { expect(validationResult.error).toBeFalsy(); }); + + describe('Custom dimension granularities: ', () => { + it('no granularity interval', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = { + name: 'Orders', + fileName: 'fileName', + sql: () => 'select * from tbl', + public: true, + dimensions: { + createdAt: { + public: true, + sql: () => 'created_at', + type: 'time', + granularities: { + half_year: {} + } + }, + status: { + type: 'string', + sql: () => 'status', + } + }, + measures: { + count: { + sql: () => 'count', + type: 'count' + } + } + }; + + const validationResult = cubeValidator.validate(cube, { + error: (message, e) => { + console.log(message); + expect(message).toContain('(dimensions.createdAt.granularities.half_year.interval) is required'); + } + } as any); + + expect(validationResult.error).toBeTruthy(); + }); + + it('granularity with interval', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = { + name: 'Orders', + fileName: 'fileName', + sql: () => 'select * from tbl', + public: true, + dimensions: { + createdAt: { + public: true, + sql: () => 'created_at', + type: 'time', + granularities: { + half_year: { + interval: '6 months' + } + } + }, + status: { + type: 'string', + sql: () => 'status', + } + }, + measures: { + count: { + sql: () => 'count', + type: 'count' + } + } + }; + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + }); + + it('granularity with interval+offset', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = { + name: 'Orders', + fileName: 'fileName', + sql: () => 'select * from tbl', + public: true, + dimensions: { + createdAt: { + public: true, + sql: () => 'created_at', + type: 'time', + granularities: { + half_year: { + interval: '6 months', + offset: '2 months 3 weeks 4 days', + } + } + }, + status: { + type: 'string', + sql: () => 'status', + } + }, + measures: { + count: { + sql: () => 'count', + type: 'count' + } + } + }; + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + }); + + it('granularity with sql, no interval', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = { + name: 'Orders', + fileName: 'fileName', + sql: () => 'select * from tbl', + public: true, + dimensions: { + createdAt: { + public: true, + sql: () => 'created_at', + type: 'time', + granularities: { + half_year: { + sql: () => 'some-sql', + } + } + }, + status: { + type: 'string', + sql: () => 'status', + } + }, + measures: { + count: { + sql: () => 'count', + type: 'count' + } + } + }; + + const validationResult = cubeValidator.validate(cube, { + error: (message, e) => { + console.log(message); + expect(message).toContain('(dimensions.createdAt.granularities.half_year.interval) is required'); + } + } as any); + + expect(validationResult.error).toBeTruthy(); + }); + + it('granularity with sql, interval, no rollup_granularity', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = { + name: 'Orders', + fileName: 'fileName', + sql: () => 'select * from tbl', + public: true, + dimensions: { + createdAt: { + public: true, + sql: () => 'created_at', + type: 'time', + granularities: { + half_year: { + sql: () => 'some-sql', + interval: '6 months', + } + } + }, + status: { + type: 'string', + sql: () => 'status', + } + }, + measures: { + count: { + sql: () => 'count', + type: 'count' + } + } + }; + + const validationResult = cubeValidator.validate(cube, { + error: (message, e) => { + console.log(message); + expect(message).toContain('(dimensions.createdAt.granularities.half_year.rollupGranularity) is required'); + } + } as any); + + expect(validationResult.error).toBeTruthy(); + }); + + it('granularity with sql, interval, rollupGranularity', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = { + name: 'Orders', + fileName: 'fileName', + sql: () => 'select * from tbl', + public: true, + dimensions: { + createdAt: { + public: true, + sql: () => 'created_at', + type: 'time', + granularities: { + half_year: { + sql: () => 'some-sql', + interval: '6 months', + rollupGranularity: 'month', + } + } + }, + status: { + type: 'string', + sql: () => 'status', + } + }, + measures: { + count: { + sql: () => 'count', + type: 'count' + } + } + }; + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + }); + + it('2 granularities: simple + sql', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = { + name: 'Orders', + fileName: 'fileName', + sql: () => 'select * from tbl', + public: true, + dimensions: { + createdAt: { + public: true, + sql: () => 'created_at', + type: 'time', + granularities: { + half_year: { + sql: () => 'some-sql', + interval: '6 months', + rollupGranularity: 'month', + }, + half_year_by_1st_april: { + interval: '6 months', + offset: '3 months' + } + } + }, + status: { + type: 'string', + sql: () => 'status', + } + }, + measures: { + count: { + sql: () => 'count', + type: 'count' + } + } + }; + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + }); + }); }); From 0e97d89c53167c7a3bfa6caf9713892b4c285bcc Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 22 Aug 2024 21:02:26 +0300 Subject: [PATCH 21/84] Fix overTimeSeriesQuery with custom granularities --- packages/cubejs-schema-compiler/src/adapter/BaseQuery.js | 2 +- .../src/adapter/BaseTimeDimension.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 4842fe9c3845f..aa678ce27988f 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -1365,7 +1365,7 @@ export class BaseQuery { () => baseQueryFn(cumulativeMeasures, filters), cumulativeMeasure.shouldUngroupForCumulative(), !cumulativeMeasure.shouldUngroupForCumulative() && this.minGranularity( - cumulativeMeasure.windowGranularity(), this.timeDimensions.find(d => d.granularity).granularity + cumulativeMeasure.windowGranularity(), this.timeDimensions.find(d => d.granularity).resolvedGranularity() ) || undefined ); const baseQueryAlias = this.cubeAlias('base'); diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index d9c74864d09ca..90effa9082fdb 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -295,6 +295,14 @@ export class BaseTimeDimension extends BaseFilter { return timeSeriesFromCustomInterval(this.granularityInterval, [this.dateFromFormatted(), this.dateToFormatted()], this.granularityOffset, { timestampPrecision: this.query.timestampPrecision() }); } + public resolvedGranularity() { + if (this.isPredefined) { + return this.granularity; + } + + return this.query.granularityFromInterval(this.granularityInterval); + } + public wildcardRange() { return [FROM_PARTITION_RANGE, TO_PARTITION_RANGE]; } From d0217699fee17d03447daf262f5d06b80cfbd189 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 22 Aug 2024 21:04:49 +0300 Subject: [PATCH 22/84] Add tests for sql query generation for queries with custom granularities --- .../test/unit/base-query.test.ts | 227 +++++++++++++++++- .../cubejs-schema-compiler/test/unit/utils.ts | 51 ++++ 2 files changed, 277 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts index 8e74bb852cda3..d9e5e1d519f01 100644 --- a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts @@ -1,7 +1,13 @@ import moment from 'moment-timezone'; import { BaseQuery, PostgresQuery, MssqlQuery, UserError } from '../../src'; import { prepareCompiler, prepareYamlCompiler } from './PrepareCompiler'; -import { createCubeSchema, createCubeSchemaYaml, createJoinedCubesSchema, createSchemaYaml } from './utils'; +import { + createCubeSchema, + createCubeSchemaWithCustomGranularities, + createCubeSchemaYaml, + createJoinedCubesSchema, + createSchemaYaml +} from './utils'; import { BigqueryQuery } from '../../src/adapter/BigqueryQuery'; describe('SQL Generation', () => { @@ -48,6 +54,225 @@ describe('SQL Generation', () => { }); }); + describe('Custom granularities', () => { + const compilers = /** @type Compilers */ prepareCompiler( + createCubeSchemaWithCustomGranularities('orders') + ); + + const queries = [ + { + measures: [ + 'orders.count' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [], + filters: [], + timezone: 'Europe/Kyiv' + }, + { + measures: [ + 'orders.count' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [], + filters: [], + timezone: 'Europe/Kyiv' + }, + { + measures: [ + 'orders.rollingCountByUnbounded' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [ + 'orders.status' + ], + filters: [], + timezone: 'Europe/Kyiv' + }, + { + measures: [ + 'orders.rollingCountByUnbounded' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [ + 'orders.status' + ], + filters: [], + timezone: 'Europe/Kyiv' + }, + { + measures: [ + 'orders.rollingCountByTrailing2Day' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [ + 'orders.status' + ], + filters: [], + timezone: 'Europe/Kyiv' + }, + { + measures: [ + 'orders.rollingCountByTrailing2Day' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [ + 'orders.status' + ], + filters: [], + timezone: 'Europe/Kyiv' + }, + { + measures: [ + 'orders.rollingCountByLeading2Day' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [ + 'orders.status' + ], + filters: [], + timezone: 'Europe/Kyiv' + }, + { + measures: [ + 'orders.rollingCountByLeading2Day' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [ + 'orders.status' + ], + filters: [], + timezone: 'Europe/Kyiv' + } + ]; + + it('Test time series with different granularities', async () => { + await compilers.compiler.compile(); + + const query = new BaseQuery(compilers, queries[0]); + + { + const timeDimension = query.newTimeDimension({ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2021-01-01', '2021-12-31'] + }); + expect(timeDimension.timeSeries()).toEqual([ + ['2021-01-01T00:00:00.000', '2021-06-30T23:59:59.999'], + ['2021-07-01T00:00:00.000', '2021-12-31T23:59:59.999'] + ]); + } + + { + const timeDimension = query.newTimeDimension({ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2021-01-01', '2021-12-31'] + }); + expect(timeDimension.timeSeries()).toEqual([ + ['2020-10-01T00:00:00.000', '2021-03-31T23:59:59.999'], + ['2021-04-01T00:00:00.000', '2021-09-30T23:59:59.999'], + ['2021-10-01T00:00:00.000', '2022-03-31T23:59:59.999'] + ]); + } + }); + + describe('via PostgresQuery', () => { + beforeAll(async () => { + await compilers.compiler.compile(); + }); + + queries.forEach(q => { + it(`measure "${q.measures[0]}" + granularity "${q.timeDimensions[0].granularity}"`, () => { + const query = new PostgresQuery(compilers, q); + const queryAndParams = query.buildSqlAndParams(); + const queryString = queryAndParams[0]; + console.log('Generated query: ', queryString); + + if (q.measures[0].includes('count')) { + expect(queryString.includes('INTERVAL \'6 months\'')).toBeTruthy(); + } else if (q.measures[0].includes('rollingCountByTrailing2Day')) { + expect(queryString.includes('- interval \'2 day\'')).toBeTruthy(); + } else if (q.measures[0].includes('rollingCountByLeading2Day')) { + expect(queryString.includes('+ interval \'3 day\'')).toBeTruthy(); + } + }); + }); + }); + }); + describe('Common - JS', () => { const compilers = /** @type Compilers */ prepareCompiler( createCubeSchema({ diff --git a/packages/cubejs-schema-compiler/test/unit/utils.ts b/packages/cubejs-schema-compiler/test/unit/utils.ts index 3307a8e235601..6623fdcd305fd 100644 --- a/packages/cubejs-schema-compiler/test/unit/utils.ts +++ b/packages/cubejs-schema-compiler/test/unit/utils.ts @@ -76,6 +76,57 @@ export function createCubeSchema({ name, refreshKey = '', preAggregations = '', `; } +export function createCubeSchemaWithCustomGranularities(name: string): string { + return `cube('${name}', { + sql: 'select * from orders', + public: true, + dimensions: { + createdAt: { + public: true, + sql: 'created_at', + type: 'time', + granularities: { + half_year: { + interval: '6 months', + }, + half_year_by_1st_april: { + interval: '6 months', + offset: '3 months' + } + } + }, + status: { + type: 'string', + sql: 'status', + } + }, + measures: { + count: { + sql: 'count', + type: 'count' + }, + rollingCountByTrailing2Day: { + type: 'count', + rollingWindow: { + trailing: '2 day' + } + }, + rollingCountByLeading2Day: { + type: 'count', + rollingWindow: { + leading: '3 day' + } + }, + rollingCountByUnbounded: { + type: 'count', + rollingWindow: { + trailing: 'unbounded' + } + } + } + })`; +} + export type CreateSchemaOptions = { cubes?: unknown[], views?: unknown[] From 56a43d04d45289d838a64b0dd9ec8a7faafed42d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 22 Aug 2024 21:10:39 +0300 Subject: [PATCH 23/84] return back deleted test --- packages/cubejs-backend-shared/test/time.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/cubejs-backend-shared/test/time.test.ts b/packages/cubejs-backend-shared/test/time.test.ts index a8ea61afdecae..79fed5d155480 100644 --- a/packages/cubejs-backend-shared/test/time.test.ts +++ b/packages/cubejs-backend-shared/test/time.test.ts @@ -141,6 +141,10 @@ describe('time', () => { expect(inDbTimeZone('UTC', 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', '2020-01-01T00:00:00.000000')).toEqual( '2020-01-01T00:00:00.000000Z' ); + + expect(inDbTimeZone('UTC', 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', '2020-01-31T23:59:59.999999')).toEqual( + '2020-01-31T23:59:59.999999Z' + ); }); it('isPredefinedGranularity', () => { From ecfd06c496f77998c87a7da0ed3f7aed26ce1e87 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 23 Aug 2024 15:05:37 +0300 Subject: [PATCH 24/84] Remove comment, add types --- packages/cubejs-api-gateway/src/query.js | 3 +-- packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cubejs-api-gateway/src/query.js b/packages/cubejs-api-gateway/src/query.js index 7b14d6b6bad96..ada2647106a6b 100644 --- a/packages/cubejs-api-gateway/src/query.js +++ b/packages/cubejs-api-gateway/src/query.js @@ -103,8 +103,7 @@ const querySchema = Joi.object().keys({ filters: Joi.array().items(oneFilter, oneCondition), timeDimensions: Joi.array().items(Joi.object().keys({ dimension: id.required(), - // granularity: Joi.valid('quarter', 'day', 'month', 'year', 'week', 'hour', 'minute', 'second', null), - granularity: Joi.string(), // To support custom granularities + granularity: Joi.string(), // Custom granularities have arbitrary names dateRange: [ Joi.array().items(Joi.string()).min(1).max(2), Joi.string() diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts b/packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts index 9638a5c0e28f5..347bbf094232d 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts @@ -172,7 +172,7 @@ export class BaseMeasure { return undefined; } - public minGranularity(granularityA, granularityB) { + public minGranularity(granularityA: string | undefined, granularityB: string | undefined) { return this.query.minGranularity(granularityA, granularityB); } From 6689cd16708c0f0d3561f1b2d524883118e90b7e Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 23 Aug 2024 15:06:18 +0300 Subject: [PATCH 25/84] update cube validation scheme: remove sql, add origin --- .../src/compiler/CubeValidator.ts | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts index f13018cb1effe..aed264f19bb8d 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts @@ -82,7 +82,6 @@ const everyCronTimeZone = Joi.string().custom((value, helper) => { } }); -const RollupGranularity = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'); const GranularityInterval = Joi.string().pattern(/^\d+\s+(second|minute|hour|day|week|month|year)s?(\s\d+\s+(second|minute|hour|day|week|month|year)s?){0,7}$/, 'granularity interval'); // Do not allow negative intervals for granularities, while offsets could be negative const GranularityOffset = Joi.string().pattern(/^-?(\d+\s+)(second|minute|hour|day|week|month|year)s?(\s-?\d+\s+(second|minute|hour|day|week|month|year)s?){0,7}$/, 'granularity offset'); @@ -112,13 +111,50 @@ const BaseDimensionWithoutSubQuery = { then: Joi.object().pattern(identifierRegex, Joi.alternatives([ Joi.object().keys({ - sql: Joi.func().required(), - rollupGranularity: RollupGranularity.required(), interval: GranularityInterval.required(), + origin: Joi.string().required().custom((value, helpers) => { + const date = new Date(value); + + if (Number.isNaN(date.getTime())) { + return helpers.message({ custom: 'Origin should be valid date-only form: YYYY[-MM[-DD]] or date-time form: YYYY-MM-DD[T]HH:mm[:ss[.sss[Z]]]' }); + } + return value; + }), }), Joi.object().keys({ + interval: GranularityInterval.required().custom((value, helper) => { + const intParsed = value.split(' '); + const msg = { custom: 'Arbitrary intervals cannot be used without origin point specified' }; + + if (intParsed.length !== 2) { + return helper.message(msg); + } + + const v = parseInt(intParsed[0], 10); + const unit = intParsed[1]; + + const validIntervals = { + // Any number of years is valid + year: () => true, + // Only months divisible by a year with no remainder are valid + month: () => 12 % v === 0, + // Only quarters divisible by a year with no remainder are valid + quarter: () => 4 % v === 0, + // Only 1 day is valid + day: () => v === 1, + // Only hours divisible by a day with no remainder are valid + hour: () => 24 % v === 0, + // Only minutes divisible by an hour with no remainder are valid + minute: () => 60 % v === 0, + // Only seconds divisible by a minute with no remainder are valid + second: () => 60 % v === 0, + }; + + const isValid = Object.keys(validIntervals).some(key => unit.includes(key) && validIntervals[key]()); + + return isValid ? value : helper.message(msg); + }), offset: GranularityOffset.optional(), - interval: GranularityInterval.required(), }) ])).optional(), otherwise: Joi.forbidden() @@ -137,7 +173,6 @@ const FixedRollingWindow = { offset: Joi.any().valid('start', 'end') }; -// const GranularitySchema = Joi.string().valid('second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year').required(); const GranularitySchema = Joi.string().required(); // To support custom granularities const YearToDate = { From 0c05cdf6f1d5b7ee839325d5e76394987df29b1e Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 23 Aug 2024 15:06:31 +0300 Subject: [PATCH 26/84] add tests for cube validator --- .../test/unit/cube-validator.test.ts | 534 +++++++++++------- 1 file changed, 322 insertions(+), 212 deletions(-) diff --git a/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts b/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts index bf9e1683e6ecb..6d48068c6fae7 100644 --- a/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts @@ -546,37 +546,39 @@ describe('Cube Validation', () => { }); describe('Custom dimension granularities: ', () => { - it('no granularity interval', async () => { - const cubeValidator = new CubeValidator(new CubeSymbols()); - const cube = { - name: 'Orders', - fileName: 'fileName', - sql: () => 'select * from tbl', - public: true, - dimensions: { - createdAt: { - public: true, - sql: () => 'created_at', - type: 'time', - granularities: { - half_year: {} - } - }, - status: { - type: 'string', - sql: () => 'status', - } + const newCube = (granularities) => ({ + name: 'Orders', + fileName: 'fileName', + sql: () => 'select * from tbl', + public: true, + dimensions: { + createdAt: { + public: true, + sql: () => 'created_at', + type: 'time', + granularities }, - measures: { - count: { - sql: () => 'count', - type: 'count' - } + status: { + type: 'string', + sql: () => 'status', } - }; + }, + measures: { + count: { + sql: () => 'count', + type: 'count' + } + } + }); + + it('no granularity interval', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = newCube({ + half_year: {} + }); const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, e: any) => { console.log(message); expect(message).toContain('(dimensions.createdAt.granularities.half_year.interval) is required'); } @@ -585,236 +587,344 @@ describe('Cube Validation', () => { expect(validationResult.error).toBeTruthy(); }); - it('granularity with interval', async () => { + it('granularity with aligned interval', async () => { const cubeValidator = new CubeValidator(new CubeSymbols()); - const cube = { - name: 'Orders', - fileName: 'fileName', - sql: () => 'select * from tbl', - public: true, - dimensions: { - createdAt: { - public: true, - sql: () => 'created_at', - type: 'time', - granularities: { - half_year: { - interval: '6 months' - } - } - }, - status: { - type: 'string', - sql: () => 'status', + { + const cube = newCube({ + half_year: { + interval: '10 years' // useless, but still valid } - }, - measures: { - count: { - sql: () => 'count', - type: 'count' + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '6 months' } - } - }; + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '1 day' + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } - const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); - expect(validationResult.error).toBeFalsy(); + { + const cube = newCube({ + half_year: { + interval: '6 hours' + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '15 minutes' + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '30 seconds' + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } }); - it('granularity with interval+offset', async () => { + it('granularity with aligned interval + offset', async () => { const cubeValidator = new CubeValidator(new CubeSymbols()); - const cube = { - name: 'Orders', - fileName: 'fileName', - sql: () => 'select * from tbl', - public: true, - dimensions: { - createdAt: { - public: true, - sql: () => 'created_at', - type: 'time', - granularities: { - half_year: { - interval: '6 months', - offset: '2 months 3 weeks 4 days', - } - } - }, - status: { - type: 'string', - sql: () => 'status', + { + const cube = newCube({ + half_year: { + interval: '10 years', // useless, but still valid + offset: '2 months 3 weeks 4 days', } - }, - measures: { - count: { - sql: () => 'count', - type: 'count' + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '6 months', + offset: '4 weeks 5 days 6 hours', } - } - }; + }); - const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); - expect(validationResult.error).toBeFalsy(); + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '1 day', + offset: '5 days 6 hours 7 minutes', + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '6 hours', + offset: '5 days 6 hours 7 minutes 8 seconds', + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '15 minutes', + offset: '1 hours 7 minutes 8 seconds', + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '30 seconds', + offset: '8 seconds', + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } }); - it('granularity with sql, no interval', async () => { + it('granularity with unaligned interval', async () => { const cubeValidator = new CubeValidator(new CubeSymbols()); - const cube = { - name: 'Orders', - fileName: 'fileName', - sql: () => 'select * from tbl', - public: true, - dimensions: { - createdAt: { - public: true, - sql: () => 'created_at', - type: 'time', - granularities: { - half_year: { - sql: () => 'some-sql', - } - } - }, - status: { - type: 'string', - sql: () => 'status', + + { + const cube = newCube({ + half_year: { + interval: '5 months', } - }, - measures: { - count: { - sql: () => 'count', - type: 'count' + }); + + const validationResult = cubeValidator.validate(cube, { + error: (message: any, e: any) => { + console.log(message); + expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); + } + } as any); + + expect(validationResult.error).toBeTruthy(); + } + + // Offset doesn't matter in this case + { + const cube = newCube({ + half_year: { + interval: '15 days', + offset: '1 hours 7 minutes 8 seconds', } + }); + + const validationResult = cubeValidator.validate(cube, { + error: (message: any, e: any) => { + console.log(message); + expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); + } + } as any); + + expect(validationResult.error).toBeTruthy(); + } + }); + + it('granularity with invalid interval', async () => { + const cubeValidator = new CubeValidator(new CubeSymbols()); + const cube = newCube({ + half_year: { + interval: 'invalid', } - }; + }); const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, e: any) => { console.log(message); - expect(message).toContain('(dimensions.createdAt.granularities.half_year.interval) is required'); + expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); } } as any); expect(validationResult.error).toBeTruthy(); }); - it('granularity with sql, interval, no rollup_granularity', async () => { + it('granularity with origin + invalid interval', async () => { const cubeValidator = new CubeValidator(new CubeSymbols()); - const cube = { - name: 'Orders', - fileName: 'fileName', - sql: () => 'select * from tbl', - public: true, - dimensions: { - createdAt: { - public: true, - sql: () => 'created_at', - type: 'time', - granularities: { - half_year: { - sql: () => 'some-sql', - interval: '6 months', - } - } - }, - status: { - type: 'string', - sql: () => 'status', - } - }, - measures: { - count: { - sql: () => 'count', - type: 'count' - } + const cube = newCube({ + half_year: { + origin: '2024', + interval: 'invalid', } - }; + }); const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, e: any) => { console.log(message); - expect(message).toContain('(dimensions.createdAt.granularities.half_year.rollupGranularity) is required'); + expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); } } as any); expect(validationResult.error).toBeTruthy(); }); - it('granularity with sql, interval, rollupGranularity', async () => { + it('granularity with invalid origin + interval', async () => { const cubeValidator = new CubeValidator(new CubeSymbols()); - const cube = { - name: 'Orders', - fileName: 'fileName', - sql: () => 'select * from tbl', - public: true, - dimensions: { - createdAt: { - public: true, - sql: () => 'created_at', - type: 'time', - granularities: { - half_year: { - sql: () => 'some-sql', - interval: '6 months', - rollupGranularity: 'month', - } - } - }, - status: { - type: 'string', - sql: () => 'status', - } - }, - measures: { - count: { - sql: () => 'count', - type: 'count' - } + const cube = newCube({ + half_year: { + origin: 'invalid', + interval: '3 months', + } + }); + + const validationResult = cubeValidator.validate(cube, { + error: (message: any, e: any) => { + console.log(message); + expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); } - }; + } as any); - const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); - expect(validationResult.error).toBeFalsy(); + expect(validationResult.error).toBeTruthy(); }); - it('2 granularities: simple + sql', async () => { + it('granularity with origin + interval', async () => { const cubeValidator = new CubeValidator(new CubeSymbols()); - const cube = { - name: 'Orders', - fileName: 'fileName', - sql: () => 'select * from tbl', - public: true, - dimensions: { - createdAt: { - public: true, - sql: () => 'created_at', - type: 'time', - granularities: { - half_year: { - sql: () => 'some-sql', - interval: '6 months', - rollupGranularity: 'month', - }, - half_year_by_1st_april: { - interval: '6 months', - offset: '3 months' - } - } - }, - status: { - type: 'string', - sql: () => 'status', + + { + const cube = newCube({ + half_year: { + interval: '10 years', // useless, but still valid + origin: '2024', } - }, - measures: { - count: { - sql: () => 'count', - type: 'count' + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '10 months', + origin: '2024-04', } - } - }; + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '15 day', + origin: '2024-05-25', + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '8 hours', + origin: '2024-09-20 10:00' + } + }); - const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); - expect(validationResult.error).toBeFalsy(); + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '15 minutes', + origin: '2024-09-20 16:40' + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '30 seconds', + origin: '2024-09-20 16:40:33' + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '2 months 30 seconds', + origin: '2024-09-20T16:40:33.345' + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } + + { + const cube = newCube({ + half_year: { + interval: '2 months 12 days 14 hours 30 seconds', + origin: '2024-09-20T16:40:33.345Z' + } + }); + + const validationResult = cubeValidator.validate(cube, new ConsoleErrorReporter()); + expect(validationResult.error).toBeFalsy(); + } }); }); }); From b26abc9be4fe7dfda4de8880cc3a434cd74a999a Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 23 Aug 2024 15:46:55 +0300 Subject: [PATCH 27/84] add another test for yaml schema compiler --- .../test/unit/cube-validator.test.ts | 44 +++++++++---------- .../test/unit/yaml-schema.test.ts | 35 +++++++++++++++ 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts b/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts index 6d48068c6fae7..3ed807cdd0879 100644 --- a/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/cube-validator.test.ts @@ -4,7 +4,7 @@ import { ErrorReporter } from '../../src/compiler/ErrorReporter'; describe('Cube Validation', () => { class ConsoleErrorReporter extends ErrorReporter { - public error(message, e) { + public error(message: any, _e: any) { console.log(message); } } @@ -107,7 +107,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('You must use either sql or sqlTable within a model, but not both'); } @@ -126,7 +126,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); } } as any); @@ -146,7 +146,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('(refreshKey.every = 12h)'); expect(message).toContain('does not match regexp'); @@ -170,7 +170,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('unknown timezone'); } @@ -193,7 +193,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('must be one of [count, number,'); } @@ -217,7 +217,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('timeDimension) is required'); } @@ -240,7 +240,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('granularity) is required'); expect(message).toContain('rollups) is required'); @@ -275,7 +275,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('granularity) is required'); } @@ -300,7 +300,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('(preAggregations.eventsByType.scheduledRefresh = true) must be [false]'); } @@ -328,7 +328,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('number.sql) is required'); expect(message).toContain('number.columns) is required'); @@ -363,7 +363,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (_message: any, _e: any) => { // this callback should not be invoked expect(true).toBeFalsy(); } @@ -386,7 +386,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('must be'); } @@ -410,7 +410,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('are deprecated, please, use'); } @@ -434,7 +434,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { // this callback should not be invoked expect(true).toBeFalsy(); } @@ -468,7 +468,7 @@ describe('Cube Validation', () => { }; const validationResult = cubeValidator.validate(cube, { - error: (message, e) => { + error: (message: any, _e: any) => { console.log(message); // this callback should not be invoked expect(true).toBeFalsy(); @@ -537,7 +537,7 @@ describe('Cube Validation', () => { const cubeValidator = new CubeValidator(cubeSymbols); const validationResult = cubeValidator.validate(cubeSymbols.getCubeDefinition('CubeA'), { inContext: () => false, - error: (message, _e) => { + error: (message: any, _e: any) => { console.log(message); } } as any); @@ -742,7 +742,7 @@ describe('Cube Validation', () => { }); const validationResult = cubeValidator.validate(cube, { - error: (message: any, e: any) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); } @@ -761,7 +761,7 @@ describe('Cube Validation', () => { }); const validationResult = cubeValidator.validate(cube, { - error: (message: any, e: any) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); } @@ -780,7 +780,7 @@ describe('Cube Validation', () => { }); const validationResult = cubeValidator.validate(cube, { - error: (message: any, e: any) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); } @@ -799,7 +799,7 @@ describe('Cube Validation', () => { }); const validationResult = cubeValidator.validate(cube, { - error: (message: any, e: any) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); } @@ -818,7 +818,7 @@ describe('Cube Validation', () => { }); const validationResult = cubeValidator.validate(cube, { - error: (message: any, e: any) => { + error: (message: any, _e: any) => { console.log(message); expect(message).toContain('"dimensions.createdAt" does not match any of the allowed types'); } diff --git a/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts b/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts index baf681d336f42..59ecf8dd52785 100644 --- a/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts @@ -310,5 +310,40 @@ describe('Yaml Schema Testing', () => { expect(e.message).toContain('dimension.granularitys must be defined as array'); } }); + + it('4 correct granularities', async () => { + const { compiler } = prepareYamlCompiler( + ` + cubes: + - name: Orders + sql: "select * from tbl" + dimensions: + - name: created_at + sql: created_at + type: time + granularities: + - name: six_months + interval: 6 months + - name: three_months_offset + interval: 3 months + offset: 2 weeks + - name: fiscal_year_1st_april + interval: 1 year + origin: > + 2024-04-01 + - name: timestamp_offseted_3_weeks + interval: 3 weeks + origin: "2024-02-15 10:15:25" + - name: status + sql: status + type: string + measures: + - name: count + type: count + ` + ); + + await compiler.compile(); + }); }); }); From 804078078498c5cc923fda7e0dbed5f544f2eecd Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 23 Aug 2024 16:37:33 +0300 Subject: [PATCH 28/84] move isGranularityNaturalAligned() to utils to be reused --- .../src/compiler/CubeValidator.ts | 29 ++---------------- .../src/compiler/utils.ts | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts index aed264f19bb8d..57d8356c60956 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts @@ -3,6 +3,7 @@ import cronParser from 'cron-parser'; import type { CubeSymbols } from './CubeSymbols'; import type { ErrorReporter } from './ErrorReporter'; +import {isGranularityNaturalAligned} from "./utils"; /* ***************************** * ATTENTION: @@ -123,35 +124,9 @@ const BaseDimensionWithoutSubQuery = { }), Joi.object().keys({ interval: GranularityInterval.required().custom((value, helper) => { - const intParsed = value.split(' '); + const isValid = isGranularityNaturalAligned(value); const msg = { custom: 'Arbitrary intervals cannot be used without origin point specified' }; - if (intParsed.length !== 2) { - return helper.message(msg); - } - - const v = parseInt(intParsed[0], 10); - const unit = intParsed[1]; - - const validIntervals = { - // Any number of years is valid - year: () => true, - // Only months divisible by a year with no remainder are valid - month: () => 12 % v === 0, - // Only quarters divisible by a year with no remainder are valid - quarter: () => 4 % v === 0, - // Only 1 day is valid - day: () => v === 1, - // Only hours divisible by a day with no remainder are valid - hour: () => 24 % v === 0, - // Only minutes divisible by an hour with no remainder are valid - minute: () => 60 % v === 0, - // Only seconds divisible by a minute with no remainder are valid - second: () => 60 % v === 0, - }; - - const isValid = Object.keys(validIntervals).some(key => unit.includes(key) && validIntervals[key]()); - return isValid ? value : helper.message(msg); }), offset: GranularityOffset.optional(), diff --git a/packages/cubejs-schema-compiler/src/compiler/utils.ts b/packages/cubejs-schema-compiler/src/compiler/utils.ts index 64380629e7883..16bbc8c8430cb 100644 --- a/packages/cubejs-schema-compiler/src/compiler/utils.ts +++ b/packages/cubejs-schema-compiler/src/compiler/utils.ts @@ -51,3 +51,33 @@ export function camelizeCube(cube: any): unknown { return cube; } + +export function isGranularityNaturalAligned(interval: string): boolean { + const intParsed = interval.split(' '); + + if (intParsed.length !== 2) { + return false; + } + + const v = parseInt(intParsed[0], 10); + const unit = intParsed[1]; + + const validIntervals = { + // Any number of years is valid + year: () => true, + // Only months divisible by a year with no remainder are valid + month: () => 12 % v === 0, + // Only quarters divisible by a year with no remainder are valid + quarter: () => 4 % v === 0, + // Only 1 day is valid + day: () => v === 1, + // Only hours divisible by a day with no remainder are valid + hour: () => 24 % v === 0, + // Only minutes divisible by an hour with no remainder are valid + minute: () => 60 % v === 0, + // Only seconds divisible by a minute with no remainder are valid + second: () => 60 % v === 0, + }; + + return Object.keys(validIntervals).some(key => unit.includes(key) && validIntervals[key]()); +} From 4cf64afab23189cfe7ad6d34ac378a2742073bfb Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Sat, 24 Aug 2024 01:03:48 +0300 Subject: [PATCH 29/84] Add Granularity entity and move all related processing there also implement date_bin for postgres --- packages/cubejs-backend-shared/src/time.ts | 13 ++- .../src/adapter/BaseQuery.js | 33 ++++-- .../src/adapter/BaseTimeDimension.ts | 106 ++++++------------ .../src/adapter/Granularity.ts | 89 +++++++++++++++ .../src/adapter/PostgresQuery.ts | 32 +----- .../src/compiler/CubeValidator.ts | 30 ++++- .../src/compiler/utils.ts | 30 ----- 7 files changed, 186 insertions(+), 147 deletions(-) create mode 100644 packages/cubejs-schema-compiler/src/adapter/Granularity.ts diff --git a/packages/cubejs-backend-shared/src/time.ts b/packages/cubejs-backend-shared/src/time.ts index d3be5382c4357..515d121aeb320 100644 --- a/packages/cubejs-backend-shared/src/time.ts +++ b/packages/cubejs-backend-shared/src/time.ts @@ -5,9 +5,9 @@ const Moment = require('moment-timezone'); const moment = extendMoment(Moment); -type QueryDateRange = [string, string]; +export type QueryDateRange = [string, string]; type SqlInterval = string; -type TimeSeriesOptions = { +export type TimeSeriesOptions = { timestampPrecision: number }; type ParsedInterval = Partial>; @@ -37,7 +37,7 @@ export const TIME_SERIES: Record { @@ -63,7 +63,7 @@ function addInterval(date: moment.Moment, interval: ParsedInterval): moment.Mome return res; } -function subtractInterval(date: moment.Moment, interval: ParsedInterval): moment.Moment { +export function subtractInterval(date: moment.Moment, interval: ParsedInterval): moment.Moment { const res = date.clone(); Object.entries(interval).forEach(([key, value]) => { @@ -133,6 +133,9 @@ export const timeSeriesFromCustomInterval = (intervalStr: string, [startStr, end return dates; }; +/** + * Returns array of date ranges for a predefined granularity aligned with the start of the year as pivot point + */ export const timeSeries = (granularity: string, dateRange: QueryDateRange, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] => { if (!TIME_SERIES[granularity]) { // TODO error diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index aa678ce27988f..f8981907eb594 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -2742,31 +2742,44 @@ export class BaseQuery { throw new Error('Not implemented'); } + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point + * @param {string} interval (a value expression of type interval) + * @param {string} source (a value expression of type timestamp/date) + * @param {string} origin (a value expression of type timestamp/date) + * @returns {string} + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + dateBin(interval, source, origin) { + throw new Error('Date bin function is not implemented'); + // Different syntax possible in different DBs + } + /** * @param {string} dimension - * @param {string} interval - * @param {string | undefined} offset + * @param {import('./Granularity').Granularity} granularity * @return {string} */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - dimensionTimeGroupedColumn(dimension, interval, offset) { + dimensionTimeGroupedColumn(dimension, granularity) { let dtDate; // Interval is aligned with natural calendar, so we can use DATE_TRUNC - if (this.isGranularityNaturalAligned(interval)) { - if (offset) { + if (this.isGranularityNaturalAligned(granularity.granularityInterval)) { + if (granularity.granularityOffset) { // Example: DATE_TRUNC(interval, dimension - INTERVAL 'offset') + INTERVAL 'offset' - dtDate = this.subtractInterval(dimension, offset); - dtDate = this.timeGroupedColumn(this.granularityFromInterval(interval), dtDate); - dtDate = this.addInterval(dtDate, offset); + dtDate = this.subtractInterval(dimension, granularity.granularityOffset); + dtDate = this.timeGroupedColumn(this.granularityFromInterval(granularity.granularityInterval), dtDate); + dtDate = this.addInterval(dtDate, granularity.granularityOffset); return dtDate; } - return this.timeGroupedColumn(this.granularityFromInterval(interval), dimension); + return this.timeGroupedColumn(this.granularityFromInterval(granularity.granularityInterval), dimension); } - throw new Error('Complex time grouped queries with arbitrary interval is not implemented'); + return this.dateBin(granularity.granularityInterval, dimension, granularity.originFormatted()); } /** diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts index 90effa9082fdb..c3d6afd5c0d8d 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BaseTimeDimension.ts @@ -1,8 +1,5 @@ import moment from 'moment-timezone'; import { - timeSeries, - isPredefinedGranularity, - timeSeriesFromCustomInterval, FROM_PARTITION_RANGE, TO_PARTITION_RANGE, BUILD_RANGE_START_LOCAL, @@ -13,21 +10,12 @@ import { BaseFilter } from './BaseFilter'; import { UserError } from '../compiler/UserError'; import { BaseQuery } from './BaseQuery'; import { DimensionDefinition, SegmentDefinition } from '../compiler/CubeEvaluator'; +import { Granularity } from './Granularity'; export class BaseTimeDimension extends BaseFilter { public readonly dateRange: any; - public readonly granularity: string; - - public readonly isPredefined: boolean; - - public readonly baseRollupGranularity: string | undefined; - - public readonly granularityInterval: string; - - public readonly granularityOffset: string | undefined; - - public readonly granularitySql: Function | undefined; + public readonly granularityObj: Granularity | undefined; public readonly boundaryDateRange: any; @@ -43,29 +31,21 @@ export class BaseTimeDimension extends BaseFilter { values: timeDimension.dateRange }); this.dateRange = timeDimension.dateRange; - this.granularity = timeDimension.granularity; - this.isPredefined = isPredefinedGranularity(this.granularity); - if (this.granularity && !this.isPredefined) { - const customGranularity = this.query.cubeEvaluator - .byPath('dimensions', timeDimension.dimension) - .granularities?.[this.granularity]; - - this.baseRollupGranularity = customGranularity?.rollupGranularity; - this.granularitySql = customGranularity?.sql; - this.granularityInterval = customGranularity?.interval; - this.granularityOffset = customGranularity?.offset; - } else if (this.granularity) { - this.granularityInterval = `1 ${this.granularity}`; - } else { - this.granularityInterval = '1 hour'; + if (timeDimension.granularity) { + this.granularityObj = new Granularity(query, timeDimension); } this.boundaryDateRange = timeDimension.boundaryDateRange; this.shiftInterval = timeDimension.shiftInterval; } + // TODO: find and fix all hidden references to granularity to rely on granularityObj instead? + public get granularity(): string | undefined { + return this.granularityObj?.granularity; + } + public selectColumns() { const context = this.query.safeEvaluateSymbolContext(); - if (!context.granularityOverride && !this.granularity) { + if (!context.granularityOverride && !this.granularityObj) { return null; } @@ -74,7 +54,7 @@ export class BaseTimeDimension extends BaseFilter { public hasNoRemapping() { const context = this.query.safeEvaluateSymbolContext(); - if (!context.granularityOverride && !this.granularity) { + if (!context.granularityOverride && !this.granularityObj) { return false; } @@ -83,7 +63,7 @@ export class BaseTimeDimension extends BaseFilter { public aliasName() { const context = this.query.safeEvaluateSymbolContext(); - if (!context.granularityOverride && !this.granularity) { + if (!context.granularityOverride && !this.granularityObj) { return null; } @@ -92,7 +72,7 @@ export class BaseTimeDimension extends BaseFilter { // @ts-ignore public unescapedAliasName(granularity: string) { - const actualGranularity = granularity || this.granularity || 'day'; + const actualGranularity = granularity || this.granularityObj?.granularity || 'day'; return `${this.query.aliasName(this.dimension)}_${actualGranularity}`; // TODO date here for rollups } @@ -102,7 +82,7 @@ export class BaseTimeDimension extends BaseFilter { } public dateSeriesSelectColumn(dateSeriesAliasName) { - if (!this.granularity) { + if (!this.granularityObj) { return null; } return `${dateSeriesAliasName || this.dateSeriesAliasName()}.${this.query.escapeColumnName('date_from')} ${this.aliasName()}`; @@ -110,26 +90,31 @@ export class BaseTimeDimension extends BaseFilter { public dimensionSql() { const context = this.query.safeEvaluateSymbolContext(); - const granularity = context.granularityOverride || this.granularity; - const path = granularity ? `${this.expressionPath()}.${granularity}` : this.expressionPath(); - const granularityInterval = isPredefinedGranularity(granularity) ? `1 ${granularity}` : this.granularityInterval; + const granularityName = context.granularityOverride || this.granularityObj?.granularity; + const path = granularityName ? `${this.expressionPath()}.${granularityName}` : this.expressionPath(); + const granularity = granularityName && this.granularityObj?.granularity !== granularityName ? + new Granularity(this.query, { + dimension: this.dimension, + granularity: granularityName + }) : this.granularityObj; + if ((context.renderedReference || {})[path]) { return context.renderedReference[path]; } if (context.rollupQuery || context.wrapQuery) { - if (context.rollupGranularity === this.granularity) { + if (context.rollupGranularity === this.granularityObj?.granularity) { return super.dimensionSql(); } - return this.query.dimensionTimeGroupedColumn(this.query.dimensionSql(this), granularityInterval, this.granularityOffset); + return this.query.dimensionTimeGroupedColumn(this.query.dimensionSql(this), granularity); } if (context.ungrouped) { return this.convertedToTz(); } - return this.query.dimensionTimeGroupedColumn(this.convertedToTz(), granularityInterval, this.granularityOffset); + return this.query.dimensionTimeGroupedColumn(this.convertedToTz(), granularity); } public dimensionDefinition(): DimensionDefinition | SegmentDefinition { @@ -246,28 +231,13 @@ export class BaseTimeDimension extends BaseFilter { if (!this.rollupGranularityValue) { this.rollupGranularityValue = this.query.cacheValue( - ['rollupGranularity', this.granularity].concat(this.dateRange), + ['rollupGranularity', this.granularityObj?.granularity].concat(this.dateRange), () => { - if (!this.granularity) { + if (!this.granularityObj) { return this.dateRangeGranularity(); - } else if (this.isPredefined) { - return this.query.minGranularity(this.granularity, this.dateRangeGranularity()); - } else if (this.granularityInterval && !this.granularityOffset) { - return this.query.minGranularity( - this.query.granularityFromInterval(this.granularityInterval), - this.dateRangeGranularity() - ); - } else if (this.granularityInterval && this.granularityOffset) { // There is offset too - return this.query.minGranularity( - this.query.minGranularity( - this.query.granularityFromInterval(this.granularityInterval), - this.query.granularityFromInterval(this.granularityOffset) - ), - this.dateRangeGranularity() - ); } - return this.dateRangeGranularity(); + return this.query.minGranularity(this.granularityObj.minGranularity(), this.dateRangeGranularity()); } ); } @@ -280,27 +250,15 @@ export class BaseTimeDimension extends BaseFilter { throw new UserError('Time series queries without dateRange aren\'t supported'); } - if (!this.granularity) { - return [ - [this.dateFromFormatted(), this.dateToFormatted()] - ]; - } - - if (this.isPredefined) { - return timeSeries(this.granularity, [this.dateFromFormatted(), this.dateToFormatted()], { - timestampPrecision: this.query.timestampPrecision(), - }); + if (!this.granularityObj) { + return [[this.dateFromFormatted(), this.dateToFormatted()]]; } - return timeSeriesFromCustomInterval(this.granularityInterval, [this.dateFromFormatted(), this.dateToFormatted()], this.granularityOffset, { timestampPrecision: this.query.timestampPrecision() }); + return this.granularityObj.timeSeriesForInterval([this.dateFromFormatted(), this.dateToFormatted()], { timestampPrecision: this.query.timestampPrecision() }); } public resolvedGranularity() { - if (this.isPredefined) { - return this.granularity; - } - - return this.query.granularityFromInterval(this.granularityInterval); + return this.granularityObj?.resolvedGranularity(); } public wildcardRange() { diff --git a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts new file mode 100644 index 0000000000000..559bb673b4c22 --- /dev/null +++ b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts @@ -0,0 +1,89 @@ +import moment from 'moment-timezone'; +import { + addInterval, + isPredefinedGranularity, parseSqlInterval, + QueryDateRange, timeSeries, + timeSeriesFromCustomInterval, + TimeSeriesOptions +} from '@cubejs-backend/shared'; +import { BaseQuery } from './BaseQuery'; + +export class Granularity { + public readonly granularity: string; + + public readonly granularityInterval: string; + + public readonly granularityOffset: string | undefined; + + public readonly origin: moment.Moment; + + private readonly predefinedGranularity: boolean; + + public constructor( + private readonly query: BaseQuery, + timeDimension: any + ) { + this.granularity = timeDimension.granularity; + this.predefinedGranularity = isPredefinedGranularity(this.granularity); + this.origin = moment.tz(this.query.timezone).startOf('year'); // Defaults to current year start + + if (this.predefinedGranularity) { + this.granularityInterval = `1 ${this.granularity}`; + } else { + const customGranularity = this.query.cacheValue( + ['customGranularity', timeDimension.dimension, this.granularity], + () => query.cubeEvaluator + .byPath('dimensions', timeDimension.dimension) + .granularities?.[this.granularity] + ); + + if (!customGranularity) { + throw new Error(`Granularity "${timeDimension.granularity}" does not exist in dimension ${timeDimension.dimension}`); + } + + this.granularityInterval = customGranularity.interval; + + if (customGranularity.origin) { + this.origin = moment.tz(new Date(origin), this.query.timezone); + } else if (customGranularity.offset) { + this.granularityOffset = customGranularity.offset; + this.origin = addInterval(this.origin, parseSqlInterval(customGranularity.offset)); + } + } + } + + public originFormatted(): string { + return this.origin.format('YYYY-MM-DDTHH:mm:ss.SSS'); + } + + public minGranularity(): string { + if (this.predefinedGranularity) { + return this.granularity; + } + + if (this.granularityOffset) { + return this.query.minGranularity( + this.query.granularityFromInterval(this.granularityInterval), + this.query.granularityFromInterval(this.granularityOffset) + ); + } + + return this.query.granularityFromInterval(this.granularityInterval); + } + + public timeSeriesForInterval(dateRange: QueryDateRange, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] { + if (this.predefinedGranularity) { + return timeSeries(this.granularity, dateRange, options); + } + + return timeSeriesFromCustomInterval(this.granularityInterval, dateRange, this.granularityOffset, options); + } + + public resolvedGranularity(): string { + if (this.predefinedGranularity) { + return this.granularity; + } + + return this.query.granularityFromInterval(this.granularityInterval); + } +} diff --git a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts index 0261772703bdf..b5421b6c5d86a 100644 --- a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts @@ -31,33 +31,13 @@ export class PostgresQuery extends BaseQuery { return `date_trunc('${GRANULARITY_TO_INTERVAL[granularity]}', ${dimension})`; } - public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { - if (this.isGranularityNaturalAligned(interval)) { - return super.dimensionTimeGroupedColumn(dimension, interval, offset); - } - - // Formula: - // SELECT ((DATE_TRUNC('year', dimension) + offset?) + - // FLOOR( - // EXTRACT(EPOCH FROM (dimension - (DATE_TRUNC('year', dimension) + offset?))) / - // EXTRACT(EPOCH FROM interval) - // ) * interval) - // + public dateBin(interval: string, source: string, origin: string): string { // Should also work for AWS RedShift - - let dtDate = this.timeGroupedColumn('year', dimension); - if (offset) { - dtDate = this.addInterval(dtDate, offset); - } - - return `${dtDate} + FLOOR( - EXTRACT(EPOCH FROM (${dimension} - (${dtDate}))) / - EXTRACT(EPOCH FROM INTERVAL '${interval}') - ) * INTERVAL '${interval}'`; - } - - public startOfTheYearTimestampSql() { - return 'date_trunc(\'year\', CURRENT_TIMESTAMP)'; + return `'${origin}'::timestamp + INTERVAL '${interval}' * + FLOOR( + EXTRACT(EPOCH FROM (${source} - '${origin}'::timestamp)) / + EXTRACT(EPOCH FROM INTERVAL '${interval}') + )`; } public hllInit(sql) { diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts index 57d8356c60956..9781a6f1d488c 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts @@ -3,7 +3,6 @@ import cronParser from 'cron-parser'; import type { CubeSymbols } from './CubeSymbols'; import type { ErrorReporter } from './ErrorReporter'; -import {isGranularityNaturalAligned} from "./utils"; /* ***************************** * ATTENTION: @@ -124,9 +123,36 @@ const BaseDimensionWithoutSubQuery = { }), Joi.object().keys({ interval: GranularityInterval.required().custom((value, helper) => { - const isValid = isGranularityNaturalAligned(value); + // const isValid = isGranularityNaturalAligned(value); + const intParsed = value.split(' '); const msg = { custom: 'Arbitrary intervals cannot be used without origin point specified' }; + if (intParsed.length !== 2) { + return helper.message(msg); + } + + const v = parseInt(intParsed[0], 10); + const unit = intParsed[1]; + + const validIntervals = { + // Any number of years is valid + year: () => true, + // Only months divisible by a year with no remainder are valid + month: () => 12 % v === 0, + // Only quarters divisible by a year with no remainder are valid + quarter: () => 4 % v === 0, + // Only 1 day is valid + day: () => v === 1, + // Only hours divisible by a day with no remainder are valid + hour: () => 24 % v === 0, + // Only minutes divisible by an hour with no remainder are valid + minute: () => 60 % v === 0, + // Only seconds divisible by a minute with no remainder are valid + second: () => 60 % v === 0, + }; + + const isValid = Object.keys(validIntervals).some(key => unit.includes(key) && validIntervals[key]()); + return isValid ? value : helper.message(msg); }), offset: GranularityOffset.optional(), diff --git a/packages/cubejs-schema-compiler/src/compiler/utils.ts b/packages/cubejs-schema-compiler/src/compiler/utils.ts index 16bbc8c8430cb..64380629e7883 100644 --- a/packages/cubejs-schema-compiler/src/compiler/utils.ts +++ b/packages/cubejs-schema-compiler/src/compiler/utils.ts @@ -51,33 +51,3 @@ export function camelizeCube(cube: any): unknown { return cube; } - -export function isGranularityNaturalAligned(interval: string): boolean { - const intParsed = interval.split(' '); - - if (intParsed.length !== 2) { - return false; - } - - const v = parseInt(intParsed[0], 10); - const unit = intParsed[1]; - - const validIntervals = { - // Any number of years is valid - year: () => true, - // Only months divisible by a year with no remainder are valid - month: () => 12 % v === 0, - // Only quarters divisible by a year with no remainder are valid - quarter: () => 4 % v === 0, - // Only 1 day is valid - day: () => v === 1, - // Only hours divisible by a day with no remainder are valid - hour: () => 24 % v === 0, - // Only minutes divisible by an hour with no remainder are valid - minute: () => 60 % v === 0, - // Only seconds divisible by a minute with no remainder are valid - second: () => 60 % v === 0, - }; - - return Object.keys(validIntervals).some(key => unit.includes(key) && validIntervals[key]()); -} From 41f9e9f9b419a6d1b61ecb9002d8ed430aa37b59 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Sat, 24 Aug 2024 01:22:31 +0300 Subject: [PATCH 30/84] temporary comment out dimensionTimeGroupedColumn --- .../src/adapter/MysqlQuery.ts | 62 ++++++++-------- .../src/adapter/SnowflakeQuery.ts | 70 +++++++++---------- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts b/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts index f270ca0f0a1eb..1dacff1f7f13b 100644 --- a/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts @@ -60,37 +60,37 @@ export class MysqlQuery extends BaseQuery { return `CAST(${GRANULARITY_TO_INTERVAL[granularity](dimension)} AS DATETIME)`; } - public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { - if (this.isGranularityNaturalAligned(interval)) { - return super.dimensionTimeGroupedColumn(dimension, interval, offset); - } - - // Formula: - // SELECT TIMESTAMPADD( - // SECOND, - // FLOOR(TIMESTAMPDIFF(SECOND, DATE_ADD(DATE_FORMAT(dimension, '%Y-01-01 00:00:00'), INTERVAL offset), - // dimension) / - // TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL interval)) - // * TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL interval), - // DATE_ADD(DATE_FORMAT(dimension, '%Y-01-01 00:00:00'), INTERVAL offset) - // ) - // - // The formula operates with seconds so it won't produce dates aligned with offset date parts, like: - // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add - // exact number of seconds in the "6 months 3 days" without aligning with natural calendar. - - let dtDate = this.timeGroupedColumn('year', dimension); - if (offset) { - dtDate = this.addInterval(dtDate, offset); - } - - return `TIMESTAMPADD( - SECOND, - FLOOR(TIMESTAMPDIFF(SECOND, ${dtDate}, ${dimension}) / - TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${interval})) - * TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${interval}), - ${dtDate})`; - } + // public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { + // if (this.isGranularityNaturalAligned(interval)) { + // return super.dimensionTimeGroupedColumn(dimension, interval, offset); + // } + // + // // Formula: + // // SELECT TIMESTAMPADD( + // // SECOND, + // // FLOOR(TIMESTAMPDIFF(SECOND, DATE_ADD(DATE_FORMAT(dimension, '%Y-01-01 00:00:00'), INTERVAL offset), + // // dimension) / + // // TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL interval)) + // // * TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL interval), + // // DATE_ADD(DATE_FORMAT(dimension, '%Y-01-01 00:00:00'), INTERVAL offset) + // // ) + // // + // // The formula operates with seconds so it won't produce dates aligned with offset date parts, like: + // // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add + // // exact number of seconds in the "6 months 3 days" without aligning with natural calendar. + // + // let dtDate = this.timeGroupedColumn('year', dimension); + // if (offset) { + // dtDate = this.addInterval(dtDate, offset); + // } + // + // return `TIMESTAMPADD( + // SECOND, + // FLOOR(TIMESTAMPDIFF(SECOND, ${dtDate}, ${dimension}) / + // TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${interval})) + // * TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${interval}), + // ${dtDate})`; + // } public escapeColumnName(name) { return `\`${name}\``; diff --git a/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts b/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts index dd0b938d59807..32291f37fbf42 100644 --- a/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts @@ -42,41 +42,41 @@ export class SnowflakeQuery extends BaseQuery { return `date_trunc('${GRANULARITY_TO_INTERVAL[granularity]}', ${dimension})`; } - public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { - if (offset) { - offset = this.formatInterval(offset); - } - - if (this.isGranularityNaturalAligned(interval)) { - return super.dimensionTimeGroupedColumn(dimension, interval, offset); - } - - // Formula: - // SELECT DATEADD(second, - // FLOOR( - // DATEDIFF(seconds, DATE_TRUNC('year', dimension) + offset?, dimension) / - // DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)) - // ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)), - // DATE_TRUNC('year', dimension) + offset?) - // - // The formula operates with seconds so it won't produce dates aligned with offset date parts, like: - // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add - // exact number of seconds in the "6 months 3 days" without aligning with natural calendar. - - let dtDate = this.timeGroupedColumn('year', dimension); - if (offset) { - dtDate = this.addInterval(dtDate, offset); - } - - interval = this.formatInterval(interval); - - return `DATEADD(second, - FLOOR( - DATEDIFF(seconds, ${dtDate}, CURRENT_TIMESTAMP) / - DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${interval}')) - ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${interval}')), - ${dtDate})`; - } + // public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { + // if (offset) { + // offset = this.formatInterval(offset); + // } + // + // if (this.isGranularityNaturalAligned(interval)) { + // return super.dimensionTimeGroupedColumn(dimension, interval, offset); + // } + // + // // Formula: + // // SELECT DATEADD(second, + // // FLOOR( + // // DATEDIFF(seconds, DATE_TRUNC('year', dimension) + offset?, dimension) / + // // DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)) + // // ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)), + // // DATE_TRUNC('year', dimension) + offset?) + // // + // // The formula operates with seconds so it won't produce dates aligned with offset date parts, like: + // // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add + // // exact number of seconds in the "6 months 3 days" without aligning with natural calendar. + // + // let dtDate = this.timeGroupedColumn('year', dimension); + // if (offset) { + // dtDate = this.addInterval(dtDate, offset); + // } + // + // interval = this.formatInterval(interval); + // + // return `DATEADD(second, + // FLOOR( + // DATEDIFF(seconds, ${dtDate}, CURRENT_TIMESTAMP) / + // DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${interval}')) + // ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${interval}')), + // ${dtDate})`; + // } /** * The input interval in format "2 years 3 months 4 weeks 5 days...." From c31a55cab748f282027eca29d0151c29fb040cea Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 12:12:28 +0300 Subject: [PATCH 31/84] Fix timeSeriesFromCustomInterval generation --- packages/cubejs-backend-shared/src/time.ts | 42 ++++++++++--------- .../src/adapter/Granularity.ts | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/cubejs-backend-shared/src/time.ts b/packages/cubejs-backend-shared/src/time.ts index 515d121aeb320..1486d0bb778c3 100644 --- a/packages/cubejs-backend-shared/src/time.ts +++ b/packages/cubejs-backend-shared/src/time.ts @@ -74,48 +74,50 @@ export function subtractInterval(date: moment.Moment, interval: ParsedInterval): } /** - * Returns the closest date prior to date parameter aligned with the offset and interval - * If no offset provided, the beginning of the year will be taken as pivot point + * Returns the closest date prior to date parameter aligned with the origin point */ -function alignToOffset(date: moment.Moment, interval: ParsedInterval, offset?: ParsedInterval): moment.Moment { - let alignedDate = date.clone(); +function alignToOrigin(startDate: moment.Moment, interval: ParsedInterval, origin: moment.Moment): moment.Moment { + let alignedDate = startDate.clone(); let intervalOp; + let isIntervalNegative = false; - const startOfYear = moment().year(date.year()).startOf('year'); - let offsetDate = offset ? addInterval(startOfYear, offset) : startOfYear; + let offsetDate = addInterval(origin, interval); - if (date.isBefore(offsetDate)) { - intervalOp = offsetDate.isBefore(startOfYear) ? addInterval : subtractInterval; + // The easiest way to check the interval sign + if (offsetDate.isBefore(origin)) { + isIntervalNegative = true; + } + + offsetDate = origin.clone(); - while (date.isBefore(offsetDate)) { + if (startDate.isBefore(origin)) { + intervalOp = isIntervalNegative ? addInterval : subtractInterval; + + while (offsetDate.isAfter(startDate)) { offsetDate = intervalOp(offsetDate, interval); } alignedDate = offsetDate; - } else if (offsetDate.isBefore(startOfYear)) { - intervalOp = offsetDate.isBefore(startOfYear) ? addInterval : subtractInterval; + } else { + intervalOp = isIntervalNegative ? subtractInterval : addInterval; - while (date.isAfter(offsetDate)) { + while (offsetDate.isBefore(startDate)) { alignedDate = offsetDate.clone(); offsetDate = intervalOp(offsetDate, interval); } - } else { - intervalOp = offsetDate.isBefore(startOfYear) ? subtractInterval : addInterval; - while (date.isAfter(offsetDate)) { - alignedDate = offsetDate.clone(); - offsetDate = intervalOp(offsetDate, interval); + if (offsetDate.isSame(startDate)) { + alignedDate = offsetDate; } } return alignedDate; } -export const timeSeriesFromCustomInterval = (intervalStr: string, [startStr, endStr]: QueryDateRange, offsetStr?: string, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] => { +export const timeSeriesFromCustomInterval = (intervalStr: string, [startStr, endStr]: QueryDateRange, origin: moment.Moment, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] => { const intervalParsed = parseSqlInterval(intervalStr); - const offsetParsed = offsetStr ? parseSqlInterval(offsetStr) : undefined; const start = moment(startStr); const end = moment(endStr); - let alignedStart = alignToOffset(start, intervalParsed, offsetParsed); + let alignedStart = alignToOrigin(start, intervalParsed, origin); const dates: QueryDateRange[] = []; diff --git a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts index 559bb673b4c22..f0fe1c4c391d4 100644 --- a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts +++ b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts @@ -76,7 +76,7 @@ export class Granularity { return timeSeries(this.granularity, dateRange, options); } - return timeSeriesFromCustomInterval(this.granularityInterval, dateRange, this.granularityOffset, options); + return timeSeriesFromCustomInterval(this.granularityInterval, dateRange, this.origin, options); } public resolvedGranularity(): string { From a71ef83989b6bed9a011f794b28e09ea159051cb Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 12:12:52 +0300 Subject: [PATCH 32/84] Fix tests timeSeriesFromCustomInterval --- .../cubejs-backend-shared/test/time.test.ts | 122 +++++++++++------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/packages/cubejs-backend-shared/test/time.test.ts b/packages/cubejs-backend-shared/test/time.test.ts index 79fed5d155480..87cdd28df2f41 100644 --- a/packages/cubejs-backend-shared/test/time.test.ts +++ b/packages/cubejs-backend-shared/test/time.test.ts @@ -1,3 +1,4 @@ +import moment from 'moment-timezone'; import { inDbTimeZone, timeSeries, isPredefinedGranularity, timeSeriesFromCustomInterval } from '../src'; describe('time', () => { @@ -29,32 +30,64 @@ describe('time', () => { ]); }); - it('time series - custom: interval - 1 year, no offset', () => { - expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2023-12-31'])).toEqual([ + it('time series - custom: interval - 1 year, origin - 2021-01-01', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2023-12-31'], moment('2021-01-01'))).toEqual([ ['2021-01-01T00:00:00.000', '2021-12-31T23:59:59.999'], ['2022-01-01T00:00:00.000', '2022-12-31T23:59:59.999'], ['2023-01-01T00:00:00.000', '2023-12-31T23:59:59.999'] ]); }); - it('time series - custom: interval - 1 year, offset - 2 month', () => { - expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2022-12-31'], '2 month')).toEqual([ + it('time series - custom: interval - 1 year, origin - 2020-01-01', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2023-12-31'], moment('2020-01-01'))).toEqual([ + ['2021-01-01T00:00:00.000', '2021-12-31T23:59:59.999'], + ['2022-01-01T00:00:00.000', '2022-12-31T23:59:59.999'], + ['2023-01-01T00:00:00.000', '2023-12-31T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 1 year, origin - 2025-01-01', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2023-12-31'], moment('2025-01-01'))).toEqual([ + ['2021-01-01T00:00:00.000', '2021-12-31T23:59:59.999'], + ['2022-01-01T00:00:00.000', '2022-12-31T23:59:59.999'], + ['2023-01-01T00:00:00.000', '2023-12-31T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 1 year, origin - 2025-03-01', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2022-12-31'], moment('2025-03-01'))).toEqual([ + ['2020-03-01T00:00:00.000', '2021-02-28T23:59:59.999'], + ['2021-03-01T00:00:00.000', '2022-02-28T23:59:59.999'], + ['2022-03-01T00:00:00.000', '2023-02-28T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 1 year, origin - 2015-03-01', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2022-12-31'], moment('2015-03-01'))).toEqual([ ['2020-03-01T00:00:00.000', '2021-02-28T23:59:59.999'], ['2021-03-01T00:00:00.000', '2022-02-28T23:59:59.999'], ['2022-03-01T00:00:00.000', '2023-02-28T23:59:59.999'] ]); }); - it('time series - custom: interval - 1 year, offset - 2 month 14 days', () => { - expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2022-12-31'], '2 month 14 days')).toEqual([ + it('time series - custom: interval - 1 year, origin - 2020-03-15', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2022-12-31'], moment('2020-03-15'))).toEqual([ ['2020-03-15T00:00:00.000', '2021-03-14T23:59:59.999'], ['2021-03-15T00:00:00.000', '2022-03-14T23:59:59.999'], ['2022-03-15T00:00:00.000', '2023-03-14T23:59:59.999'] ]); }); - it('time series - custom: interval - 2 months, no offset', () => { - expect(timeSeriesFromCustomInterval('2 months', ['2021-01-01', '2021-12-31'])).toEqual([ + it('time series - custom: interval - 1 year, origin - 2019-03-15', () => { + expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2022-12-31'], moment('2019-03-15'))).toEqual([ + ['2020-03-15T00:00:00.000', '2021-03-14T23:59:59.999'], + ['2021-03-15T00:00:00.000', '2022-03-14T23:59:59.999'], + ['2022-03-15T00:00:00.000', '2023-03-14T23:59:59.999'] + ]); + }); + + it('time series - custom: interval - 2 months, origin - 2019-01-01', () => { + expect(timeSeriesFromCustomInterval('2 months', ['2021-01-01', '2021-12-31'], moment('2019-01-01'))).toEqual([ ['2021-01-01T00:00:00.000', '2021-02-28T23:59:59.999'], ['2021-03-01T00:00:00.000', '2021-04-30T23:59:59.999'], ['2021-05-01T00:00:00.000', '2021-06-30T23:59:59.999'], @@ -64,8 +97,8 @@ describe('time', () => { ]); }); - it('time series - custom: interval - 2 months, offset - 2 month 14 days', () => { - expect(timeSeriesFromCustomInterval('2 months', ['2021-01-01', '2021-12-31'], '2 month 14 days')).toEqual([ + it('time series - custom: interval - 2 months, origin - 2019-03-15', () => { + expect(timeSeriesFromCustomInterval('2 months', ['2021-01-01', '2021-12-31'], moment('2019-03-15'))).toEqual([ ['2020-11-15T00:00:00.000', '2021-01-14T23:59:59.999'], ['2021-01-15T00:00:00.000', '2021-03-14T23:59:59.999'], ['2021-03-15T00:00:00.000', '2021-05-14T23:59:59.999'], @@ -76,22 +109,22 @@ describe('time', () => { ]); }); - it('time series - custom: interval - 1 months 2 weeks 3 days, offset - 3 weeks 4 days', () => { - expect(timeSeriesFromCustomInterval('1 months 2 weeks 3 days', ['2021-01-01', '2021-12-31'], '3 weeks 4 days')).toEqual([ - ['2020-12-09T00:00:00.000', '2021-01-25T23:59:59.999'], - ['2021-01-26T00:00:00.000', '2021-03-14T23:59:59.999'], - ['2021-03-15T00:00:00.000', '2021-05-01T23:59:59.999'], - ['2021-05-02T00:00:00.000', '2021-06-18T23:59:59.999'], - ['2021-06-19T00:00:00.000', '2021-08-04T23:59:59.999'], - ['2021-08-05T00:00:00.000', '2021-09-21T23:59:59.999'], - ['2021-09-22T00:00:00.000', '2021-11-07T23:59:59.999'], - ['2021-11-08T00:00:00.000', '2021-12-24T23:59:59.999'], - ['2021-12-25T00:00:00.000', '2022-02-10T23:59:59.999'], + it('time series - custom: interval - 1 months 2 weeks 3 days, origin - 2021-01-25', () => { + expect(timeSeriesFromCustomInterval('1 months 2 weeks 3 days', ['2021-01-01', '2021-12-31'], moment('2021-01-25'))).toEqual([ + ['2020-12-08T00:00:00.000', '2021-01-24T23:59:59.999'], + ['2021-01-25T00:00:00.000', '2021-03-13T23:59:59.999'], + ['2021-03-14T00:00:00.000', '2021-04-30T23:59:59.999'], + ['2021-05-01T00:00:00.000', '2021-06-17T23:59:59.999'], + ['2021-06-18T00:00:00.000', '2021-08-03T23:59:59.999'], + ['2021-08-04T00:00:00.000', '2021-09-20T23:59:59.999'], + ['2021-09-21T00:00:00.000', '2021-11-06T23:59:59.999'], + ['2021-11-07T00:00:00.000', '2021-12-23T23:59:59.999'], + ['2021-12-24T00:00:00.000', '2022-02-09T23:59:59.999'], ]); }); - it('time series - custom: interval - 3 weeks, negative offset - -3 weeks 4 days', () => { - expect(timeSeriesFromCustomInterval('3 weeks', ['2021-01-01', '2021-03-01'], '-3 weeks 4 days')).toEqual([ + it('time series - custom: interval - 3 weeks, origin - 2020-12-15', () => { + expect(timeSeriesFromCustomInterval('3 weeks', ['2021-01-01', '2021-03-01'], moment('2020-12-15'))).toEqual([ ['2020-12-15T00:00:00.000', '2021-01-04T23:59:59.999'], ['2021-01-05T00:00:00.000', '2021-01-25T23:59:59.999'], ['2021-01-26T00:00:00.000', '2021-02-15T23:59:59.999'], @@ -99,25 +132,8 @@ describe('time', () => { ]); }); - it('time series - custom: interval - 3 weeks, big offset - 9 months', () => { - expect(timeSeriesFromCustomInterval('3 weeks', ['2021-01-01', '2021-03-01'], '9 months')).toEqual([ - ['2021-01-01T00:00:00.000', '2021-01-21T23:59:59.999'], - ['2021-01-22T00:00:00.000', '2021-02-11T23:59:59.999'], - ['2021-02-12T00:00:00.000', '2021-03-04T23:59:59.999'] - ]); - }); - - it('time series - custom: interval - 3 weeks, big offset - 10 months', () => { - expect(timeSeriesFromCustomInterval('3 weeks', ['2021-01-01', '2021-03-01'], '10 months')).toEqual([ - ['2020-12-21T00:00:00.000', '2021-01-10T23:59:59.999'], - ['2021-01-11T00:00:00.000', '2021-01-31T23:59:59.999'], - ['2021-02-01T00:00:00.000', '2021-02-21T23:59:59.999'], - ['2021-02-22T00:00:00.000', '2021-03-14T23:59:59.999'] - ]); - }); - - it('time series - custom: interval - 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds, no offset', () => { - expect(timeSeriesFromCustomInterval('2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', ['2021-01-01', '2021-12-31'])).toEqual([ + it('time series - custom: interval - 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds, origin - 2021-01-01', () => { + expect(timeSeriesFromCustomInterval('2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', ['2021-01-01', '2021-12-31'], moment('2021-01-01'))).toEqual([ ['2021-01-01T00:00:00.000', '2021-03-26T05:06:06.999'], ['2021-03-26T05:06:07.000', '2021-06-20T10:12:13.999'], ['2021-06-20T10:12:14.000', '2021-09-14T15:18:20.999'], @@ -126,14 +142,20 @@ describe('time', () => { ]); }); - it('time series - custom: interval - 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds, offset - 2 months 3 weeks 4 days 5 hours 6 minutes 8 seconds', () => { - expect(timeSeriesFromCustomInterval('2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', ['2021-01-01', '2021-12-31'], '2 months 3 weeks 4 days 5 hours 6 minutes 8 seconds')).toEqual([ - ['2020-10-06T18:53:54.000', '2021-01-01T00:00:00.999'], - ['2021-01-01T00:00:01.000', '2021-03-26T05:06:07.999'], - ['2021-03-26T05:06:08.000', '2021-06-20T10:12:14.999'], - ['2021-06-20T10:12:15.000', '2021-09-14T15:18:21.999'], - ['2021-09-14T15:18:22.000', '2021-12-09T20:24:28.999'], - ['2021-12-09T20:24:29.000', '2022-03-07T01:30:35.999'] + it('time series - custom: interval - 10 minutes 15 seconds, origin - 2021-02-01 09:59:45', () => { + expect(timeSeriesFromCustomInterval('10 minutes 15 seconds', ['2021-02-01 10:00:00', '2021-02-01 12:00:00'], moment('2021-02-01 09:59:45'))).toEqual([ + ['2021-02-01T09:59:45.000', '2021-02-01T10:09:59.999'], + ['2021-02-01T10:10:00.000', '2021-02-01T10:20:14.999'], + ['2021-02-01T10:20:15.000', '2021-02-01T10:30:29.999'], + ['2021-02-01T10:30:30.000', '2021-02-01T10:40:44.999'], + ['2021-02-01T10:40:45.000', '2021-02-01T10:50:59.999'], + ['2021-02-01T10:51:00.000', '2021-02-01T11:01:14.999'], + ['2021-02-01T11:01:15.000', '2021-02-01T11:11:29.999'], + ['2021-02-01T11:11:30.000', '2021-02-01T11:21:44.999'], + ['2021-02-01T11:21:45.000', '2021-02-01T11:31:59.999'], + ['2021-02-01T11:32:00.000', '2021-02-01T11:42:14.999'], + ['2021-02-01T11:42:15.000', '2021-02-01T11:52:29.999'], + ['2021-02-01T11:52:30.000', '2021-02-01T12:02:44.999'] ]); }); From 441fd90a610002adc53eb9f9be157643b636c06f Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 12:54:46 +0300 Subject: [PATCH 33/84] Add more tests for timeSeriesFromCustomInterval --- packages/cubejs-backend-shared/test/time.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/cubejs-backend-shared/test/time.test.ts b/packages/cubejs-backend-shared/test/time.test.ts index 87cdd28df2f41..fa4c72c7d720d 100644 --- a/packages/cubejs-backend-shared/test/time.test.ts +++ b/packages/cubejs-backend-shared/test/time.test.ts @@ -132,6 +132,13 @@ describe('time', () => { ]); }); + it('time series - custom: interval - 6 months, origin - 2021-01-01', () => { + expect(timeSeriesFromCustomInterval('6 months', ['2021-01-01', '2021-12-31'], moment('2021-01-01'))).toEqual([ + ['2021-01-01T00:00:00.000', '2021-06-30T23:59:59.999'], + ['2021-07-01T00:00:00.000', '2021-12-31T23:59:59.999'] + ]); + }); + it('time series - custom: interval - 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds, origin - 2021-01-01', () => { expect(timeSeriesFromCustomInterval('2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', ['2021-01-01', '2021-12-31'], moment('2021-01-01'))).toEqual([ ['2021-01-01T00:00:00.000', '2021-03-26T05:06:06.999'], From 4cb2ce35e38308ad27fae5a5f12cfcd4b91e623d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 13:02:20 +0300 Subject: [PATCH 34/84] Fix Granularity constructor --- packages/cubejs-schema-compiler/src/adapter/Granularity.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts index f0fe1c4c391d4..499ae5f151249 100644 --- a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts +++ b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts @@ -25,7 +25,7 @@ export class Granularity { ) { this.granularity = timeDimension.granularity; this.predefinedGranularity = isPredefinedGranularity(this.granularity); - this.origin = moment.tz(this.query.timezone).startOf('year'); // Defaults to current year start + this.origin = moment().startOf('year'); // Defaults to current year start if (this.predefinedGranularity) { this.granularityInterval = `1 ${this.granularity}`; @@ -44,7 +44,7 @@ export class Granularity { this.granularityInterval = customGranularity.interval; if (customGranularity.origin) { - this.origin = moment.tz(new Date(origin), this.query.timezone); + this.origin = moment(new Date(origin)); } else if (customGranularity.offset) { this.granularityOffset = customGranularity.offset; this.origin = addInterval(this.origin, parseSqlInterval(customGranularity.offset)); From e23b3b2b0d7ba30075bfbf7fcd871d37b9d77b31 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 17:09:18 +0300 Subject: [PATCH 35/84] Add integration tests for Custom Granularities for PostgreSQL --- .../postgres/custom-granularities.test.ts | 820 ++++++++++++++++++ 1 file changed, 820 insertions(+) create mode 100644 packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts new file mode 100644 index 0000000000000..1e58bfac83e8d --- /dev/null +++ b/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts @@ -0,0 +1,820 @@ +import { prepareYamlCompiler } from '../../unit/PrepareCompiler'; +import { dbRunner } from './PostgresDBRunner'; + +describe('Custom Granularities', () => { + jest.setTimeout(200000); + + const { compiler, joinGraph, cubeEvaluator } = prepareYamlCompiler(` + cubes: + - name: orders + sql: > + SELECT order_id, + created_at, + CASE + WHEN order_id % 3 = 1 THEN 'processing' + WHEN order_id % 3 = 2 THEN 'completed' + ELSE 'shipped' + END AS status + FROM (SELECT GENERATE_SERIES(1, 60) AS order_id, + GENERATE_SERIES('2024-01-01'::date, '2026-04-10'::date, '2 weeks') AS created_at) AS subquery + + dimensions: + - name: order_id + sql: order_id + type: number + primary_key: true + public: true + + - name: status + sql: status + type: string + + - name: createdAt + sql: created_at + type: time + granularities: + - name: half_year + interval: 6 months + - name: half_year_by_1st_april + interval: 6 months + offset: 3 months + - name: two_weeks_by_friday + interval: 2 weeks + origin: '2024-08-23' + - name: one_hour_by_5min_offset + interval: 1 hour + offset: 5 minutes + - name: twenty_five_minutes + interval: 25 minutes + origin: '2024-01-01 10:15:00' + - name: fiscal_year_by_1st_feb + interval: 1 year + origin: '2024-02-01' + - name: fiscal_year_by_15th_march + interval: 1 year + origin: '2024-03-15' + + measures: + - name: count + type: count + + - name: rollingCountByTrailing3Months + type: count + rolling_window: + trailing: 3 months + + - name: rollingCountByLeading4Months + type: count + rolling_window: + leading: 4 months + + - name: rollingCountByUnbounded + type: count + rolling_window: + trailing: unbounded + `); + + it('works with half_year custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: '13', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + }, + { + orders__count: '13', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + }, + { + orders__count: '13', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + }, + { + orders__count: '13', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + }, + { + orders__count: '1', + orders__created_at_half_year: '2026-01-01T00:00:00.000Z', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: '7', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + }, + { + orders__count: '13', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + }, + { + orders__count: '13', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + }, + { + orders__count: '13', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + }, + { + orders__count: '7', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: '4', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '5', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__status: 'shipped', + }, + { + orders__count: '5', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '4', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__status: 'shipped', + }, + { + orders__count: '4', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '4', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '5', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__status: 'shipped', + }, + { + orders__count: '4', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '5', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__status: 'shipped', + }, + { + orders__count: '1', + orders__created_at_half_year: '2026-01-01T00:00:00.000Z', + orders__status: 'completed', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom with dimension granularity query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: '2', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '3', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '2', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__status: 'shipped', + }, + { + orders__count: '5', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__status: 'shipped', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '5', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__status: 'shipped', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '5', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__status: 'shipped', + }, + { + orders__count: '3', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__status: 'completed', + }, + { + orders__count: '2', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__status: 'processing', + }, + { + orders__count: '2', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '13', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '27', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '40', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '53', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '4', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '4', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '5', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '9', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '9', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '9', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '14', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '13', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '13', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '18', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '17', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '18', + orders__status: 'processing', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity with dimension with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '3', + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '2', + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '2', + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '6', + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '7', + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '7', + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '15', + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '16', + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '15', + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '19', + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '20', + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '20', + orders__status: 'processing', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '6', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '7', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '7', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '7', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '3', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '3', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '3', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '2', + orders__status: 'processing', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '7', + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '7', + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '6', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '6', + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '6', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '9', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '8', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '8', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '7', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '2', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '2', + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '2', + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '2', + orders__status: 'processing', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '9', + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '9', + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '9', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '9', + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '1', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); +}); From 410b50aed6eb8d093f2f8624f224675f4d4c01ea Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 18:42:41 +0300 Subject: [PATCH 36/84] Fix Custom Granularities tests in CI (needs order by) --- .../postgres/custom-granularities.test.ts | 140 +++++++++--------- 1 file changed, 74 insertions(+), 66 deletions(-) diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts index 1e58bfac83e8d..24d8beb3292d7 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts @@ -365,67 +365,71 @@ describe('Custom Granularities', () => { }], dimensions: ['orders.status'], filters: [], + order: [ + ['orders.createdAt', 'asc'], + ['orders.status', 'asc'], + ], timezone: 'Europe/London' }, [ { - orders__created_at_half_year: '2024-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '4', - orders__status: 'completed', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '14', + orders__status: 'processing', }, { - orders__created_at_half_year: '2024-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '4', - orders__status: 'shipped', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '18', + orders__status: 'processing', }, { - orders__created_at_half_year: '2024-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '5', - orders__status: 'processing', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '13', + orders__status: 'completed', }, { orders__created_at_half_year: '2024-07-01T00:00:00.000Z', orders__rolling_count_by_unbounded: '9', - orders__status: 'processing', + orders__status: 'completed', }, { - orders__created_at_half_year: '2024-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '9', - orders__status: 'shipped', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '4', + orders__status: 'completed', }, { - orders__created_at_half_year: '2024-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '9', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '18', orders__status: 'completed', }, { - orders__created_at_half_year: '2025-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '14', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '5', orders__status: 'processing', }, { - orders__created_at_half_year: '2025-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '13', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '4', orders__status: 'shipped', }, { - orders__created_at_half_year: '2025-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '13', - orders__status: 'completed', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '17', + orders__status: 'shipped', }, { - orders__created_at_half_year: '2025-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '18', - orders__status: 'completed', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '9', + orders__status: 'shipped', }, { - orders__created_at_half_year: '2025-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '17', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '13', orders__status: 'shipped', }, { - orders__created_at_half_year: '2025-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '18', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '9', orders__status: 'processing', }, ], @@ -442,22 +446,16 @@ describe('Custom Granularities', () => { }], dimensions: ['orders.status'], filters: [], + order: [ + ['orders.createdAt', 'asc'], + ['orders.status', 'asc'], + ], timezone: 'Europe/London' }, [ { - orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '3', - orders__status: 'processing', - }, - { - orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '2', - orders__status: 'shipped', - }, - { - orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '2', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '7', orders__status: 'completed', }, { @@ -466,34 +464,44 @@ describe('Custom Granularities', () => { orders__status: 'shipped', }, { - orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '7', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '15', orders__status: 'completed', }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '19', + orders__status: 'shipped', + }, { orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', orders__rolling_count_by_unbounded: '7', orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '11', - orders__status: 'processing', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '15', + orders__status: 'shipped', }, { - orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '11', - orders__status: 'completed', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '20', + orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '11', - orders__status: 'shipped', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '3', + orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '15', - orders__status: 'shipped', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '20', + orders__status: 'completed', }, { orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', @@ -501,24 +509,24 @@ describe('Custom Granularities', () => { orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '15', - orders__status: 'completed', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '2', + orders__status: 'shipped', }, { - orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '19', - orders__status: 'shipped', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', + orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '20', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', orders__status: 'completed', }, { - orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '20', - orders__status: 'processing', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '2', + orders__status: 'completed', }, ], { joinGraph, cubeEvaluator, compiler } From 6a316e5ec8e8be7bdb2c76ebd1ac0639b1485776 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 19:50:56 +0300 Subject: [PATCH 37/84] Implement date_bin for Snowflake --- .../src/adapter/SnowflakeQuery.ts | 71 ++++++++++--------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts b/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts index 32291f37fbf42..d43622d21bd96 100644 --- a/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts @@ -42,41 +42,22 @@ export class SnowflakeQuery extends BaseQuery { return `date_trunc('${GRANULARITY_TO_INTERVAL[granularity]}', ${dimension})`; } - // public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { - // if (offset) { - // offset = this.formatInterval(offset); - // } - // - // if (this.isGranularityNaturalAligned(interval)) { - // return super.dimensionTimeGroupedColumn(dimension, interval, offset); - // } - // - // // Formula: - // // SELECT DATEADD(second, - // // FLOOR( - // // DATEDIFF(seconds, DATE_TRUNC('year', dimension) + offset?, dimension) / - // // DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)) - // // ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)), - // // DATE_TRUNC('year', dimension) + offset?) - // // - // // The formula operates with seconds so it won't produce dates aligned with offset date parts, like: - // // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add - // // exact number of seconds in the "6 months 3 days" without aligning with natural calendar. - // - // let dtDate = this.timeGroupedColumn('year', dimension); - // if (offset) { - // dtDate = this.addInterval(dtDate, offset); - // } - // - // interval = this.formatInterval(interval); - // - // return `DATEADD(second, - // FLOOR( - // DATEDIFF(seconds, ${dtDate}, CURRENT_TIMESTAMP) / - // DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${interval}')) - // ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${interval}')), - // ${dtDate})`; - // } + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point. + */ + public dateBin(interval: string, source: string, origin: string): string { + const intervalFormatted = this.formatInterval(interval); + const timeUnit = this.diffTimeUnitForInterval(interval); + const beginOfTime = 'TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0)'; + + return `DATEADD(${timeUnit}, + FLOOR( + DATEDIFF(${timeUnit}, ${this.timeStampCast(`'${origin}'`)}, ${source}) / + DATEDIFF(${timeUnit}, ${beginOfTime}, (${beginOfTime} + interval '${intervalFormatted}')) + ) * DATEDIFF(${timeUnit}, ${beginOfTime}, (${beginOfTime} + interval '${intervalFormatted}')), + ${this.timeStampCast(`'${origin}'`)})`; + } /** * The input interval in format "2 years 3 months 4 weeks 5 days...." @@ -91,6 +72,26 @@ export class SnowflakeQuery extends BaseQuery { }).join(' '); } + private diffTimeUnitForInterval(interval: string): string { + if (/second/i.test(interval)) { + return 'second'; + } else if (/minute/i.test(interval)) { + return 'minute'; + } else if (/hour/i.test(interval)) { + return 'hour'; + } else if (/day/i.test(interval)) { + return 'day'; + } else if (/week/i.test(interval)) { + return 'day'; + } else if (/month/i.test(interval)) { + return 'month'; + } else if (/quarter/i.test(interval)) { + return 'month'; + } else /* if (/year/i.test(interval)) */ { + return 'year'; + } + } + public timeStampCast(value) { return `${value}::timestamp_tz`; } From fd3290dc6d40d3edc849a20b8d34cfe62df20bb8 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 20:17:29 +0300 Subject: [PATCH 38/84] Move BaseDbRunner from postgres to separate folder --- .../test/integration/mssql/MSSqlDbRunner.js | 2 +- .../test/integration/mysql/MySqlDbRunner.js | 2 +- .../test/integration/postgres/PostgresDBRunner.js | 2 +- .../test/integration/{postgres => utils}/BaseDbRunner.ts | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename packages/cubejs-schema-compiler/test/integration/{postgres => utils}/BaseDbRunner.ts (100%) diff --git a/packages/cubejs-schema-compiler/test/integration/mssql/MSSqlDbRunner.js b/packages/cubejs-schema-compiler/test/integration/mssql/MSSqlDbRunner.js index 99c95a611dda2..fcba4cdc58919 100644 --- a/packages/cubejs-schema-compiler/test/integration/mssql/MSSqlDbRunner.js +++ b/packages/cubejs-schema-compiler/test/integration/mssql/MSSqlDbRunner.js @@ -2,7 +2,7 @@ import { GenericContainer, Wait } from 'testcontainers'; import sql from 'mssql'; -import { BaseDbRunner } from '../postgres/BaseDbRunner'; +import { BaseDbRunner } from '../utils/BaseDbRunner'; export class MSSqlDbRunner extends BaseDbRunner { async connectionLazyInit(port) { diff --git a/packages/cubejs-schema-compiler/test/integration/mysql/MySqlDbRunner.js b/packages/cubejs-schema-compiler/test/integration/mysql/MySqlDbRunner.js index 11655895fae16..41cc906f36533 100644 --- a/packages/cubejs-schema-compiler/test/integration/mysql/MySqlDbRunner.js +++ b/packages/cubejs-schema-compiler/test/integration/mysql/MySqlDbRunner.js @@ -2,7 +2,7 @@ import { promisify } from 'util'; import { GenericContainer } from 'testcontainers'; import mysql from 'mysql'; -import { BaseDbRunner } from '../postgres/BaseDbRunner'; +import { BaseDbRunner } from '../utils/BaseDbRunner'; export class MySqlDbRunner extends BaseDbRunner { async connectionLazyInit(port) { diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/PostgresDBRunner.js b/packages/cubejs-schema-compiler/test/integration/postgres/PostgresDBRunner.js index 166f70fa9f96d..5e8ded3aec0f3 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/PostgresDBRunner.js +++ b/packages/cubejs-schema-compiler/test/integration/postgres/PostgresDBRunner.js @@ -4,7 +4,7 @@ import { PostgresQuery } from '../../../src'; const pgPromise = require('pg-promise'); // eslint-disable-next-line import/no-extraneous-dependencies const { GenericContainer, Wait } = require('testcontainers'); -const { BaseDbRunner } = require('./BaseDbRunner'); +const { BaseDbRunner } = require('../utils/BaseDbRunner'); process.env.TZ = 'GMT'; diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/BaseDbRunner.ts b/packages/cubejs-schema-compiler/test/integration/utils/BaseDbRunner.ts similarity index 100% rename from packages/cubejs-schema-compiler/test/integration/postgres/BaseDbRunner.ts rename to packages/cubejs-schema-compiler/test/integration/utils/BaseDbRunner.ts From c41ecddf39047ed3cf8926e30c81084238de8db0 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 22:52:32 +0300 Subject: [PATCH 39/84] fix customGranularity.origin init --- packages/cubejs-schema-compiler/src/adapter/Granularity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts index 499ae5f151249..20cdc289f281b 100644 --- a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts +++ b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts @@ -44,7 +44,7 @@ export class Granularity { this.granularityInterval = customGranularity.interval; if (customGranularity.origin) { - this.origin = moment(new Date(origin)); + this.origin = moment(new Date(customGranularity.origin)); } else if (customGranularity.offset) { this.granularityOffset = customGranularity.offset; this.origin = addInterval(this.origin, parseSqlInterval(customGranularity.offset)); From c510ae6c8f5193c4895aaee8dd51950f5ce65491 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 22:53:14 +0300 Subject: [PATCH 40/84] implement dateBin in MySQL --- .../src/adapter/MysqlQuery.ts | 93 ++++++++++++------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts b/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts index 1dacff1f7f13b..c49ccf3d9e58b 100644 --- a/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts @@ -1,5 +1,7 @@ import moment from 'moment-timezone'; +import { parseSqlInterval } from '@cubejs-backend/shared'; + import { BaseQuery } from './BaseQuery'; import { BaseFilter } from './BaseFilter'; import { UserError } from '../compiler/UserError'; @@ -60,37 +62,66 @@ export class MysqlQuery extends BaseQuery { return `CAST(${GRANULARITY_TO_INTERVAL[granularity](dimension)} AS DATETIME)`; } - // public dimensionTimeGroupedColumn(dimension: string, interval: string, offset: string): string { - // if (this.isGranularityNaturalAligned(interval)) { - // return super.dimensionTimeGroupedColumn(dimension, interval, offset); - // } - // - // // Formula: - // // SELECT TIMESTAMPADD( - // // SECOND, - // // FLOOR(TIMESTAMPDIFF(SECOND, DATE_ADD(DATE_FORMAT(dimension, '%Y-01-01 00:00:00'), INTERVAL offset), - // // dimension) / - // // TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL interval)) - // // * TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL interval), - // // DATE_ADD(DATE_FORMAT(dimension, '%Y-01-01 00:00:00'), INTERVAL offset) - // // ) - // // - // // The formula operates with seconds so it won't produce dates aligned with offset date parts, like: - // // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add - // // exact number of seconds in the "6 months 3 days" without aligning with natural calendar. - // - // let dtDate = this.timeGroupedColumn('year', dimension); - // if (offset) { - // dtDate = this.addInterval(dtDate, offset); - // } - // - // return `TIMESTAMPADD( - // SECOND, - // FLOOR(TIMESTAMPDIFF(SECOND, ${dtDate}, ${dimension}) / - // TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${interval})) - // * TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${interval}), - // ${dtDate})`; - // } + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point. + */ + public dateBin(interval: string, source: string, origin: string): string { + const intervalFormatted = this.formatInterval(interval); + const timeUnit = this.isIntervalYM(interval) ? 'MONTH' : 'SECOND'; + + return `TIMESTAMPADD(${timeUnit}, + FLOOR( + TIMESTAMPDIFF(${timeUnit}, ${this.timeStampCast(`'${origin}'`)}, ${source}) / + TIMESTAMPDIFF(${timeUnit}, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${intervalFormatted}) + ) * TIMESTAMPDIFF(${timeUnit}, '1970-01-01 00:00:00', '1970-01-01 00:00:00' + INTERVAL ${intervalFormatted}), + ${this.timeStampCast(`'${origin}'`)} + )`; + } + + private isIntervalYM(interval: string): boolean { + return /(year|month|quarter)/i.test(interval); + } + + /** + * The input interval with (possible) plural units, like "2 years", "3 months", "4 weeks", "5 days"... + * will be converted to MYSQL dialect. + * @see https://dev.mysql.com/doc/refman/8.4/en/expressions.html#temporal-intervals + */ + private formatInterval(interval: string): string { + const intervalParsed = parseSqlInterval(interval); + const intKeys = Object.keys(intervalParsed).length; + + if (intervalParsed.year && intKeys === 1) { + return `${intervalParsed.year} YEAR`; + } else if (intervalParsed.year && intervalParsed.month && intKeys === 2) { + return `'${intervalParsed.year}-${intervalParsed.month}' YEAR_MONTH`; + } else if (intervalParsed.quarter && intKeys === 1) { + return `${intervalParsed.quarter} QUARTER`; + } else if (intervalParsed.month && intKeys === 1) { + return `${intervalParsed.month} MONTH`; + } else if (intervalParsed.week && intKeys === 1) { + return `${intervalParsed.week} WEEK`; + } else if (intervalParsed.day && intKeys === 1) { + return `${intervalParsed.day} DAY`; + } else if (intervalParsed.day && intervalParsed.hour && intKeys === 2) { + return `'${intervalParsed.day} ${intervalParsed.hour}' DAY_HOUR`; + } else if (intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intKeys === 3) { + return `'${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}' DAY_MINUTE`; + } else if (intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intervalParsed.second && intKeys === 4) { + return `'${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}:${intervalParsed.second}' DAY_SECOND`; + } else if (intervalParsed.hour && intervalParsed.minute && intKeys === 2) { + return `'${intervalParsed.hour}:${intervalParsed.minute}' HOUR_MINUTE`; + } else if (intervalParsed.hour && intervalParsed.minute && intervalParsed.second && intKeys === 3) { + return `'${intervalParsed.hour}:${intervalParsed.minute}:${intervalParsed.second}' HOUR_SECOND`; + } else if (intervalParsed.minute && intervalParsed.second && intKeys === 2) { + return `'${intervalParsed.minute}:${intervalParsed.second}' MINUTE_SECOND`; + } + + // No need to support microseconds. + + throw new Error(`Cannot transform interval expression "${interval}" to MySQL dialect`); + } public escapeColumnName(name) { return `\`${name}\``; From 5c59bdebfb35ba1c64158e3566cc6b8fc1ca62fa Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 22:53:42 +0300 Subject: [PATCH 41/84] fix interval math ops in MySQL --- packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts b/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts index c49ccf3d9e58b..a2584f2163c82 100644 --- a/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts @@ -51,11 +51,11 @@ export class MysqlQuery extends BaseQuery { } public subtractInterval(date, interval) { - return `DATE_SUB(${date}, INTERVAL ${interval})`; + return `DATE_SUB(${date}, INTERVAL ${this.formatInterval(interval)})`; } public addInterval(date, interval) { - return `DATE_ADD(${date}, INTERVAL ${interval})`; + return `DATE_ADD(${date}, INTERVAL ${this.formatInterval(interval)})`; } public timeGroupedColumn(granularity, dimension) { From 23b123734bc1017ed7f1915ccb7ca5b95ceca06c Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 10:54:25 +0300 Subject: [PATCH 42/84] fix postgres custom granularities tests --- .../postgres/custom-granularities.test.ts | 188 +++++++++--------- 1 file changed, 93 insertions(+), 95 deletions(-) diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts index 24d8beb3292d7..c69cdbd2bb568 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/custom-granularities.test.ts @@ -158,6 +158,7 @@ describe('Custom Granularities', () => { }], dimensions: ['orders.status'], filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], timezone: 'Europe/London' }, [ @@ -240,6 +241,7 @@ describe('Custom Granularities', () => { }], dimensions: ['orders.status'], filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], timezone: 'Europe/London' }, [ @@ -365,27 +367,24 @@ describe('Custom Granularities', () => { }], dimensions: ['orders.status'], filters: [], - order: [ - ['orders.createdAt', 'asc'], - ['orders.status', 'asc'], - ], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], timezone: 'Europe/London' }, [ { - orders__created_at_half_year: '2025-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '14', - orders__status: 'processing', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '4', + orders__status: 'completed', }, { - orders__created_at_half_year: '2025-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '18', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '5', orders__status: 'processing', }, { - orders__created_at_half_year: '2025-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '13', - orders__status: 'completed', + orders__created_at_half_year: '2024-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '4', + orders__status: 'shipped', }, { orders__created_at_half_year: '2024-07-01T00:00:00.000Z', @@ -393,45 +392,45 @@ describe('Custom Granularities', () => { orders__status: 'completed', }, { - orders__created_at_half_year: '2024-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '4', - orders__status: 'completed', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '9', + orders__status: 'processing', }, { - orders__created_at_half_year: '2025-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '18', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '9', + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '13', orders__status: 'completed', }, { - orders__created_at_half_year: '2024-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '5', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '14', orders__status: 'processing', }, { - orders__created_at_half_year: '2024-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '4', + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '13', orders__status: 'shipped', }, { orders__created_at_half_year: '2025-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '17', - orders__status: 'shipped', + orders__rolling_count_by_unbounded: '18', + orders__status: 'completed', }, { - orders__created_at_half_year: '2024-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '9', - orders__status: 'shipped', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '18', + orders__status: 'processing', }, { - orders__created_at_half_year: '2025-01-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '13', + orders__created_at_half_year: '2025-07-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '17', orders__status: 'shipped', }, - { - orders__created_at_half_year: '2024-07-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '9', - orders__status: 'processing', - }, ], { joinGraph, cubeEvaluator, compiler } )); @@ -446,61 +445,58 @@ describe('Custom Granularities', () => { }], dimensions: ['orders.status'], filters: [], - order: [ - ['orders.createdAt', 'asc'], - ['orders.status', 'asc'], - ], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], timezone: 'Europe/London' }, [ { - orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '7', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '2', orders__status: 'completed', }, { - orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '6', - orders__status: 'shipped', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '3', + orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '11', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '2', orders__status: 'shipped', }, { - orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '15', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '7', orders__status: 'completed', }, - { - orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '19', - orders__status: 'shipped', - }, { orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', orders__rolling_count_by_unbounded: '7', orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '15', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '6', orders__status: 'shipped', }, { - orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '20', - orders__status: 'processing', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', + orders__status: 'completed', }, { - orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '3', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '20', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '11', + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '15', orders__status: 'completed', }, { @@ -509,24 +505,24 @@ describe('Custom Granularities', () => { orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '2', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '15', orders__status: 'shipped', }, { - orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '11', - orders__status: 'processing', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '20', + orders__status: 'completed', }, { - orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '11', - orders__status: 'completed', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '20', + orders__status: 'processing', }, { - orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000Z', - orders__rolling_count_by_unbounded: '2', - orders__status: 'completed', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000Z', + orders__rolling_count_by_unbounded: '19', + orders__status: 'shipped', }, ], { joinGraph, cubeEvaluator, compiler } @@ -575,6 +571,7 @@ describe('Custom Granularities', () => { }], dimensions: ['orders.status'], filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], timezone: 'Europe/London' }, [ @@ -586,38 +583,38 @@ describe('Custom Granularities', () => { { orders__created_at_half_year: '2024-01-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '2', - orders__status: 'shipped', + orders__status: 'processing', }, { orders__created_at_half_year: '2024-01-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '2', - orders__status: 'processing', + orders__status: 'shipped', }, { orders__created_at_half_year: '2024-07-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '2', orders__status: 'completed', }, - { - orders__created_at_half_year: '2024-07-01T00:00:00.000Z', - orders__rolling_count_by_trailing3_months: '3', - orders__status: 'shipped', - }, { orders__created_at_half_year: '2024-07-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '2', orders__status: 'processing', }, { - orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '3', - orders__status: 'processing', + orders__status: 'shipped', }, { orders__created_at_half_year: '2025-01-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '2', orders__status: 'completed', }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_trailing3_months: '3', + orders__status: 'processing', + }, { orders__created_at_half_year: '2025-01-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '2', @@ -631,12 +628,12 @@ describe('Custom Granularities', () => { { orders__created_at_half_year: '2025-07-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '2', - orders__status: 'shipped', + orders__status: 'processing', }, { orders__created_at_half_year: '2025-07-01T00:00:00.000Z', orders__rolling_count_by_trailing3_months: '2', - orders__status: 'processing', + orders__status: 'shipped', }, ], { joinGraph, cubeEvaluator, compiler } @@ -722,6 +719,7 @@ describe('Custom Granularities', () => { }], dimensions: ['orders.status'], filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], timezone: 'Europe/London' }, [ @@ -733,38 +731,38 @@ describe('Custom Granularities', () => { { orders__created_at_half_year: '2024-01-01T00:00:00.000Z', orders__rolling_count_by_leading4_months: '3', - orders__status: 'shipped', + orders__status: 'processing', }, { orders__created_at_half_year: '2024-01-01T00:00:00.000Z', orders__rolling_count_by_leading4_months: '3', - orders__status: 'processing', + orders__status: 'shipped', }, { orders__created_at_half_year: '2024-07-01T00:00:00.000Z', orders__rolling_count_by_leading4_months: '3', orders__status: 'completed', }, - { - orders__created_at_half_year: '2024-07-01T00:00:00.000Z', - orders__rolling_count_by_leading4_months: '2', - orders__status: 'shipped', - }, { orders__created_at_half_year: '2024-07-01T00:00:00.000Z', orders__rolling_count_by_leading4_months: '3', orders__status: 'processing', }, { - orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__created_at_half_year: '2024-07-01T00:00:00.000Z', orders__rolling_count_by_leading4_months: '2', - orders__status: 'processing', + orders__status: 'shipped', }, { orders__created_at_half_year: '2025-01-01T00:00:00.000Z', orders__rolling_count_by_leading4_months: '3', orders__status: 'completed', }, + { + orders__created_at_half_year: '2025-01-01T00:00:00.000Z', + orders__rolling_count_by_leading4_months: '2', + orders__status: 'processing', + }, { orders__created_at_half_year: '2025-01-01T00:00:00.000Z', orders__rolling_count_by_leading4_months: '3', @@ -777,13 +775,13 @@ describe('Custom Granularities', () => { }, { orders__created_at_half_year: '2025-07-01T00:00:00.000Z', - orders__rolling_count_by_leading4_months: '3', - orders__status: 'shipped', + orders__rolling_count_by_leading4_months: '2', + orders__status: 'processing', }, { orders__created_at_half_year: '2025-07-01T00:00:00.000Z', - orders__rolling_count_by_leading4_months: '2', - orders__status: 'processing', + orders__rolling_count_by_leading4_months: '3', + orders__status: 'shipped', }, ], { joinGraph, cubeEvaluator, compiler } From d81dfb022186a28f5d754d99ffd1bc2ecc7c81cb Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 26 Aug 2024 22:54:12 +0300 Subject: [PATCH 43/84] Add tests for custom granularities in MySQL --- .../test/integration/mysql/MySqlDbRunner.js | 15 +- .../mysql/custom-granularities.test.ts | 859 ++++++++++++++++++ 2 files changed, 873 insertions(+), 1 deletion(-) create mode 100644 packages/cubejs-schema-compiler/test/integration/mysql/custom-granularities.test.ts diff --git a/packages/cubejs-schema-compiler/test/integration/mysql/MySqlDbRunner.js b/packages/cubejs-schema-compiler/test/integration/mysql/MySqlDbRunner.js index 41cc906f36533..faf678be642c3 100644 --- a/packages/cubejs-schema-compiler/test/integration/mysql/MySqlDbRunner.js +++ b/packages/cubejs-schema-compiler/test/integration/mysql/MySqlDbRunner.js @@ -1,8 +1,8 @@ import { promisify } from 'util'; import { GenericContainer } from 'testcontainers'; import mysql from 'mysql'; - import { BaseDbRunner } from '../utils/BaseDbRunner'; +import { MysqlQuery } from '../../../src'; export class MySqlDbRunner extends BaseDbRunner { async connectionLazyInit(port) { @@ -73,6 +73,15 @@ export class MySqlDbRunner extends BaseDbRunner { (2, 1, 2), (3, 3, 6) `); + await query(`CREATE TEMPORARY TABLE numbers (num INT);`); + await query(` + INSERT INTO numbers (num) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9), + (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), + (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), + (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), + (40), (41), (42), (43), (44), (45), (46), (47), (48), (49), + (50), (51), (52), (53), (54), (55), (56), (57), (58), (59); + `); } password() { @@ -93,4 +102,8 @@ export class MySqlDbRunner extends BaseDbRunner { port() { return 3306; } + + newTestQuery(compilers, query) { + return new MysqlQuery(compilers, query); + } } diff --git a/packages/cubejs-schema-compiler/test/integration/mysql/custom-granularities.test.ts b/packages/cubejs-schema-compiler/test/integration/mysql/custom-granularities.test.ts new file mode 100644 index 0000000000000..a69c9a97c527e --- /dev/null +++ b/packages/cubejs-schema-compiler/test/integration/mysql/custom-granularities.test.ts @@ -0,0 +1,859 @@ +import { prepareYamlCompiler } from '../../unit/PrepareCompiler'; +import { MySqlDbRunner } from './MySqlDbRunner'; + +describe('Custom Granularities', () => { + jest.setTimeout(200000); + + const dbRunner = new MySqlDbRunner(); + + const { compiler, joinGraph, cubeEvaluator } = prepareYamlCompiler(` + cubes: + - name: orders + sql: > + SELECT + num + 1 AS order_id, + DATE_ADD(TIMESTAMP ('2024-01-01'), INTERVAL (num * 2) WEEK) AS created_at, + CASE + WHEN (num + 1) % 3 = 1 THEN 'processing' + WHEN (num + 1) % 3 = 2 THEN 'completed' + ELSE 'shipped' + END AS status + FROM numbers + + dimensions: + - name: order_id + sql: order_id + type: number + primary_key: true + public: true + + - name: status + sql: status + type: string + + - name: createdAt + sql: created_at + type: time + granularities: + - name: half_year + interval: 6 months + - name: half_year_by_1st_april + interval: 6 months + offset: 3 months + - name: two_weeks_by_friday + interval: 2 weeks + origin: '2024-08-23' + - name: one_hour_by_5min_offset + interval: 1 hour + offset: 5 minutes + - name: twenty_five_minutes + interval: 25 minutes + origin: '2024-01-01 10:15:00' + - name: fifteen_days_hours_minutes_seconds + interval: 15 days 3 hours 25 minutes 40 seconds + origin: '2024-01-01 10:15:00' + - name: fiscal_year_by_1st_feb + interval: 1 year + origin: '2024-02-01' + - name: fiscal_year_by_15th_march + interval: 1 year + origin: '2024-03-15' + + measures: + - name: count + type: count + + - name: rollingCountByTrailing3Months + type: count + rolling_window: + trailing: 3 months + + - name: rollingCountByLeading4Months + type: count + rolling_window: + leading: 4 months + + - name: rollingCountByUnbounded + type: count + rolling_window: + trailing: unbounded + `); + + it('works with half_year custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: 13, + orders__created_at_half_year: '2024-01-01 00:00:00.000', + }, + { + orders__count: 14, + orders__created_at_half_year: '2024-07-01 00:00:00.000', + }, + { + orders__count: 13, + orders__created_at_half_year: '2025-01-01 00:00:00.000', + }, + { + orders__count: 13, + orders__created_at_half_year: '2025-07-01 00:00:00.000', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000', + }, + { + orders__count: 15, + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000', + }, + { + orders__count: 13, + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000', + }, + { + orders__count: 13, + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000', + }, + { + orders__count: 7, + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__count: 4, + orders__created_at_half_year: '2024-01-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 5, + orders__created_at_half_year: '2024-01-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year: '2024-01-01 00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: 5, + orders__created_at_half_year: '2024-07-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 4, + orders__created_at_half_year: '2024-07-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 5, + orders__created_at_half_year: '2024-07-01 00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: 4, + orders__created_at_half_year: '2025-01-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 5, + orders__created_at_half_year: '2025-01-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year: '2025-01-01 00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: 5, + orders__created_at_half_year: '2025-07-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 4, + orders__created_at_half_year: '2025-07-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year: '2025-07-01 00:00:00.000', + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom with dimension granularity query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__count: 2, + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 2, + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 1, + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: 3, + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: 2, + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: 2, + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000', + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 13, + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 27, + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 40, + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 53, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 4, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 5, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 4, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 9, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 9, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 9, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 13, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 14, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 13, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 18, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 18, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 17, + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity with dimension with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 3, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 2, + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 7, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 7, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 6, + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 11, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 11, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 11, + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 15, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 16, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 15, + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 20, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 20, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000000', + orders__rolling_count_by_unbounded: 19, + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 6, + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 7, + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 7, + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 7, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 3, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 3, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 3, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 7, + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 7, + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 6, + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 6, + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000000', + orders__rolling_count_by_trailing3_months: 6, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 8, + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 8, + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 7, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 3, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 3, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-01-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 3, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 3, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 3, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2024-07-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 2, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 3, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-01-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 3, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: '2025-07-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 3, + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: '2023-10-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year_by_1st_april: '2024-04-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year_by_1st_april: '2024-10-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year_by_1st_april: '2025-04-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year_by_1st_april: '2025-10-01 00:00:00.000000', + orders__rolling_count_by_leading4_months: 1, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with fifteen_days_hours_minutes_seconds custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'fifteen_days_hours_minutes_seconds', + dateRange: ['2024-01-01', '2024-02-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: '2023-12-17 06:49:20.000', + }, + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: '2024-01-01 10:15:00.000', + }, + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: '2024-01-16 13:40:40.000', + }, + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: '2024-01-31 17:06:20.000', + }, + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: '2024-02-15 20:32:00.000', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); +}); From 8cd6c0de774545af795cf64e66bba0c0945efb55 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 13:15:12 +0300 Subject: [PATCH 44/84] =?UTF-8?q?move=20granularityFromIntervalString=20fr?= =?UTF-8?q?om=20BaseQuery=20=E2=86=92=20Granularity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/adapter/BaseQuery.js | 31 +----------- .../src/adapter/Granularity.ts | 48 +++++++++++++++++-- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index f8981907eb594..cb87e8bbf34a8 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -2770,13 +2770,13 @@ export class BaseQuery { if (granularity.granularityOffset) { // Example: DATE_TRUNC(interval, dimension - INTERVAL 'offset') + INTERVAL 'offset' dtDate = this.subtractInterval(dimension, granularity.granularityOffset); - dtDate = this.timeGroupedColumn(this.granularityFromInterval(granularity.granularityInterval), dtDate); + dtDate = this.timeGroupedColumn(granularity.granularityFromInterval(), dtDate); dtDate = this.addInterval(dtDate, granularity.granularityOffset); return dtDate; } - return this.timeGroupedColumn(this.granularityFromInterval(granularity.granularityInterval), dimension); + return this.timeGroupedColumn(granularity.granularityFromInterval(), dimension); } return this.dateBin(granularity.granularityInterval, dimension, granularity.originFormatted()); @@ -2793,33 +2793,6 @@ export class BaseQuery { return !(intParsed.length !== 2 || intParsed[0] !== '1'); } - /** - * Returns the smallest granularity for the provided interval string - * Interval may be presented as `1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds - * It is important to bubble up from the smallest, as this is used e.g. for minimum rollup granularity - * @param {string} interval - * @returns {string} - */ - granularityFromInterval(interval) { - if (interval.match(/second/)) { - return 'second'; - } else if (interval.match(/minute/)) { - return 'minute'; - } else if (interval.match(/hour/)) { - return 'hour'; - } else if (interval.match(/day/)) { - return 'day'; - } else if (interval.match(/week/)) { - return 'week'; - } else if (interval.match(/month/)) { - return 'month'; - } else if (interval.match(/quarter/)) { - return 'quarter'; - } else /* if (interval.match(/year/)) */ { - return 'year'; - } - } - /** * Evaluate alias for specific cube's property. * @param {string} name Property name. diff --git a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts index 20cdc289f281b..a43381a4d2c65 100644 --- a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts +++ b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts @@ -63,12 +63,12 @@ export class Granularity { if (this.granularityOffset) { return this.query.minGranularity( - this.query.granularityFromInterval(this.granularityInterval), - this.query.granularityFromInterval(this.granularityOffset) + this.granularityFromInterval(), + this.granularityFromOffset() ); } - return this.query.granularityFromInterval(this.granularityInterval); + return this.granularityFromInterval(); } public timeSeriesForInterval(dateRange: QueryDateRange, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] { @@ -84,6 +84,46 @@ export class Granularity { return this.granularity; } - return this.query.granularityFromInterval(this.granularityInterval); + return this.granularityFromInterval(); } + + /** + * Returns the smallest granularity for the granularityInterval + */ + public granularityFromInterval(): string { + return this.granularityFromIntervalString(this.granularityInterval); + } + + /** + * Returns the smallest granularity for the granularityOffset + */ + public granularityFromOffset(): string { + return this.granularityOffset ? this.granularityFromIntervalString(this.granularityOffset) : ''; + } + + /** + * Returns the smallest granularity for the provided interval string + * Interval may be presented as `1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds + * It is important to bubble up from the smallest, as this is used e.g. for minimum rollup granularity + */ + private granularityFromIntervalString(interval: string): string { + if (interval.match(/second/)) { + return 'second'; + } else if (interval.match(/minute/)) { + return 'minute'; + } else if (interval.match(/hour/)) { + return 'hour'; + } else if (interval.match(/day/)) { + return 'day'; + } else if (interval.match(/week/)) { + return 'week'; + } else if (interval.match(/month/)) { + return 'month'; + } else if (interval.match(/quarter/)) { + return 'quarter'; + } else /* if (interval.match(/year/)) */ { + return 'year'; + } + } + } From 32ee114fd4684684ede6694e880a1c70534b7d3d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 13:15:30 +0300 Subject: [PATCH 45/84] fix granularityFromIntervalString --- .../src/adapter/Granularity.ts | 28 +++++++++++-------- .../test/unit/base-query.test.ts | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts index a43381a4d2c65..dfeec44fff47e 100644 --- a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts +++ b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts @@ -107,23 +107,29 @@ export class Granularity { * It is important to bubble up from the smallest, as this is used e.g. for minimum rollup granularity */ private granularityFromIntervalString(interval: string): string { - if (interval.match(/second/)) { + const intervalParsed = parseSqlInterval(interval); + const intervalKeys = Object.keys(intervalParsed); + + if (intervalKeys.length === 1) { + return intervalKeys[0]; + } + + if (intervalParsed.second) { return 'second'; - } else if (interval.match(/minute/)) { + } else if (intervalParsed.minute) { return 'minute'; - } else if (interval.match(/hour/)) { + } else if (intervalParsed.hour) { return 'hour'; - } else if (interval.match(/day/)) { + } else if (intervalParsed.day) { return 'day'; - } else if (interval.match(/week/)) { - return 'week'; - } else if (interval.match(/month/)) { + } else if (intervalParsed.week) { + return 'day'; + } else if (intervalParsed.month) { + return 'month'; + } else if (intervalParsed.quarter) { // Only quarter+years possible return 'month'; - } else if (interval.match(/quarter/)) { - return 'quarter'; - } else /* if (interval.match(/year/)) */ { + } else /* if (intervalParsed.year) */ { return 'year'; } } - } diff --git a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts index d9e5e1d519f01..0b4a5096ffbac 100644 --- a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts @@ -436,7 +436,7 @@ describe('SQL Generation', () => { const queryString = queryAndParams[0]; expect(queryString.includes('date_trunc(\'quarter\'')).toBeTruthy(); expect(queryString.includes('cards__created_at_quarter')).toBeTruthy(); - expect(queryString.includes('date_trunc(\'month\'')).toBeTruthy(); + expect(queryString.includes('date_trunc(\'day\'')).toBeTruthy(); expect(queryString.includes('cards__created_at_month')).toBeTruthy(); }); From d198127874f5db4ab4cf1b7982f222e8d830b117 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 19:37:28 +0300 Subject: [PATCH 46/84] improve addInterval / subtractInterval in MS SQL Query (now supports intervals with >1 time units) --- .../src/adapter/MssqlQuery.ts | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts b/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts index f8334e102a76d..5908c28b35403 100644 --- a/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts @@ -1,7 +1,7 @@ import R from 'ramda'; import moment from 'moment-timezone'; -import { QueryAlias } from '@cubejs-backend/shared'; +import { QueryAlias, parseSqlInterval } from '@cubejs-backend/shared'; import { BaseQuery } from './BaseQuery'; import { BaseFilter } from './BaseFilter'; import { ParamAllocator } from './ParamAllocator'; @@ -70,11 +70,11 @@ export class MssqlQuery extends BaseQuery { return `TODATETIMEOFFSET(${field}, '${moment().tz(this.timezone).format('Z')}')`; } - public timeStampCast(value) { - return `CAST(${value} AS DATETIME2)`; // TODO + public timeStampCast(value: string) { + return this.dateTimeCast(value); } - public dateTimeCast(value) { + public dateTimeCast(value: string) { return `CAST(${value} AS DATETIME2)`; } @@ -178,15 +178,26 @@ export class MssqlQuery extends BaseQuery { )} date_to FROM (VALUES ${values}) ${this.asSyntaxTable} dates (date_from, date_to)`; } - public subtractInterval(date, interval) { - const amountInterval = interval.split(' ', 2); - const negativeInterval = (amountInterval[0]) * -1; - return `DATEADD(${amountInterval[1]}, ${negativeInterval}, ${date})`; + public subtractInterval(date: string, interval: string): string { + const intervalParsed = parseSqlInterval(interval); + let res = date; + + for (const [key, value] of Object.entries(intervalParsed)) { + res = `DATEADD(${key}, ${value * -1}, ${res})`; + } + + return res; } - public addInterval(date, interval) { - const amountInterval = interval.split(' ', 2); - return `DATEADD(${amountInterval[1]}, ${amountInterval[0]}, ${date})`; + public addInterval(date: string, interval: string): string { + const intervalParsed = parseSqlInterval(interval); + let res = date; + + for (const [key, value] of Object.entries(intervalParsed)) { + res = `DATEADD(${key}, ${value}, ${res})`; + } + + return res; } public sqlTemplates() { From fa8f60b55aa6c5bd35eea0c0902f21a6dc6a8843 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 19:37:45 +0300 Subject: [PATCH 47/84] Implement dateBin for MS SQL --- .../src/adapter/MssqlQuery.ts | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts b/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts index 5908c28b35403..f69f69b4e51b1 100644 --- a/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts @@ -78,10 +78,49 @@ export class MssqlQuery extends BaseQuery { return `CAST(${value} AS DATETIME2)`; } - public timeGroupedColumn(granularity, dimension) { + public timeGroupedColumn(granularity: string, dimension: string): string { return GRANULARITY_TO_INTERVAL[granularity](dimension); } + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point. + * The formula operates with seconds diffs so it won't produce human-expected dates aligned with offset date parts. + */ + public dateBin(interval: string, source: string, origin: string): string { + const beginOfTime = this.timeStampCast('DATEFROMPARTS(1970, 1, 1)'); + const timeUnit = this.diffTimeUnitForInterval(interval); + + // Need to explicitly cast one argument of floor to float to trigger correct sign logic + return `DATEADD(${timeUnit}, + FLOOR( + CAST(DATEDIFF(${timeUnit}, ${this.timeStampCast(`'${origin}'`)}, ${source}) AS FLOAT) / + DATEDIFF(${timeUnit}, ${beginOfTime}, ${this.addInterval(beginOfTime, interval)}) + ) * DATEDIFF(${timeUnit}, ${beginOfTime}, ${this.addInterval(beginOfTime, interval)}), + ${this.timeStampCast(`'${origin}'`)} + )`; + } + + private diffTimeUnitForInterval(interval: string): string { + if (/second/i.test(interval)) { + return 'second'; + } else if (/minute/i.test(interval)) { + return 'minute'; + } else if (/hour/i.test(interval)) { + return 'hour'; + } else if (/day/i.test(interval)) { + return 'day'; + } else if (/week/i.test(interval)) { + return 'day'; + } else if (/month/i.test(interval)) { + return 'month'; + } else if (/quarter/i.test(interval)) { + return 'month'; + } else /* if (/year/i.test(interval)) */ { + return 'year'; + } + } + public newParamAllocator(expressionParams) { return new MssqlParamAllocator(expressionParams); } From 98b64181e69487640b7a05c913c3a28d9b28263d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 19:38:05 +0300 Subject: [PATCH 48/84] Add tests for custom granularities in MS SQL --- .../test/integration/mssql/MSSqlDbRunner.js | 14 + .../mssql/custom-granularities.test.ts | 859 ++++++++++++++++++ 2 files changed, 873 insertions(+) create mode 100644 packages/cubejs-schema-compiler/test/integration/mssql/custom-granularities.test.ts diff --git a/packages/cubejs-schema-compiler/test/integration/mssql/MSSqlDbRunner.js b/packages/cubejs-schema-compiler/test/integration/mssql/MSSqlDbRunner.js index fcba4cdc58919..5c1b3aedb4d17 100644 --- a/packages/cubejs-schema-compiler/test/integration/mssql/MSSqlDbRunner.js +++ b/packages/cubejs-schema-compiler/test/integration/mssql/MSSqlDbRunner.js @@ -3,6 +3,7 @@ import { GenericContainer, Wait } from 'testcontainers'; import sql from 'mssql'; import { BaseDbRunner } from '../utils/BaseDbRunner'; +import { MssqlQuery } from '../../../src'; export class MSSqlDbRunner extends BaseDbRunner { async connectionLazyInit(port) { @@ -83,6 +84,15 @@ export class MSSqlDbRunner extends BaseDbRunner { (2, 1, 2), (3, 3, 6) `); + await query(`CREATE TABLE ##numbers (num INT);`); + await query(` + INSERT INTO ##numbers (num) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9), + (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), + (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), + (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), + (40), (41), (42), (43), (44), (45), (46), (47), (48), (49), + (50), (51), (52), (53), (54), (55), (56), (57), (58), (59); + `); } password() { @@ -128,4 +138,8 @@ export class MSSqlDbRunner extends BaseDbRunner { port() { return 1433; } + + newTestQuery(compilers, query) { + return new MssqlQuery(compilers, query); + } } diff --git a/packages/cubejs-schema-compiler/test/integration/mssql/custom-granularities.test.ts b/packages/cubejs-schema-compiler/test/integration/mssql/custom-granularities.test.ts new file mode 100644 index 0000000000000..762e88230af76 --- /dev/null +++ b/packages/cubejs-schema-compiler/test/integration/mssql/custom-granularities.test.ts @@ -0,0 +1,859 @@ +import { prepareYamlCompiler } from '../../unit/PrepareCompiler'; +import { MSSqlDbRunner } from './MSSqlDbRunner'; + +describe('Custom Granularities', () => { + jest.setTimeout(200000); + + const dbRunner = new MSSqlDbRunner(); + + const { compiler, joinGraph, cubeEvaluator } = prepareYamlCompiler(` + cubes: + - name: orders + sql: > + SELECT + num + 1 AS order_id, + DATEADD(WEEK, num * 2, '2024-01-01T00:00:00.000Z') AS created_at, + CASE + WHEN (num + 1) % 3 = 1 THEN 'processing' + WHEN (num + 1) % 3 = 2 THEN 'completed' + ELSE 'shipped' + END AS status + FROM ##numbers + + dimensions: + - name: order_id + sql: order_id + type: number + primary_key: true + public: true + + - name: status + sql: status + type: string + + - name: createdAt + sql: created_at + type: time + granularities: + - name: half_year + interval: 6 months + - name: half_year_by_1st_april + interval: 6 months + offset: 3 months + - name: two_weeks_by_friday + interval: 2 weeks + origin: '2024-08-23' + - name: one_hour_by_5min_offset + interval: 1 hour + offset: 5 minutes + - name: twenty_five_minutes + interval: 25 minutes + origin: '2024-01-01 10:15:00' + - name: fifteen_days_hours_minutes_seconds + interval: 15 days 3 hours 25 minutes 40 seconds + origin: '2024-01-01 10:15:00' + - name: fiscal_year_by_1st_feb + interval: 1 year + origin: '2024-02-01' + - name: fiscal_year_by_15th_march + interval: 1 year + origin: '2024-03-15' + + measures: + - name: count + type: count + + - name: rollingCountByTrailing3Months + type: count + rolling_window: + trailing: 3 months + + - name: rollingCountByLeading4Months + type: count + rolling_window: + leading: 4 months + + - name: rollingCountByUnbounded + type: count + rolling_window: + trailing: unbounded + `); + + it('works with half_year custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'UTC' + }, + [ + { + orders__count: 13, + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + }, + { + orders__count: 14, + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + }, + { + orders__count: 13, + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + }, + { + orders__count: 13, + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: 7, + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + }, + { + orders__count: 13, + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + }, + { + orders__count: 13, + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + }, + { + orders__count: 13, + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + }, + { + orders__count: 7, + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'UTC' + }, + [ + { + orders__count: 4, + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 5, + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + { + orders__count: 5, + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 4, + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 5, + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + { + orders__count: 4, + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 5, + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + { + orders__count: 5, + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 4, + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom with dimension granularity query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__count: 2, + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 3, + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 2, + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 5, + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 4, + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + { + orders__count: 3, + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + orders__status: 'completed', + }, + { + orders__count: 2, + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + orders__status: 'processing', + }, + { + orders__count: 2, + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 13, + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 27, + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 40, + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 53, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 4, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 5, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 4, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 9, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 9, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 9, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 13, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 14, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 13, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 18, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 18, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 17, + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity with dimension with unbounded rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByUnbounded'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 3, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 2, + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 7, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 7, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 6, + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 11, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 11, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 11, + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 15, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 16, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 15, + orders__status: 'shipped', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 20, + orders__status: 'completed', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 20, + orders__status: 'processing', + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + orders__rolling_count_by_unbounded: 19, + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 6, + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 7, + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 7, + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 7, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 3, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 3, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 3, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 2, + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions with trailing rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByTrailing3Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 7, + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 7, + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 6, + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 6, + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + orders__rolling_count_by_trailing3_months: 6, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity w/o dimensions with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 8, + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 8, + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 7, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 3, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 3, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2024-01-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 3, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 3, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 3, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2024-07-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 2, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 3, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2025-01-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 3, + orders__status: 'shipped', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 2, + orders__status: 'completed', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 2, + orders__status: 'processing', + }, + { + orders__created_at_half_year: new Date('2025-07-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 3, + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions with leading rolling window query', async () => dbRunner.runQueryTest( + { + measures: ['orders.rollingCountByLeading4Months'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__created_at_half_year_by_1st_april: new Date('2023-10-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-04-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year_by_1st_april: new Date('2024-10-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-04-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 9, + }, + { + orders__created_at_half_year_by_1st_april: new Date('2025-10-01T00:00:00.000Z'), + orders__rolling_count_by_leading4_months: 1, + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with fifteen_days_hours_minutes_seconds custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'fifteen_days_hours_minutes_seconds', + dateRange: ['2024-01-01', '2024-02-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: new Date('2023-12-17T06:49:20.000Z'), + }, + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: new Date('2024-01-01T10:15:00.000Z'), + }, + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: new Date('2024-01-16T13:40:40.000Z'), + }, + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: new Date('2024-01-31T17:06:20.000Z'), + }, + { + orders__count: 1, + orders__created_at_fifteen_days_hours_minutes_seconds: new Date('2024-02-15T20:32:00.000Z'), + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); +}); From 39c4b083bc5552e39409ef63e0c7606167201778 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 21:46:10 +0300 Subject: [PATCH 49/84] fix 2 granularities test (actually revert the bad changes back) --- packages/cubejs-schema-compiler/test/unit/base-query.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts index 0b4a5096ffbac..d9e5e1d519f01 100644 --- a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts @@ -436,7 +436,7 @@ describe('SQL Generation', () => { const queryString = queryAndParams[0]; expect(queryString.includes('date_trunc(\'quarter\'')).toBeTruthy(); expect(queryString.includes('cards__created_at_quarter')).toBeTruthy(); - expect(queryString.includes('date_trunc(\'day\'')).toBeTruthy(); + expect(queryString.includes('date_trunc(\'month\'')).toBeTruthy(); expect(queryString.includes('cards__created_at_month')).toBeTruthy(); }); From 8ebc08c081086e1c0822a072ba02299b1a10bd50 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 21:46:34 +0300 Subject: [PATCH 50/84] add comment for dateBin in postgresql query --- .../cubejs-schema-compiler/src/adapter/PostgresQuery.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts index b5421b6c5d86a..b2286a135090e 100644 --- a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts @@ -31,8 +31,14 @@ export class PostgresQuery extends BaseQuery { return `date_trunc('${GRANULARITY_TO_INTERVAL[granularity]}', ${dimension})`; } + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point. + * Postgres operates with whole intervals as is without measuring them in plain seconds, + * so the resulting date will be human-expected aligned with intervals. + * This implementation should also work for AWS RedShift. + */ public dateBin(interval: string, source: string, origin: string): string { - // Should also work for AWS RedShift return `'${origin}'::timestamp + INTERVAL '${interval}' * FLOOR( EXTRACT(EPOCH FROM (${source} - '${origin}'::timestamp)) / From 3ed167fc5f6b9699ede7ff9711ec2ae2ff94c675 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 21:47:49 +0300 Subject: [PATCH 51/84] Implement dateBin for Databricks --- .../src/DatabricksQuery.ts | 75 +++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/packages/cubejs-databricks-jdbc-driver/src/DatabricksQuery.ts b/packages/cubejs-databricks-jdbc-driver/src/DatabricksQuery.ts index 6262333eb28ba..a9959926f8e56 100644 --- a/packages/cubejs-databricks-jdbc-driver/src/DatabricksQuery.ts +++ b/packages/cubejs-databricks-jdbc-driver/src/DatabricksQuery.ts @@ -1,5 +1,6 @@ import R from 'ramda'; import { BaseFilter, BaseQuery } from '@cubejs-backend/schema-compiler'; +import { parseSqlInterval } from '@cubejs-backend/shared'; const GRANULARITY_TO_INTERVAL: Record = { day: 'day', @@ -61,22 +62,84 @@ export class DatabricksQuery extends BaseQuery { return `from_utc_timestamp(${value}, 'UTC')`; // TODO } - public subtractInterval(date: string, interval: string) { - const [number, type] = this.parseInterval(interval); + public subtractInterval(date: string, interval: string): string { + const intervalParsed = parseSqlInterval(interval); + let res = date; - return `(${date} - INTERVAL '${number}' ${type})`; + for (const [key, value] of Object.entries(intervalParsed)) { + res = `(${res} - INTERVAL '${value}' ${key})`; + } + + return res; } - public addInterval(date: string, interval: string) { - const [number, type] = this.parseInterval(interval); + public addInterval(date: string, interval: string): string { + const intervalParsed = parseSqlInterval(interval); + let res = date; - return `(${date} + INTERVAL '${number}' ${type})`; + for (const [key, value] of Object.entries(intervalParsed)) { + res = `(${res} + INTERVAL '${value}' ${key})`; + } + + return res; } public timeGroupedColumn(granularity: string, dimension: string): string { return `date_trunc('${GRANULARITY_TO_INTERVAL[granularity]}', ${dimension})`; } + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point. + */ + public dateBin(interval: string, source: string, origin: string): string { + const [intervalFormatted, timeUnit] = this.formatInterval(interval); + const beginOfTime = this.dateTimeCast('\'1970-01-01T00:00:00\''); + + return `${this.timeStampCast(`'${origin}'`)} + INTERVAL ${intervalFormatted} * + floor( + date_diff(${timeUnit}, ${this.timeStampCast(`'${origin}'`)}, ${source}) / + date_diff(${timeUnit}, ${beginOfTime}, ${beginOfTime} + INTERVAL ${intervalFormatted}) + )`; + } + + /** + * The input interval with (possible) plural units, like "2 years", "3 months", "4 weeks", "5 days"... + * will be converted to Databricks dialect. + * @see https://docs.databricks.com/en/sql/language-manual/data-types/interval-type.html + * It returns a tuple of (formatted interval, timeUnit to use in datediff functions) + */ + private formatInterval(interval: string): [string, string] { + const intervalParsed = parseSqlInterval(interval); + const intKeys = Object.keys(intervalParsed).length; + + if (intervalParsed.year && intKeys === 1) { + return [`'${intervalParsed.year}' YEAR`, 'YEAR']; + } else if (intervalParsed.year && intervalParsed.month && intKeys === 2) { + return [`'${intervalParsed.year}-${intervalParsed.month}' YEAR TO MONTH`, 'MONTH']; + } else if (intervalParsed.month && intKeys === 1) { + return [`'${intervalParsed.month}' MONTH`, 'MONTH']; + } else if (intervalParsed.day && intKeys === 1) { + return [`'${intervalParsed.day}' DAY`, 'DAY']; + } else if (intervalParsed.day && intervalParsed.hour && intKeys === 2) { + return [`'${intervalParsed.day} ${intervalParsed.hour}' DAY TO HOUR`, 'HOUR']; + } else if (intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intKeys === 3) { + return [`'${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}' DAY TO MINUTE`, 'MINUTE']; + } else if (intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intervalParsed.second && intKeys === 4) { + return [`'${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}:${intervalParsed.second}' DAY TO SECOND`, 'SECOND']; + } else if (intervalParsed.hour && intervalParsed.minute && intKeys === 2) { + return [`'${intervalParsed.hour}:${intervalParsed.minute}' HOUR TO MINUTE`, 'MINUTE']; + } else if (intervalParsed.hour && intervalParsed.minute && intervalParsed.second && intKeys === 3) { + return [`'${intervalParsed.hour}:${intervalParsed.minute}:${intervalParsed.second}' HOUR TO SECOND`, 'SECOND']; + } else if (intervalParsed.minute && intervalParsed.second && intKeys === 2) { + return [`'${intervalParsed.minute}:${intervalParsed.second}' MINUTE TO SECOND`, 'SECOND']; + } + + // No need to support microseconds. + + throw new Error(`Cannot transform interval expression "${interval}" to Databricks dialect`); + } + public escapeColumnName(name: string) { return `\`${name}\``; } From 58f58904c618cfea0e6cbf5f2e45ede1e6f6fde2 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 27 Aug 2024 23:14:52 +0300 Subject: [PATCH 52/84] Implement dateBin for DuckDB --- .../cubejs-duckdb-driver/src/DuckDBQuery.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/cubejs-duckdb-driver/src/DuckDBQuery.ts b/packages/cubejs-duckdb-driver/src/DuckDBQuery.ts index c4dad625eeb1c..ce5fc5ff4edd5 100644 --- a/packages/cubejs-duckdb-driver/src/DuckDBQuery.ts +++ b/packages/cubejs-duckdb-driver/src/DuckDBQuery.ts @@ -23,10 +23,51 @@ export class DuckDBQuery extends BaseQuery { return `timezone('${this.timezone}', ${field}::timestamptz)`; } + public timeStampCast(value: string) { + return `'${value}'::TIMESTAMPTZ`; + } + public timeGroupedColumn(granularity: string, dimension: string) { return GRANULARITY_TO_INTERVAL[granularity](dimension); } + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point. + * DuckDB operates with whole intervals as is without measuring them in plain seconds, + * so the resulting date will be human-expected aligned with intervals. + */ + public dateBin(interval: string, source: string, origin: string): string { + const timeUnit = this.diffTimeUnitForInterval(interval); + const beginOfTime = this.timeStampCast('1970-01-01 00:00:00.000'); + + return `${this.timeStampCast(origin)}' + INTERVAL '${interval}' * + floor( + date_diff('${timeUnit}', ${this.timeStampCast(origin)}, ${source}) / + date_diff('${timeUnit}', ${beginOfTime}, ${beginOfTime} + INTERVAL '${interval}') + )::int`; + } + + private diffTimeUnitForInterval(interval: string): string { + if (/second/i.test(interval)) { + return 'second'; + } else if (/minute/i.test(interval)) { + return 'minute'; + } else if (/hour/i.test(interval)) { + return 'hour'; + } else if (/day/i.test(interval)) { + return 'day'; + } else if (/week/i.test(interval)) { + return 'day'; + } else if (/month/i.test(interval)) { + return 'month'; + } else if (/quarter/i.test(interval)) { + return 'month'; + } else /* if (/year/i.test(interval)) */ { + return 'year'; + } + } + public countDistinctApprox(sql: string) { return `approx_count_distinct(${sql})`; } From e4c18d922c304978003d1c7779c6999c4490c72b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 12:34:33 +0300 Subject: [PATCH 53/84] implement dateBin for BigQuery --- .../src/adapter/BigqueryQuery.ts | 82 ++++++++++++++++++- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts b/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts index 3140da436c496..33d89ebb894e6 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts @@ -1,3 +1,4 @@ +import { parseSqlInterval } from '@cubejs-backend/shared'; import { BaseQuery } from './BaseQuery'; import { BaseFilter } from './BaseFilter'; import { BaseTimeDimension } from './BaseTimeDimension'; @@ -60,6 +61,79 @@ export class BigqueryQuery extends BaseQuery { return `DATETIME_TRUNC(${dimension}, ${GRANULARITY_TO_INTERVAL[granularity]})`; } + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point. + * BigQuery operates with whole intervals as is without measuring them in plain seconds. + */ + public dateBin(interval: string, source: string, origin: string): string { + const [intervalFormatted, timeUnit] = this.formatInterval(interval); + const beginOfTime = this.dateTimeCast('\'1970-01-01T00:00:00\''); + + return `${this.dateTimeCast(`'${origin}'`)} + INTERVAL ${intervalFormatted} * + CAST(FLOOR( + DATETIME_DIFF(${source}, ${this.dateTimeCast(`'${origin}'`)}, ${timeUnit}) / + DATETIME_DIFF(${beginOfTime} + INTERVAL ${intervalFormatted}, ${beginOfTime}, ${timeUnit}) + ) AS INT64))`; + } + + /** + * The input interval with (possible) plural units, like "2 years", "3 months", "4 weeks", "5 days"... + * will be converted to BigQuery dialect. + * @see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#interval_type + * It returns a tuple of (formatted interval, timeUnit to use in datediff functions) + */ + private formatInterval(interval: string): [string, string] { + const intervalParsed = parseSqlInterval(interval); + const intKeys = Object.keys(intervalParsed).length; + + if (intervalParsed.year && intKeys === 1) { + return [`${intervalParsed.year} YEAR`, 'YEAR']; + } else if (intervalParsed.year && intervalParsed.month && intKeys === 2) { + return [`'${intervalParsed.year}-${intervalParsed.month}' YEAR TO MONTH`, 'MONTH']; + } else if (intervalParsed.year && intervalParsed.month && intervalParsed.day && intKeys === 3) { + return [`'${intervalParsed.year}-${intervalParsed.month} ${intervalParsed.day}' YEAR TO DAY`, 'DAY']; + } else if (intervalParsed.year && intervalParsed.month && intervalParsed.day && intervalParsed.hour && intKeys === 4) { + return [`'${intervalParsed.year}-${intervalParsed.month} ${intervalParsed.day} ${intervalParsed.hour}' YEAR TO HOUR`, 'HOUR']; + } else if (intervalParsed.year && intervalParsed.month && intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intKeys === 5) { + return [`'${intervalParsed.year}-${intervalParsed.month} ${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}' YEAR TO MINUTE`, 'MINUTE']; + } else if (intervalParsed.year && intervalParsed.month && intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intervalParsed.second && intKeys === 6) { + return [`'${intervalParsed.year}-${intervalParsed.month} ${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}:${intervalParsed.second}' YEAR TO SECOND`, 'SECOND']; + } else if (intervalParsed.quarter && intKeys === 1) { + return [`${intervalParsed.quarter} QUARTER`, 'QUARTER']; + } else if (intervalParsed.month && intKeys === 1) { + return [`${intervalParsed.month} MONTH`, 'MONTH']; + } else if (intervalParsed.month && intervalParsed.day && intKeys === 2) { + return [`'${intervalParsed.month} ${intervalParsed.day}' MONTH TO DAY`, 'DAY']; + } else if (intervalParsed.month && intervalParsed.day && intervalParsed.hour && intKeys === 3) { + return [`'${intervalParsed.month} ${intervalParsed.day} ${intervalParsed.hour}' MONTH TO HOUR`, 'HOUR']; + } else if (intervalParsed.month && intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intKeys === 4) { + return [`'${intervalParsed.month} ${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}' MONTH TO MINUTE`, 'MINUTE']; + } else if (intervalParsed.month && intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intervalParsed.second && intKeys === 5) { + return [`'${intervalParsed.month} ${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}:${intervalParsed.second}' MONTH TO SECOND`, 'SECOND']; + } else if (intervalParsed.week && intKeys === 1) { + return [`${intervalParsed.week} WEEK`, 'DAY']; + } else if (intervalParsed.day && intKeys === 1) { + return [`${intervalParsed.day} DAY`, 'DAY']; + } else if (intervalParsed.day && intervalParsed.hour && intKeys === 2) { + return [`'${intervalParsed.day} ${intervalParsed.hour}' DAY TO HOUR`, 'HOUR']; + } else if (intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intKeys === 3) { + return [`'${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}' DAY TO MINUTE`, 'MINUTE']; + } else if (intervalParsed.day && intervalParsed.hour && intervalParsed.minute && intervalParsed.second && intKeys === 4) { + return [`'${intervalParsed.day} ${intervalParsed.hour}:${intervalParsed.minute}:${intervalParsed.second}' DAY TO SECOND`, 'SECOND']; + } else if (intervalParsed.hour && intervalParsed.minute && intKeys === 2) { + return [`'${intervalParsed.hour}:${intervalParsed.minute}' HOUR TO MINUTE`, 'MINUTE']; + } else if (intervalParsed.hour && intervalParsed.minute && intervalParsed.second && intKeys === 3) { + return [`'${intervalParsed.hour}:${intervalParsed.minute}:${intervalParsed.second}' HOUR TO SECOND`, 'SECOND']; + } else if (intervalParsed.minute && intervalParsed.second && intKeys === 2) { + return [`'${intervalParsed.minute}:${intervalParsed.second}' MINUTE TO SECOND`, 'SECOND']; + } + + // No need to support microseconds. + + throw new Error(`Cannot transform interval expression "${interval}" to BigQuery dialect`); + } + public newFilter(filter) { return new BigqueryFilter(this, filter); } @@ -104,19 +178,19 @@ export class BigqueryQuery extends BaseQuery { } public subtractInterval(date, interval) { - return `DATETIME_SUB(${date}, INTERVAL ${interval})`; + return `DATETIME_SUB(${date}, INTERVAL ${this.formatInterval(interval)[0]})`; } public addInterval(date, interval) { - return `DATETIME_ADD(${date}, INTERVAL ${interval})`; + return `DATETIME_ADD(${date}, INTERVAL ${this.formatInterval(interval)[0]})`; } public subtractTimestampInterval(date, interval) { - return `TIMESTAMP_SUB(${date}, INTERVAL ${interval})`; + return `TIMESTAMP_SUB(${date}, INTERVAL ${this.formatInterval(interval)[0]})`; } public addTimestampInterval(date, interval) { - return `TIMESTAMP_ADD(${date}, INTERVAL ${interval})`; + return `TIMESTAMP_ADD(${date}, INTERVAL ${this.formatInterval(interval)[0]})`; } public nowTimestampSql() { From 60756e4a2f19abefa533fe9228764da317085bfd Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 13:45:40 +0300 Subject: [PATCH 54/84] extend testing fixtures with custom granularities --- .../fixtures/_schemas.json | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-testing-drivers/fixtures/_schemas.json b/packages/cubejs-testing-drivers/fixtures/_schemas.json index 1d3c44feced4f..74f31cc4ce9c9 100644 --- a/packages/cubejs-testing-drivers/fixtures/_schemas.json +++ b/packages/cubejs-testing-drivers/fixtures/_schemas.json @@ -90,6 +90,32 @@ "sql": "order_date", "type": "time" }, + { + "name": "customOrderDateNoPreAgg", + "sql": "order_date", + "type": "time", + "granularities": [ + { + "name": "half_year", + "interval": "6 months" + }, + { + "name": "half_year_by_1st_april", + "interval": "6 months", + "offset": "3 months" + }, + { + "name": "two_mo_by_feb", + "interval": "2 months", + "origin": "2020-02-01 10:00:00" + }, + { + "name": "three_months_by_march", + "interval": "3 month 3 days 3 hours", + "origin": "2020-03-15" + } + ] + }, { "name": "completedDate", "sql": "completed_date", @@ -167,6 +193,27 @@ "sql": "profit", "type": "sum", "shown": false + }, + { + "name": "rollingCountByTrailing", + "type": "count", + "rollingWindow": { + "trailing": "2 month" + } + }, + { + "name": "rollingCountByLeading", + "type": "count", + "rollingWindow": { + "leading": "3 month" + } + }, + { + "name": "rollingCountByUnbounded", + "type": "count", + "rollingWindow": { + "trailing": "unbounded" + } } ] }, @@ -367,4 +414,4 @@ ] } ] -} \ No newline at end of file +} From 700dced1ae4ae86900b40aa486191f562d033fe5 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 17:58:47 +0300 Subject: [PATCH 55/84] fix TimeDimensionGranularity type to allow custom granularities --- packages/cubejs-client-core/index.d.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-client-core/index.d.ts b/packages/cubejs-client-core/index.d.ts index ce2b4c6e8ee46..1483af07dfb61 100644 --- a/packages/cubejs-client-core/index.d.ts +++ b/packages/cubejs-client-core/index.d.ts @@ -787,7 +787,9 @@ declare module '@cubejs-client/core' { | 'afterDate' | 'afterOrOnDate'; - export type TimeDimensionGranularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'; + export type TimeDimensionPredefinedGranularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'; + + export type TimeDimensionGranularity = TimeDimensionPredefinedGranularity | string; export type DateRange = string | [string, string]; From 6350fc10091684202ff9a598c2933a11a2555e06 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 18:29:45 +0300 Subject: [PATCH 56/84] Add more unit tests for granularities --- .../test/unit/base-query.test.ts | 36 +++++++++++++++++++ .../cubejs-schema-compiler/test/unit/utils.ts | 8 +++++ 2 files changed, 44 insertions(+) diff --git a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts index d9e5e1d519f01..0f28474a518a7 100644 --- a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts @@ -96,6 +96,42 @@ describe('SQL Generation', () => { filters: [], timezone: 'Europe/Kyiv' }, + { + measures: [ + 'orders.count' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_march', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [], + filters: [], + timezone: 'Europe/Kyiv' + }, + { + measures: [ + 'orders.count' + ], + timeDimensions: [ + { + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_june', + dateRange: [ + '2020-01-01', + '2021-12-31' + ] + } + ], + dimensions: [], + filters: [], + timezone: 'Europe/Kyiv' + }, { measures: [ 'orders.rollingCountByUnbounded' diff --git a/packages/cubejs-schema-compiler/test/unit/utils.ts b/packages/cubejs-schema-compiler/test/unit/utils.ts index 6623fdcd305fd..ce7fc0e99ade2 100644 --- a/packages/cubejs-schema-compiler/test/unit/utils.ts +++ b/packages/cubejs-schema-compiler/test/unit/utils.ts @@ -92,6 +92,14 @@ export function createCubeSchemaWithCustomGranularities(name: string): string { half_year_by_1st_april: { interval: '6 months', offset: '3 months' + }, + half_year_by_1st_march: { + interval: '6 months', + origin: '2020-03-01' + }, + half_year_by_1st_june: { + interval: '6 months', + origin: '2020-06-01 10:00:00' } } }, From eaac9fa3a82f7cb9d9eb5c13c6a0a9f2185b0cca Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 20:43:01 +0300 Subject: [PATCH 57/84] fix writing yaml with test cubes definitions containing origin with dashes --- packages/cubejs-testing-drivers/src/helpers/getSchemaPath.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-testing-drivers/src/helpers/getSchemaPath.ts b/packages/cubejs-testing-drivers/src/helpers/getSchemaPath.ts index bc060597572f3..45401abc025c8 100644 --- a/packages/cubejs-testing-drivers/src/helpers/getSchemaPath.ts +++ b/packages/cubejs-testing-drivers/src/helpers/getSchemaPath.ts @@ -67,7 +67,7 @@ export function getSchemaPath(type: string, suf?: string): [path: string, file: fs.writeFileSync( path.resolve(_path, _file), - YAML.stringify(_content), + YAML.stringify(_content, { version: '1.1' }), ); return [_path, _file]; } From 251ded85358e00b002510eaed36639a8ea5f13a2 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 21:19:58 +0300 Subject: [PATCH 58/84] add tests for custom granularities in testing drivers --- .../src/tests/testQueries.ts | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/packages/cubejs-testing-drivers/src/tests/testQueries.ts b/packages/cubejs-testing-drivers/src/tests/testQueries.ts index edf8ca70d6643..acec3d7afc698 100644 --- a/packages/cubejs-testing-drivers/src/tests/testQueries.ts +++ b/packages/cubejs-testing-drivers/src/tests/testQueries.ts @@ -1609,6 +1609,147 @@ export function testQueries(type: string, { includeIncrementalSchemaSuite, exten expect(response.rawData()).toMatchSnapshot(); }); + execute('querying custom granularities ECommerce: count by half_year + no dimension', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.count', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'half_year', + dateRange: ['2020-01-01', '2020-12-31'], + }], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + + execute('querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.count', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'half_year_by_1st_april', + dateRange: ['2020-01-01', '2020-12-31'], + }], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + + execute('querying custom granularities ECommerce: count by three_months_by_march + no dimension', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.count', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'three_months_by_march', + dateRange: ['2020-01-01', '2020-12-31'], + }], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + + execute('querying custom granularities ECommerce: count by half_year + dimension', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.count', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'half_year', + dateRange: ['2020-01-01', '2020-12-31'], + }], + dimensions: ['ECommerce.city'], + order: [ + ['ECommerce.customOrderDateNoPreAgg', 'asc'], + ['ECommerce.city', 'asc'] + ], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + + execute('querying custom granularities ECommerce: count by half_year_by_1st_april + dimension', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.count', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'half_year_by_1st_april', + dateRange: ['2020-01-01', '2020-12-31'], + }], + dimensions: ['ECommerce.city'], + order: [ + ['ECommerce.customOrderDateNoPreAgg', 'asc'], + ['ECommerce.city', 'asc'] + ], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + + execute('querying custom granularities ECommerce: count by three_months_by_march + dimension', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.count', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'three_months_by_march', + dateRange: ['2020-01-01', '2020-12-31'], + }], + dimensions: ['ECommerce.city'], + order: [ + ['ECommerce.customOrderDateNoPreAgg', 'asc'], + ['ECommerce.city', 'asc'] + ], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + + execute('querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.rollingCountByUnbounded', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'two_mo_by_feb', + dateRange: ['2020-01-01', '2020-12-31'], + }], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + + execute('querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.rollingCountByTrailing', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'two_mo_by_feb', + dateRange: ['2020-01-01', '2020-12-31'], + }], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + + execute('querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading', async () => { + const response = await client.load({ + measures: [ + 'ECommerce.rollingCountByLeading', + ], + timeDimensions: [{ + dimension: 'ECommerce.customOrderDateNoPreAgg', + granularity: 'two_mo_by_feb', + dateRange: ['2020-01-01', '2020-12-31'], + }], + }); + expect(response.rawData()).toMatchSnapshot(); + }); + if (includeIncrementalSchemaSuite) { incrementalSchemaLoadingSuite(execute, () => driver, tables); } From 7775927f7e5daa8e2650a8bd4996849f13664de5 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 21:20:19 +0300 Subject: [PATCH 59/84] skip custom granularities tests for athena --- .../cubejs-testing-drivers/fixtures/athena.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-testing-drivers/fixtures/athena.json b/packages/cubejs-testing-drivers/fixtures/athena.json index a453044d8b32d..a11390cc8ecfe 100644 --- a/packages/cubejs-testing-drivers/fixtures/athena.json +++ b/packages/cubejs-testing-drivers/fixtures/athena.json @@ -133,7 +133,7 @@ "---------------------------------------", "---------------------------------------", - "SKIPED FOR ALL ", + "SKIPPED FOR ALL ", "---------------------------------------", "querying Products: dimensions -- doesn't work wo ordering", "querying ECommerce: total quantity, avg discount, total sales, total profit by product + order + total -- rounding in athena", @@ -148,6 +148,19 @@ "--------------------", "querying BigECommerce: rolling window by 2 week", + "---------------------------------------", + "Custom Granularities ", + "---------------------------------------", + "querying custom granularities ECommerce: count by half_year + no dimension", + "querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension", + "querying custom granularities ECommerce: count by three_months_by_march + no dimension", + "querying custom granularities ECommerce: count by half_year + dimension", + "querying custom granularities ECommerce: count by half_year_by_1st_april + dimension", + "querying custom granularities ECommerce: count by three_months_by_march + dimension", + "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded", + "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing", + "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading", + "SKIPPED SQL API (Need work)", "---------------------------------------", "SQL API: Simple Rollup", From fb82b5c87b13b5afe8c33327ca23f1bd3afc56c7 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 21:20:32 +0300 Subject: [PATCH 60/84] skip custom granularities tests for clickhouse --- .../fixtures/clickhouse.json | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-testing-drivers/fixtures/clickhouse.json b/packages/cubejs-testing-drivers/fixtures/clickhouse.json index d085a30082883..6d0f3e62a25da 100644 --- a/packages/cubejs-testing-drivers/fixtures/clickhouse.json +++ b/packages/cubejs-testing-drivers/fixtures/clickhouse.json @@ -142,7 +142,7 @@ ] }, "skip": [ - "SKIPED FOR ALL", + "SKIPPED FOR ALL", "---------------------------------------", "querying Products: dimensions -- doesn't work wo ordering", "querying ECommerce: total quantity, avg discount, total sales, total profit by product + order + total -- rounding in athena", @@ -157,6 +157,19 @@ "--------------------", "querying BigECommerce: rolling window by 2 day", "querying BigECommerce: rolling window by 2 week", - "querying BigECommerce: rolling window by 2 month" + "querying BigECommerce: rolling window by 2 month", + + "---------------------------------------", + "Custom Granularities ", + "---------------------------------------", + "querying custom granularities ECommerce: count by half_year + no dimension", + "querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension", + "querying custom granularities ECommerce: count by three_months_by_march + no dimension", + "querying custom granularities ECommerce: count by half_year + dimension", + "querying custom granularities ECommerce: count by half_year_by_1st_april + dimension", + "querying custom granularities ECommerce: count by three_months_by_march + dimension", + "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded", + "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing", + "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading" ] } From 9e4081ff8c40f5fe96da71fb5eccd954d534c4dc Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 22:06:12 +0300 Subject: [PATCH 61/84] add test snapshots for custom granularities tests for postgres --- .../__snapshots__/postgres-full.test.ts.snap | 769 ++++++++++++++++++ 1 file changed, 769 insertions(+) diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/postgres-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/postgres-full.test.ts.snap index 5b2af75a5d99f..91e577c321eb2 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/postgres-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/postgres-full.test.ts.snap @@ -15637,3 +15637,772 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2019-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "9", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2019-07-01T00:00:00.000", + }, + Object { + "ECommerce.count": "17", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": "26", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "6", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by three_months_by_march + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by three_months_by_march + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.count": "12", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "8", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "19", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "10", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "28", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, +] +`; From 4d939f86b6c795828b38ea0cbb13a97811ab004f Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 22:07:31 +0300 Subject: [PATCH 62/84] skip custom granularities tests for mysql (some) --- packages/cubejs-testing-drivers/fixtures/mysql.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-testing-drivers/fixtures/mysql.json b/packages/cubejs-testing-drivers/fixtures/mysql.json index 2fd95b3f49e4d..2bbdf1bba1a24 100644 --- a/packages/cubejs-testing-drivers/fixtures/mysql.json +++ b/packages/cubejs-testing-drivers/fixtures/mysql.json @@ -116,7 +116,7 @@ "---------------------------------------", "querying ECommerce: dimensions + order + total + offset", "querying Customers: dimensions + order + total + offset", - "SKIPED FOR ALL", + "SKIPPED FOR ALL", "---------------------------------------", "querying Products: dimensions -- doesn't work wo ordering", "querying ECommerce: total quantity, avg discount, total sales, total profit by product + order + total -- rounding in athena", @@ -128,6 +128,12 @@ "querying BigECommerce: null sum", "querying BigECommerce: null boolean", + "---------------------------------------", + "Custom Granularities ", + "---------------------------------------", + "querying custom granularities ECommerce: count by three_months_by_march + no dimension", + "querying custom granularities ECommerce: count by three_months_by_march + dimension", + "SKIPPED SQL API (Need work)", "---------------------------------------", "SQL API: reuse params", From 49a2665c5f85c98b87b152139ed10930ed792ac7 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 22:39:06 +0300 Subject: [PATCH 63/84] add test snapshots for custom granularities tests for ms sql --- .../__snapshots__/mssql-full.test.ts.snap | 764 ++++++++++++++++++ 1 file changed, 764 insertions(+) diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/mssql-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/mssql-full.test.ts.snap index f17621df02c0c..e5c4f287f4873 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/mssql-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/mssql-full.test.ts.snap @@ -5343,3 +5343,767 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 4, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 9, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 18, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": 26, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 4, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 5, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": 19, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": 20, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by three_months_by_march + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by three_months_by_march + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.count": 12, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.count": 19, + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 8, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 12, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 6, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 19, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 16, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 12, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 10, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 16, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/mssql-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 6, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 18, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 18, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 28, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 44, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 44, + }, +] +`; From 0186c6bd677d7eca13267c2f5a54c039ece5f2d0 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 22:39:28 +0300 Subject: [PATCH 64/84] skip custom granularities tests for mssql (some) From 1224869df1f2577e53627ddc85399063e59dce8b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 22:48:30 +0300 Subject: [PATCH 65/84] add test snapshots for custom granularities tests for snowflake --- .../__snapshots__/snowflake-full.test.ts.snap | 2296 +++++++++++++++++ 1 file changed, 2296 insertions(+) diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-full.test.ts.snap index bdb0632b531ae..323b79fadeacd 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-full.test.ts.snap @@ -7023,3 +7023,2299 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-07-17T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2019-07-17T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "9", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-07-17T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2019-07-17T00:00:00.000", + }, + Object { + "ECommerce.count": "17", + "ECommerce.customOrderDateNoPreAgg": "2020-01-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-14T00:00:00.000", + }, + Object { + "ECommerce.count": "26", + "ECommerce.customOrderDateNoPreAgg": "2020-07-13T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-13T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-16T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-16T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-16T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-16T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-16T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-16T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-16T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-16T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-16T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-16T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-16T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-16T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "6", + "ECommerce.customOrderDateNoPreAgg": "2019-10-16T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-16T00:00:00.000", + }, + Object { + "ECommerce.count": "18", + "ECommerce.customOrderDateNoPreAgg": "2020-04-14T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-14T00:00:00.000", + }, + Object { + "ECommerce.count": "20", + "ECommerce.customOrderDateNoPreAgg": "2020-10-12T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-12T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by three_months_by_march + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by three_months_by_march + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.count": "12", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "8", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "19", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "10", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "28", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "9", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "18", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": "26", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "5", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": "20", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by three_months_by_march + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by three_months_by_march + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.count": "12", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "8", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "19", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "10", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "28", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, +] +`; +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "9", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "18", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": "26", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "5", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": "20", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by three_months_by_march + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by three_months_by_march + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.count": "12", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "8", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "19", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "10", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "28", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, +] +`; From 389c7ea7084b3bbce8a09c2bddbb1af73edad6c8 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 28 Aug 2024 23:20:10 +0300 Subject: [PATCH 66/84] add test snapshots for custom granularities tests for bigQuery --- .../__snapshots__/bigquery-full.test.ts.snap | 1533 +++++++++++++++++ 1 file changed, 1533 insertions(+) diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/bigquery-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/bigquery-full.test.ts.snap index f8dd744fcff72..b78dcfb3eb418 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/bigquery-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/bigquery-full.test.ts.snap @@ -8862,3 +8862,1536 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2019-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 4, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 9, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2019-07-01T00:00:00.000", + }, + Object { + "ECommerce.count": 17, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": 26, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 4, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 6, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": 18, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": 20, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by three_months_by_march + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by three_months_by_march + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.count": 12, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.count": 19, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 8, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 12, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 6, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 19, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 16, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 12, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 10, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 16, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 6, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 18, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 18, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 28, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 44, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 44, + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 4, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 9, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 18, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": 26, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 4, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 5, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": 19, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": 20, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by three_months_by_march + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by three_months_by_march + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2019-12-11T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-11T21:00:00.000", + }, + Object { + "ECommerce.count": 12, + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.count": 8, + "ECommerce.customOrderDateNoPreAgg": "2020-06-18T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-18T03:00:00.000", + }, + Object { + "ECommerce.count": 19, + "ECommerce.customOrderDateNoPreAgg": "2020-09-21T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-21T06:00:00.000", + }, + Object { + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-12-24T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-24T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 8, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 12, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 6, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 19, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 16, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 12, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 10, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 16, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 6, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 18, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 18, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 28, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 44, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 44, + }, +] +`; From fcbf366014577f79520da4dc81c9f825eb35dd29 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 29 Aug 2024 00:31:55 +0300 Subject: [PATCH 67/84] skip custom granularities tests for databricks (some) --- .../cubejs-testing-drivers/fixtures/databricks-jdbc.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-testing-drivers/fixtures/databricks-jdbc.json b/packages/cubejs-testing-drivers/fixtures/databricks-jdbc.json index f4fcfbd47746e..c337c052fb4a8 100644 --- a/packages/cubejs-testing-drivers/fixtures/databricks-jdbc.json +++ b/packages/cubejs-testing-drivers/fixtures/databricks-jdbc.json @@ -151,13 +151,19 @@ "for the ECommerce.TimeAnalysisExternal", "---------------------------------------", - "SKIPED FOR ALL ", + "SKIPPED FOR ALL ", "---------------------------------------", "querying Products: dimensions -- doesn't work wo ordering", "querying ECommerce: total quantity, avg discount, total sales, total profit by product + order + total -- rounding in athena", "querying ECommerce: total sales, total profit by month + order (date) + total -- doesn't work with the BigQuery", "querying ECommerce: total quantity, avg discount, total sales, total profit by product + order + total -- noisy test", + "---------------------------------------", + "Custom Granularities ", + "---------------------------------------", + "querying custom granularities ECommerce: count by three_months_by_march + no dimension", + "querying custom granularities ECommerce: count by three_months_by_march + dimension", + "SKIPPED SQL API (Need work)", "---------------------------------------", "SQL API: Nested Rollup", From 39f96e2808337a8792706a9baeab247340f777b1 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 29 Aug 2024 00:32:19 +0300 Subject: [PATCH 68/84] add test snapshots for custom granularities tests for Databricks --- .../databricks-jdbc-full.test.ts.snap | 1071 +++++++++++++++++ 1 file changed, 1071 insertions(+) diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-full.test.ts.snap index 030446384b593..5f8434c2808b2 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-full.test.ts.snap @@ -13516,3 +13516,1074 @@ Array [ }, ] `; +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2019-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "9", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2019-07-01T00:00:00.000", + }, + Object { + "ECommerce.count": "17", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": "26", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "6", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": "18", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": "20", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "8", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "19", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "10", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "28", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "7", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2021-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2021-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2021-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2021-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2021-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2021-01-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "11", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": "27", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.count": "6", + "ECommerce.customOrderDateNoPreAgg": "2021-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2021-01-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "9", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": "16", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": "25", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "8", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "19", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "12", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "10", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": "16", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "3", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "6", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "18", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "28", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": "44", + }, +] +`; From 7a4aecca6e6d7c4242ce5fe03dcc21ddd89075ee Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 29 Aug 2024 18:13:03 +0300 Subject: [PATCH 69/84] Implement dateBin for ClickHouse --- .../src/adapter/ClickHouseQuery.ts | 77 ++++++++++++++----- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts index c0ba5373657ca..50d4c53fbb54e 100644 --- a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts @@ -2,6 +2,7 @@ import { BaseQuery } from './BaseQuery'; import { BaseFilter } from './BaseFilter'; import { UserError } from '../compiler/UserError'; import { BaseTimeDimension } from './BaseTimeDimension'; +import { parseSqlInterval } from '@cubejs-backend/shared'; const GRANULARITY_TO_INTERVAL = { day: 'Day', @@ -61,38 +62,72 @@ export class ClickHouseQuery extends BaseQuery { } } - public calcInterval(operation, date, interval) { - const [intervalValue, intervalUnit] = interval.split(' '); - // eslint-disable-next-line prefer-template - const fn = operation + intervalUnit[0].toUpperCase() + intervalUnit.substring(1) + 's'; - return `${fn}(${date}, ${intervalValue})`; + /** + * Returns sql for source expression floored to timestamps aligned with + * intervals relative to origin timestamp point. + */ + public dateBin(interval: string, source: string, origin: string): string { + const intervalFormatted = this.formatInterval(interval); + const timeUnit = this.diffTimeUnitForInterval(interval); + const beginOfTime = 'fromUnixTimestamp(0)'; + + return `date_add(${timeUnit}, + FLOOR( + date_diff(${timeUnit}, ${this.timeStampCast(`'${origin}'`)}, ${source}) / + date_diff(${timeUnit}, ${beginOfTime}, ${beginOfTime} + ${intervalFormatted}) + ) * date_diff(${timeUnit}, ${beginOfTime}, ${beginOfTime} + ${intervalFormatted}), + ${this.timeStampCast(`'${origin}'`)} + )`; } - public subtractInterval(date, interval) { - return this.calcInterval('subtract', date, interval); + private diffTimeUnitForInterval(interval: string): string { + if (/second/i.test(interval)) { + return 'second'; + } else if (/minute/i.test(interval)) { + return 'minute'; + } else if (/hour/i.test(interval)) { + return 'hour'; + } else if (/day/i.test(interval)) { + return 'day'; + } else if (/week/i.test(interval)) { + return 'day'; + } else if (/month/i.test(interval)) { + return 'month'; + } else if (/quarter/i.test(interval)) { + return 'month'; + } else /* if (/year/i.test(interval)) */ { + return 'year'; + } } - public addInterval(date, interval) { - return this.calcInterval('add', date, interval); + public subtractInterval(date: string, interval: string): string { + return `subDate(${date}, ${this.formatInterval(interval)})`; } - public timeStampCast(value) { - // value yields a string formatted in ISO8601, so this function returns a expression to parse a string to a DateTime + public addInterval(date: string, interval: string): string { + return `addDate(${date}, ${this.formatInterval(interval)})`; + } - // - // ClickHouse provides toDateTime which expects dates in UTC in format YYYY-MM-DD HH:MM:SS - // - // However parseDateTimeBestEffort works with ISO8601 - // - return `parseDateTimeBestEffort(${value})`; + /** + * The input interval with (possible) plural units, like "2 years", "3 months", "4 weeks", "5 days"... + * will be converted to ClickHouse form of sum of single intervals. + * @see https://clickhouse.com/docs/en/sql-reference/data-types/special-data-types/interval + */ + private formatInterval(interval: string): string { + const intervalParsed = parseSqlInterval(interval); + + return Object.entries(intervalParsed) + .map(([key, value]) => `INTERVAL ${value} ${key.toUpperCase()}`) + .join(' + '); } - public dateTimeCast(value) { - // value yields a string formatted in ISO8601, so this function returns a expression to parse a string to a DateTime + public timeStampCast(value: string): string { + return this.dateTimeCast(value); + } - // + public dateTimeCast(value: string): string { + // value yields a string formatted in ISO8601, so this function returns a expression to parse a string to a DateTime // ClickHouse provides toDateTime which expects dates in UTC in format YYYY-MM-DD HH:MM:SS - // // However parseDateTimeBestEffort works with ISO8601 // return `parseDateTimeBestEffort(${value})`; From 8b96ae5aeb9a8a43d9f8b6c3bff7dbf7604e4339 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 29 Aug 2024 18:13:33 +0300 Subject: [PATCH 70/84] Add custom granularities integration tests for ClickHouse --- .../clickhouse/ClickHouseDbRunner.js | 109 ++-- .../clickhouse/custom-granularities.test.ts | 565 ++++++++++++++++++ 2 files changed, 632 insertions(+), 42 deletions(-) create mode 100644 packages/cubejs-schema-compiler/test/integration/clickhouse/custom-granularities.test.ts diff --git a/packages/cubejs-schema-compiler/test/integration/clickhouse/ClickHouseDbRunner.js b/packages/cubejs-schema-compiler/test/integration/clickhouse/ClickHouseDbRunner.js index cbca907a44a6f..d7cb4a295cc85 100644 --- a/packages/cubejs-schema-compiler/test/integration/clickhouse/ClickHouseDbRunner.js +++ b/packages/cubejs-schema-compiler/test/integration/clickhouse/ClickHouseDbRunner.js @@ -3,14 +3,17 @@ import ClickHouse from '@cubejs-backend/apla-clickhouse'; import { GenericContainer } from 'testcontainers'; import { format as formatSql } from 'sqlstring'; import { v4 as uuidv4 } from 'uuid'; +import { ClickHouseQuery } from '../../../src/adapter/ClickHouseQuery'; +import { BaseDbRunner } from "../utils/BaseDbRunner"; process.env.TZ = 'GMT'; -export class ClickHouseDbRunner { +export class ClickHouseDbRunner extends BaseDbRunner { adapter = 'clickhouse'; container = null; clickHouseVersion = process.env.TEST_CLICKHOUSE_VERSION || '23.11'; supportsExtendedDateTimeResults = this.clickHouseVersion >= '22.9'; + allowExperimentalJoinCondition = this.clickHouseVersion >= '24.5'; tearDown = async () => { if (this.container) { @@ -25,22 +28,25 @@ export class ClickHouseDbRunner { await clickHouse.querying(` CREATE TEMPORARY TABLE visitors (id UInt64, amount UInt64, created_at DateTime, updated_at DateTime, status UInt64, source Nullable(String), latitude Float64, longitude Float64) - ENGINE = ${engine} - `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }), - await clickHouse.querying(` + ENGINE = ${engine}`, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + + await clickHouse.querying(` CREATE TEMPORARY TABLE visitor_checkins (id UInt64, visitor_id UInt64, created_at DateTime, source Nullable(String)) - ENGINE = ${engine} - `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }), - await clickHouse.querying(` + ENGINE = ${engine}`, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + + await clickHouse.querying(` CREATE TEMPORARY TABLE cards (id UInt64, visitor_id UInt64, visitor_checkin_id UInt64) - ENGINE = ${engine} - `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }), - await clickHouse.querying(` + ENGINE = ${engine}`, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + + await clickHouse.querying(` CREATE TEMPORARY TABLE events (id UInt64, type String, name String, started_at DateTime64, ended_at Nullable(DateTime64)) - ENGINE = ${engine} - `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }), + ENGINE = ${engine}`, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); - await clickHouse.querying(` + await clickHouse.querying(` + CREATE TEMPORARY TABLE numbers (num Int) + ENGINE = ${engine}`, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + + await clickHouse.querying(` INSERT INTO visitors (id, amount, created_at, updated_at, status, source, latitude, longitude) VALUES @@ -50,35 +56,50 @@ export class ClickHouseDbRunner { (4, 400, '2017-01-06 16:00:00', '2017-01-24 16:00:00', 2, null, 120.120, 10.60), (5, 500, '2017-01-06 16:00:00', '2017-01-24 16:00:00', 2, null, 120.120, 58.10), (6, 500, '2016-09-06 16:00:00', '2016-09-06 16:00:00', 2, null, 120.120, 58.10) - `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }), - await clickHouse.querying(` - INSERT INTO - visitor_checkins - (id, visitor_id, created_at, source) VALUES - (1, 1, '2017-01-02 16:00:00', null), - (2, 1, '2017-01-03 16:00:00', null), - (3, 1, '2017-01-04 16:00:00', 'google'), - (4, 2, '2017-01-04 16:00:00', null), - (5, 2, '2017-01-04 16:00:00', null), - (6, 3, '2017-01-05 16:00:00', null) - `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }), - await clickHouse.querying(` - INSERT INTO - cards - (id, visitor_id, visitor_checkin_id) VALUES - (1, 1, 1), - (2, 1, 2), - (3, 3, 6) - `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }), - await clickHouse.querying(` - INSERT INTO - events - (id, type, name, started_at, ended_at) VALUES - (1, 'moon_missions', 'Apollo 10', '1969-05-18 16:49:00', '1969-05-26 16:52:23'), - (2, 'moon_missions', 'Apollo 11', '1969-07-16 13:32:00', '1969-07-24 16:50:35'), - (3, 'moon_missions', 'Artemis I', '2021-11-16 06:32:00', '2021-12-11 18:50:00'), - (4, 'private_missions', 'Axiom Mission 1', '2022-04-08 15:17:12', '2022-04-25 17:06:00') - `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + + await clickHouse.querying(` + INSERT INTO + visitor_checkins + (id, visitor_id, created_at, source) VALUES + (1, 1, '2017-01-02 16:00:00', null), + (2, 1, '2017-01-03 16:00:00', null), + (3, 1, '2017-01-04 16:00:00', 'google'), + (4, 2, '2017-01-04 16:00:00', null), + (5, 2, '2017-01-04 16:00:00', null), + (6, 3, '2017-01-05 16:00:00', null) + `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + + await clickHouse.querying(` + INSERT INTO + cards + (id, visitor_id, visitor_checkin_id) VALUES + (1, 1, 1), + (2, 1, 2), + (3, 3, 6) + `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + + await clickHouse.querying(` + INSERT INTO + events + (id, type, name, started_at, ended_at) VALUES + (1, 'moon_missions', 'Apollo 10', '1969-05-18 16:49:00', '1969-05-26 16:52:23'), + (2, 'moon_missions', 'Apollo 11', '1969-07-16 13:32:00', '1969-07-24 16:50:35'), + (3, 'moon_missions', 'Artemis I', '2021-11-16 06:32:00', '2021-12-11 18:50:00'), + (4, 'private_missions', 'Axiom Mission 1', '2022-04-08 15:17:12', '2022-04-25 17:06:00') + `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); + + await clickHouse.querying(` + INSERT INTO + numbers + (num) VALUES + (0), (1), (2), (3), (4), (5), (6), (7), (8), (9), + (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), + (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), + (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), + (40), (41), (42), (43), (44), (45), (46), (47), (48), (49), + (50), (51), (52), (53), (54), (55), (56), (57), (58), (59) + `, { queryOptions: { session_id: clickHouse.sessionId, join_use_nulls: '1' } }); }; testQueries = async (queries, prepareDataSet) => { @@ -136,6 +157,10 @@ export class ClickHouseDbRunner { port() { return 8123; } + + newTestQuery(compilers, query) { + return new ClickHouseQuery(compilers, query); + } } // diff --git a/packages/cubejs-schema-compiler/test/integration/clickhouse/custom-granularities.test.ts b/packages/cubejs-schema-compiler/test/integration/clickhouse/custom-granularities.test.ts new file mode 100644 index 0000000000000..21ec19ce3b03a --- /dev/null +++ b/packages/cubejs-schema-compiler/test/integration/clickhouse/custom-granularities.test.ts @@ -0,0 +1,565 @@ +import { prepareYamlCompiler } from '../../unit/PrepareCompiler'; +import { ClickHouseDbRunner } from './ClickHouseDbRunner'; + +describe('Custom Granularities', () => { + jest.setTimeout(200000); + + const dbRunner = new ClickHouseDbRunner(); + + afterAll(async () => { + await dbRunner.tearDown(); + }); + + const { compiler, joinGraph, cubeEvaluator } = prepareYamlCompiler(` + cubes: + - name: orders + sql: > + SELECT + num + 1 AS order_id, + DATE_ADD(TIMESTAMP ('2024-01-01'), INTERVAL (num * 2) WEEK) AS created_at, + CASE + WHEN (num + 1) % 3 = 1 THEN 'processing' + WHEN (num + 1) % 3 = 2 THEN 'completed' + ELSE 'shipped' + END AS status + FROM numbers + + dimensions: + - name: order_id + sql: order_id + type: number + primary_key: true + public: true + + - name: status + sql: status + type: string + + - name: createdAt + sql: created_at + type: time + granularities: + - name: half_year + interval: 6 months + - name: half_year_by_1st_april + interval: 6 months + offset: 3 months + - name: two_weeks_by_friday + interval: 2 weeks + origin: '2024-08-23' + - name: one_hour_by_5min_offset + interval: 1 hour + offset: 5 minutes + - name: twenty_five_minutes + interval: 25 minutes + origin: '2024-01-01 10:15:00' + - name: fifteen_days_hours_minutes_seconds + interval: 15 days 3 hours 25 minutes 40 seconds + origin: '2024-01-01 10:15:00' + - name: fiscal_year_by_1st_feb + interval: 1 year + origin: '2024-02-01' + - name: fiscal_year_by_15th_march + interval: 1 year + origin: '2024-03-15' + + measures: + - name: count + type: count + + - name: rollingCountByTrailing3Months + type: count + rolling_window: + trailing: 3 months + + - name: rollingCountByLeading4Months + type: count + rolling_window: + leading: 4 months + + - name: rollingCountByUnbounded + type: count + rolling_window: + trailing: unbounded + `); + + it('works with half_year custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: '13', + orders__created_at_half_year: '2024-01-01T00:00:00.000', + }, + { + orders__count: '14', + orders__created_at_half_year: '2024-07-01T00:00:00.000', + }, + { + orders__count: '13', + orders__created_at_half_year: '2025-01-01T00:00:00.000', + }, + { + orders__count: '13', + orders__created_at_half_year: '2025-07-01T00:00:00.000', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: '7', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000', + }, + { + orders__count: '13', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000', + }, + { + orders__count: '13', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000', + }, + { + orders__count: '13', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000', + }, + { + orders__count: '7', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year custom granularity with dimension query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__count: '4', + orders__created_at_half_year: '2024-01-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '5', + orders__created_at_half_year: '2024-01-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year: '2024-01-01T00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: '5', + orders__created_at_half_year: '2024-07-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '4', + orders__created_at_half_year: '2024-07-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '5', + orders__created_at_half_year: '2024-07-01T00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: '4', + orders__created_at_half_year: '2025-01-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '5', + orders__created_at_half_year: '2025-01-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year: '2025-01-01T00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: '5', + orders__created_at_half_year: '2025-07-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '4', + orders__created_at_half_year: '2025-07-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year: '2025-07-01T00:00:00.000', + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('works with half_year_by_1st_april custom with dimension granularity query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'half_year_by_1st_april', + dateRange: ['2024-01-01', '2025-12-31'] + }], + dimensions: ['orders.status'], + filters: [], + order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + timezone: 'Europe/London' + }, + [ + { + orders__count: '2', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '3', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '2', + orders__created_at_half_year_by_1st_april: '2023-10-01T00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: '5', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2024-04-01T00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '5', + orders__created_at_half_year_by_1st_april: '2024-10-01T00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '5', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '4', + orders__created_at_half_year_by_1st_april: '2025-04-01T00:00:00.000', + orders__status: 'shipped', + }, + { + orders__count: '3', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000', + orders__status: 'completed', + }, + { + orders__count: '2', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000', + orders__status: 'processing', + }, + { + orders__count: '2', + orders__created_at_half_year_by_1st_april: '2025-10-01T00:00:00.000', + orders__status: 'shipped', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year custom granularity w/o dimensions with unbounded rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByUnbounded'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: [], + // filters: [], + // timezone: 'Europe/London' + // }, + // [ + // // TODO Fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year custom granularity with dimension with unbounded rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByUnbounded'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: ['orders.status'], + // filters: [], + // order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + // timezone: 'Europe/London' + // }, + // [ + // // TODO Fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year_by_1st_april custom granularity with dimension with unbounded rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByUnbounded'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year_by_1st_april', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: ['orders.status'], + // filters: [], + // order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + // timezone: 'Europe/London' + // }, + // [ + // // TODO Fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year custom granularity w/o dimensions with trailing rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByTrailing3Months'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: [], + // filters: [], + // timezone: 'Europe/London' + // }, + // [ + // // TODO Fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year custom granularity with dimension with trailing rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByTrailing3Months'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: ['orders.status'], + // filters: [], + // order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + // timezone: 'Europe/London' + // }, + // [ + // // TODO Fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year_by_1st_april custom granularity w/o dimensions with trailing rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByTrailing3Months'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year_by_1st_april', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: [], + // filters: [], + // timezone: 'Europe/London' + // }, + // [ + // // TODO Fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year custom granularity w/o dimensions with leading rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByLeading4Months'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: [], + // filters: [], + // timezone: 'Europe/London' + // }, + // [ + // // TODO Fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year custom granularity with dimension with leading rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByLeading4Months'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: ['orders.status'], + // filters: [], + // order: [{ id: 'orders.createdAt' }, { id: 'orders.status' }], + // timezone: 'Europe/London' + // }, + // [ + // // TODO fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + // This requires inequal join condition support, which is available in + // ClickHouse >= 24.5 (as experimental feature). + // But it also conflicts with "join_use_nulls: '1'" which is turned on in our runner. + // @see https://clickhouse.com/docs/en/sql-reference/statements/select/join#experimental-join-with-inequality-conditions-for-columns-from-different-tables + // it('works with half_year_by_1st_april custom granularity w/o dimensions with leading rolling window query', async () => dbRunner.runQueryTest( + // { + // measures: ['orders.rollingCountByLeading4Months'], + // timeDimensions: [{ + // dimension: 'orders.createdAt', + // granularity: 'half_year_by_1st_april', + // dateRange: ['2024-01-01', '2025-12-31'] + // }], + // dimensions: [], + // filters: [], + // timezone: 'Europe/London' + // }, + // [ + // // TODO: fill + // ], + // { joinGraph, cubeEvaluator, compiler } + // )); + + it('works with fifteen_days_hours_minutes_seconds custom granularity w/o dimensions query', async () => dbRunner.runQueryTest( + { + measures: ['orders.count'], + timeDimensions: [{ + dimension: 'orders.createdAt', + granularity: 'fifteen_days_hours_minutes_seconds', + dateRange: ['2024-01-01', '2024-02-31'] + }], + dimensions: [], + filters: [], + timezone: 'Europe/London' + }, + [ + { + orders__count: '1', + orders__created_at_fifteen_days_hours_minutes_seconds: '2023-12-17T06:49:20.000', + }, + { + orders__count: '1', + orders__created_at_fifteen_days_hours_minutes_seconds: '2024-01-01T10:15:00.000', + }, + { + orders__count: '1', + orders__created_at_fifteen_days_hours_minutes_seconds: '2024-01-16T13:40:40.000', + }, + { + orders__count: '1', + orders__created_at_fifteen_days_hours_minutes_seconds: '2024-01-31T17:06:20.000', + }, + { + orders__count: '1', + orders__created_at_fifteen_days_hours_minutes_seconds: '2024-02-15T20:32:00.000', + }, + ], + { joinGraph, cubeEvaluator, compiler } + )); +}); From d2a5fa820c0926356e7ce49b6d38c553812d2857 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 29 Aug 2024 18:19:22 +0300 Subject: [PATCH 71/84] unskip custom granularities tests for clickhouse (some) --- packages/cubejs-testing-drivers/fixtures/clickhouse.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/cubejs-testing-drivers/fixtures/clickhouse.json b/packages/cubejs-testing-drivers/fixtures/clickhouse.json index 6d0f3e62a25da..61b1286caed8a 100644 --- a/packages/cubejs-testing-drivers/fixtures/clickhouse.json +++ b/packages/cubejs-testing-drivers/fixtures/clickhouse.json @@ -162,12 +162,6 @@ "---------------------------------------", "Custom Granularities ", "---------------------------------------", - "querying custom granularities ECommerce: count by half_year + no dimension", - "querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension", - "querying custom granularities ECommerce: count by three_months_by_march + no dimension", - "querying custom granularities ECommerce: count by half_year + dimension", - "querying custom granularities ECommerce: count by half_year_by_1st_april + dimension", - "querying custom granularities ECommerce: count by three_months_by_march + dimension", "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded", "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing", "querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading" From f0488889c318d5f54f85b223fe79b35ee709df20 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 29 Aug 2024 18:20:43 +0300 Subject: [PATCH 72/84] add test snapshots for custom granularities tests for ClickHouse --- .../clickhouse-full.test.ts.snap | 644 ++++++++++++++++++ 1 file changed, 644 insertions(+) diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-full.test.ts.snap index 806b0c7a4ff32..bb2e01b4fb990 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-full.test.ts.snap @@ -9684,3 +9684,647 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/clickhouse-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "9", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/clickhouse-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "18", + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": "26", + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/clickhouse-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "4", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/clickhouse-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "5", + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": "20", + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/clickhouse-driver querying custom granularities ECommerce: count by three_months_by_march + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": "1", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/clickhouse-driver querying custom granularities ECommerce: count by three_months_by_march + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": "3", + "ECommerce.customOrderDateNoPreAgg": "2019-12-12T21:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2019-12-12T21:00:00.000", + }, + Object { + "ECommerce.count": "12", + "ECommerce.customOrderDateNoPreAgg": "2020-03-15T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-03-15T00:00:00.000", + }, + Object { + "ECommerce.count": "8", + "ECommerce.customOrderDateNoPreAgg": "2020-06-16T03:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-06-16T03:00:00.000", + }, + Object { + "ECommerce.count": "19", + "ECommerce.customOrderDateNoPreAgg": "2020-09-17T06:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-09-17T06:00:00.000", + }, + Object { + "ECommerce.count": "2", + "ECommerce.customOrderDateNoPreAgg": "2020-12-19T09:00:00.000", + "ECommerce.customOrderDateNoPreAgg.three_months_by_march": "2020-12-19T09:00:00.000", + }, +] +`; From 8c82e27b70685043d0c18d628c6f0821c9ed82f6 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 29 Aug 2024 19:19:13 +0300 Subject: [PATCH 73/84] fix linting --- packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts index 50d4c53fbb54e..2dabec9abc6cb 100644 --- a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts @@ -1,8 +1,8 @@ +import { parseSqlInterval } from '@cubejs-backend/shared'; import { BaseQuery } from './BaseQuery'; import { BaseFilter } from './BaseFilter'; import { UserError } from '../compiler/UserError'; import { BaseTimeDimension } from './BaseTimeDimension'; -import { parseSqlInterval } from '@cubejs-backend/shared'; const GRANULARITY_TO_INTERVAL = { day: 'Day', From 6bf8aa61f39ebf6c818297dc8b3c6300e1cb0ed7 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 29 Aug 2024 19:51:09 +0300 Subject: [PATCH 74/84] fix integration tests for clickhouse v22.x --- .../test/integration/clickhouse/custom-granularities.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/test/integration/clickhouse/custom-granularities.test.ts b/packages/cubejs-schema-compiler/test/integration/clickhouse/custom-granularities.test.ts index 21ec19ce3b03a..966ba6846e6e4 100644 --- a/packages/cubejs-schema-compiler/test/integration/clickhouse/custom-granularities.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/clickhouse/custom-granularities.test.ts @@ -16,7 +16,7 @@ describe('Custom Granularities', () => { sql: > SELECT num + 1 AS order_id, - DATE_ADD(TIMESTAMP ('2024-01-01'), INTERVAL (num * 2) WEEK) AS created_at, + DATE_ADD(toDateTime('2024-01-01'), INTERVAL (num * 2) WEEK) AS created_at, CASE WHEN (num + 1) % 3 = 1 THEN 'processing' WHEN (num + 1) % 3 = 2 THEN 'completed' From 1bdec914ed3d71c9bc7e0bdaade6174b4e8a678e Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 2 Sep 2024 16:14:39 +0300 Subject: [PATCH 75/84] fix in dateBin for Postgresql and Bigquery --- .../cubejs-schema-compiler/src/adapter/BigqueryQuery.ts | 2 +- .../cubejs-schema-compiler/src/adapter/PostgresQuery.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts b/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts index 33d89ebb894e6..9f5b83e3df3cd 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts @@ -70,7 +70,7 @@ export class BigqueryQuery extends BaseQuery { const [intervalFormatted, timeUnit] = this.formatInterval(interval); const beginOfTime = this.dateTimeCast('\'1970-01-01T00:00:00\''); - return `${this.dateTimeCast(`'${origin}'`)} + INTERVAL ${intervalFormatted} * + return `(${this.dateTimeCast(`'${origin}'`)} + INTERVAL ${intervalFormatted} * CAST(FLOOR( DATETIME_DIFF(${source}, ${this.dateTimeCast(`'${origin}'`)}, ${timeUnit}) / DATETIME_DIFF(${beginOfTime} + INTERVAL ${intervalFormatted}, ${beginOfTime}, ${timeUnit}) diff --git a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts index b2286a135090e..01f763e4339dd 100644 --- a/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/PostgresQuery.ts @@ -34,16 +34,15 @@ export class PostgresQuery extends BaseQuery { /** * Returns sql for source expression floored to timestamps aligned with * intervals relative to origin timestamp point. - * Postgres operates with whole intervals as is without measuring them in plain seconds, - * so the resulting date will be human-expected aligned with intervals. + * Postgres operates with whole intervals as is without measuring them in plain seconds. * This implementation should also work for AWS RedShift. */ public dateBin(interval: string, source: string, origin: string): string { - return `'${origin}'::timestamp + INTERVAL '${interval}' * + return `('${origin}'::timestamp + INTERVAL '${interval}' * FLOOR( EXTRACT(EPOCH FROM (${source} - '${origin}'::timestamp)) / EXTRACT(EPOCH FROM INTERVAL '${interval}') - )`; + ))`; } public hllInit(sql) { From e62cfc43df284ef983ee8c61be7e6616352c7a11 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 3 Sep 2024 16:29:42 +0300 Subject: [PATCH 76/84] Add char limits for granularities names in queries --- packages/cubejs-api-gateway/src/query.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-api-gateway/src/query.js b/packages/cubejs-api-gateway/src/query.js index ada2647106a6b..b83148c80c668 100644 --- a/packages/cubejs-api-gateway/src/query.js +++ b/packages/cubejs-api-gateway/src/query.js @@ -103,7 +103,7 @@ const querySchema = Joi.object().keys({ filters: Joi.array().items(oneFilter, oneCondition), timeDimensions: Joi.array().items(Joi.object().keys({ dimension: id.required(), - granularity: Joi.string(), // Custom granularities have arbitrary names + granularity: Joi.string().max(128, 'utf8'), // Custom granularities may have arbitrary names dateRange: [ Joi.array().items(Joi.string()).min(1).max(2), Joi.string() From e021cadc665692a3b60ba104111a6e14139703a7 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 5 Sep 2024 12:07:46 +0300 Subject: [PATCH 77/84] Add time series date range limit checking before generating --- packages/cubejs-backend-shared/src/time.ts | 31 +++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-backend-shared/src/time.ts b/packages/cubejs-backend-shared/src/time.ts index 1486d0bb778c3..b651b39ab8353 100644 --- a/packages/cubejs-backend-shared/src/time.ts +++ b/packages/cubejs-backend-shared/src/time.ts @@ -113,7 +113,33 @@ function alignToOrigin(startDate: moment.Moment, interval: ParsedInterval, origi return alignedDate; } +function parsedSqlIntervalToDuration(parsedInterval: ParsedInterval): moment.Duration { + const duration = moment.duration(); + + Object.entries(parsedInterval).forEach(([key, value]) => { + duration.add(value, key as unitOfTime.DurationConstructor); + }); + + return duration; +} + +function allowSeriesForDateRange(intervalStr: string, [startStr, endStr]: QueryDateRange): boolean { + const intervalParsed = parseSqlInterval(intervalStr); + const intervalAsSeconds = parsedSqlIntervalToDuration(intervalParsed).asSeconds(); + const start = moment(startStr); + const end = moment(endStr); + const rangeSeconds = end.diff(start, 'seconds'); + + const limit = 50000; // TODO Make this as configurable soft limit + + return rangeSeconds / intervalAsSeconds < limit; +} + export const timeSeriesFromCustomInterval = (intervalStr: string, [startStr, endStr]: QueryDateRange, origin: moment.Moment, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] => { + if (!allowSeriesForDateRange(intervalStr, [startStr, endStr])) { + throw new Error(`The count of generated date ranges for the request from [${startStr}] to [${endStr}] by ${intervalStr} reached the limits. Please reduce the requested date interval or use bigger granularity.`); + } + const intervalParsed = parseSqlInterval(intervalStr); const start = moment(startStr); const end = moment(endStr); @@ -140,7 +166,6 @@ export const timeSeriesFromCustomInterval = (intervalStr: string, [startStr, end */ export const timeSeries = (granularity: string, dateRange: QueryDateRange, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] => { if (!TIME_SERIES[granularity]) { - // TODO error throw new Error(`Unsupported time granularity: ${granularity}`); } @@ -148,6 +173,10 @@ export const timeSeries = (granularity: string, dateRange: QueryDateRange, optio throw new Error(`options.timestampPrecision is required, actual: ${options.timestampPrecision}`); } + if (!allowSeriesForDateRange(`1 ${granularity}`, dateRange)) { + throw new Error(`The count of generated date ranges for the request from [${dateRange[0]}] to [${dateRange[1]}] by ${granularity} reached the limits. Please reduce the requested date interval or use bigger granularity.`); + } + // moment.range works with strings const range = moment.range(dateRange[0], dateRange[1]); From ad8f39508531b2882bb9b166ee6619a5f279a50b Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 5 Sep 2024 12:08:05 +0300 Subject: [PATCH 78/84] =?UTF-8?q?add=20tests=20for=C2=A0limit=20checking?= =?UTF-8?q?=20in=C2=A0time=20series=20generation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cubejs-backend-shared/test/time.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/cubejs-backend-shared/test/time.test.ts b/packages/cubejs-backend-shared/test/time.test.ts index fa4c72c7d720d..a4b0a0bc73b1a 100644 --- a/packages/cubejs-backend-shared/test/time.test.ts +++ b/packages/cubejs-backend-shared/test/time.test.ts @@ -30,6 +30,12 @@ describe('time', () => { ]); }); + it('time series - reach limits', () => { + expect(() => { + timeSeries('second', ['1970-01-01', '2021-01-02']); + }).toThrowError(/The count of generated date ranges for the request.*reached the limits/); + }); + it('time series - custom: interval - 1 year, origin - 2021-01-01', () => { expect(timeSeriesFromCustomInterval('1 year', ['2021-01-01', '2023-12-31'], moment('2021-01-01'))).toEqual([ ['2021-01-01T00:00:00.000', '2021-12-31T23:59:59.999'], @@ -166,6 +172,12 @@ describe('time', () => { ]); }); + it('time series - custom: interval - reach limits', () => { + expect(() => { + timeSeriesFromCustomInterval('10 minutes 15 seconds', ['1970-01-01', '2021-01-02'], moment('2021-02-01 09:59:45')); + }).toThrowError(/The count of generated date ranges for the request.*reached the limits/); + }); + it('inDbTimeZone', () => { expect(inDbTimeZone('UTC', 'YYYY-MM-DD[T]HH:mm:ss.SSSSSS[Z]', '2020-01-01T00:00:00.000000')).toEqual( '2020-01-01T00:00:00.000000Z' From dab2a4927c8a0bfc032ee88abe3c840a1da4727f Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 5 Sep 2024 23:09:01 +0300 Subject: [PATCH 79/84] =?UTF-8?q?Improve=20error=20message=20for=C2=A0time?= =?UTF-8?q?=20series=20limit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cubejs-backend-shared/src/time.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/cubejs-backend-shared/src/time.ts b/packages/cubejs-backend-shared/src/time.ts index b651b39ab8353..2d795cf074bb8 100644 --- a/packages/cubejs-backend-shared/src/time.ts +++ b/packages/cubejs-backend-shared/src/time.ts @@ -123,7 +123,7 @@ function parsedSqlIntervalToDuration(parsedInterval: ParsedInterval): moment.Dur return duration; } -function allowSeriesForDateRange(intervalStr: string, [startStr, endStr]: QueryDateRange): boolean { +function checkSeriesForDateRange(intervalStr: string, [startStr, endStr]: QueryDateRange): void { const intervalParsed = parseSqlInterval(intervalStr); const intervalAsSeconds = parsedSqlIntervalToDuration(intervalParsed).asSeconds(); const start = moment(startStr); @@ -131,14 +131,15 @@ function allowSeriesForDateRange(intervalStr: string, [startStr, endStr]: QueryD const rangeSeconds = end.diff(start, 'seconds'); const limit = 50000; // TODO Make this as configurable soft limit + const count = rangeSeconds / intervalAsSeconds; - return rangeSeconds / intervalAsSeconds < limit; + if (count > limit) { + throw new Error(`The count of generated date ranges (${count}) for the request from [${startStr}] to [${endStr}] by ${intervalStr} is over limit (${limit}). Please reduce the requested date interval or use bigger granularity.`); + } } export const timeSeriesFromCustomInterval = (intervalStr: string, [startStr, endStr]: QueryDateRange, origin: moment.Moment, options: TimeSeriesOptions = { timestampPrecision: 3 }): QueryDateRange[] => { - if (!allowSeriesForDateRange(intervalStr, [startStr, endStr])) { - throw new Error(`The count of generated date ranges for the request from [${startStr}] to [${endStr}] by ${intervalStr} reached the limits. Please reduce the requested date interval or use bigger granularity.`); - } + checkSeriesForDateRange(intervalStr, [startStr, endStr]); const intervalParsed = parseSqlInterval(intervalStr); const start = moment(startStr); @@ -173,9 +174,7 @@ export const timeSeries = (granularity: string, dateRange: QueryDateRange, optio throw new Error(`options.timestampPrecision is required, actual: ${options.timestampPrecision}`); } - if (!allowSeriesForDateRange(`1 ${granularity}`, dateRange)) { - throw new Error(`The count of generated date ranges for the request from [${dateRange[0]}] to [${dateRange[1]}] by ${granularity} reached the limits. Please reduce the requested date interval or use bigger granularity.`); - } + checkSeriesForDateRange(`1 ${granularity}`, dateRange); // moment.range works with strings const range = moment.range(dateRange[0], dateRange[1]); From e8596c2c736b30ce1122b3961b2a4c6e7b6ff714 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Thu, 5 Sep 2024 23:15:01 +0300 Subject: [PATCH 80/84] fix tests for time series limit --- packages/cubejs-backend-shared/test/time.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-backend-shared/test/time.test.ts b/packages/cubejs-backend-shared/test/time.test.ts index a4b0a0bc73b1a..ec300db674c79 100644 --- a/packages/cubejs-backend-shared/test/time.test.ts +++ b/packages/cubejs-backend-shared/test/time.test.ts @@ -33,7 +33,7 @@ describe('time', () => { it('time series - reach limits', () => { expect(() => { timeSeries('second', ['1970-01-01', '2021-01-02']); - }).toThrowError(/The count of generated date ranges for the request.*reached the limits/); + }).toThrowError(/The count of generated date ranges.*for the request.*is over limit/); }); it('time series - custom: interval - 1 year, origin - 2021-01-01', () => { @@ -175,7 +175,7 @@ describe('time', () => { it('time series - custom: interval - reach limits', () => { expect(() => { timeSeriesFromCustomInterval('10 minutes 15 seconds', ['1970-01-01', '2021-01-02'], moment('2021-02-01 09:59:45')); - }).toThrowError(/The count of generated date ranges for the request.*reached the limits/); + }).toThrowError(/The count of generated date ranges.*for the request.*is over limit/); }); it('inDbTimeZone', () => { From 535e9db27f18f303c0c27cc42b509642f33e0976 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 6 Sep 2024 11:52:34 +0300 Subject: [PATCH 81/84] =?UTF-8?q?Refactor:=20move=20isGranularityNaturalAl?= =?UTF-8?q?igned=20from=20Basequery=20=E2=86=92=20Granularity=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cubejs-schema-compiler/src/adapter/BaseQuery.js | 13 +------------ .../src/adapter/Granularity.ts | 6 ++++++ .../src/compiler/CubeValidator.ts | 1 - 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index cb87e8bbf34a8..fa7af2b4c073f 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -2766,7 +2766,7 @@ export class BaseQuery { let dtDate; // Interval is aligned with natural calendar, so we can use DATE_TRUNC - if (this.isGranularityNaturalAligned(granularity.granularityInterval)) { + if (granularity.isNaturalAligned()) { if (granularity.granularityOffset) { // Example: DATE_TRUNC(interval, dimension - INTERVAL 'offset') + INTERVAL 'offset' dtDate = this.subtractInterval(dimension, granularity.granularityOffset); @@ -2782,17 +2782,6 @@ export class BaseQuery { return this.dateBin(granularity.granularityInterval, dimension, granularity.originFormatted()); } - /** - * @protected - * @param {string} interval - * @returns {boolean} - */ - isGranularityNaturalAligned(interval) { - const intParsed = interval.split(' '); - - return !(intParsed.length !== 2 || intParsed[0] !== '1'); - } - /** * Evaluate alias for specific cube's property. * @param {string} name Property name. diff --git a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts index dfeec44fff47e..47c5068ffc4c3 100644 --- a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts +++ b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts @@ -132,4 +132,10 @@ export class Granularity { return 'year'; } } + + public isNaturalAligned(): boolean { + const intParsed = this.granularityInterval.split(' '); + + return !(intParsed.length !== 2 || intParsed[0] !== '1'); + } } diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts index 9781a6f1d488c..aed264f19bb8d 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts @@ -123,7 +123,6 @@ const BaseDimensionWithoutSubQuery = { }), Joi.object().keys({ interval: GranularityInterval.required().custom((value, helper) => { - // const isValid = isGranularityNaturalAligned(value); const intParsed = value.split(' '); const msg = { custom: 'Arbitrary intervals cannot be used without origin point specified' }; From 4f5e6ccd5bd186de9c56129aecfe301f9f74e7fb Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 6 Sep 2024 22:07:48 +0300 Subject: [PATCH 82/84] add test snapshots for custom granularities tests for MySQL --- .../__snapshots__/mysql-full.test.ts.snap | 536 ++++++++++++++++++ 1 file changed, 536 insertions(+) diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/mysql-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/mysql-full.test.ts.snap index 9e522f70280b8..12f49d94dd1a6 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/mysql-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/mysql-full.test.ts.snap @@ -6569,3 +6569,539 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/mysql-driver querying custom granularities ECommerce: count by half_year + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 7, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 4, + "ECommerce.customOrderDateNoPreAgg": "2021-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2021-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2021-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2021-01-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2021-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2021-01-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mysql-driver querying custom granularities ECommerce: count by half_year + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 11, + "ECommerce.customOrderDateNoPreAgg": "2020-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-01-01T00:00:00.000", + }, + Object { + "ECommerce.count": 27, + "ECommerce.customOrderDateNoPreAgg": "2020-07-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2020-07-01T00:00:00.000", + }, + Object { + "ECommerce.count": 6, + "ECommerce.customOrderDateNoPreAgg": "2021-01-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year": "2021-01-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mysql-driver querying custom granularities ECommerce: count by half_year_by_1st_april + dimension 1`] = ` +Array [ + Object { + "ECommerce.city": "Decatur", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lorain", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Auburn", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Baltimore", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Detroit", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Houston", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Los Angeles", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Louisville", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 4, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Olympia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Omaha", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Arlington", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bakersfield", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Bowling", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Columbus", + "ECommerce.count": 9, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Dallas", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Glendale", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lafayette", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Lakewood", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Marion", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Morristown", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "New York City", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Oakland", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Philadelphia", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Provo", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "San Francisco", + "ECommerce.count": 2, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, + Object { + "ECommerce.city": "Vancouver", + "ECommerce.count": 1, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mysql-driver querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension 1`] = ` +Array [ + Object { + "ECommerce.count": 3, + "ECommerce.customOrderDateNoPreAgg": "2019-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2019-10-01T00:00:00.000", + }, + Object { + "ECommerce.count": 16, + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-04-01T00:00:00.000", + }, + Object { + "ECommerce.count": 25, + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T00:00:00.000", + "ECommerce.customOrderDateNoPreAgg.half_year_by_1st_april": "2020-10-01T00:00:00.000", + }, +] +`; + +exports[`Queries with the @cubejs-backend/mysql-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 8, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 12, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 6, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 19, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByLeading": 16, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByLeading": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/mysql-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 12, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 10, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": 16, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByTrailing": null, + }, +] +`; + +exports[`Queries with the @cubejs-backend/mysql-driver querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByUnbounded 1`] = ` +Array [ + Object { + "ECommerce.customOrderDateNoPreAgg": "2019-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2019-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 3, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-02-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-02-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 6, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-04-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-04-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 18, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-06-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-06-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 18, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-08-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-08-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 28, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-10-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-10-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 44, + }, + Object { + "ECommerce.customOrderDateNoPreAgg": "2020-12-01T10:00:00.000", + "ECommerce.customOrderDateNoPreAgg.two_mo_by_feb": "2020-12-01T10:00:00.000", + "ECommerce.rollingCountByUnbounded": 44, + }, +] +`; From 93ea144a7c9f04da0f1b5bd0615611a7b13464ef Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 9 Sep 2024 12:30:55 +0300 Subject: [PATCH 83/84] =?UTF-8?q?move=20diffTimeUnitForInterval=20to=C2=A0?= =?UTF-8?q?BaseQuery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cubejs-duckdb-driver/src/DuckDBQuery.ts | 20 -------------- .../src/adapter/BaseQuery.js | 26 +++++++++++++++++++ .../src/adapter/ClickHouseQuery.ts | 20 -------------- .../src/adapter/MssqlQuery.ts | 20 -------------- .../src/adapter/SnowflakeQuery.ts | 20 -------------- 5 files changed, 26 insertions(+), 80 deletions(-) diff --git a/packages/cubejs-duckdb-driver/src/DuckDBQuery.ts b/packages/cubejs-duckdb-driver/src/DuckDBQuery.ts index ce5fc5ff4edd5..41d60189e7e9e 100644 --- a/packages/cubejs-duckdb-driver/src/DuckDBQuery.ts +++ b/packages/cubejs-duckdb-driver/src/DuckDBQuery.ts @@ -48,26 +48,6 @@ export class DuckDBQuery extends BaseQuery { )::int`; } - private diffTimeUnitForInterval(interval: string): string { - if (/second/i.test(interval)) { - return 'second'; - } else if (/minute/i.test(interval)) { - return 'minute'; - } else if (/hour/i.test(interval)) { - return 'hour'; - } else if (/day/i.test(interval)) { - return 'day'; - } else if (/week/i.test(interval)) { - return 'day'; - } else if (/month/i.test(interval)) { - return 'month'; - } else if (/quarter/i.test(interval)) { - return 'month'; - } else /* if (/year/i.test(interval)) */ { - return 'year'; - } - } - public countDistinctApprox(sql: string) { return `approx_count_distinct(${sql})`; } diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index fa7af2b4c073f..5f1075f372c7c 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -2756,6 +2756,32 @@ export class BaseQuery { // Different syntax possible in different DBs } + /** + * Returns the lowest time unit for the interval + * @protected + * @param {string} interval + * @returns {string} + */ + diffTimeUnitForInterval(interval) { + if (/second/i.test(interval)) { + return 'second'; + } else if (/minute/i.test(interval)) { + return 'minute'; + } else if (/hour/i.test(interval)) { + return 'hour'; + } else if (/day/i.test(interval)) { + return 'day'; + } else if (/week/i.test(interval)) { + return 'day'; + } else if (/month/i.test(interval)) { + return 'month'; + } else if (/quarter/i.test(interval)) { + return 'month'; + } else /* if (/year/i.test(interval)) */ { + return 'year'; + } + } + /** * @param {string} dimension * @param {import('./Granularity').Granularity} granularity diff --git a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts index 2dabec9abc6cb..761ccc9acaae1 100644 --- a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts @@ -80,26 +80,6 @@ export class ClickHouseQuery extends BaseQuery { )`; } - private diffTimeUnitForInterval(interval: string): string { - if (/second/i.test(interval)) { - return 'second'; - } else if (/minute/i.test(interval)) { - return 'minute'; - } else if (/hour/i.test(interval)) { - return 'hour'; - } else if (/day/i.test(interval)) { - return 'day'; - } else if (/week/i.test(interval)) { - return 'day'; - } else if (/month/i.test(interval)) { - return 'month'; - } else if (/quarter/i.test(interval)) { - return 'month'; - } else /* if (/year/i.test(interval)) */ { - return 'year'; - } - } - public subtractInterval(date: string, interval: string): string { return `subDate(${date}, ${this.formatInterval(interval)})`; } diff --git a/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts b/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts index f69f69b4e51b1..12e7af9091c1b 100644 --- a/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts @@ -101,26 +101,6 @@ export class MssqlQuery extends BaseQuery { )`; } - private diffTimeUnitForInterval(interval: string): string { - if (/second/i.test(interval)) { - return 'second'; - } else if (/minute/i.test(interval)) { - return 'minute'; - } else if (/hour/i.test(interval)) { - return 'hour'; - } else if (/day/i.test(interval)) { - return 'day'; - } else if (/week/i.test(interval)) { - return 'day'; - } else if (/month/i.test(interval)) { - return 'month'; - } else if (/quarter/i.test(interval)) { - return 'month'; - } else /* if (/year/i.test(interval)) */ { - return 'year'; - } - } - public newParamAllocator(expressionParams) { return new MssqlParamAllocator(expressionParams); } diff --git a/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts b/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts index d43622d21bd96..0e3a5c50db690 100644 --- a/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/SnowflakeQuery.ts @@ -72,26 +72,6 @@ export class SnowflakeQuery extends BaseQuery { }).join(' '); } - private diffTimeUnitForInterval(interval: string): string { - if (/second/i.test(interval)) { - return 'second'; - } else if (/minute/i.test(interval)) { - return 'minute'; - } else if (/hour/i.test(interval)) { - return 'hour'; - } else if (/day/i.test(interval)) { - return 'day'; - } else if (/week/i.test(interval)) { - return 'day'; - } else if (/month/i.test(interval)) { - return 'month'; - } else if (/quarter/i.test(interval)) { - return 'month'; - } else /* if (/year/i.test(interval)) */ { - return 'year'; - } - } - public timeStampCast(value) { return `${value}::timestamp_tz`; } From dde3c250d869a403aeb15930db25f6875843713d Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Wed, 11 Sep 2024 11:57:59 +0300 Subject: [PATCH 84/84] =?UTF-8?q?fix=20in=C2=A0choosing=20minGranularity?= =?UTF-8?q?=20for=20granularity=20with=20origin=20point?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cubejs-schema-compiler/src/adapter/Granularity.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts index 47c5068ffc4c3..6ad793c437c4b 100644 --- a/packages/cubejs-schema-compiler/src/adapter/Granularity.ts +++ b/packages/cubejs-schema-compiler/src/adapter/Granularity.ts @@ -44,7 +44,7 @@ export class Granularity { this.granularityInterval = customGranularity.interval; if (customGranularity.origin) { - this.origin = moment(new Date(customGranularity.origin)); + this.origin = moment.tz(customGranularity.origin, 'UTC'); } else if (customGranularity.offset) { this.granularityOffset = customGranularity.offset; this.origin = addInterval(this.origin, parseSqlInterval(customGranularity.offset)); @@ -61,6 +61,13 @@ export class Granularity { return this.granularity; } + if (this.origin) { + return this.query.minGranularity( + this.granularityFromInterval(), + this.query.granularityFor(this.origin.utc()) + ); + } + if (this.granularityOffset) { return this.query.minGranularity( this.granularityFromInterval(),