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
4 changes: 3 additions & 1 deletion packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -692,9 +692,11 @@ export class BaseQuery {
rowLimit: this.options.rowLimit ? this.options.rowLimit.toString() : null,
offset: this.options.offset ? this.options.offset.toString() : null,
baseTools: this,
ungrouped: this.options.ungrouped
ungrouped: this.options.ungrouped,
exportAnnotatedSql: exportAnnotatedSql === true

};

const buildResult = nativeBuildSqlAndParams(queryParams);

if (buildResult.error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PostgresQuery } from '../../../src/adapter/PostgresQuery';
import { prepareYamlCompiler } from '../../unit/PrepareCompiler';
import { dbRunner } from './PostgresDBRunner';

describe('Member Expression', () => {
describe('Member Expression Multistage', () => {
jest.setTimeout(200000);

const { compiler, joinGraph, cubeEvaluator } = prepareYamlCompiler(`
Expand Down Expand Up @@ -37,6 +37,170 @@ cubes:
- name: count
type: count

- name: orders
sql: >
select 10 AS ID, 'complited' AS STATUS, '2021-01-05 00:00:00'::timestamp AS CREATED_AT, 100 AS CUSTOMER_ID, 50.0 as revenue
UNION ALL
select 11 AS ID, 'complited' AS STATUS, '2021-05-01 00:00:00'::timestamp AS CREATED_AT, 100 AS CUSTOMER_ID, 150.0 as revenue
UNION ALL
select 12 AS ID, 'complited' AS STATUS, '2021-06-01 00:00:00'::timestamp AS CREATED_AT, 100 AS CUSTOMER_ID, 200.0 as revenue
UNION ALL
select 13 AS ID, 'complited' AS STATUS, '2022-01-04 00:00:00'::timestamp AS CREATED_AT, 100 AS CUSTOMER_ID, 10.0 as revenue
UNION ALL
select 14 AS ID, 'complited' AS STATUS, '2022-05-04 00:00:00'::timestamp AS CREATED_AT, 100 AS CUSTOMER_ID, 30.0 as revenue
public: false

joins:
- name: line_items
sql: "{CUBE}.ID = {line_items}.order_id"
relationship: many_to_one

- name: customers
sql: "{CUBE}.CUSTOMER_ID = {customers}.ID"
relationship: many_to_one

dimensions:
- name: id
sql: ID
type: number
primary_key: true

- name: status
sql: STATUS
type: string

- name: date
sql: CREATED_AT
type: time

- name: amount
sql: '{line_items.total_amount}'
type: number
sub_query: true

measures:
- name: count
type: count

- name: completed_count
type: count
filters:
- sql: "{CUBE}.STATUS = 'completed'"

- name: returned_count
type: count
filters:
- sql: "{CUBE}.STATUS = 'returned'"

- name: return_rate
type: number
sql: "({returned_count} / NULLIF({completed_count}, 0)) * 100.0"
description: "Percentage of returned orders out of completed, exclude just placed orders."
format: percent

- name: total_amount
sql: '{CUBE.amount}'
type: sum

- name: revenue
sql: "revenue"
type: sum
format: currency

- name: average_order_value
sql: '{CUBE.amount}'
type: avg

- name: revenue_1_y_ago
sql: "{revenue}"
multi_stage: true
type: number
format: currency
time_shift:
- time_dimension: date
interval: 1 year
type: prior
- time_dimension: orders_view.date
interval: 1 year
type: prior

- name: cagr_1_y
sql: "(({revenue} / {revenue_1_y_ago}) - 1)"
type: number
format: percent
description: "Annual CAGR, year over year growth in revenue"

- name: line_items
sql: >
SELECT 10 AS ID, 10 AS PRODUCT_ID, '2021-01-01 00:00:00'::timestamp AS CREATED_AT, 10 as order_id
UNION ALL
SELECT 11 AS ID, 10 AS PRODUCT_ID, '2021-01-01 00:00:00'::timestamp AS CREATED_AT, 11 as order_id
UNION ALL
SELECT 12 AS ID, 10 AS PRODUCT_ID, '2021-01-01 00:00:00'::timestamp AS CREATED_AT, 11 as order_id
UNION ALL
SELECT 13 AS ID, 10 AS PRODUCT_ID, '2021-01-01 00:00:00'::timestamp AS CREATED_AT, 12 as order_id
public: false

joins:
- name: products
sql: "{CUBE}.PRODUCT_ID = {products}.ID"
relationship: many_to_one

dimensions:
- name: id
sql: ID
type: number
primary_key: true

- name: created_at
sql: CREATED_AT
type: time

- name: price
sql: "{products.price}"
type: number

measures:
- name: count
type: count

- name: total_amount
sql: "{price}"
type: sum
- name: products
sql: >
SELECT 10 AS ID, 'Clothes' AS PRODUCT_CATEGORY, 'Shirt' AS NAME, 10 AS PRICE
UNION ALL
SELECT 11 AS ID, 'Clothes' AS PRODUCT_CATEGORY, 'Shirt' AS NAME, 20 AS PRICE
public: false
description: >
Products and categories in our e-commerce store.

dimensions:
- name: id
sql: ID
type: number
primary_key: true

- name: product_category
sql: PRODUCT_CATEGORY
type: string

- name: name
sql: NAME
type: string

- name: price
sql: PRICE
type: number

measures:
- name: count
type: count




views:
- name: customers_view

Expand All @@ -47,6 +211,29 @@ views:

- city

- name: orders_view
cubes:
- join_path: orders
includes:
- count
- date
- revenue
- cagr_1_y
- return_rate

- join_path: line_items.products
prefix: true
includes:
- product_category

- join_path: orders.customers
prefix: true
includes:
- city
- count
- id


`);

async function runQueryTest(q, expectedResult) {
Expand Down Expand Up @@ -124,4 +311,77 @@ views:
},

[{ count: 1, city: 'New York', cubejoinfield: 'NULL' }, { count: 1, city: 'New York', cubejoinfield: 'NULL' }]));
if (getEnv('nativeSqlPlanner')) {
it('member expression multi stage', async () => runQueryTest({
measures: [
{
// eslint-disable-next-line no-new-func
expression: new Function(
'orders',
// eslint-disable-next-line no-template-curly-in-string
'return `${orders.cagr_1_y}`'
),
// eslint-disable-next-line no-template-curly-in-string
definition: '${orders.cagr_1_y}',
expressionName: 'orders__cagr_2023',
cubeName: 'orders',
},
],
timeDimensions: [
{
dimension: 'orders.date',
dateRange: ['2022-01-01', '2022-10-31'],
},
],
timezone: 'America/Los_Angeles'
},

[{ orders__cagr_2023: '-0.90000000000000000000' }]));
} else {
it.skip('member expression multi stage', () => {
// Skipping because it works only in Tesseract
});
}

if (getEnv('nativeSqlPlanner')) {
it('member expression multi stage with time dimension segment', async () => runQueryTest({
measures: [
{
// eslint-disable-next-line no-new-func
expression: new Function(
'orders',
// eslint-disable-next-line no-template-curly-in-string
'return `${orders.cagr_1_y}`'
),
// eslint-disable-next-line no-template-curly-in-string
definition: '${orders.cagr_1_y}',
expressionName: 'orders__cagr_2023',
cubeName: 'orders',
},
],
segments: [
{
cubeName: 'orders',
name: 'orders_date____c',
expressionName: 'orders_date____c',
// eslint-disable-next-line no-new-func
expression: new Function(
'orders',
// eslint-disable-next-line no-template-curly-in-string
'return `((${orders.date} >= CAST(\'2022-01-01\' AS TIMESTAMP)) AND (${orders.date} < CAST(\'2022-10-31\' AS TIMESTAMP)))`'
),
// eslint-disable-next-line no-template-curly-in-string
definition: '{"cube_name":"orders","alias":"orders_date____c","cube_params":["orders"],"expr":"((${orders.date} >= CAST($0$ AS TIMESTAMP)) AND (${orders.date} < CAST($1$ AS TIMESTAMP)))","grouping_set":null}',
}
],

timezone: 'America/Los_Angeles'
},

[{ orders__cagr_2023: '-0.90000000000000000000' }]));
} else {
it.skip('member expression multi stage with time dimension segment', () => {
// Skipping because it works only in Tesseract
});
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub struct BaseQueryOptionsStatic {
pub row_limit: Option<String>,
pub offset: Option<String>,
pub ungrouped: Option<bool>,
#[serde(rename = "exportAnnotatedSql")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, instead of tagging every property, you can tag the struct with #[serde(rename_all = "camelCase")] and it will do everything you need :) but it's just one line → code looks cleaner and more compact.

pub export_annotated_sql: bool,
}

#[nativebridge::native_bridge(BaseQueryOptionsStatic)]
Expand Down
17 changes: 17 additions & 0 deletions rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ impl BaseDimension {
default_alias,
}))
}
MemberSymbol::MemberExpression(expression_symbol) => {
let full_name = expression_symbol.full_name();
let cube_name = expression_symbol.cube_name().clone();
let name = expression_symbol.name().clone();
let member_expression_definition = expression_symbol.definition().clone();
let default_alias = PlanSqlTemplates::alias_name(&name);
Some(Rc::new(Self {
dimension: full_name,
query_tools: query_tools.clone(),
member_evaluator: evaluation_node,
definition: None,
cube_name,
name,
member_expression_definition,
default_alias,
}))
}
_ => None,
};
Ok(result)
Expand Down
20 changes: 19 additions & 1 deletion rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl BaseMember for BaseMeasure {
}

fn full_name(&self) -> String {
format!("{}.{}", self.cube_name, self.name)
self.member_evaluator.full_name()
}

fn cube_name(&self) -> &String {
Expand Down Expand Up @@ -151,6 +151,24 @@ impl BaseMeasure {
default_alias,
}))
}
MemberSymbol::MemberExpression(expression_symbol) => {
let full_name = expression_symbol.full_name();
let cube_name = expression_symbol.cube_name().clone();
let name = expression_symbol.name().clone();
let member_expression_definition = expression_symbol.definition().clone();
let default_alias = PlanSqlTemplates::alias_name(&name);
Some(Rc::new(Self {
measure: full_name,
query_tools: query_tools.clone(),
member_evaluator: evaluation_node,
definition: None,
cube_name,
name,
member_expression_definition,
default_alias,
time_shifts: vec![],
}))
}
_ => None,
};
Ok(res)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl<IT: InnerTypes> BaseQuery<IT> {
options.base_tools()?,
options.join_graph()?,
options.static_data().timezone.clone(),
options.static_data().export_annotated_sql,
)?;

let request = QueryProperties::try_new(query_tools.clone(), options)?;
Expand Down
Loading
Loading