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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/pages/guides/recipes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ These recipes will show you the best practices of using Cube.
### Analytics

- [Calculating daily, weekly, monthly active users](/guides/recipes/analytics/active-users)
- [Calculating the internal rate of return (XIRR)](/guides/recipes/analytics/xirr)
- [Implementing event analytics](/guides/recipes/analytics/event-analytics)
- [Implementing funnel analysis](/guides/recipes/analytics/funnels)
- [Implementing retention analysis & cohorts](/guides/recipes/analytics/cohort-retention)
Expand Down
1 change: 1 addition & 0 deletions docs/pages/guides/recipes/analytics/_meta.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
"active-users": "Daily, Weekly, Monthly Active Users (DAU, WAU, MAU)",
"xirr": "XIRR",
"event-analytics": "Implementing event analytics",
"cohort-retention": "Implementing retention analysis & cohorts",
"funnels": "Implementing Funnel Analysis"
Expand Down
202 changes: 202 additions & 0 deletions docs/pages/guides/recipes/analytics/xirr.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Calculating the internal rate of return (XIRR)

## Use case

We'd like to calculate the internal rate of return (XIRR) for a schedule of cash
flows that is not necessarily periodic.

## Data modeling

XIRR calculation is enabled by the `XIRR` function, implemented in [SQL API][ref-sql-api],
[DAX API][ref-dax-api], and [MDX API][ref-mdx-api]. It means that queries to any of these
APIs can use the this function.

The `XIRR` function is also implemented in Cube Store, meaning that queries to the SQL API
or the [REST API][ref-rest-api] that hit pre-aggregations can also use this function.
That function would need to be used in a measure that makes use of [multi-stage
calculations][ref-multi-stage-calculations].

<InfoBox>

Consequently, queries that don't hit pre-aggregations would fail with the following error:
`function xirr(numeric, date) does not exist`.

</InfoBox>

<WarningBox>

Multi-stage calculations are powered by Tesseract, the [next-generation data modeling
engine][link-tesseract]. Tesseract is currently in preview. Use the
`CUBEJS_TESSERACT_SQL_PLANNER` environment variable to enable it.

</WarningBox>

Consider the following data model:

<CodeTabs>

```yaml
cubes:
- name: payments
sql: >
SELECT '2014-01-01'::date AS date, -10000.0 AS payment UNION ALL
SELECT '2014-03-01'::date AS date, 2750.0 AS payment UNION ALL
SELECT '2014-10-30'::date AS date, 4250.0 AS payment UNION ALL
SELECT '2015-02-15'::date AS date, 3250.0 AS payment UNION ALL
SELECT '2015-04-01'::date AS date, 2750.0 AS payment

dimensions:
- name: date
sql: date
type: time

- name: payment
sql: payment
type: number

# Everything below this line is only needed for querying
# pre-aggregations in Cube Store
dimensions:
- name: date__day
sql: "{date.day}"
type: time

measures:
- name: total_payments
sql: payment
type: sum

- name: xirr
multi_stage: true
sql: "XIRR({total_payments}, {date__day})"
type: number_agg
add_group_by:
- date__day

pre_aggregations:
- name: main_xirr
measures:
- total_payments
time_dimension: date
granularity: day
```

```javascript
cube(`payments`, {
sql: `
SELECT '2014-01-01'::date AS date, -10000.0 AS payment UNION ALL
SELECT '2014-03-01'::date AS date, 2750.0 AS payment UNION ALL
SELECT '2014-10-30'::date AS date, 4250.0 AS payment UNION ALL
SELECT '2015-02-15'::date AS date, 3250.0 AS payment UNION ALL
SELECT '2015-04-01'::date AS date, 2750.0 AS payment
`,

dimensions: {
date: {
sql: `date`,
type: `time`
},

payment: {
sql: `payment`,
type: `number`
},

// Everything below this line is only needed for querying
// pre-aggregations in Cube Store
date__day: {
sql: `${CUBE.date.day}`,
type: `time`
}
},

measures: {
total_payments: {
sql: `payment`,
type: `sum`
},

xirr: {
multi_stage: true,
sql: `XIRR(${CUBE.total_payments}, ${CUBE.date__day})`,
type: `number_agg`,
add_group_by: [
date__day
]
}
},

pre_aggregations: {
main_xirr: {
measures: [
total_payments
],
time_dimension: date,
granularity: `day`
}
}
})
```

