Skip to main content

vantage_table/table/impls/
selectable.rs

1use vantage_core::Result;
2use vantage_expressions::traits::selectable::Selectable;
3use vantage_expressions::{Expression, Expressive, SelectableDataSource, expr_any};
4use vantage_types::Entity;
5
6use crate::{
7    column::core::ColumnType, table::Table, traits::column_like::ColumnLike,
8    traits::table_source::TableSource,
9};
10
11impl<T, E> Table<T, E>
12where
13    T: SelectableDataSource<T::Value, T::Condition> + TableSource,
14    T::Value: From<String>, // that's because table is specified as a string
15    E: Entity<T::Value>,
16{
17    /// Create a select query with table configuration applied
18    pub fn select(&self) -> T::Select {
19        let mut select = self.data_source.select();
20
21        // Set the table as source
22        select.add_source(self.table_name(), None);
23
24        // Add all columns as fields (or expressions if defined)
25        for column in self.columns.values() {
26            if let Some(expr_fn) = self.expressions.get(column.name()) {
27                let expr = expr_fn(self);
28                self.data_source.add_select_column(
29                    &mut select,
30                    expr_any!("({})", (expr)),
31                    Some(column.name()),
32                );
33            } else if let Some(alias) = column.alias() {
34                let expr = self.data_source.expr(column.name(), vec![]);
35                self.data_source
36                    .add_select_column(&mut select, expr, Some(alias));
37            } else {
38                select.add_field(column.name());
39            }
40        }
41
42        // Add expressions that don't correspond to any column
43        for (name, expr_fn) in &self.expressions {
44            if !self.columns.contains_key(name) {
45                let expr = expr_fn(self);
46                self.data_source.add_select_column(
47                    &mut select,
48                    expr_any!("({})", (expr)),
49                    Some(name),
50                );
51            }
52        }
53
54        // Add all conditions
55        for condition in self.conditions.values() {
56            select.add_where_condition(condition.clone());
57        }
58
59        // Add all order clauses
60        for (expr, direction) in self.order_by.values() {
61            let order = match direction {
62                crate::sorting::SortDirection::Ascending => vantage_expressions::Order::Asc,
63                crate::sorting::SortDirection::Descending => vantage_expressions::Order::Desc,
64            };
65            select.add_order_by(expr.clone(), order);
66        }
67
68        // Apply pagination
69        if let Some(pagination) = &self.pagination {
70            select.set_limit(Some(pagination.limit()), Some(pagination.skip()));
71        }
72
73        select
74    }
75    /// Get count of records in the table
76    pub async fn get_count(&self) -> Result<i64> {
77        self.data_source.get_table_count(self).await
78    }
79
80    /// Get sum of a column in the table
81    pub async fn get_sum(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
82        self.data_source.get_table_sum(self, column).await
83    }
84
85    /// Get max of a column in the table
86    pub async fn get_max(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
87        self.data_source.get_table_max(self, column).await
88    }
89
90    /// Get min of a column in the table
91    pub async fn get_min(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
92        self.data_source.get_table_min(self, column).await
93    }
94
95    /// Create a count query expression (does not execute)
96    pub fn get_count_query(&self) -> Expression<T::Value> {
97        self.select().as_count()
98    }
99
100    /// Create a sum query expression for a column (does not execute)
101    pub fn get_sum_query<Type>(&self, column: &T::Column<Type>) -> Expression<T::Value>
102    where
103        Type: ColumnType,
104        T::Column<Type>: Expressive<T::Value>,
105    {
106        self.select().as_sum(column.expr())
107    }
108}
109
110// Specific implementation for serde_json::Value that can use QuerySource
111impl<T, E> Table<T, E>
112where
113    T: SelectableDataSource<serde_json::Value, T::Condition>
114        + TableSource<Value = serde_json::Value>
115        + vantage_expressions::traits::datasource::ExprDataSource<serde_json::Value>,
116    T::Value: From<String>,
117    E: Entity<serde_json::Value>,
118{
119    /// Get count using QuerySource for serde_json::Value
120    pub async fn get_count_via_query(&self) -> Result<i64> {
121        let count_query = self.get_count_query();
122        let result = self.data_source.execute(&count_query).await?;
123
124        // Extract count from result - could be {"count": 42} or just 42
125        if let Some(count) = result.get("count").and_then(|v| v.as_i64()) {
126            Ok(count)
127        } else if let Some(count) = result.as_i64() {
128            Ok(count)
129        } else {
130            Ok(0)
131        }
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use crate::mocks::mock_table_source::MockTableSource;
139    use serde_json::json;
140    use vantage_expressions::mocks::datasource::MockSelectableDataSource;
141    use vantage_expressions::traits::datasource::ExprDataSource;
142
143    #[tokio::test]
144    async fn test_selectable_functionality() {
145        let mock_select_source = MockSelectableDataSource::new(json!([
146            {"id": "1", "name": "Alice", "age": 30},
147            {"id": "2", "name": "Bob", "age": 25}
148        ]));
149
150        let mock_query_source = vantage_expressions::mocks::mock_builder::new()
151            .on_exact_select("SELECT COUNT(*) FROM \"users\"", json!(42));
152
153        let table = MockTableSource::new()
154            .with_data(
155                "users",
156                vec![
157                    json!({"id": "1", "name": "Alice", "age": 30}),
158                    json!({"id": "2", "name": "Bob", "age": 25}),
159                ],
160            )
161            .await
162            .with_select_source(mock_select_source)
163            .with_query_source(mock_query_source);
164        let table = Table::<_, vantage_types::EmptyEntity>::new("users", table);
165
166        // Basic select
167        let select = table.select();
168        assert_eq!(select.source(), Some("users"));
169
170        // Validate SQL query generation
171        let query_expr: vantage_expressions::Expression<serde_json::Value> = select.into();
172        assert_eq!(query_expr.preview(), "SELECT * FROM users");
173
174        // Test count query generation
175        let count_query = table.get_count_query();
176        assert_eq!(count_query.preview(), "SELECT COUNT(*) FROM \"users\"");
177
178        // TODO: This does not work with MockColumn - because it does not implement Expressive
179        // // Test sum query generation
180        // let age_column = table.data_source().create_column::<i64>("age");
181        // let sum_query = table.get_sum_query(&age_column);
182        // assert_eq!(sum_query.preview(), "SELECT SUM(age) FROM \"users\"");
183
184        // Test actual count/sum methods - get_count should return 42 from mock query source
185        let count = table.get_count_via_query().await.unwrap();
186        assert_eq!(count, 42);
187    }
188
189    #[tokio::test]
190    #[should_panic(expected = "MockTableSource select source not set")]
191    async fn test_panics_without_select_source() {
192        let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
193        let _select = table.select();
194    }
195
196    #[tokio::test]
197    #[should_panic(expected = "MockTableSource query source not set")]
198    async fn test_panics_without_query_source() {
199        let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
200        let query = table.data_source().expr("SELECT COUNT(*)", vec![]);
201        let _result = table.data_source().execute(&query).await;
202    }
203}