1use std::collections::BTreeMap;
14
15use serde_json::Value as JsonValue;
16
17use crate::{
18 BinaryOp, CompactRow, Expr, ObjectGroupBy as CoreObjectGroupBy,
19 RawSqlProjection as CoreRawSqlProjection, RelationAggregate as RuntimeRelationAggregate,
20 SelectQuery, SmartList, Value,
21};
22
23pub const COUNT_ALIAS: &str = "count";
28pub const TYPE_FIELD: &str = "internal_type";
29pub const TYPE_GROUP_FIELD: &str = "type_group";
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum FieldOperator {
37 Equal,
38 NotEqual,
39 GreaterThan,
40 GreaterThanOrEqual,
41 LessThan,
42 LessThanOrEqual,
43 Between,
44 In,
45 NotIn,
46 Contain,
47 NotContain,
48 BeginWith,
49 NotBeginWith,
50 EndWith,
51 NotEndWith,
52 SoundsLike,
53 IsNull,
54 IsNotNull,
55}
56
57#[derive(Clone, Debug, PartialEq)]
62pub struct DateRange<T> {
63 pub start: T,
64 pub end: T,
65}
66
67impl<T> DateRange<T> {
68 pub fn new(start: T, end: T) -> Self {
69 Self { start, end }
70 }
71}
72
73pub trait EntityReference {
78 fn entity_id_value(self) -> Value;
79}
80
81impl EntityReference for Value {
82 fn entity_id_value(self) -> Value {
83 self
84 }
85}
86
87impl EntityReference for u64 {
88 fn entity_id_value(self) -> Value {
89 Value::U64(self)
90 }
91}
92
93#[derive(Clone, Debug, PartialEq)]
98pub struct QuerySelection {
99 pub query: SelectQuery,
100 pub relation_selections: Vec<RelationSelection>,
101 pub relation_filters: Vec<RelationFilter>,
102 pub child_enhancements: Vec<QuerySelection>,
103 pub query_options: QueryOptions,
104}
105
106impl QuerySelection {
107 pub fn new(query: impl Into<SelectQuery>) -> Self {
108 Self {
109 query: query.into(),
110 relation_selections: Vec::new(),
111 relation_filters: Vec::new(),
112 child_enhancements: Vec::new(),
113 query_options: QueryOptions::default(),
114 }
115 }
116
117 pub fn into_query(self) -> SelectQuery {
118 let query = apply_relation_selections(self.query, self.relation_selections);
119 apply_runtime_metadata(query, &self.query_options, &self.child_enhancements)
120 }
121}
122
123impl From<SelectQuery> for QuerySelection {
124 fn from(query: SelectQuery) -> Self {
125 QuerySelection::new(query)
126 }
127}
128
129#[derive(Clone, Debug, PartialEq)]
134pub struct RelationSelection {
135 pub name: String,
136 pub query: SelectQuery,
137 pub relation_selections: Vec<RelationSelection>,
138 pub relation_filters: Vec<RelationFilter>,
139 pub child_enhancements: Vec<QuerySelection>,
140 pub query_options: QueryOptions,
141}
142
143impl RelationSelection {
144 pub fn new(name: impl Into<String>, selection: impl Into<QuerySelection>) -> Self {
145 let selection = selection.into();
146 Self {
147 name: name.into(),
148 query: selection.query,
149 relation_selections: selection.relation_selections,
150 relation_filters: selection.relation_filters,
151 child_enhancements: selection.child_enhancements,
152 query_options: selection.query_options,
153 }
154 }
155
156 pub fn into_query(self) -> SelectQuery {
157 let query = apply_relation_selections(self.query, self.relation_selections);
158 apply_runtime_metadata(query, &self.query_options, &self.child_enhancements)
159 }
160}
161
162#[derive(Clone, Debug, PartialEq)]
167pub struct RelationFilter {
168 pub name: String,
169 pub query: SelectQuery,
170 pub relation_selections: Vec<RelationSelection>,
171 pub relation_filters: Vec<RelationFilter>,
172 pub child_enhancements: Vec<QuerySelection>,
173 pub query_options: QueryOptions,
174}
175
176impl RelationFilter {
177 pub fn new(name: impl Into<String>, selection: impl Into<QuerySelection>) -> Self {
178 let selection = selection.into();
179 Self {
180 name: name.into(),
181 query: selection.query,
182 relation_selections: selection.relation_selections,
183 relation_filters: selection.relation_filters,
184 child_enhancements: selection.child_enhancements,
185 query_options: selection.query_options,
186 }
187 }
188}
189
190#[derive(Clone, Debug, Default, PartialEq)]
195pub struct QueryOptions {
196 pub comment: Option<String>,
197 pub raw_sql: Option<String>,
198 pub raw_sql_search_criteria: Vec<String>,
199 pub dynamic_properties: Vec<RawDynamicProperty>,
200 pub raw_projections: Vec<RawProjection>,
201 pub relation_aggregates: Vec<RelationAggregate>,
202 pub object_group_bys: Vec<ObjectGroupBy>,
203 pub facets: Vec<FacetRequest>,
204}
205
206#[derive(Clone, Debug, PartialEq, Eq)]
211pub struct UnsafeRawSqlSegment {
212 sql: String,
213}
214
215impl UnsafeRawSqlSegment {
216 pub fn trusted(sql: impl Into<String>) -> Self {
217 Self { sql: sql.into() }
218 }
219
220 pub fn into_sql(self) -> String {
221 self.sql
222 }
223}
224
225#[derive(Clone, Debug, PartialEq, Eq)]
230pub struct RawDynamicProperty {
231 pub property_name: String,
232 pub raw_sql_segment: String,
233}
234
235impl RawDynamicProperty {
236 pub fn new(property_name: impl Into<String>, raw_sql_segment: UnsafeRawSqlSegment) -> Self {
237 Self {
238 property_name: property_name.into(),
239 raw_sql_segment: raw_sql_segment.into_sql(),
240 }
241 }
242}
243
244#[derive(Clone, Debug, PartialEq, Eq)]
249pub struct RawProjection {
250 pub property_name: String,
251 pub raw_sql_segment: String,
252}
253
254impl RawProjection {
255 pub fn new(property_name: impl Into<String>, raw_sql_segment: UnsafeRawSqlSegment) -> Self {
256 Self {
257 property_name: property_name.into(),
258 raw_sql_segment: raw_sql_segment.into_sql(),
259 }
260 }
261}
262
263#[derive(Clone, Debug, PartialEq)]
268pub struct RelationAggregate {
269 pub relation_name: String,
270 pub alias: String,
271 pub query: QuerySelection,
272 pub single_result: bool,
273}
274
275impl RelationAggregate {
276 pub fn new(
277 relation_name: impl Into<String>,
278 alias: impl Into<String>,
279 query: impl Into<QuerySelection>,
280 single_result: bool,
281 ) -> Self {
282 Self {
283 relation_name: relation_name.into(),
284 alias: alias.into(),
285 query: query.into(),
286 single_result,
287 }
288 }
289}
290
291#[derive(Clone, Debug, PartialEq)]
296pub struct FacetRequest {
297 pub facet_name: String,
298 pub relation_name: String,
299 pub query: QuerySelection,
300 pub include_all_facets: bool,
301}
302
303impl FacetRequest {
304 pub fn new(
305 facet_name: impl Into<String>,
306 relation_name: impl Into<String>,
307 query: impl Into<QuerySelection>,
308 include_all_facets: bool,
309 ) -> Self {
310 Self {
311 facet_name: facet_name.into(),
312 relation_name: relation_name.into(),
313 query: query.into(),
314 include_all_facets,
315 }
316 }
317}
318
319#[derive(Clone, Debug, PartialEq)]
324pub struct ObjectGroupBy {
325 pub property_name: String,
326 pub storage_field: String,
327 pub query: QuerySelection,
328}
329
330impl ObjectGroupBy {
331 pub fn new(
332 property_name: impl Into<String>,
333 storage_field: impl Into<String>,
334 query: impl Into<QuerySelection>,
335 ) -> Self {
336 Self {
337 property_name: property_name.into(),
338 storage_field: storage_field.into(),
339 query: query.into(),
340 }
341 }
342}
343
344pub fn apply_relation_selections(
349 mut query: SelectQuery,
350 relation_selections: Vec<RelationSelection>,
351) -> SelectQuery {
352 for selection in relation_selections {
353 query = query.relation_query(selection.name.clone(), selection.into_query());
354 }
355 query
356}
357
358pub fn apply_runtime_metadata(
359 mut query: SelectQuery,
360 options: &QueryOptions,
361 child_enhancements: &[QuerySelection],
362) -> SelectQuery {
363 if let Some(c) = options.comment.clone() {
364 query = query.comment(c);
365 }
366 query.raw_sql = options.raw_sql.clone();
367 query.facets = options.facets.clone();
368 query.relation_aggregates = runtime_relation_aggregates(options);
369 query.raw_sql_search_criteria = options.raw_sql_search_criteria.clone();
370 query.dynamic_properties = options
371 .dynamic_properties
372 .iter()
373 .map(|projection| {
374 CoreRawSqlProjection::new(
375 projection.property_name.clone(),
376 projection.raw_sql_segment.clone(),
377 )
378 })
379 .collect();
380 query.raw_projections = options
381 .raw_projections
382 .iter()
383 .map(|projection| {
384 CoreRawSqlProjection::new(
385 projection.property_name.clone(),
386 projection.raw_sql_segment.clone(),
387 )
388 })
389 .collect();
390 query.object_group_bys = options
391 .object_group_bys
392 .iter()
393 .map(|group_by| {
394 CoreObjectGroupBy::new(
395 group_by.property_name.clone(),
396 group_by.storage_field.clone(),
397 group_by.query.clone().into_query(),
398 )
399 })
400 .collect();
401 query.child_enhancements = child_enhancements
402 .iter()
403 .cloned()
404 .map(QuerySelection::into_query)
405 .collect();
406 query
407}
408
409pub fn runtime_relation_aggregates(options: &QueryOptions) -> Vec<RuntimeRelationAggregate> {
414 options
415 .relation_aggregates
416 .iter()
417 .map(|aggregate| {
418 RuntimeRelationAggregate::new(
419 aggregate.relation_name.clone(),
420 aggregate.alias.clone(),
421 aggregate.query.clone().into_query(),
422 aggregate.single_result,
423 )
424 })
425 .collect()
426}
427
428pub fn merge_outer_filter_into_facet_aggregates(
433 selection: &mut QuerySelection,
434 outer_query: &SelectQuery,
435) {
436 let Some(filter) = outer_query.filter.clone() else {
437 return;
438 };
439 for aggregate in &mut selection.query_options.relation_aggregates {
440 if aggregate.query.query.entity == outer_query.entity {
441 aggregate.query.query = aggregate.query.query.clone().and_filter(filter.clone());
442 }
443 }
444}
445
446pub fn attach_facets<T>(rows: &mut SmartList<T>, facets: BTreeMap<String, SmartList<CompactRow>>) {
447 for (name, facet) in facets {
448 rows.add_facet(name, facet);
449 }
450}
451
452pub fn field_operator_expr(field: &str, operator: FieldOperator, values: Vec<Value>) -> Expr {
457 match operator {
458 FieldOperator::Equal => Expr::eq(field, required_value(operator, &values, 0)),
459 FieldOperator::NotEqual => Expr::ne(field, required_value(operator, &values, 0)),
460 FieldOperator::GreaterThan => Expr::gt(field, required_value(operator, &values, 0)),
461 FieldOperator::GreaterThanOrEqual => Expr::gte(field, required_value(operator, &values, 0)),
462 FieldOperator::LessThan => Expr::lt(field, required_value(operator, &values, 0)),
463 FieldOperator::LessThanOrEqual => Expr::lte(field, required_value(operator, &values, 0)),
464 FieldOperator::Between => Expr::between(
465 field,
466 required_value(operator, &values, 0),
467 required_value(operator, &values, 1),
468 ),
469 FieldOperator::In => Expr::in_list(field, values),
470 FieldOperator::NotIn => Expr::not_in_list(field, values),
471 FieldOperator::Contain => Expr::contain(field, required_text(operator, &values, 0)),
472 FieldOperator::NotContain => Expr::not_contain(field, required_text(operator, &values, 0)),
473 FieldOperator::BeginWith => Expr::begin_with(field, required_text(operator, &values, 0)),
474 FieldOperator::NotBeginWith => {
475 Expr::not_begin_with(field, required_text(operator, &values, 0))
476 }
477 FieldOperator::EndWith => Expr::end_with(field, required_text(operator, &values, 0)),
478 FieldOperator::NotEndWith => Expr::not_end_with(field, required_text(operator, &values, 0)),
479 FieldOperator::SoundsLike => Expr::sound_like(field, required_value(operator, &values, 0)),
480 FieldOperator::IsNull => Expr::is_null(field),
481 FieldOperator::IsNotNull => Expr::is_not_null(field),
482 }
483}
484
485pub fn field_operator_column_expr(field: &str, operator: FieldOperator, other_field: &str) -> Expr {
486 let binary_op = match operator {
487 FieldOperator::Equal => BinaryOp::Eq,
488 FieldOperator::NotEqual => BinaryOp::Ne,
489 FieldOperator::GreaterThan => BinaryOp::Gt,
490 FieldOperator::GreaterThanOrEqual => BinaryOp::Gte,
491 FieldOperator::LessThan => BinaryOp::Lt,
492 FieldOperator::LessThanOrEqual => BinaryOp::Lte,
493 FieldOperator::Contain => BinaryOp::Like,
494 FieldOperator::NotContain => BinaryOp::NotLike,
495 FieldOperator::BeginWith => BinaryOp::Like,
496 FieldOperator::NotBeginWith => BinaryOp::NotLike,
497 FieldOperator::EndWith => BinaryOp::Like,
498 FieldOperator::NotEndWith => BinaryOp::NotLike,
499 unsupported => panic!("{unsupported:?} is not supported for property-to-property filters"),
500 };
501 Expr::compare_columns(field, binary_op, other_field)
502}
503
504pub fn required_value(operator: FieldOperator, values: &[Value], index: usize) -> Value {
509 values
510 .get(index)
511 .cloned()
512 .unwrap_or_else(|| panic!("{operator:?} requires value at index {index}"))
513}
514
515pub fn required_text(operator: FieldOperator, values: &[Value], index: usize) -> String {
516 match required_value(operator, values, index) {
517 Value::Text(value) => value,
518 value => panic!("{operator:?} requires text value, got {value:?}"),
519 }
520}
521
522pub fn remove_default_live_filter(filter: Option<Expr>) -> Option<Expr> {
527 let default_filter = Expr::gt("version", 0_i64);
528 remove_filter_expr(filter?, &default_filter)
529}
530
531pub fn remove_filter_expr(filter: Expr, target: &Expr) -> Option<Expr> {
532 if &filter == target {
533 return None;
534 }
535 match filter {
536 Expr::And(parts) => {
537 let mut retained = parts
538 .into_iter()
539 .filter_map(|part| remove_filter_expr(part, target))
540 .collect::<Vec<_>>();
541 match retained.len() {
542 0 => None,
543 1 => retained.pop(),
544 _ => Some(Expr::And(retained)),
545 }
546 }
547 other => Some(other),
548 }
549}
550
551pub fn dynamic_json_value_to_teaql_value(value: &JsonValue) -> Value {
556 match value {
557 JsonValue::Null => Value::Null,
558 JsonValue::Bool(value) => Value::Bool(*value),
559 JsonValue::Number(value) => value
560 .as_i64()
561 .map(Value::I64)
562 .or_else(|| value.as_u64().map(Value::U64))
563 .or_else(|| value.as_f64().map(Value::F64))
564 .unwrap_or(Value::Null),
565 JsonValue::String(value) => Value::Text(value.trim().to_owned()),
566 JsonValue::Array(values) => Value::List(
567 values
568 .iter()
569 .map(dynamic_json_value_to_teaql_value)
570 .collect(),
571 ),
572 JsonValue::Object(object) => object
573 .get("id")
574 .map(dynamic_json_value_to_teaql_value)
575 .unwrap_or(Value::Null),
576 }
577}
578
579pub fn dynamic_json_values(value: &JsonValue) -> Vec<Value> {
580 match value {
581 JsonValue::Array(values) => values
582 .iter()
583 .map(dynamic_json_value_to_teaql_value)
584 .collect(),
585 value => vec![dynamic_json_value_to_teaql_value(value)],
586 }
587}
588
589pub fn dynamic_json_operator(value: &JsonValue) -> FieldOperator {
590 match value {
591 JsonValue::String(value) if value.eq_ignore_ascii_case("__is_null__") => {
592 FieldOperator::IsNull
593 }
594 JsonValue::String(value) if value.eq_ignore_ascii_case("__is_not_null__") => {
595 FieldOperator::IsNotNull
596 }
597 JsonValue::String(_) => FieldOperator::Contain,
598 JsonValue::Number(_) | JsonValue::Bool(_) => FieldOperator::Equal,
599 JsonValue::Array(values) if values.first().map(JsonValue::is_string).unwrap_or(false) => {
600 FieldOperator::In
601 }
602 JsonValue::Array(values) if values.first().map(JsonValue::is_object).unwrap_or(false) => {
603 FieldOperator::In
604 }
605 JsonValue::Array(values) if values.len() == 2 => FieldOperator::Between,
606 _ => FieldOperator::Equal,
607 }
608}
609
610pub fn dynamic_json_filter_expr(field: &str, value: &JsonValue) -> Expr {
611 let operator = dynamic_json_operator(value);
612 field_operator_expr(field, operator, dynamic_json_values(value))
613}
614
615pub fn dynamic_json_u64_field(
616 object: &serde_json::Map<String, JsonValue>,
617 field: &str,
618) -> Option<u64> {
619 object.get(field).and_then(|value| {
620 value
621 .as_u64()
622 .or_else(|| value.as_i64().and_then(|value| u64::try_from(value).ok()))
623 })
624}