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