diff --git a/docs/pages/guides/_meta.js b/docs/pages/guides/_meta.js index 91adfa0b61623..5446fe5fc9e96 100644 --- a/docs/pages/guides/_meta.js +++ b/docs/pages/guides/_meta.js @@ -1,7 +1,7 @@ module.exports = { + "recipes": "Recipes", "dbt": "Using Cube with dbt", "designing-metrics": "Designing Metrics", - "recipes": "Recipes", "style-guide": "Style guide", "data-store-cost-saving-guide": "Cost saving guide" } \ No newline at end of file diff --git a/docs/pages/guides/recipes.mdx b/docs/pages/guides/recipes.mdx index dcf3ac1419bb4..5dfd8a4fbbd1b 100644 --- a/docs/pages/guides/recipes.mdx +++ b/docs/pages/guides/recipes.mdx @@ -32,7 +32,7 @@ These recipes will show you the best practices of using Cube. - [Calculating average and percentiles](/guides/recipes/data-modeling/percentiles) - [Calculating nested aggregates](/guides/recipes/data-modeling/nested-aggregates) - [Calculating period-over-period changes](/guides/recipes/data-modeling/period-over-period) -- [Implementing fiscal year or fiscal quarter dimensions](/guides/recipes/data-modeling/fiscal-year-quarter-dimensions) +- [Implementing custom time dimension granularities](/guides/recipes/data-modeling/custom-granularity) - [Implementing Entity-Attribute-Value model](/guides/recipes/data-modeling/entity-attribute-value) - [Implementing data snapshots](/guides/recipes/data-modeling/snapshots) - [Using different data models for tenants](/guides/recipes/access-control/using-different-schemas-for-tenants) diff --git a/docs/pages/guides/recipes/data-modeling/_meta.js b/docs/pages/guides/recipes/data-modeling/_meta.js index c814c06abb2c2..bd9f5e7fc1fd1 100644 --- a/docs/pages/guides/recipes/data-modeling/_meta.js +++ b/docs/pages/guides/recipes/data-modeling/_meta.js @@ -2,9 +2,9 @@ module.exports = { "percentiles": "Calculating averages and percentiles", "nested-aggregates": "Calculating nested aggregates", "period-over-period": "Calculating period-over-period changes", + "custom-granularity": "Implementing custom time dimension granularities", "snapshots": "Implementing data snapshots", "entity-attribute-value": "Implementing Entity-Attribute-Value Model (EAV)", - "fiscal-year-quarter-dimensions": "Implementing fiscal year or fiscal quarter dimensions", "passing-dynamic-parameters-in-a-query": "Passing dynamic parameters in a query", "using-dynamic-measures": "Using dynamic measures", "dynamic-union-tables": "Using dynamic union tables", diff --git a/docs/pages/guides/recipes/data-modeling/custom-granularity.mdx b/docs/pages/guides/recipes/data-modeling/custom-granularity.mdx new file mode 100644 index 0000000000000..6291e4f325740 --- /dev/null +++ b/docs/pages/guides/recipes/data-modeling/custom-granularity.mdx @@ -0,0 +1,161 @@ +# Implementing custom time dimension granularities + +This recipe show examples of commonly used [custom +granularities][ref-custom-granularities]. + +## Use case + +Sometimes, you might need to group the result set by units of time that are +different from [default granularities][ref-default-granularities] such as `week` +(starting on Monday) or `year` (starting on January 1). + +Below, we explore the following examples of custom granularities: +* *Week starting on Sunday*, commonly used in the US and some other countries. +* *[Fiscal year][wiki-fiscal-year]* and *fiscal quarter*, commonly used in +accounting and financial reporting. + +## Data modeling + +Consider the following data model. `interval` and `offset` parameters are used to +configure each custom granularity in `granularities`. + +Note that custom granularities are also exposed via [proxy +dimensions][ref-proxy-granularity] so that we can conveniently query them via +[Playground][ref-playground] or BI tools connected via the [SQL API][ref-sql-api]. +We can also use them in further calculations like rendering `fiscal_quarter_label`. + + + +```yaml +cubes: + - name: custom_granularities + sql: > + SELECT '2024-01-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-02-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-03-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-04-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-05-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-06-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-07-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-08-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-09-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-10-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-11-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-12-15'::TIMESTAMP AS timestamp + + dimensions: + - name: timestamp + sql: timestamp + type: time + + granularities: + - name: sunday_week + interval: 1 week + offset: -1 day + + - name: fiscal_year + title: Federal fiscal year in the United States + interval: 1 year + offset: -3 months + + - name: fiscal_quarter + title: Federal fiscal quarter in the United States + interval: 1 quarter + offset: -3 months + + - name: sunday_week + sql: "{timestamp.sunday_week}" + type: time + + - name: fiscal_year + sql: "{timestamp.fiscal_year}" + type: time + + - name: fiscal_quarter + sql: "{timestamp.fiscal_quarter}" + type: time + + - name: fiscal_quarter_label + sql: > + 'FY' || EXTRACT(YEAR FROM {timestamp.fiscal_year}) || + '-Q' || EXTRACT(QUARTER FROM {timestamp.fiscal_quarter}) + type: string +``` + +```javascript +cube(`custom_granularities`, { + sql: ` + SELECT '2024-01-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-02-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-03-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-04-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-05-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-06-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-07-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-08-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-09-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-10-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-11-15'::TIMESTAMP AS timestamp UNION ALL + SELECT '2024-12-15'::TIMESTAMP AS timestamp + `, + + dimensions: { + timestamp: { + sql: `timestamp`, + type: `time`, + + granularities: { + sunday_week: { + interval: `1 week`, + offset: `-1 day` + }, + + fiscal_year: { + title: `Federal fiscal year in the United States`, + interval: `1 year`, + offset: `-3 months` + }, + + fiscal_quarter: { + title: `Federal fiscal quarter in the United States`, + interval: `1 quarter`, + offset: `-3 months` + } + } + }, + + sunday_week: { + sql: `${timestamp.sunday_week}`, + type: `time` + }, + + fiscal_year: { + sql: `${timestamp.fiscal_year}`, + type: `time` + }, + + fiscal_quarter: { + sql: `${timestamp.fiscal_quarter}`, + type: `time` + }, + + fiscal_quarter_label: { + sql: ` + 'FY' || EXTRACT(YEAR FROM ${timestamp.fiscal_year}) || + '-Q' || EXTRACT(QUARTER FROM ${timestamp.fiscal_quarter}) + `, + type: `string` + } + } +}) +``` + + + + +[ref-custom-granularities]: /reference/data-model/dimensions#granularities +[ref-default-granularities]: /product/data-modeling/concepts#time-dimensions +[wiki-fiscal-year]: https://en.wikipedia.org/wiki/Fiscal_year +[ref-playground]: /product/workspace/playground +[ref-sql-api]: /product/apis-integrations/sql-api +[ref-proxy-granularity]: /product/data-modeling/concepts/calculated-members#time-dimension-granularity \ No newline at end of file diff --git a/docs/pages/guides/recipes/data-modeling/fiscal-year-quarter-dimensions.mdx b/docs/pages/guides/recipes/data-modeling/fiscal-year-quarter-dimensions.mdx deleted file mode 100644 index 95a942f4551a8..0000000000000 --- a/docs/pages/guides/recipes/data-modeling/fiscal-year-quarter-dimensions.mdx +++ /dev/null @@ -1,84 +0,0 @@ -# Implementing fiscal year or fiscal quarter dimensions - -Businesses and governments use the concept of a [fiscal year][wiki-fiscal-year] -for the convenience of their accounting, budgeting, and reporting. Often, -the fiscal year would not align with the calendar year (January 1 to December 31). -In that case, the fiscal year may either lag or lead by a number of months. - -## Use case - -We'd like to analyze business metrics by attributing them to specific fiscal -years and quarters within those years. In this recipe, we'll be using -[Australia][wiki-fiscal-year-australia] as an example: in this country, the -"financial year" begins 6 month earlier than the calendar year, i.e., FY 2024 -started on July 1, 2023. - -## Data modeling - -Let's define a couple of auxillary dimensions (`fiscal_year_internal` and -`fiscal_quarter_internal`) that would translate the time dimension into -numeric values of the fiscal year and fiscal quarter, respectively. Make -sure to adjust the calculations in these dimensions if your fiscal year is -defined differently. Also, you can see that these dimensions are defined -as [private][ref-dimension-public] so they are not visible to end users. - -Then, define a string dimension (`fiscal_quarter`) that would format the -fiscal quarter value and expose it to end users: - -```yml -cubes: - - name: fiscal - sql: > - SELECT '2024-01-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-02-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-03-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-04-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-05-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-06-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-07-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-08-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-09-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-10-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-11-15T00:00:00.000Z'::TIMESTAMP AS timestamp UNION ALL - SELECT '2024-12-15T00:00:00.000Z'::TIMESTAMP AS timestamp - - dimensions: - - name: timestamp - sql: timestamp - type: time - - # TODO: Adjust to your fiscal calendar - - name: fiscal_year_internal - sql: "EXTRACT(YEAR FROM {timestamp} + INTERVAL '6 MONTH')" - type: string - public: false - - # TODO: Adjust to your fiscal calendar - - name: fiscal_quarter_internal - sql: > - CASE - WHEN EXTRACT(MONTH FROM {timestamp}) BETWEEN 7 AND 9 THEN 1 - WHEN EXTRACT(MONTH FROM {timestamp}) BETWEEN 10 AND 12 THEN 2 - WHEN EXTRACT(MONTH FROM {timestamp}) BETWEEN 1 AND 3 THEN 3 - WHEN EXTRACT(MONTH FROM {timestamp}) BETWEEN 4 AND 6 THEN 4 - END - type: string - public: false - - - name: fiscal_quarter - sql: > - 'FY' || {fiscal_year_internal} || '-Q' || {fiscal_quarter_internal} - type: string -``` - -## Result - -Now you can use the `fiscal_quarter` dimension in your queries and get desired result: - - - - -[wiki-fiscal-year]: https://en.wikipedia.org/wiki/Fiscal_year -[wiki-fiscal-year-australia]: https://en.wikipedia.org/wiki/Fiscal_year#Australia - -[ref-dimension-public]: /reference/data-model/dimensions#public \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/rest-api/query-format.mdx b/docs/pages/product/apis-integrations/rest-api/query-format.mdx index 6c0f1846ff5ff..350580c3ec5d4 100644 --- a/docs/pages/product/apis-integrations/rest-api/query-format.mdx +++ b/docs/pages/product/apis-integrations/rest-api/query-format.mdx @@ -5,19 +5,17 @@ redirect_from: # Query format in the REST API -Cube Queries are plain JavaScript objects, describing an analytics query. The -basic elements of a query (query members) are `measures`, `dimensions`, and -`segments`. +Queries to the REST API are plain JavaScript objects, describing an analytics +query. The basic elements of a query (query members) are `measures`, `dimensions`, +and `segments`. -The query member format name is `CUBE_NAME.MEMBER_NAME`, for example the -dimension `email` in the Cube `Users` would have the name `Users.email`. +The query member format name is `cube_name.member_name`, for example the `email` +dimension in the `users` cube would have the `users.email` name. -In the case of dimension of type `time` granularity could be optionally added to -the name, in the following format `CUBE_NAME.TIME_DIMENSION_NAME.GRANULARITY`, -ex: `stories.time.month`. - -Supported granularities: `second`, `minute`, `hour`, `day`, `week`, `month`, -`quarter` and `year`. +In the case of a dimension of the `time` type, a granularity could be optionally +added to the name, in the following format: `cube_name.time_dimension_name.granularity_name`, +e.g., `stories.time.week`. It can be one of the [default granularities][ref-default-granularities] +(e.g., `year` or `week`) or a [custom granularity][ref-custom-granularities]. The Cube client also accepts an array of queries. By default, it will be treated as a Data Blending query type. @@ -95,8 +93,8 @@ query][ref-ungrouped-query]. If the `order` property is not specified in the query, Cube sorts results by default using the following rules: -- The first time dimension with granularity, ascending. If no time dimension - with granularity exists... +- The first time dimension with a granularity, ascending. If no time dimension + with a granularity exists... - The first measure, descending. If no measure exists... - The first dimension, ascending. @@ -530,9 +528,10 @@ provides a convenient shortcut to pass a dimension and a filter as a range][ref-relative-date-range], for example, `last quarter`. - `compareDateRange`: An array of date ranges to compare measure values. See [compare date range queries][ref-compare-date-range] for details. -- `granularity`: A granularity for a time dimension. It supports the following - values `second`, `minute`, `hour`, `day`, `week`, `month`, `quarter`, `year`. - If you pass `null` to the granularity, Cube will only perform filtering by a +- `granularity`: A granularity for a time dimension. It can be one of the [default +granularities][ref-default-granularities] (e.g., `year` or `week`) or a [custom +granularity][ref-custom-granularities]. + If you don't provide a granularity, Cube will only perform filtering by a specified time dimension, without grouping. ```json @@ -617,4 +616,6 @@ refer to its documentation for more examples. [ref-compare-date-range]: /product/apis-integrations/queries#compare-date-range-query [ref-total-query]: /product/apis-integrations/queries#total-query [ref-ungrouped-query]: /product/apis-integrations/queries#ungrouped-query -[ref-default-order]: /product/apis-integrations/queries#order \ No newline at end of file +[ref-default-order]: /product/apis-integrations/queries#order +[ref-default-granularities]: /product/data-modeling/concepts#time-dimensions +[ref-custom-granularities]: /reference/data-model/dimensions#granularities \ No newline at end of file diff --git a/docs/pages/product/data-modeling/concepts.mdx b/docs/pages/product/data-modeling/concepts.mdx index 0ddcbba41153a..a691752116ef4 100644 --- a/docs/pages/product/data-modeling/concepts.mdx +++ b/docs/pages/product/data-modeling/concepts.mdx @@ -286,11 +286,27 @@ implicitly. Dimensions can be of different types. See the [dimension type reference][ref-schema-dimension-types] for details. -Time-based properties should be modeled as dimensions of the `time` type. -Time dimensions allow grouping the result set by a unit of time (e.g. hours, -days, weeks, etc.). In analytics, this is also known as "granularity". +### Time dimensions -We can add the necessary time dimensions to both data models as follows: +Time-based properties are modeled using dimensions of the [`time` +type][ref-ref-time-dimensions]. They allow grouping the result set by a unit of +time (e.g., days, weeks, month, etc.), also known as the *time dimension +granularity*. + +The following granularities are available by default for any time dimension: +`year`, `quarter`, `month`, `week` (starting on Monday), `day`, `hour`, `minute`, +`second`. You can also define [custom granularities][ref-ref-dimension-granularities] +and optionally expose them via [proxy dimensions][ref-proxy-granularity] in case +you need to use weeks starting on Sunday, fiscal years, etc. + + + +See [this recipe][ref-custom-granularity-recipe] for more custom granularity +examples. + + + +Here's how we can add time dimensions to the data model: @@ -301,15 +317,25 @@ cube(`orders`, { dimensions: { created_at: { sql: `created_at`, - type: `time`, + type: `time` + // You can use this time dimension with all default granularities: + // year, quarter, month, week, day, hour, minute, second }, completed_at: { sql: `completed_at`, type: `time`, - }, - }, -}); + // You can use this time dimension with all default granularities + // and an additional custom granularity defined below + granularities: { + fiscal_year_starting_on_february_01: { + interval: `1 year`, + offset: `1 month` + } + } + } + } +}) ``` ```yaml @@ -321,38 +347,18 @@ cubes: - name: created_at sql: created_at type: time + # You can use this time dimension with all default granularities: + # year, quarter, month, week, day, hour, minute, second - name: completed_at sql: completed_at type: time -``` - - - - - -```javascript -cube(`line_items`, { - // ... - - dimensions: { - created_at: { - sql: `created_at`, - type: `time`, - }, - }, -}); -``` - -```yaml -cubes: - - name: line_items - # ... - - dimensions: - - name: created_at - sql: created_at - type: time + # You can use this time dimension with all default granularities + # and an additional custom granularity defined below + granularities: + - name: fiscal_year_starting_on_february_01 + interval: 1 year + offset: 1 month ``` @@ -693,7 +699,7 @@ Pre-Aggregations][ref-caching-preaggs-intro]. /product/apis-integrations/rest-api/query-format#filters-format [ref-caching-preaggs-intro]: /product/caching/getting-started-pre-aggregations [ref-caching-use-preaggs-partition-time]: - /product/caching/using-pre-aggregations#time-partitioning + /product/caching/using-pre-aggregations#partitioning [ref-schema-dimension-types]: /reference/data-model/types-and-formats#dimension-types [ref-schema-measure-types]: @@ -720,4 +726,8 @@ Pre-Aggregations][ref-caching-preaggs-intro]. [ref-syntax-references-column]: /product/data-modeling/syntax#column [ref-calculated-measures]: /product/data-modeling/overview#4-using-calculated-measures [ref-multitenancy]: /product/configuration/advanced/multitenancy -[ref-pmc]: /product/deployment/cloud/deployment-types#production-multi-cluster \ No newline at end of file +[ref-pmc]: /product/deployment/cloud/deployment-types#production-multi-cluster +[ref-ref-time-dimensions]: /reference/data-model/types-and-formats#time-1 +[ref-ref-dimension-granularities]: /reference/data-model/dimensions#granularities +[ref-custom-granularity-recipe]: /guides/recipes/data-modeling/custom-granularity +[ref-proxy-granularity]: /product/data-modeling/concepts/calculated-members#time-dimension-granularity \ No newline at end of file diff --git a/docs/pages/product/data-modeling/concepts/calculated-members.mdx b/docs/pages/product/data-modeling/concepts/calculated-members.mdx index ef8534f2eefa7..aacc6218947bd 100644 --- a/docs/pages/product/data-modeling/concepts/calculated-members.mdx +++ b/docs/pages/product/data-modeling/concepts/calculated-members.mdx @@ -92,8 +92,76 @@ FROM ( ## Proxy dimensions -**Proxy dimensions reference dimensions from other cubes.** Proxy dimensions provide -a way to reference some dimensions as if they were defined in multiple cubes. +**Proxy dimensions reference dimensions from the same cube or other cubes.** +Proxy dimensions are convenient for reusing existing dimensions when definiting +new ones. + +### Members of the same cube + +If you have a dimension with a non-trivial definition, you can reference that +dimension to reuse the existing definition and reduce code duplication. + +In the following example, the `full_name` dimension references `initials` and +`last_name` dimensions of the same cube: + + + +```yaml +cubes: + - name: users + sql_table: users + + dimensions: + - name: initials + sql: "SUBSTR(first_name, 1, 1)" + type: string + + - name: last_name + sql: "UPPER(last_name)" + type: string + + - name: full_name + sql: "{initials} || '. ' || {last_name}" + type: string +``` + +```javascript +cube(`users`, { + sql_table: `users`, + + dimensions: { + initials: { + sql: `SUBSTR(first_name, 1, 1)`, + type: `string` + }, + + last_name: { + sql: `UPPER(last_name)`, + type: `string` + }, + + full_name: { + sql: `${initials} || '. ' || ${last_name}`, + type: `string` + } + } +}) +``` + + + +If you query for `users.full_name`, Cube will generate the following SQL: + +```sql +SELECT + SUBSTR(first_name, 1, 1) || '. ' || UPPER(last_name) "users__full_name" +FROM + users AS "users" +GROUP BY + 1 +``` + +### Members of other cubes If you have `first_cube` that is [joined][ref-joins] to `second_cube`, you can use a proxy dimension to bring `second_cube.dimension` to `first_cube` as `dimension` (or @@ -228,6 +296,94 @@ FROM ( GROUP BY 1 ``` +### Time dimension granularity + +When referencing a [time dimension][ref-time-dimension] of the same or another +cube, you can specificy a granularity to refer to a time value with that specific +granularity. It can be one of the [default granularities][ref-default-granularities] +(e.g., `year` or `week`) or a [custom granularity][ref-custom-granularities]: + + + +```yaml +cubes: + - name: users + sql: > + SELECT '2025-01-01T00:00:00Z' AS created_at UNION ALL + SELECT '2025-02-01T00:00:00Z' AS created_at UNION ALL + SELECT '2025-03-01T00:00:00Z' AS created_at + + dimensions: + - name: created_at + sql: created_at + type: time + + granularities: + - name: sunday_week + interval: 1 week + offset: -1 day + + - name: created_at__year + sql: "{created_at.year}" + type: time + + - name: created_at__sunday_week + sql: "{created_at.sunday_week}" + type: time +``` + +```javascript +cube(`users`, { + sql: ` + SELECT '2025-01-01T00:00:00Z' AS created_at UNION ALL + SELECT '2025-02-01T00:00:00Z' AS created_at UNION ALL + SELECT '2025-03-01T00:00:00Z' AS created_at + `, + + dimensions: { + created_at: { + sql: `created_at`, + type: `time`, + + granularities: { + sunday_week: { + interval: `1 week`, + offset: `-1 day` + } + } + }, + + created_at__year: { + sql: `${created_at.year}`, + type: `time` + }, + + created_at__sunday_week: { + sql: `${created_at.sunday_week}`, + type: `time` + } + } +}) +``` + + + +If you query for `users.created_at`, `users.created_at__sunday_week`, and +`users.created_at__year` dimensions, Cube will generate the following SQL: + +```sql +SELECT + "users".created_at "users__created_at", + date_trunc('week', ("users".created_at::timestamptz AT TIME ZONE 'UTC') - interval '-1 day') + interval '-1 day' "users__created_at__sunday_week", + date_trunc('year', ("users".created_at::timestamptz AT TIME ZONE 'UTC')) "users__created_at__year" +FROM ( + SELECT '2025-01-01T00:00:00Z' AS created_at UNION ALL + SELECT '2025-02-01T00:00:00Z' AS created_at UNION ALL + SELECT '2025-03-01T00:00:00Z' AS created_at +) AS "users" +GROUP BY 1, 2, 3 +``` + ## Subquery dimensions **Subquery dimensions reference measures from other cubes.** Subquery dimensions @@ -406,6 +562,8 @@ GROUP BY 1, 2 [ref-decomposition-recipe]: /guides/recipes/query-acceleration/non-additivity#decomposing-into-a-formula-with-additive-measures [ref-nested-aggregates-recipe]: /guides/recipes/data-modeling/nested-aggregates [ref-non-additive]: /product/data-modeling/concepts#measure-additivity - [link-postgres-division]: https://www.postgresql.org/docs/current/functions-math.html#FUNCTIONS-MATH -[wiki-correlated-subquery]: https://en.wikipedia.org/wiki/Correlated_subquery \ No newline at end of file +[wiki-correlated-subquery]: https://en.wikipedia.org/wiki/Correlated_subquery +[ref-time-dimension]: /reference/data-model/types-and-formats#time-1 +[ref-default-granularities]: /product/data-modeling/concepts#time-dimensions +[ref-custom-granularities]: /reference/data-model/dimensions#granularities \ No newline at end of file diff --git a/docs/pages/product/data-modeling/syntax.mdx b/docs/pages/product/data-modeling/syntax.mdx index ffffadbb46487..2a8b82d9f8e88 100644 --- a/docs/pages/product/data-modeling/syntax.mdx +++ b/docs/pages/product/data-modeling/syntax.mdx @@ -116,10 +116,6 @@ cubes: - name: status sql: "UPPER(status)" type: string - - - - ``` ```javascript @@ -181,7 +177,7 @@ cube(`orders`, { ## References -To write reusable data models, it is important to be able to reference +To write versatile data models, it is important to be able to reference members of cubes and views, such as measures or dimensions, as well as table columns. Cube supports the following syntax for references. @@ -202,8 +198,6 @@ cubes: - name: name sql: name type: string - - ``` ```javascript @@ -251,10 +245,6 @@ cubes: - name: full_name sql: "CONCAT({name}, ' ', {surname})" type: string - - - - ``` ```javascript @@ -286,6 +276,71 @@ This syntax works great for simple use cases. However, there are cases (like [subquery][ref-subquery]) when you'd like to reference members of other cubes. See below how to do that. +### `{time_dimension.granularity}` + +When referencing a [time dimension][ref-time-dimension], you can specificy a +granularity to refer to a time value with that specific granularity. It can be +one of the [default granularities][ref-default-granularities] (e.g., `year` or +`week`) or a [custom granularity][ref-custom-granularities]: + + + +```yaml +cubes: + - name: users + sql_table: users + + dimensions: + - name: created_at + sql: created_at + type: time + + granularities: + - name: sunday_week + interval: 1 week + offset: -1 day + + - name: created_at__year + sql: "{created_at.year}" + type: time + + - name: created_at__sunday_week + sql: "{created_at.sunday_week}" + type: time +``` + +```javascript +cube(`users`, { + sql_table: `users`, + + dimensions: { + created_at: { + sql: `created_at`, + type: `time`, + + granularities: { + sunday_week: { + interval: `1 week`, + offset: `-1 day` + } + } + }, + + created_at__year: { + sql: `${created_at.year}`, + type: `time` + }, + + created_at__sunday_week: { + sql: `${created_at.sunday_week}`, + type: `time` + } + } +}) +``` + + + ### `{cube}.column`, `{cube.member}` You can qualify column and member names with the name of a cube to remove @@ -325,15 +380,6 @@ cubes: - name: name sql: "{contacts}.name" type: string - - - - - - - - - ``` ```javascript @@ -470,15 +516,6 @@ cubes: - name: name sql: "{CUBE}.name" type: string - - - - - - - - - ``` @@ -533,10 +570,6 @@ cubes: - CUBE.status measures: - CUBE.count - - - - ``` ```javascript @@ -592,4 +625,7 @@ defining dynamic data models. [link-snowflake-listagg]: https://docs.snowflake.com/en/sql-reference/functions/listagg [link-bigquery-stringagg]: https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#string_agg [link-sql-udf]: https://en.wikipedia.org/wiki/User-defined_function#Databases -[ref-style-guide]: /guides/style-guide \ No newline at end of file +[ref-time-dimension]: /reference/data-model/types-and-formats#time-1 +[ref-default-granularities]: /product/data-modeling/concepts#time-dimensions +[ref-custom-granularities]: /reference/data-model/dimensions#granularities +[ref-style-guide]: /guides/style-guide diff --git a/docs/pages/reference/data-model/dimensions.mdx b/docs/pages/reference/data-model/dimensions.mdx index a18620076cf9a..223fa2e5b6a41 100644 --- a/docs/pages/reference/data-model/dimensions.mdx +++ b/docs/pages/reference/data-model/dimensions.mdx @@ -363,12 +363,12 @@ cube(`products`, { dimensions: { comment: { - sql: `comments`, + sql: `comment`, type: `string`, - public: false, - }, - }, -}); + public: false + } + } +}) ``` ```yaml @@ -378,9 +378,9 @@ cubes: dimensions: - name: comment - sql: comments + sql: comment + type: string public: false - shown: false ``` @@ -525,6 +525,103 @@ cubes: +### `granularities` + +By default, the following granularities are available for time dimensions: +`year`, `quarter`, `month`, `week` (starting on Monday), `day`, `hour`, `minute`, +`second`. + +You can use the `granularities` parameter with any dimension of the [type +`time`][ref-time-dimensions] to define one or more custom granularities, such as +a *week starting on Sunday* or a *fiscal year*. + + + +See [this recipe][ref-custom-granularity-recipe] for more custom granularity +examples. + + + +For each custom granularity, the `interval` parameter is required. It specifies +the duration of the time interval and has the following format: +`quantity unit [quantity unit...]`, e.g., `5 days` or `1 year 6 months`. + +Optionally, a custom granularity might use the `offset` parameter to specify how +the time interval is shifted forward or backward in time. It has the the same +format as `interval`, however, you can also provide negative quantities, e.g., +`-1 day` or `1 month -10 days`. + +Alternatively, instead of `offset`, you can provide the `origin` parameter. +When `origin` is provided, time intervals will be shifted in a way that one of +them will match the provided origin. It accepts an ISO 8601-compliant [date time +string][link-date-time-string], e.g., `2024-01-02` or `2024-01-02T12:00:00.000Z`. + +Optionally, a custom granularity might have the `title` parameter with a +human-friendly description. + + + +```yaml +cubes: + - name: orders + sql: > + SELECT '2025-01-01T00:12:00.000Z'::TIMESTAMP AS time UNION ALL + SELECT '2025-02-01T00:15:00.000Z'::TIMESTAMP AS time UNION ALL + SELECT '2025-03-01T00:18:00.000Z'::TIMESTAMP AS time + + dimensions: + - name: time + sql: time + type: time + + granularities: + - name: quarter_hour + interval: 15 minutes + + - name: week_starting_on_sunday + interval: 1 week + offset: -1 day + + - name: fiscal_year_starting_on_april_01 + title: Corporate and government fiscal year in the United Kingdom + interval: 1 year + origin: "2025-04-01" + # You have to use quotes here to make `origin` a valid YAML string +``` + +```javascript +cube(`orders`, { + sql: ` + SELECT '2025-01-01T00:12:00.000Z'::TIMESTAMP AS time UNION ALL + SELECT '2025-02-01T00:15:00.000Z'::TIMESTAMP AS time UNION ALL + SELECT '2025-03-01T00:18:00.000Z'::TIMESTAMP AS time + `, + + dimensions: { + time: { + sql: `time`, + type: `time`, + granularities: { + quarter_hour: { + interval: `15 minutes` + }, + week_starting_on_sunday: { + interval: `1 week`, + offset: `-1 day` + }, + fiscal_year_starting_on_april_01: { + title: `Corporate and government fiscal year in the United Kingdom`, + interval: `1 year`, + origin: `2025-04-01` + } + } + } + } +}) +``` + + + [ref-schema-ref-joins]: /reference/data-model/joins [ref-subquery]: /product/data-modeling/concepts/calculated-members#subquery-dimensions @@ -535,4 +632,7 @@ cubes: [ref-schema-ref-dims-formats]: /reference/data-model/types-and-formats#dimension-formats [ref-playground]: /product/workspace/playground -[ref-apis]: /product/apis-integrations \ No newline at end of file +[ref-apis]: /product/apis-integrations +[ref-time-dimensions]: /reference/data-model/types-and-formats#time-1 +[link-date-time-string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format +[ref-custom-granularity-recipe]: /guides/recipes/data-modeling/custom-granularity \ No newline at end of file diff --git a/docs/redirects.json b/docs/redirects.json index c533a15924324..62f19d77c508f 100644 --- a/docs/redirects.json +++ b/docs/redirects.json @@ -1,4 +1,9 @@ [ + { + "source": "/guides/recipes/data-modeling/fiscal-year-quarter-dimensions", + "destination": "/guides/recipes/data-modeling/custom-granularity", + "permanent": true + }, { "source": "/product/workspace/semantic-layer-sync", "destination": "/product/apis-integrations/semantic-layer-sync",