Skip to main content

teaql_core/
query.rs

1use std::collections::BTreeMap;
2
3use crate::{Expr, Value};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum SortDirection {
7    Asc,
8    Desc,
9}
10
11#[derive(Debug, Clone, PartialEq)]
12pub struct NamedExpr {
13    pub alias: String,
14    pub expr: Expr,
15}
16
17impl NamedExpr {
18    pub fn new(alias: impl Into<String>, expr: Expr) -> Self {
19        Self {
20            alias: alias.into(),
21            expr,
22        }
23    }
24}
25
26#[derive(Debug, Clone, PartialEq)]
27pub struct OrderBy {
28    pub field: String,
29    pub expr: Option<Expr>,
30    pub direction: SortDirection,
31}
32
33impl OrderBy {
34    pub fn new(field: impl Into<String>, direction: SortDirection) -> Self {
35        Self {
36            field: field.into(),
37            expr: None,
38            direction,
39        }
40    }
41
42    pub fn expr(expr: Expr, direction: SortDirection) -> Self {
43        Self {
44            field: String::new(),
45            expr: Some(expr),
46            direction,
47        }
48    }
49
50    pub fn asc(field: impl Into<String>) -> Self {
51        Self::new(field, SortDirection::Asc)
52    }
53
54    pub fn desc(field: impl Into<String>) -> Self {
55        Self::new(field, SortDirection::Desc)
56    }
57
58    pub fn asc_expr(expr: Expr) -> Self {
59        Self::expr(expr, SortDirection::Asc)
60    }
61
62    pub fn desc_expr(expr: Expr) -> Self {
63        Self::expr(expr, SortDirection::Desc)
64    }
65
66    pub fn asc_gbk(field: impl Into<String>) -> Self {
67        Self::asc_expr(Expr::gbk(Expr::column(field)))
68    }
69
70    pub fn desc_gbk(field: impl Into<String>) -> Self {
71        Self::desc_expr(Expr::gbk(Expr::column(field)))
72    }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum AggregateFunction {
77    Count,
78    Sum,
79    Avg,
80    Min,
81    Max,
82    Stddev,
83    StddevPop,
84    VarSamp,
85    VarPop,
86    BitAnd,
87    BitOr,
88    BitXor,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct Aggregate {
93    pub function: AggregateFunction,
94    pub field: String,
95    pub alias: String,
96}
97
98impl Aggregate {
99    pub fn new(
100        function: AggregateFunction,
101        field: impl Into<String>,
102        alias: impl Into<String>,
103    ) -> Self {
104        Self {
105            function,
106            field: field.into(),
107            alias: alias.into(),
108        }
109    }
110
111    pub fn count(alias: impl Into<String>) -> Self {
112        Self::new(AggregateFunction::Count, "*", alias)
113    }
114
115    pub fn count_field(field: impl Into<String>, alias: impl Into<String>) -> Self {
116        Self::new(AggregateFunction::Count, field, alias)
117    }
118
119    pub fn sum(field: impl Into<String>, alias: impl Into<String>) -> Self {
120        Self::new(AggregateFunction::Sum, field, alias)
121    }
122
123    pub fn avg(field: impl Into<String>, alias: impl Into<String>) -> Self {
124        Self::new(AggregateFunction::Avg, field, alias)
125    }
126
127    pub fn min(field: impl Into<String>, alias: impl Into<String>) -> Self {
128        Self::new(AggregateFunction::Min, field, alias)
129    }
130
131    pub fn max(field: impl Into<String>, alias: impl Into<String>) -> Self {
132        Self::new(AggregateFunction::Max, field, alias)
133    }
134
135    pub fn stddev(field: impl Into<String>, alias: impl Into<String>) -> Self {
136        Self::new(AggregateFunction::Stddev, field, alias)
137    }
138
139    pub fn stddev_pop(field: impl Into<String>, alias: impl Into<String>) -> Self {
140        Self::new(AggregateFunction::StddevPop, field, alias)
141    }
142
143    pub fn var_samp(field: impl Into<String>, alias: impl Into<String>) -> Self {
144        Self::new(AggregateFunction::VarSamp, field, alias)
145    }
146
147    pub fn var_pop(field: impl Into<String>, alias: impl Into<String>) -> Self {
148        Self::new(AggregateFunction::VarPop, field, alias)
149    }
150
151    pub fn bit_and(field: impl Into<String>, alias: impl Into<String>) -> Self {
152        Self::new(AggregateFunction::BitAnd, field, alias)
153    }
154
155    pub fn bit_or(field: impl Into<String>, alias: impl Into<String>) -> Self {
156        Self::new(AggregateFunction::BitOr, field, alias)
157    }
158
159    pub fn bit_xor(field: impl Into<String>, alias: impl Into<String>) -> Self {
160        Self::new(AggregateFunction::BitXor, field, alias)
161    }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub struct Slice {
166    pub limit: Option<u64>,
167    pub offset: u64,
168}
169
170#[derive(Debug, Clone, PartialEq)]
171pub struct RelationLoad {
172    pub name: String,
173    pub query: Option<Box<SelectQuery>>,
174}
175
176impl RelationLoad {
177    pub fn new(name: impl Into<String>) -> Self {
178        Self {
179            name: name.into(),
180            query: None,
181        }
182    }
183
184    pub fn with_query(name: impl Into<String>, query: SelectQuery) -> Self {
185        Self {
186            name: name.into(),
187            query: Some(Box::new(query)),
188        }
189    }
190}
191
192#[derive(Debug, Clone, PartialEq)]
193pub struct RelationAggregate {
194    pub relation_name: String,
195    pub alias: String,
196    pub query: SelectQuery,
197    pub single_result: bool,
198}
199
200impl RelationAggregate {
201    pub fn new(
202        relation_name: impl Into<String>,
203        alias: impl Into<String>,
204        query: SelectQuery,
205        single_result: bool,
206    ) -> Self {
207        Self {
208            relation_name: relation_name.into(),
209            alias: alias.into(),
210            query,
211            single_result,
212        }
213    }
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
217pub struct RawSqlProjection {
218    pub property_name: String,
219    pub raw_sql_segment: String,
220}
221
222impl RawSqlProjection {
223    pub fn new(property_name: impl Into<String>, raw_sql_segment: impl Into<String>) -> Self {
224        Self {
225            property_name: property_name.into(),
226            raw_sql_segment: raw_sql_segment.into(),
227        }
228    }
229}
230
231#[derive(Debug, Clone, PartialEq)]
232pub struct ObjectGroupBy {
233    pub property_name: String,
234    pub storage_field: String,
235    pub query: SelectQuery,
236}
237
238impl ObjectGroupBy {
239    pub fn new(
240        property_name: impl Into<String>,
241        storage_field: impl Into<String>,
242        query: SelectQuery,
243    ) -> Self {
244        Self {
245            property_name: property_name.into(),
246            storage_field: storage_field.into(),
247            query,
248        }
249    }
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq)]
253pub struct AggregationCacheOptions {
254    pub enabled: bool,
255    pub cache_expired_millis: u64,
256    pub propagate: bool,
257    pub propagate_cache_expired_millis: u64,
258}
259
260impl AggregationCacheOptions {
261    pub fn enabled(cache_expired_millis: u64) -> Self {
262        Self {
263            enabled: true,
264            cache_expired_millis,
265            propagate: false,
266            propagate_cache_expired_millis: 0,
267        }
268    }
269
270    pub fn propagate(mut self, cache_expired_millis: u64) -> Self {
271        self.propagate = true;
272        self.propagate_cache_expired_millis = cache_expired_millis;
273        self
274    }
275}
276
277#[derive(Debug, Clone, PartialEq)]
278pub struct SelectQuery {
279    pub entity: String,
280    pub projection: Vec<String>,
281    pub expr_projection: Vec<NamedExpr>,
282    pub search_with_text: Option<String>,
283    pub filter: Option<Expr>,
284    pub having: Option<Expr>,
285    pub order_by: Vec<OrderBy>,
286    pub slice: Option<Slice>,
287    pub aggregates: Vec<Aggregate>,
288    pub group_by: Vec<String>,
289    pub relations: Vec<RelationLoad>,
290    pub aggregation_cache: Option<AggregationCacheOptions>,
291    pub comment: Option<String>,
292    pub trace_chain: Vec<crate::TraceNode>,
293    pub raw_sql: Option<String>,
294    pub raw_sql_search_criteria: Vec<String>,
295    pub dynamic_properties: Vec<RawSqlProjection>,
296    pub raw_projections: Vec<RawSqlProjection>,
297    pub object_group_bys: Vec<ObjectGroupBy>,
298    pub child_enhancements: Vec<SelectQuery>,
299}
300
301impl SelectQuery {
302    pub fn new(entity: impl Into<String>) -> Self {
303        Self {
304            entity: entity.into(),
305            projection: Vec::new(),
306            expr_projection: Vec::new(),
307            search_with_text: None,
308            filter: None,
309            having: None,
310            order_by: Vec::new(),
311            slice: None,
312            aggregates: Vec::new(),
313            group_by: Vec::new(),
314            relations: Vec::new(),
315            aggregation_cache: None,
316            comment: None,
317            trace_chain: Vec::new(),
318            raw_sql: None,
319            raw_sql_search_criteria: Vec::new(),
320            dynamic_properties: Vec::new(),
321            raw_projections: Vec::new(),
322            object_group_bys: Vec::new(),
323            child_enhancements: Vec::new(),
324        }
325    }
326
327    pub fn project(mut self, field: impl Into<String>) -> Self {
328        self.projection.push(field.into());
329        self
330    }
331
332    pub fn projects(mut self, fields: impl IntoIterator<Item = impl Into<String>>) -> Self {
333        self.projection.extend(fields.into_iter().map(Into::into));
334        self
335    }
336
337    pub fn project_expr(mut self, alias: impl Into<String>, expr: Expr) -> Self {
338        self.expr_projection.push(NamedExpr::new(alias, expr));
339        self
340    }
341
342    pub fn project_raw(
343        mut self,
344        alias: impl Into<String>,
345        raw_sql_segment: impl Into<String>,
346    ) -> Self {
347        self.raw_projections
348            .push(RawSqlProjection::new(alias, raw_sql_segment));
349        self
350    }
351
352    pub fn dynamic_property_raw(
353        mut self,
354        alias: impl Into<String>,
355        raw_sql_segment: impl Into<String>,
356    ) -> Self {
357        self.dynamic_properties
358            .push(RawSqlProjection::new(alias, raw_sql_segment));
359        self
360    }
361
362    pub fn search_with_text(mut self, text: impl Into<String>) -> Self {
363        self.search_with_text = Some(text.into());
364        self
365    }
366
367    pub fn filter(mut self, filter: Expr) -> Self {
368        self.filter = Some(filter);
369        self
370    }
371
372    pub fn and_filter(mut self, filter: Expr) -> Self {
373        self.filter = Some(match self.filter.take() {
374            Some(existing) => existing.and_expr(filter),
375            None => filter,
376        });
377        self
378    }
379
380    pub fn or_filter(mut self, filter: Expr) -> Self {
381        self.filter = Some(match self.filter.take() {
382            Some(existing) => existing.or_expr(filter),
383            None => filter,
384        });
385        self
386    }
387
388    pub fn having(mut self, having: Expr) -> Self {
389        self.having = Some(having);
390        self
391    }
392
393    pub fn and_having(mut self, having: Expr) -> Self {
394        self.having = Some(match self.having.take() {
395            Some(existing) => existing.and_expr(having),
396            None => having,
397        });
398        self
399    }
400
401    pub fn or_having(mut self, having: Expr) -> Self {
402        self.having = Some(match self.having.take() {
403            Some(existing) => existing.or_expr(having),
404            None => having,
405        });
406        self
407    }
408
409    pub fn order_by(mut self, order: OrderBy) -> Self {
410        self.order_by.push(order);
411        self
412    }
413
414    pub fn order_asc(self, field: impl Into<String>) -> Self {
415        self.order_by(OrderBy::asc(field))
416    }
417
418    pub fn order_desc(self, field: impl Into<String>) -> Self {
419        self.order_by(OrderBy::desc(field))
420    }
421
422    pub fn order_expr_asc(self, expr: Expr) -> Self {
423        self.order_by(OrderBy::asc_expr(expr))
424    }
425
426    pub fn order_expr_desc(self, expr: Expr) -> Self {
427        self.order_by(OrderBy::desc_expr(expr))
428    }
429
430    pub fn order_gbk_asc(self, field: impl Into<String>) -> Self {
431        self.order_by(OrderBy::asc_gbk(field))
432    }
433
434    pub fn order_gbk_desc(self, field: impl Into<String>) -> Self {
435        self.order_by(OrderBy::desc_gbk(field))
436    }
437
438    pub fn group_by(mut self, field: impl Into<String>) -> Self {
439        self.group_by.push(field.into());
440        self
441    }
442
443    pub fn aggregate(mut self, aggregate: Aggregate) -> Self {
444        self.aggregates.push(aggregate);
445        self
446    }
447
448    pub fn count(self, alias: impl Into<String>) -> Self {
449        self.aggregate(Aggregate::count(alias))
450    }
451
452    pub fn count_field(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
453        self.aggregate(Aggregate::count_field(field, alias))
454    }
455
456    pub fn sum(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
457        self.aggregate(Aggregate::sum(field, alias))
458    }
459
460    pub fn avg(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
461        self.aggregate(Aggregate::avg(field, alias))
462    }
463
464    pub fn min(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
465        self.aggregate(Aggregate::min(field, alias))
466    }
467
468    pub fn max(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
469        self.aggregate(Aggregate::max(field, alias))
470    }
471
472    pub fn stddev(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
473        self.aggregate(Aggregate::stddev(field, alias))
474    }
475
476    pub fn stddev_pop(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
477        self.aggregate(Aggregate::stddev_pop(field, alias))
478    }
479
480    pub fn var_samp(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
481        self.aggregate(Aggregate::var_samp(field, alias))
482    }
483
484    pub fn var_pop(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
485        self.aggregate(Aggregate::var_pop(field, alias))
486    }
487
488    pub fn bit_and(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
489        self.aggregate(Aggregate::bit_and(field, alias))
490    }
491
492    pub fn bit_or(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
493        self.aggregate(Aggregate::bit_or(field, alias))
494    }
495
496    pub fn bit_xor(self, field: impl Into<String>, alias: impl Into<String>) -> Self {
497        self.aggregate(Aggregate::bit_xor(field, alias))
498    }
499
500    pub fn enable_aggregation_cache(self) -> Self {
501        self.enable_aggregation_cache_for(0)
502    }
503
504    pub fn enable_aggregation_cache_for(mut self, cache_expired_millis: u64) -> Self {
505        self.aggregation_cache = Some(AggregationCacheOptions::enabled(cache_expired_millis));
506        self
507    }
508
509    pub fn propagate_aggregation_cache(mut self, cache_expired_millis: u64) -> Self {
510        self.aggregation_cache = Some(
511            self.aggregation_cache
512                .unwrap_or_else(|| AggregationCacheOptions::enabled(0))
513                .propagate(cache_expired_millis),
514        );
515        self
516    }
517
518    pub fn comment(mut self, comment: impl Into<String>) -> Self {
519        let comment_str = comment.into();
520        self.comment = Some(comment_str.clone());
521        self.trace_chain.push(crate::TraceNode {
522            entity_type: self.entity.clone(),
523            entity_id: None,
524            comment: comment_str,
525        });
526        self
527    }
528
529    pub fn raw_sql(mut self, raw_sql: impl Into<String>) -> Self {
530        self.raw_sql = Some(raw_sql.into());
531        self
532    }
533
534    pub fn raw_sql_search_criteria(mut self, raw_sql: impl Into<String>) -> Self {
535        self.raw_sql_search_criteria.push(raw_sql.into());
536        self
537    }
538
539    pub fn object_group_by(
540        mut self,
541        property_name: impl Into<String>,
542        storage_field: impl Into<String>,
543        query: SelectQuery,
544    ) -> Self {
545        self.object_group_bys
546            .push(ObjectGroupBy::new(property_name, storage_field, query));
547        self
548    }
549
550    pub fn child_enhancement(mut self, query: SelectQuery) -> Self {
551        self.child_enhancements.push(query);
552        self
553    }
554
555    pub fn relation(mut self, name: impl Into<String>) -> Self {
556        self.relations.push(RelationLoad::new(name));
557        self
558    }
559
560    pub fn relation_query(mut self, name: impl Into<String>, query: SelectQuery) -> Self {
561        self.relations.push(RelationLoad::with_query(name, query));
562        self
563    }
564
565    pub fn limit(mut self, limit: u64) -> Self {
566        let slice = self.slice.get_or_insert(Slice {
567            limit: None,
568            offset: 0,
569        });
570        slice.limit = Some(limit);
571        self
572    }
573
574    pub fn offset(mut self, offset: u64) -> Self {
575        let slice = self.slice.get_or_insert(Slice {
576            limit: None,
577            offset: 0,
578        });
579        slice.offset = offset;
580        self
581    }
582
583    pub fn page(self, offset: u64, limit: u64) -> Self {
584        self.offset(offset).limit(limit)
585    }
586}
587
588pub type Record = BTreeMap<String, Value>;
589
590pub fn record_to_json_value(record: &Record) -> serde_json::Value {
591    serde_json::Value::Object(
592        record
593            .iter()
594            .map(|(key, value)| (key.clone(), value.to_json_value()))
595            .collect(),
596    )
597}