fraiseql_cli/schema/intermediate/analytics.rs
1//! Fact table/aggregate structs: `IntermediateFactTable`, `IntermediateMeasure`,
2//! `IntermediateDimensions`, `IntermediateDimensionPath`, `IntermediateFilter`,
3//! `IntermediateAggregateQuery`.
4
5use std::collections::HashMap;
6
7use serde::{Deserialize, Serialize};
8
9// =============================================================================
10// Analytics Definitions
11// =============================================================================
12
13/// Fact table definition in intermediate format (Analytics)
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct IntermediateFactTable {
16 /// Name of the fact table
17 pub table_name: String,
18 /// The type the fact table is read as: its measures, filters and dimension paths are
19 /// fields of that type, gated as a read of it (accepts `type` too).
20 #[serde(default, alias = "type", skip_serializing_if = "Option::is_none")]
21 pub type_name: Option<String>,
22 /// Measure columns (numeric aggregates)
23 pub measures: Vec<IntermediateMeasure>,
24 /// Dimension metadata
25 pub dimensions: IntermediateDimensions,
26 /// Denormalized filter columns
27 pub denormalized_filters: Vec<IntermediateFilter>,
28 /// Maps JSONB measure paths to flat SQL column names for pre-aggregated views
29 #[serde(default)]
30 pub native_measures: HashMap<String, String>,
31 /// Maps deep JSONB dimension paths to flat SQL column names for GROUP BY
32 #[serde(default)]
33 pub native_dimension_mapping: HashMap<String, String>,
34}
35
36/// Measure column definition
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub struct IntermediateMeasure {
39 /// Measure column name
40 pub name: String,
41 /// SQL data type of the measure
42 pub sql_type: String,
43 /// Whether the column can be NULL
44 pub nullable: bool,
45}
46
47/// Dimensions metadata
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49pub struct IntermediateDimensions {
50 /// Dimension name
51 pub name: String,
52 /// Paths to dimension fields within JSONB
53 pub paths: Vec<IntermediateDimensionPath>,
54}
55
56/// Dimension path within JSONB
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58pub struct IntermediateDimensionPath {
59 /// Path name identifier
60 pub name: String,
61 /// JSON path (accepts both "`json_path`" and "path" for cross-language compat)
62 #[serde(alias = "path")]
63 pub json_path: String,
64 /// Data type (accepts both "`data_type`" and "type" for cross-language compat)
65 #[serde(alias = "type")]
66 pub data_type: String,
67}
68
69/// Denormalized filter column
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub struct IntermediateFilter {
72 /// Filter column name
73 pub name: String,
74 /// SQL data type of the filter
75 pub sql_type: String,
76 /// Whether this column should be indexed
77 pub indexed: bool,
78}
79
80/// Aggregate query definition (Analytics)
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
82pub struct IntermediateAggregateQuery {
83 /// Aggregate query name
84 pub name: String,
85 /// Fact table to aggregate from
86 pub fact_table: String,
87 /// Automatically generate GROUP BY clauses
88 pub auto_group_by: bool,
89 /// Automatically generate aggregate functions
90 pub auto_aggregates: bool,
91 /// Optional description
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub description: Option<String>,
94}