</CodeTabs>

## Query

### DAX API

You can use the `XIRR` function in DAX.

### SQL API

[Query with post-processing][ref-query-wpp] in the SQL API:

```sql
SELECT
XIRR(payment, date) AS xirr
FROM (
SELECT
DATE_TRUNC('DAY', date) AS date,
SUM(payment) AS payment
FROM payments
GROUP BY 1
) AS payments;
```

[Regular query][ref-query-regular] in the SQL API that hits a pre-aggregation in Cube Store:

```sql
SELECT MEASURE(xirr) AS xirr
FROM payments;
```

### REST API

Regular query in the REST API that hits a pre-aggregation in Cube Store:

```json
{
"measures": [
"payments.xirr"
]
}
```

## Result

All queries above would yield the same result:

```
xirr
--------------------
0.3748585976775555
```


[ref-sql-api]: /product/apis-integrations/sql-api/reference#custom-functions
[ref-dax-api]: /product/apis-integrations/dax-api/reference#financial-functions
[ref-mdx-api]: /product/apis-integrations/mdx-api
[ref-rest-api]: /product/apis-integrations/rest-api
[ref-query-wpp]: /product/apis-integrations/queries#query-with-post-processing
[ref-query-regular]: /product/apis-integrations/queries#regular-query
[link-tesseract]: https://cube.dev/blog/introducing-next-generation-data-modeling-engine
[ref-multi-stage-calculations]: /product/data-modeling/concepts/multi-stage-calculations
10 changes: 9 additions & 1 deletion docs/pages/product/apis-integrations/dax-api/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,15 @@ of the DAX documentation.

</InfoBox>

No financial functions currently supported.
| Function | <nobr>Unsupported features</nobr> | Caveats |
| --- | --- | --- |
| [`XIRR`](https://learn.microsoft.com/en-us/dax/xirr-function-dax) | — | — |

<ReferenceBox>

See the [XIRR recipe](/guides/recipes/analytics/xirr) for more details.

</ReferenceBox>

### INFO functions

Expand Down
19 changes: 16 additions & 3 deletions docs/pages/product/apis-integrations/sql-api/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ SHOW ALL;
## SQL functions and operators

SQL API currently implements a subset of functions and operators [supported by
PostgreSQL][link-postgres-funcs].
PostgreSQL][link-postgres-funcs]. Additionally, it supports a few [custom
functions](#custom-functions).

### Comparison operators

Expand Down Expand Up @@ -407,12 +408,24 @@ of the PostgreSQL documentation.
| `IN` | Returns `TRUE` if a left-side value matches **any** of right-side values | ✅ Yes | <nobr>✅ Outer</nobr><br/><nobr>✅ Inner (selections)</nobr><br/><nobr>✅ Inner (projections)</nobr> |
| `NOT IN` | Returns `TRUE` if a left-side value matches **none** of right-side values | ✅ Yes | <nobr>✅ Outer</nobr><br/><nobr>✅ Inner (selections)</nobr><br/><nobr>✅ Inner (projections)</nobr> |

### Custom functions

| Function | Description |
| --- | --- |
| `XIRR` | Calculates the [internal rate of return][link-xirr] for a series of cash flows |

<ReferenceBox>

See the [XIRR recipe](/guides/recipes/analytics/xirr) for more details.

</ReferenceBox>


[ref-qpd]: /product/apis-integrations/sql-api/query-format#query-pushdown
[ref-qpp]: /product/apis-integrations/sql-api/query-format#query-post-processing
[ref-sql-api]: /product/apis-integrations/sql-api
[ref-sql-api-aggregate-functions]: /product/apis-integrations/sql-api/query-format#aggregate-functions

[link-postgres-funcs]: https://www.postgresql.org/docs/current/functions.html
[link-github-sql-api]: https://github.com/cube-js/cube/issues?q=is%3Aopen+is%3Aissue+label%3Aapi%3Asql
[link-github-new-sql-api-issue]: https://github.com/cube-js/cube/issues/new?assignees=&labels=&projects=&template=sql_api_query_issue.md&title=
[link-github-new-sql-api-issue]: https://github.com/cube-js/cube/issues/new?assignees=&labels=&projects=&template=sql_api_query_issue.md&title=
[link-xirr]: https://support.microsoft.com/en-us/office/xirr-function-de1242ec-6477-445b-b11b-a303ad9adc9d
73 changes: 58 additions & 15 deletions docs/pages/reference/data-model/types-and-formats.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
---
redirect_from:
- /types-and-formats
- /schema/reference/types-and-formats
---

# Types and Formats

## Measure Types
Expand Down Expand Up @@ -139,7 +133,7 @@ cubes:

### `number`

Type `number` is usually used, when performing
The `number` type is usually used, when performing arithmetic operations on
arithmetic operations on measures. [Learn more about Calculated
Measures][ref-schema-ref-calc-measures].

Expand Down Expand Up @@ -207,10 +201,59 @@ cubes:

</CodeTabs>

### `number_agg`

The `number_agg` type is used when you need to write a custom aggregate function
in the `sql` parameter that isn't covered by standard measure types like `sum`,
`avg`, `min`, etc.

<WarningBox>

The `number_agg` type is only available in Tesseract, the [next-generation data modeling
engine][link-tesseract]. Tesseract is currently in preview. Use the
`CUBEJS_TESSERACT_SQL_PLANNER` environment variable to enable it.

</WarningBox>

Unlike the `number` type which is used for calculations on measures (e.g.,
`SUM(revenue) / COUNT(*)`), `number_agg` indicates that the `sql` parameter contains
a direct SQL aggregate function.

The `sql` parameter is required and must include a custom aggregate function that returns a numeric
value.

<CodeTabs>

```javascript
cube(`orders`, {
// ...

measures: {
median_price: {
sql: `PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY price)`,
type: `number_agg`
}
}
})
```

