Skip to content

Commit f034a5a

Browse files
committed
fix(backend-native): Use the lowest granularity for data-transform td with granularity fallback
1 parent e2cd558 commit f034a5a

File tree

1 file changed

+206
-9
lines changed

1 file changed

+206
-9
lines changed

rust/cubeorchestrator/src/query_result_transform.rs

Lines changed: 206 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,14 @@ pub fn get_members(
197197
.any(|dim| *dim == MemberOrMemberExpression::Member(calc_member.clone()))
198198
})
199199
{
200-
members_map.insert(calc_member.clone(), column.clone());
201-
members_arr.push(calc_member);
200+
// For cases when the same dimension with few different granularities is present
201+
// we should not duplicate the dimension without granularity
202+
if members_map
203+
.insert(calc_member.clone(), column.clone())
204+
.is_none()
205+
{
206+
members_arr.push(calc_member);
207+
}
202208
}
203209
}
204210

@@ -290,6 +296,21 @@ pub fn get_vanilla_row(
290296
) -> Result<HashMap<String, DBResponsePrimitive>> {
291297
let mut row = HashMap::new();
292298

299+
// FIXME: For now custom granularities are not supported, only common ones.
300+
// There is no granularity type/class implementation in rust yet.
301+
let granularity_levels: HashMap<&str, u8> = HashMap::from([
302+
("second", 1),
303+
("minute", 2),
304+
("hour", 3),
305+
("day", 4),
306+
("week", 5),
307+
("month", 6),
308+
("quarter", 7),
309+
("year", 8),
310+
]);
311+
let default_level_for_unknown = 10;
312+
let mut minimal_granularities: HashMap<String, (u8, DBResponsePrimitive)> = HashMap::new();
313+
293314
for (alias, &index) in columns_pos {
294315
if let Some(value) = db_row.get(index) {
295316
let member_name = match alias_to_member_name_map.get(alias) {
@@ -324,23 +345,46 @@ pub fn get_vanilla_row(
324345
row.insert(member_name.clone(), transformed_value.clone());
325346

326347
// Handle deprecated time dimensions without granularity
348+
// Try to collect minimal granularity value for time dimensions without granularity
349+
// as there might be more than one granularity column for the same dimension
327350
let path: Vec<&str> = member_name.split(MEMBER_SEPARATOR).collect();
328-
let member_name_without_granularity =
329-
format!("{}{}{}", path[0], MEMBER_SEPARATOR, path[1]);
330-
if path.len() == 3
331-
&& query.dimensions.as_ref().map_or(true, |dims| {
351+
if path.len() == 3 {
352+
let granularity = path[2];
353+
let member_name_without_granularity =
354+
format!("{}{}{}", path[0], MEMBER_SEPARATOR, path[1]);
355+
356+
// Check that a member without granularity is absent in the query
357+
if query.dimensions.as_ref().map_or(true, |dims| {
332358
!dims.iter().any(|dim| {
333359
*dim == MemberOrMemberExpression::Member(
334360
member_name_without_granularity.clone(),
335361
)
336362
})
337-
})
338-
{
339-
row.insert(member_name_without_granularity, transformed_value);
363+
}) {
364+
let level = granularity_levels
365+
.get(granularity)
366+
.cloned()
367+
.unwrap_or(default_level_for_unknown);
368+
369+
match minimal_granularities.get(&member_name_without_granularity) {
370+
Some((existing_level, _)) if *existing_level < level => {}
371+
_ => {
372+
minimal_granularities.insert(
373+
member_name_without_granularity,
374+
(level, transformed_value),
375+
);
376+
}
377+
}
378+
}
340379
}
341380
}
342381
}
343382

383+
// Handle deprecated time dimensions without granularity
384+
for (member, (_, value)) in minimal_granularities {
385+
row.insert(member, value);
386+
}
387+
344388
match query_type {
345389
QueryType::CompareDateRangeQuery => {
346390
let date_range_value = get_date_range_value(query.time_dimensions.as_ref())?;
@@ -1271,6 +1315,133 @@ mod tests {
12711315
]
12721316
]
12731317
}
1318+
},
1319+
"blending_query_multiple_granularities": {
1320+
"request": {
1321+
"aliasToMemberNameMap": {
1322+
"e_commerce_records_us2021__avg_discount": "ECommerceRecordsUs2021.avg_discount",
1323+
"e_commerce_records_us2021__order_date_month": "ECommerceRecordsUs2021.orderDate.month",
1324+
"e_commerce_records_us2021__order_date_week": "ECommerceRecordsUs2021.orderDate.week"
1325+
},
1326+
"annotation": {
1327+
"ECommerceRecordsUs2021.avg_discount": {
1328+
"title": "E Commerce Records Us2021 Avg Discount",
1329+
"shortTitle": "Avg Discount",
1330+
"type": "number",
1331+
"drillMembers": [],
1332+
"drillMembersGrouped": {
1333+
"measures": [],
1334+
"dimensions": []
1335+
}
1336+
},
1337+
"ECommerceRecordsUs2021.orderDate.month": {
1338+
"title": "E Commerce Records Us2021 Order Date",
1339+
"shortTitle": "Order Date",
1340+
"type": "time"
1341+
},
1342+
"ECommerceRecordsUs2021.orderDate.week": {
1343+
"title": "E Commerce Records Us2021 Order Date",
1344+
"shortTitle": "Order Date",
1345+
"type": "time"
1346+
},
1347+
"ECommerceRecordsUs2021.orderDate": {
1348+
"title": "E Commerce Records Us2021 Order Date",
1349+
"shortTitle": "Order Date",
1350+
"type": "time"
1351+
}
1352+
},
1353+
"query": {
1354+
"measures": [
1355+
"ECommerceRecordsUs2021.avg_discount"
1356+
],
1357+
"timeDimensions": [
1358+
{
1359+
"dimension": "ECommerceRecordsUs2021.orderDate",
1360+
"granularity": "month",
1361+
"dateRange": [
1362+
"2020-01-01T00:00:00.000",
1363+
"2020-12-30T23:59:59.999"
1364+
]
1365+
},
1366+
{
1367+
"dimension": "ECommerceRecordsUs2021.orderDate",
1368+
"granularity": "week",
1369+
"dateRange": [
1370+
"2020-01-01T00:00:00.000",
1371+
"2020-12-30T23:59:59.999"
1372+
]
1373+
}
1374+
],
1375+
"filters": [
1376+
{
1377+
"operator": "equals",
1378+
"values": [
1379+
"First Class"
1380+
],
1381+
"member": "ECommerceRecordsUs2021.shipMode"
1382+
}
1383+
],
1384+
"limit": 2,
1385+
"rowLimit": 2,
1386+
"timezone": "UTC",
1387+
"order": [],
1388+
"dimensions": []
1389+
},
1390+
"queryType": "blendingQuery"
1391+
},
1392+
"queryResult": [
1393+
{
1394+
"e_commerce_records_us2021__order_date_month": "2020-01-01T00:00:00.000",
1395+
"e_commerce_records_us2021__order_date_week": "2019-12-30T00:00:00.000",
1396+
"e_commerce_records_us2021__avg_discount": "0.28571428571428571429"
1397+
},
1398+
{
1399+
"e_commerce_records_us2021__order_date_month": "2020-02-01T00:00:00.000",
1400+
"e_commerce_records_us2021__order_date_week": "2020-01-27T00:00:00.000",
1401+
"e_commerce_records_us2021__avg_discount": "0.21777777777777777778"
1402+
}
1403+
],
1404+
"finalResultDefault": [
1405+
{
1406+
"ECommerceRecordsUs2021.orderDate.month": "2020-01-01T00:00:00.000",
1407+
"ECommerceRecordsUs2021.orderDate.week": "2019-12-30T00:00:00.000",
1408+
"ECommerceRecordsUs2021.orderDate": "2019-12-30T00:00:00.000",
1409+
"ECommerceRecordsUs2021.avg_discount": "0.28571428571428571429",
1410+
"time.month": "2020-01-01T00:00:00.000"
1411+
},
1412+
{
1413+
"ECommerceRecordsUs2021.orderDate.month": "2020-02-01T00:00:00.000",
1414+
"ECommerceRecordsUs2021.orderDate.week": "2020-01-27T00:00:00.000",
1415+
"ECommerceRecordsUs2021.orderDate": "2020-01-27T00:00:00.000",
1416+
"ECommerceRecordsUs2021.avg_discount": "0.21777777777777777778",
1417+
"time.month": "2020-02-01T00:00:00.000"
1418+
}
1419+
],
1420+
"finalResultCompact": {
1421+
"members": [
1422+
"ECommerceRecordsUs2021.orderDate.month",
1423+
"ECommerceRecordsUs2021.orderDate.week",
1424+
"ECommerceRecordsUs2021.orderDate",
1425+
"ECommerceRecordsUs2021.avg_discount",
1426+
"time.month"
1427+
],
1428+
"dataset": [
1429+
[
1430+
"2020-01-01T00:00:00.000",
1431+
"2019-12-30T00:00:00.000",
1432+
"2019-12-30T00:00:00.000",
1433+
"0.28571428571428571429",
1434+
"2020-01-01T00:00:00.000"
1435+
],
1436+
[
1437+
"2020-02-01T00:00:00.000",
1438+
"2020-01-27T00:00:00.000",
1439+
"2020-01-27T00:00:00.000",
1440+
"0.21777777777777777778",
1441+
"2020-02-01T00:00:00.000"
1442+
]
1443+
]
1444+
}
12741445
}
12751446
}
12761447
"#;
@@ -1919,6 +2090,32 @@ mod tests {
19192090
Ok(())
19202091
}
19212092

2093+
#[test]
2094+
fn test_blending_query_multiple_granularities_default() -> Result<()> {
2095+
let mut test_data = TEST_SUITE_DATA
2096+
.get(&"blending_query_multiple_granularities".to_string())
2097+
.unwrap()
2098+
.clone();
2099+
test_data.request.res_type = Some(ResultType::Default);
2100+
let raw_data = QueryResult::from_js_raw_data(test_data.query_result.clone())?;
2101+
let transformed = TransformedData::transform(&test_data.request, &raw_data)?;
2102+
compare_transformed_data(&transformed, &test_data.final_result_default.unwrap())?;
2103+
Ok(())
2104+
}
2105+
2106+
#[test]
2107+
fn test_blending_query_multiple_granularities_compact() -> Result<()> {
2108+
let mut test_data = TEST_SUITE_DATA
2109+
.get(&"blending_query_multiple_granularities".to_string())
2110+
.unwrap()
2111+
.clone();
2112+
test_data.request.res_type = Some(ResultType::Compact);
2113+
let raw_data = QueryResult::from_js_raw_data(test_data.query_result.clone())?;
2114+
let transformed = TransformedData::transform(&test_data.request, &raw_data)?;
2115+
compare_transformed_data(&transformed, &test_data.final_result_compact.unwrap())?;
2116+
Ok(())
2117+
}
2118+
19222119
#[test]
19232120
fn test_get_members_no_alias_to_member_name_map() -> Result<()> {
19242121
let mut test_data = TEST_SUITE_DATA

0 commit comments

Comments
 (0)