diff --git a/docs-mintlify/docs/data-modeling/view-groups.mdx b/docs-mintlify/docs/data-modeling/view-groups.mdx index e078263a06bde..bc7cde9bf5626 100644 --- a/docs-mintlify/docs/data-modeling/view-groups.mdx +++ b/docs-mintlify/docs/data-modeling/view-groups.mdx @@ -42,16 +42,9 @@ view_group(`sales`, { ## Assigning views to a group -There are two ways to assign a view to a group, and both can be combined. -Pick whichever keeps group membership co-located with the entity you'd -rather edit — the group definition for a curated catalog, or the view -definition for ad-hoc additions. - -### From the group side - -List views on the group via the [`views`][ref-view-group-views] parameter. -This keeps the full membership in one place, which is convenient when you -want to review a group at a glance. +To assign a view to a group, list its name on the group via the +[`views`][ref-view-group-views] parameter. This keeps the full +membership in one place, which makes it easy to review a group at a glance. @@ -73,72 +66,8 @@ view_group(`sales`, { -### From the view side - -Set [`view_group`][ref-view-view-group] on the view itself. The view -declares which group it belongs to, without touching the group definition. - - - -```yaml title="YAML" -views: - - name: revenue - view_group: sales - cubes: - - join_path: order_items - includes: - - total_sale_price - - created_at -``` - -```javascript title="JavaScript" -view(`revenue`, { - view_group: sales, - cubes: [ - { - join_path: order_items, - includes: [`total_sale_price`, `created_at`] - } - ] -}) -``` - - - -### Belonging to multiple groups - -A view can belong to more than one group. Use -[`view_groups`][ref-view-view-groups] (plural) on the view to list every -group it should appear in. - - - -```yaml title="YAML" -views: - - name: revenue - view_groups: - - sales - - finance - cubes: - - join_path: order_items - includes: - - total_sale_price - - created_at -``` - -```javascript title="JavaScript" -view(`revenue`, { - view_groups: [sales, finance], - cubes: [ - { - join_path: order_items, - includes: [`total_sale_price`, `created_at`] - } - ] -}) -``` - - +A view can belong to more than one group — list it under the `views` +parameter of every group it should appear in. ## Where view groups live in the model @@ -160,6 +89,4 @@ entity and can be split across multiple files as your model grows. [ref-ai-context]: /docs/data-modeling/ai-context [ref-view-group-ref]: /reference/data-modeling/view-group [ref-view-group-views]: /reference/data-modeling/view-group#views -[ref-view-view-group]: /reference/data-modeling/view#view_group -[ref-view-view-groups]: /reference/data-modeling/view#view_groups [ref-meta-endpoint]: /reference/core-data-apis/rest-api/reference diff --git a/docs-mintlify/reference/data-modeling/view-group.mdx b/docs-mintlify/reference/data-modeling/view-group.mdx index 1642defc8c2e9..91ed9adf1ff0e 100644 --- a/docs-mintlify/reference/data-modeling/view-group.mdx +++ b/docs-mintlify/reference/data-modeling/view-group.mdx @@ -81,10 +81,7 @@ view_group(`sales`, { ### `views` -A list of view names that belong to this group. Views listed here are -merged with any views that reference this group via their own -[`view_group`][ref-view-view-group] or -[`view_groups`][ref-view-view-groups] parameter. +A list of view names that belong to this group. @@ -108,22 +105,14 @@ view_group(`sales`, { ## Assigning views to groups -There are two complementary ways to associate a view with a view group: - -1. **On the view group** — list view names in the [`views`](#views) parameter. -2. **On the view** — set [`view_group`][ref-view-view-group] (singular) or - [`view_groups`][ref-view-view-groups] (plural) on the view itself. - -Both approaches can be combined. Cube merges the membership from all -sources, so a view listed under a group's `views` *and* referencing that -group via `view_group` will appear only once. +To associate a view with a view group, list its name in the +[`views`](#views) parameter on the view group. ### Example The following model defines two view groups. The `sales` group lists -`orders_overview` in its `views` parameter; `revenue` joins the same -group via its own `view_group` property. The `customers_view` belongs to -`people` through the group-level `views` list. +`orders_overview` and `revenue` in its `views` parameter, while the +`people` group lists `customers_view`. @@ -182,7 +171,6 @@ views: - created_at - name: revenue - view_group: sales cubes: - join_path: order_items includes: @@ -200,6 +188,7 @@ view_groups: description: Revenue and order views for the sales team views: - orders_overview + - revenue - name: people title: People @@ -280,7 +269,6 @@ view(`orders_overview`, { }) view(`revenue`, { - view_group: sales, cubes: [ { join_path: order_items, @@ -304,7 +292,7 @@ view(`customers_view`, { view_group(`sales`, { title: `Sales`, description: `Revenue and order views for the sales team`, - views: [`orders_overview`] + views: [`orders_overview`, `revenue`] }) view_group(`people`, { @@ -337,12 +325,6 @@ With this model, the `/v1/meta` response includes a `viewGroups` array: } ``` -Notice that `revenue` appears in the `sales` group even though it was not -listed in the group's `views` — it was added because the view itself set -`view_group: sales`. - [ref-views]: /docs/data-modeling/views [ref-naming]: /docs/data-modeling/concepts/syntax#naming [ref-meta-endpoint]: /reference/core-data-apis/rest-api/reference -[ref-view-view-group]: /reference/data-modeling/view#view_group -[ref-view-view-groups]: /reference/data-modeling/view#view_groups diff --git a/docs-mintlify/reference/data-modeling/view.mdx b/docs-mintlify/reference/data-modeling/view.mdx index 1fe48caf2727f..bc163c6b94f58 100644 --- a/docs-mintlify/reference/data-modeling/view.mdx +++ b/docs-mintlify/reference/data-modeling/view.mdx @@ -230,72 +230,6 @@ view(`active_users`, { -### `view_group` - -Assigns this view to a single [view group][ref-ref-view-group]. The referenced -view group must be defined with `view_group()` elsewhere in the data model. - - - -```yaml title="YAML" -views: - - name: revenue - view_group: sales - cubes: - - join_path: order_items - includes: "*" -``` - -```javascript title="JavaScript" -view(`revenue`, { - view_group: sales, - cubes: [ - { - join_path: order_items, - includes: `*` - } - ] -}) -``` - - - -### `view_groups` - -Assigns this view to multiple [view groups][ref-ref-view-group] at once. -Each referenced view group must be defined with `view_group()` elsewhere -in the data model. - - - -```yaml title="YAML" -views: - - name: revenue - view_groups: - - sales - - finance - cubes: - - join_path: order_items - includes: "*" -``` - -```javascript title="JavaScript" -view(`revenue`, { - view_groups: [`sales`, `finance`], - cubes: [ - { - join_path: order_items, - includes: `*` - } - ] -}) -``` - - - -You can use both `view_group` and `view_groups` on the same view. Cube -merges them and removes duplicates. - ### `cubes` Use `cubes` parameter in view to include exposed cubes in bulk. You can build @@ -731,5 +665,4 @@ The `access_policy` parameter is used to configure [access policies][ref-ref-dap [ref-dim-title]: /reference/data-modeling/dimensions#title [ref-dim-description]: /reference/data-modeling/dimensions#description [ref-dim-format]: /reference/data-modeling/dimensions#format -[ref-dim-meta]: /reference/data-modeling/dimensions#meta -[ref-ref-view-group]: /reference/data-modeling/view-group \ No newline at end of file +[ref-dim-meta]: /reference/data-modeling/dimensions#meta \ No newline at end of file diff --git a/packages/cubejs-api-gateway/src/types/responses.ts b/packages/cubejs-api-gateway/src/types/responses.ts index 4f53d564baddd..e2dbb1a802878 100644 --- a/packages/cubejs-api-gateway/src/types/responses.ts +++ b/packages/cubejs-api-gateway/src/types/responses.ts @@ -8,11 +8,6 @@ export type DBResponsePrimitive = number | string; -export type DBResponseValue = - Date | - DBResponsePrimitive | - { value: DBResponsePrimitive }; - export type TransformDataResponse = { members: string[], dataset: DBResponsePrimitive[][] diff --git a/packages/cubejs-backend-native/src/orchestrator.rs b/packages/cubejs-backend-native/src/orchestrator.rs index 447d3909578bd..521577f697647 100644 --- a/packages/cubejs-backend-native/src/orchestrator.rs +++ b/packages/cubejs-backend-native/src/orchestrator.rs @@ -2,8 +2,7 @@ use crate::node_obj_deserializer::JsValueDeserializer; use crate::transport::MapCubeErrExt; use cubeorchestrator::query_message_parser::QueryResult; use cubeorchestrator::query_result_transform::{ - DBResponsePrimitive, DBResponseValue, RequestResultData, RequestResultDataMulti, - TransformedData, + DBResponsePrimitive, RequestResultData, RequestResultDataMulti, TransformedData, }; use cubeorchestrator::transport::{JsRawColumnarData, TransformDataRequest}; use cubesql::compile::engine::df::scan::{ColumnarValueObject, FieldValue, ValueObject}; @@ -339,7 +338,7 @@ pub fn get_cubestore_result(mut cx: FunctionContext) -> JsResult { for (key, value) in result.members.iter().zip(row.iter()) { let js_key = cx.string(key); let js_value: Handle<'_, JsValue> = match value { - DBResponseValue::Primitive(DBResponsePrimitive::Null) => cx.null().upcast(), + DBResponsePrimitive::Null => cx.null().upcast(), // For compatibility, we convert all primitives to strings other => cx.string(other.to_string()).upcast(), }; diff --git a/rust/cube/cubeorchestrator/src/query_message_parser.rs b/rust/cube/cubeorchestrator/src/query_message_parser.rs index 51ab4ac9c352e..3497acf9c25ea 100644 --- a/rust/cube/cubeorchestrator/src/query_message_parser.rs +++ b/rust/cube/cubeorchestrator/src/query_message_parser.rs @@ -1,7 +1,4 @@ -use crate::{ - query_result_transform::{DBResponsePrimitive, DBResponseValue}, - transport::JsRawColumnarData, -}; +use crate::{query_result_transform::DBResponsePrimitive, transport::JsRawColumnarData}; use cubeshared::codegen::{root_as_http_message_with_opts, HttpCommand}; use cubeshared::flatbuffers::VerifierOptions; use indexmap::IndexMap; @@ -35,7 +32,7 @@ impl std::error::Error for ParseError {} #[derive(Debug, Clone)] pub struct QueryResult { pub members: Vec, - pub rows: Vec>, + pub rows: Vec>, pub columns_pos: IndexMap, } @@ -96,10 +93,8 @@ impl QueryResult { let row_obj: Vec<_> = values .iter() .map(|val| match val.string_value() { - Some(s) => DBResponseValue::Primitive(DBResponsePrimitive::String( - s.to_owned(), - )), - None => DBResponseValue::Primitive(DBResponsePrimitive::Null), + Some(s) => DBResponsePrimitive::String(s.to_owned()), + None => DBResponsePrimitive::Null, }) .collect(); @@ -134,14 +129,14 @@ impl QueryResult { // Transpose column-major input into the row-major shape `QueryResult` // expects. Rows are pre-allocated, then we drain each column into the // matching slot to avoid per-cell clones. - let mut rows: Vec> = (0..row_count) + let mut rows: Vec> = (0..row_count) .map(|_| Vec::with_capacity(members.len())) .collect(); for column in columns.into_iter() { for (row_idx, value) in column.into_iter().enumerate() { if let Some(row) = rows.get_mut(row_idx) { - row.push(DBResponseValue::Primitive(value)); + row.push(value); } } } diff --git a/rust/cube/cubeorchestrator/src/query_result_transform.rs b/rust/cube/cubeorchestrator/src/query_result_transform.rs index a30139132e742..fc58997eb202b 100644 --- a/rust/cube/cubeorchestrator/src/query_result_transform.rs +++ b/rust/cube/cubeorchestrator/src/query_result_transform.rs @@ -38,16 +38,9 @@ pub static GRANULARITY_LEVELS: LazyLock> = LazyLock::n const DEFAULT_LEVEL_FOR_UNKNOWN: u8 = 10; /// Transform specified `value` with specified `type` to the network protocol type. -pub fn transform_value(value: DBResponseValue, type_: &str) -> DBResponsePrimitive { +pub fn transform_value(value: DBResponsePrimitive, type_: &str) -> DBResponsePrimitive { match value { - DBResponseValue::DateTime(dt) if type_ == "time" || type_.is_empty() => { - DBResponsePrimitive::String( - dt.with_timezone(&Utc) - .format("%Y-%m-%dT%H:%M:%S%.3f") - .to_string(), - ) - } - DBResponseValue::Primitive(DBResponsePrimitive::String(ref s)) if type_ == "time" => { + DBResponsePrimitive::String(ref s) if type_ == "time" => { let formatted = DateTime::parse_from_rfc3339(s) .map(|dt| dt.format("%Y-%m-%dT%H:%M:%S%.3f").to_string()) .or_else(|_| { @@ -88,9 +81,7 @@ pub fn transform_value(value: DBResponseValue, type_: &str) -> DBResponsePrimiti .unwrap_or_else(|_| s.clone()); DBResponsePrimitive::String(formatted) } - DBResponseValue::Primitive(p) => p, - DBResponseValue::Object { value } => value, - _ => DBResponsePrimitive::Null, + other => other, } } @@ -396,7 +387,7 @@ pub(crate) fn build_compact_plan<'a>( /// Convert DB response row to the compact output pub fn get_compact_row( plan: &CompactPlan<'_>, - db_row: &[DBResponseValue], + db_row: &[DBResponsePrimitive], ) -> Vec { let mut row: Vec = Vec::with_capacity(plan.entries.len()); @@ -609,7 +600,7 @@ fn build_columnar_plan<'a>( /// row-major `cube_store_result.rows` matrix. fn build_columnar_columns( plan: &[ColumnarColumnPlan<'_>], - rows: &[Vec], + rows: &[Vec], ) -> Vec> { let row_count = rows.len(); let mut columns: Vec> = @@ -623,7 +614,7 @@ fn build_columnar_columns( let cell = row .get(*index) .cloned() - .unwrap_or(DBResponseValue::Primitive(DBResponsePrimitive::Null)); + .unwrap_or(DBResponsePrimitive::Null); out.push(transform_value(cell, plan_entry.member_type)); } } @@ -644,7 +635,7 @@ pub fn get_vanilla_row( plan: &VanillaPlan<'_>, query_type: &QueryType, query: &NormalizedQuery, - db_row: &[DBResponseValue], + db_row: &[DBResponsePrimitive], ) -> Result> { // +1 to cover the optional tail entry (compareDateRange / blending key). let mut row = IndexMap::with_capacity(plan.columns.len() + 1); @@ -995,31 +986,11 @@ impl Display for DBResponsePrimitive { } } -#[derive(Debug, Clone, Deserialize)] -pub enum DBResponseValue { - DateTime(DateTime), - Primitive(DBResponsePrimitive), - // TODO: Is this variant still used? - Object { value: DBResponsePrimitive }, -} - -impl Display for DBResponseValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let str = match self { - DBResponseValue::DateTime(dt) => dt.to_rfc3339(), - DBResponseValue::Primitive(p) => p.to_string(), - DBResponseValue::Object { value } => value.to_string(), - }; - write!(f, "{}", str) - } -} - #[cfg(test)] mod tests { use super::*; use crate::transport::JsRawColumnarData; use anyhow::Result; - use chrono::{TimeZone, Timelike, Utc}; use serde_json::from_str; use std::{fmt, sync::LazyLock}; @@ -1973,43 +1944,9 @@ mod tests { } } - #[test] - fn test_transform_value_datetime_to_time() { - let dt = Utc - .with_ymd_and_hms(2024, 1, 1, 12, 30, 15) - .unwrap() - .with_nanosecond(123_000_000) - .unwrap(); - let value = DBResponseValue::DateTime(dt); - let result = transform_value(value, "time"); - - assert_eq!( - result, - DBResponsePrimitive::String("2024-01-01T12:30:15.123".to_string()) - ); - } - - #[test] - fn test_transform_value_datetime_empty_type() { - let dt = Utc - .with_ymd_and_hms(2024, 1, 1, 12, 30, 15) - .unwrap() - .with_nanosecond(123_000_000) - .unwrap(); - let value = DBResponseValue::DateTime(dt); - let result = transform_value(value, ""); - - assert_eq!( - result, - DBResponsePrimitive::String("2024-01-01T12:30:15.123".to_string()) - ); - } - #[test] fn test_transform_value_string_to_time_valid_rfc3339() { - let value = DBResponseValue::Primitive(DBResponsePrimitive::String( - "2024-01-01T12:30:15.123".to_string(), - )); + let value = DBResponsePrimitive::String("2024-01-01T12:30:15.123".to_string()); let result = transform_value(value, "time"); assert_eq!( @@ -2020,9 +1957,7 @@ mod tests { #[test] fn test_transform_value_string_wo_t_to_time_valid_rfc3339() { - let value = DBResponseValue::Primitive(DBResponsePrimitive::String( - "2024-01-01 12:30:15.123".to_string(), - )); + let value = DBResponsePrimitive::String("2024-01-01 12:30:15.123".to_string()); let result = transform_value(value, "time"); assert_eq!( @@ -2033,9 +1968,7 @@ mod tests { #[test] fn test_transform_value_string_wo_mssec_to_time_valid_rfc3339() { - let value = DBResponseValue::Primitive(DBResponsePrimitive::String( - "2024-01-01 12:30:15".to_string(), - )); + let value = DBResponsePrimitive::String("2024-01-01 12:30:15".to_string()); let result = transform_value(value, "time"); assert_eq!( @@ -2046,9 +1979,7 @@ mod tests { #[test] fn test_transform_value_string_wo_mssec_w_t_to_time_valid_rfc3339() { - let value = DBResponseValue::Primitive(DBResponsePrimitive::String( - "2024-01-01T12:30:15".to_string(), - )); + let value = DBResponsePrimitive::String("2024-01-01T12:30:15".to_string()); let result = transform_value(value, "time"); assert_eq!( @@ -2059,9 +1990,7 @@ mod tests { #[test] fn test_transform_value_string_with_tz_offset_to_time_valid_rfc3339() { - let value = DBResponseValue::Primitive(DBResponsePrimitive::String( - "2024-01-01 12:30:15.123 +00:00".to_string(), - )); + let value = DBResponsePrimitive::String("2024-01-01 12:30:15.123 +00:00".to_string()); let result = transform_value(value, "time"); assert_eq!( @@ -2072,9 +2001,7 @@ mod tests { #[test] fn test_transform_value_string_with_tz_to_time_valid_rfc3339() { - let value = DBResponseValue::Primitive(DBResponsePrimitive::String( - "2024-01-01 12:30:15.123 UTC".to_string(), - )); + let value = DBResponsePrimitive::String("2024-01-01 12:30:15.123 UTC".to_string()); let result = transform_value(value, "time"); assert_eq!( @@ -2085,8 +2012,7 @@ mod tests { #[test] fn test_transform_value_string_to_time_invalid_rfc3339() { - let value = - DBResponseValue::Primitive(DBResponsePrimitive::String("invalid-date".to_string())); + let value = DBResponsePrimitive::String("invalid-date".to_string()); let result = transform_value(value, "time"); assert_eq!( @@ -2097,8 +2023,7 @@ mod tests { #[test] fn test_transform_value_primitive_string_type_not_time() { - let value = - DBResponseValue::Primitive(DBResponsePrimitive::String("some-string".to_string())); + let value = DBResponsePrimitive::String("some-string".to_string()); let result = transform_value(value, "other"); assert_eq!( @@ -2107,25 +2032,6 @@ mod tests { ); } - #[test] - fn test_transform_value_object() { - let obj_value = DBResponsePrimitive::String("object-value".to_string()); - let value = DBResponseValue::Object { - value: obj_value.clone(), - }; - let result = transform_value(value, "time"); - - assert_eq!(result, obj_value); - } - - #[test] - fn test_transform_value_fallback_to_null() { - let value = DBResponseValue::DateTime(Utc::now()); - let result = transform_value(value, "unknown"); - - assert_eq!(result, DBResponsePrimitive::Null); - } - #[test] fn test_get_date_range_value_valid_range() -> Result<()> { let time_dimensions = vec![QueryTimeDimension {