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 /// Measure columns (numeric aggregates)
19 pub measures: Vec<IntermediateMeasure>,
20 /// Dimension metadata
21 pub dimensions: IntermediateDimensions,
22 /// Denormalized filter columns
23 pub denormalized_filters: Vec<IntermediateFilter>,
24 /// Maps JSONB measure paths to flat SQL column names for pre-aggregated views
25 #[serde(default)]
26 pub native_measures: HashMap<String, String>,
27 /// Maps deep JSONB dimension paths to flat SQL column names for GROUP BY
28 #[serde(default)]
29 pub native_dimension_mapping: HashMap<String, String>,
30}
31
32/// Measure column definition
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub struct IntermediateMeasure {
35 /// Measure column name
36 pub name: String,
37 /// SQL data type of the measure
38 pub sql_type: String,
39 /// Whether the column can be NULL
40 pub nullable: bool,
41}
42
43/// Dimensions metadata
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub struct IntermediateDimensions {
46 /// Dimension name
47 pub name: String,
48 /// Paths to dimension fields within JSONB
49 pub paths: Vec<IntermediateDimensionPath>,
50}
51
52/// Dimension path within JSONB
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54pub struct IntermediateDimensionPath {
55 /// Path name identifier
56 pub name: String,
57 /// JSON path (accepts both "`json_path`" and "path" for cross-language compat)
58 #[serde(alias = "path")]
59 pub json_path: String,
60 /// Data type (accepts both "`data_type`" and "type" for cross-language compat)
61 #[serde(alias = "type")]
62 pub data_type: String,
63}
64
65/// Denormalized filter column
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67pub struct IntermediateFilter {
68 /// Filter column name
69 pub name: String,
70 /// SQL data type of the filter
71 pub sql_type: String,
72 /// Whether this column should be indexed
73 pub indexed: bool,
74}
75
76/// Aggregate query definition (Analytics)
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub struct IntermediateAggregateQuery {
79 /// Aggregate query name
80 pub name: String,
81 /// Fact table to aggregate from
82 pub fact_table: String,
83 /// Automatically generate GROUP BY clauses
84 pub auto_group_by: bool,
85 /// Automatically generate aggregate functions
86 pub auto_aggregates: bool,
87 /// Optional description
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub description: Option<String>,
90}