```yaml
cubes:
- name: orders
# ...

measures:
- name: median_price
sql: "PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY price)"
type: number_agg
```

</CodeTabs>

### `count`

Performs a table count, similar to SQLs `COUNT` function. However, unlike
writing raw SQL, Cube will properly calculate counts even if your querys
Performs a table count, similar to SQL's `COUNT` function. However, unlike
writing raw SQL, Cube will properly calculate counts even if your query's
joins will produce row multiplication.

You do not need to include a `sql` parameter for this type.
Expand Down Expand Up @@ -254,7 +297,7 @@ cubes:

### `count_distinct`

Calculates the number of distinct values in a given field. It makes use of SQLs
Calculates the number of distinct values in a given field. It makes use of SQL's
`COUNT DISTINCT` function.

The `sql` parameter is required and must include any valid SQL expression
Expand Down Expand Up @@ -332,9 +375,9 @@ cubes:

### `sum`

Adds up the values in a given field. It is similar to SQLs `SUM` function.
Adds up the values in a given field. It is similar to SQL's `SUM` function.
However, unlike writing raw SQL, Cube will properly calculate sums even if your
querys joins will result in row duplication.
query's joins will result in row duplication.

The `sql` parameter is required and must include any valid SQL expression
of the numeric type (without an aggregate function).
Expand Down Expand Up @@ -387,9 +430,9 @@ cubes:

### `avg`

Averages the values in a given field. It is similar to SQLs AVG function.
Averages the values in a given field. It is similar to SQL's AVG function.
However, unlike writing raw SQL, Cube will properly calculate averages even if
your querys joins will result in row duplication.
your query's joins will result in row duplication.

The `sql` parameter is required and must include any valid SQL expression
of the numeric type (without an aggregate function).
Expand Down Expand Up @@ -494,7 +537,7 @@ cubes:

## Measure Formats

When creating a **measure** you can explicitly define the format youd like to
When creating a **measure** you can explicitly define the format you'd like to
see as output.

### `percent`
Expand Down