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}