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 pub operation: Option<crate::QueryOperation>,
142}
143
144impl RelationSelection {
145 pub fn new(name: impl Into<String>, selection: impl Into<QuerySelection>) -> Self {
146 let selection = selection.into();
147 Self {
148 name: name.into(),
149 query: selection.query,
150 relation_selections: selection.relation_selections,
151 relation_filters: selection.relation_filters,
152 child_enhancements: selection.child_enhancements,
153 query_options: selection.query_options,
154 operation: None,
155 }
156 }
157
158 pub fn into_query(self) -> SelectQuery {
159 let query = apply_relation_selections(self.query, self.relation_selections);
160 apply_runtime_metadata(query, &self.query_options, &self.child_enhancements)
161 }
162 pub fn with_operation(mut self, operation: crate::QueryOperation) -> Self {
163 self.operation = Some(operation);
164 self
165 }
166}
167
168#[derive(Clone, Debug, PartialEq)]
173pub struct RelationFilter {
174 pub name: String,
175 pub query: SelectQuery,
176 pub relation_selections: Vec<RelationSelection>,
177 pub relation_filters: Vec<RelationFilter>,
178 pub child_enhancements: Vec<QuerySelection>,
179 pub query_options: QueryOptions,
180}
181
182impl RelationFilter {
183 pub fn new(name: impl Into<String>, selection: impl Into<QuerySelection>) -> Self {
184 let selection = selection.into();
185 Self {
186 name: name.into(),
187 query: selection.query,
188 relation_selections: selection.relation_selections,
189 relation_filters: selection.relation_filters,
190 child_enhancements: selection.child_enhancements,
191 query_options: selection.query_options,
192 }
193 }
194}
195
196#[derive(Clone, Debug, Default, PartialEq)]
201pub struct QueryOptions {
202 pub comment: Option<String>,
203 pub page_count_operation: Option<crate::QueryOperation>,
206 pub raw_sql: Option<String>,
207 pub raw_sql_search_criteria: Vec<String>,
208 pub dynamic_properties: Vec<RawDynamicProperty>,
209 pub raw_projections: Vec<RawProjection>,
210 pub relation_aggregates: Vec<RelationAggregate>,
211 pub object_group_bys: Vec<ObjectGroupBy>,
212 pub facets: Vec<FacetRequest>,
213}
214
215#[derive(Clone, Debug, PartialEq, Eq)]
220pub struct UnsafeRawSqlSegment {
221 sql: String,
222}
223
224impl UnsafeRawSqlSegment {
225 pub fn trusted(sql: impl Into<String>) -> Self {
226 Self { sql: sql.into() }
227 }
228
229 pub fn into_sql(self) -> String {
230 self.sql
231 }
232}
233
234#[derive(Clone, Debug, PartialEq, Eq)]
239pub struct RawDynamicProperty {
240 pub property_name: String,
241 pub raw_sql_segment: String,
242}
243
244impl RawDynamicProperty {
245 pub fn new(property_name: impl Into<String>, raw_sql_segment: UnsafeRawSqlSegment) -> Self {
246 Self {
247 property_name: property_name.into(),
248 raw_sql_segment: raw_sql_segment.into_sql(),
249 }
250 }
251}
252
253#[derive(Clone, Debug, PartialEq, Eq)]
258pub struct RawProjection {
259 pub property_name: String,
260 pub raw_sql_segment: String,
261}
262
263impl RawProjection {
264 pub fn new(property_name: impl Into<String>, raw_sql_segment: UnsafeRawSqlSegment) -> Self {
265 Self {
266 property_name: property_name.into(),
267 raw_sql_segment: raw_sql_segment.into_sql(),
268 }
269 }
270}
271
272#[derive(Clone, Debug, PartialEq)]
277pub struct RelationAggregate {
278 pub relation_name: String,
279 pub alias: String,
280 pub query: QuerySelection,
281 pub single_result: bool,
282 pub operation: Option<crate::QueryOperation>,
283}
284
285impl RelationAggregate {
286 pub fn new(
287 relation_name: impl Into<String>,
288 alias: impl Into<String>,
289 query: impl Into<QuerySelection>,
290 single_result: bool,
291 ) -> Self {
292 Self {
293 relation_name: relation_name.into(),
294 alias: alias.into(),
295 query: query.into(),
296 single_result,
297 operation: None,
298 }
299 }
300 pub fn with_operation(mut self, operation: crate::QueryOperation) -> Self {
301 self.operation = Some(operation);
302 self
303 }
304}
305
306#[derive(Clone, Debug, PartialEq)]
311pub struct FacetRequest {
312 pub facet_name: String,
313 pub relation_name: String,
314 pub query: QuerySelection,
315 pub include_all_facets: bool,
316 pub operation: Option<crate::QueryOperation>,
317}
318
319impl FacetRequest {
320 pub fn new(
321 facet_name: impl Into<String>,
322 relation_name: impl Into<String>,
323 query: impl Into<QuerySelection>,
324 include_all_facets: bool,
325 ) -> Self {
326 Self {
327 facet_name: facet_name.into(),
328 relation_name: relation_name.into(),
329 query: query.into(),
330 include_all_facets,
331 operation: None,
332 }
333 }
334 pub fn with_operation(mut self, operation: crate::QueryOperation) -> Self {
335 self.operation = Some(operation);
336 self
337 }
338}
339
340#[derive(Clone, Debug, PartialEq)]
345pub struct ObjectGroupBy {
346 pub property_name: String,
347 pub storage_field: String,
348 pub query: QuerySelection,
349}
350
351impl ObjectGroupBy {
352 pub fn new(
353 property_name: impl Into<String>,
354 storage_field: impl Into<String>,
355 query: impl Into<QuerySelection>,
356 ) -> Self {
357 Self {
358 property_name: property_name.into(),
359 storage_field: storage_field.into(),
360 query: query.into(),
361 }
362 }
363}
364
365pub fn apply_relation_selections(
370 mut query: SelectQuery,
371 relation_selections: Vec<RelationSelection>,
372) -> SelectQuery {
373 for selection in relation_selections {
374 let operation = selection.operation.clone();
375 let name = selection.name.clone();
376 let mut load = crate::RelationLoad::with_query(name, selection.into_query());
377 load.operation = operation;
378 query.relations.push(load);
379 }
380 query
381}
382
383pub fn apply_runtime_metadata(
384 mut query: SelectQuery,
385 options: &QueryOptions,
386 child_enhancements: &[QuerySelection],
387) -> SelectQuery {
388 if let Some(c) = options.comment.clone() {
389 query = query.comment(c);
390 }
391 query.raw_sql = options.raw_sql.clone();
392 query.facets = options.facets.clone();
393 query.relation_aggregates = runtime_relation_aggregates(options);
394 query.raw_sql_search_criteria = options.raw_sql_search_criteria.clone();
395 query.dynamic_properties = options
396 .dynamic_properties
397 .iter()
398 .map(|projection| {
399 CoreRawSqlProjection::new(
400 projection.property_name.clone(),
401 projection.raw_sql_segment.clone(),
402 )
403 })
404 .collect();
405 query.raw_projections = options
406 .raw_projections
407 .iter()
408 .map(|projection| {
409 CoreRawSqlProjection::new(
410 projection.property_name.clone(),
411 projection.raw_sql_segment.clone(),
412 )
413 })
414 .collect();
415 query.object_group_bys = options
416 .object_group_bys
417 .iter()
418 .map(|group_by| {
419 CoreObjectGroupBy::new(
420 group_by.property_name.clone(),
421 group_by.storage_field.clone(),
422 group_by.query.clone().into_query(),
423 )
424 })
425 .collect();
426 query.child_enhancements = child_enhancements
427 .iter()
428 .cloned()
429 .map(QuerySelection::into_query)
430 .collect();
431 query
432}
433
434pub fn runtime_relation_aggregates(options: &QueryOptions) -> Vec<RuntimeRelationAggregate> {
439 options
440 .relation_aggregates
441 .iter()
442 .map(|aggregate| {
443 let mut runtime = RuntimeRelationAggregate::new(
444 aggregate.relation_name.clone(),
445 aggregate.alias.clone(),
446 aggregate.query.clone().into_query(),
447 aggregate.single_result,
448 );
449 runtime.operation = aggregate.operation.clone();
450 runtime
451 })
452 .collect()
453}
454
455pub fn merge_outer_filter_into_facet_aggregates(
460 selection: &mut QuerySelection,
461 outer_query: &SelectQuery,
462) {
463 let Some(filter) = outer_query.filter.clone() else {
464 return;
465 };
466 for aggregate in &mut selection.query_options.relation_aggregates {
467 if aggregate.query.query.entity == outer_query.entity {
468 aggregate.query.query = aggregate.query.query.clone().and_filter(filter.clone());
469 }
470 }
471}
472
473pub fn attach_facets<T>(rows: &mut SmartList<T>, facets: BTreeMap<String, SmartList<CompactRow>>) {
474 for (name, facet) in facets {
475 rows.add_facet(name, facet);
476 }
477}
478
479pub fn field_operator_expr(field: &str, operator: FieldOperator, values: Vec<Value>) -> Expr {
484 match operator {
485 FieldOperator::Equal => Expr::eq(field, required_value(operator, &values, 0)),
486 FieldOperator::NotEqual => Expr::ne(field, required_value(operator, &values, 0)),
487 FieldOperator::GreaterThan => Expr::gt(field, required_value(operator, &values, 0)),
488 FieldOperator::GreaterThanOrEqual => Expr::gte(field, required_value(operator, &values, 0)),
489 FieldOperator::LessThan => Expr::lt(field, required_value(operator, &values, 0)),
490 FieldOperator::LessThanOrEqual => Expr::lte(field, required_value(operator, &values, 0)),
491 FieldOperator::Between => Expr::between(
492 field,
493 required_value(operator, &values, 0),
494 required_value(operator, &values, 1),
495 ),
496 FieldOperator::In => Expr::in_list(field, values),
497 FieldOperator::NotIn => Expr::not_in_list(field, values),
498 FieldOperator::Contain => Expr::contain(field, required_text(operator, &values, 0)),
499 FieldOperator::NotContain => Expr::not_contain(field, required_text(operator, &values, 0)),
500 FieldOperator::BeginWith => Expr::begin_with(field, required_text(operator, &values, 0)),
501 FieldOperator::NotBeginWith => {
502 Expr::not_begin_with(field, required_text(operator, &values, 0))
503 }
504 FieldOperator::EndWith => Expr::end_with(field, required_text(operator, &values, 0)),
505 FieldOperator::NotEndWith => Expr::not_end_with(field, required_text(operator, &values, 0)),
506 FieldOperator::SoundsLike => Expr::sound_like(field, required_value(operator, &values, 0)),
507 FieldOperator::IsNull => Expr::is_null(field),
508 FieldOperator::IsNotNull => Expr::is_not_null(field),
509 }
510}
511
512pub fn field_operator_column_expr(field: &str, operator: FieldOperator, other_field: &str) -> Expr {
513 let binary_op = match operator {
514 FieldOperator::Equal => BinaryOp::Eq,
515 FieldOperator::NotEqual => BinaryOp::Ne,
516 FieldOperator::GreaterThan => BinaryOp::Gt,
517 FieldOperator::GreaterThanOrEqual => BinaryOp::Gte,
518 FieldOperator::LessThan => BinaryOp::Lt,
519 FieldOperator::LessThanOrEqual => BinaryOp::Lte,
520 FieldOperator::Contain => BinaryOp::Like,
521 FieldOperator::NotContain => BinaryOp::NotLike,
522 FieldOperator::BeginWith => BinaryOp::Like,
523 FieldOperator::NotBeginWith => BinaryOp::NotLike,
524 FieldOperator::EndWith => BinaryOp::Like,
525 FieldOperator::NotEndWith => BinaryOp::NotLike,
526 unsupported => panic!("{unsupported:?} is not supported for property-to-property filters"),
527 };
528 Expr::compare_columns(field, binary_op, other_field)
529}
530
531pub fn required_value(operator: FieldOperator, values: &[Value], index: usize) -> Value {
536 values
537 .get(index)
538 .cloned()
539 .unwrap_or_else(|| panic!("{operator:?} requires value at index {index}"))
540}
541
542pub fn required_text(operator: FieldOperator, values: &[Value], index: usize) -> String {
543 match required_value(operator, values, index) {
544 Value::Text(value) => value,
545 value => panic!("{operator:?} requires text value, got {value:?}"),
546 }
547}
548
549pub fn remove_default_live_filter(filter: Option<Expr>) -> Option<Expr> {
554 let default_filter = Expr::gt("version", 0_i64);
555 remove_filter_expr(filter?, &default_filter)
556}
557
558pub fn remove_filter_expr(filter: Expr, target: &Expr) -> Option<Expr> {
559 if &filter == target {
560 return None;
561 }
562 match filter {
563 Expr::And(parts) => {
564 let mut retained = parts
565 .into_iter()
566 .filter_map(|part| remove_filter_expr(part, target))
567 .collect::<Vec<_>>();
568 match retained.len() {
569 0 => None,
570 1 => retained.pop(),
571 _ => Some(Expr::And(retained)),
572 }
573 }
574 other => Some(other),
575 }
576}
577
578pub fn dynamic_json_value_to_teaql_value(value: &JsonValue) -> Value {
583 match value {
584 JsonValue::Null => Value::Null,
585 JsonValue::Bool(value) => Value::Bool(*value),
586 JsonValue::Number(value) => value
587 .as_i64()
588 .map(Value::I64)
589 .or_else(|| value.as_u64().map(Value::U64))
590 .or_else(|| value.as_f64().map(Value::F64))
591 .unwrap_or(Value::Null),
592 JsonValue::String(value) => Value::Text(value.trim().to_owned()),
593 JsonValue::Array(values) => Value::List(
594 values
595 .iter()
596 .map(dynamic_json_value_to_teaql_value)
597 .collect(),
598 ),
599 JsonValue::Object(object) => object
600 .get("id")
601 .map(dynamic_json_value_to_teaql_value)
602 .unwrap_or(Value::Null),
603 }
604}
605
606pub fn dynamic_json_values(value: &JsonValue) -> Vec<Value> {
607 match value {
608 JsonValue::Array(values) => values
609 .iter()
610 .map(dynamic_json_value_to_teaql_value)
611 .collect(),
612 value => vec![dynamic_json_value_to_teaql_value(value)],
613 }
614}
615
616pub fn dynamic_json_operator(value: &JsonValue) -> FieldOperator {
617 match value {
618 JsonValue::String(value) if value.eq_ignore_ascii_case("__is_null__") => {
619 FieldOperator::IsNull
620 }
621 JsonValue::String(value) if value.eq_ignore_ascii_case("__is_not_null__") => {
622 FieldOperator::IsNotNull
623 }
624 JsonValue::String(_) => FieldOperator::Contain,
625 JsonValue::Number(_) | JsonValue::Bool(_) => FieldOperator::Equal,
626 JsonValue::Array(values) if values.first().map(JsonValue::is_string).unwrap_or(false) => {
627 FieldOperator::In
628 }
629 JsonValue::Array(values) if values.first().map(JsonValue::is_object).unwrap_or(false) => {
630 FieldOperator::In
631 }
632 JsonValue::Array(values) if values.len() == 2 => FieldOperator::Between,
633 _ => FieldOperator::Equal,
634 }
635}
636
637pub fn dynamic_json_filter_expr(field: &str, value: &JsonValue) -> Expr {
638 let operator = dynamic_json_operator(value);
639 field_operator_expr(field, operator, dynamic_json_values(value))
640}
641
642pub fn dynamic_json_u64_field(
643 object: &serde_json::Map<String, JsonValue>,
644 field: &str,
645) -> Option<u64> {
646 object.get(field).and_then(|value| {
647 value
648 .as_u64()
649 .or_else(|| value.as_i64().and_then(|value| u64::try_from(value).ok()))
650 })
651}