Skip to main content

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}