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.raw_sql_search_criteria = options.raw_sql_search_criteria.clone();
368 query.dynamic_properties = options
369 .dynamic_properties
370 .iter()
371 .map(|projection| {
372 CoreRawSqlProjection::new(
373 projection.property_name.clone(),
374 projection.raw_sql_segment.clone(),
375 )
376 })
377 .collect();
378 query.raw_projections = options
379 .raw_projections
380 .iter()
381 .map(|projection| {
382 CoreRawSqlProjection::new(
383 projection.property_name.clone(),
384 projection.raw_sql_segment.clone(),
385 )
386 })
387 .collect();
388 query.object_group_bys = options
389 .object_group_bys
390 .iter()
391 .map(|group_by| {
392 CoreObjectGroupBy::new(
393 group_by.property_name.clone(),
394 group_by.storage_field.clone(),
395 group_by.query.clone().into_query(),
396 )
397 })
398 .collect();
399 query.child_enhancements = child_enhancements
400 .iter()
401 .cloned()
402 .map(QuerySelection::into_query)
403 .collect();
404 query
405}
406
407pub fn runtime_relation_aggregates(options: &QueryOptions) -> Vec<RuntimeRelationAggregate> {
412 options
413 .relation_aggregates
414 .iter()
415 .map(|aggregate| {
416 RuntimeRelationAggregate::new(
417 aggregate.relation_name.clone(),
418 aggregate.alias.clone(),
419 aggregate.query.clone().into_query(),
420 aggregate.single_result,
421 )
422 })
423 .collect()
424}
425
426pub fn merge_outer_filter_into_facet_aggregates(
431 selection: &mut QuerySelection,
432 outer_query: &SelectQuery,
433) {
434 let Some(filter) = outer_query.filter.clone() else {
435 return;
436 };
437 for aggregate in &mut selection.query_options.relation_aggregates {
438 if aggregate.query.query.entity == outer_query.entity {
439 aggregate.query.query = aggregate.query.query.clone().and_filter(filter.clone());
440 }
441 }
442}
443
444pub fn attach_facets<T>(rows: &mut SmartList<T>, facets: BTreeMap<String, SmartList<CompactRow>>) {
445 for (name, facet) in facets {
446 rows.add_facet(name, facet);
447 }
448}
449
450pub fn field_operator_expr(field: &str, operator: FieldOperator, values: Vec<Value>) -> Expr {
455 match operator {
456 FieldOperator::Equal => Expr::eq(field, required_value(operator, &values, 0)),
457 FieldOperator::NotEqual => Expr::ne(field, required_value(operator, &values, 0)),
458 FieldOperator::GreaterThan => Expr::gt(field, required_value(operator, &values, 0)),
459 FieldOperator::GreaterThanOrEqual => Expr::gte(field, required_value(operator, &values, 0)),
460 FieldOperator::LessThan => Expr::lt(field, required_value(operator, &values, 0)),
461 FieldOperator::LessThanOrEqual => Expr::lte(field, required_value(operator, &values, 0)),
462 FieldOperator::Between => Expr::between(
463 field,
464 required_value(operator, &values, 0),
465 required_value(operator, &values, 1),
466 ),
467 FieldOperator::In => Expr::in_list(field, values),
468 FieldOperator::NotIn => Expr::not_in_list(field, values),
469 FieldOperator::Contain => Expr::contain(field, required_text(operator, &values, 0)),
470 FieldOperator::NotContain => Expr::not_contain(field, required_text(operator, &values, 0)),
471 FieldOperator::BeginWith => Expr::begin_with(field, required_text(operator, &values, 0)),
472 FieldOperator::NotBeginWith => {
473 Expr::not_begin_with(field, required_text(operator, &values, 0))
474 }
475 FieldOperator::EndWith => Expr::end_with(field, required_text(operator, &values, 0)),
476 FieldOperator::NotEndWith => Expr::not_end_with(field, required_text(operator, &values, 0)),
477 FieldOperator::SoundsLike => Expr::sound_like(field, required_value(operator, &values, 0)),
478 FieldOperator::IsNull => Expr::is_null(field),
479 FieldOperator::IsNotNull => Expr::is_not_null(field),
480 }
481}
482
483pub fn field_operator_column_expr(field: &str, operator: FieldOperator, other_field: &str) -> Expr {
484 let binary_op = match operator {
485 FieldOperator::Equal => BinaryOp::Eq,
486 FieldOperator::NotEqual => BinaryOp::Ne,
487 FieldOperator::GreaterThan => BinaryOp::Gt,
488 FieldOperator::GreaterThanOrEqual => BinaryOp::Gte,
489 FieldOperator::LessThan => BinaryOp::Lt,
490 FieldOperator::LessThanOrEqual => BinaryOp::Lte,
491 FieldOperator::Contain => BinaryOp::Like,
492 FieldOperator::NotContain => BinaryOp::NotLike,
493 FieldOperator::BeginWith => BinaryOp::Like,
494 FieldOperator::NotBeginWith => BinaryOp::NotLike,
495 FieldOperator::EndWith => BinaryOp::Like,
496 FieldOperator::NotEndWith => BinaryOp::NotLike,
497 unsupported => panic!("{unsupported:?} is not supported for property-to-property filters"),
498 };
499 Expr::compare_columns(field, binary_op, other_field)
500}
501
502pub fn required_value(operator: FieldOperator, values: &[Value], index: usize) -> Value {
507 values
508 .get(index)
509 .cloned()
510 .unwrap_or_else(|| panic!("{operator:?} requires value at index {index}"))
511}
512
513pub fn required_text(operator: FieldOperator, values: &[Value], index: usize) -> String {
514 match required_value(operator, values, index) {
515 Value::Text(value) => value,
516 value => panic!("{operator:?} requires text value, got {value:?}"),
517 }
518}
519
520pub fn remove_default_live_filter(filter: Option<Expr>) -> Option<Expr> {
525 let default_filter = Expr::gt("version", 0_i64);
526 remove_filter_expr(filter?, &default_filter)
527}
528
529pub fn remove_filter_expr(filter: Expr, target: &Expr) -> Option<Expr> {
530 if &filter == target {
531 return None;
532 }
533 match filter {
534 Expr::And(parts) => {
535 let mut retained = parts
536 .into_iter()
537 .filter_map(|part| remove_filter_expr(part, target))
538 .collect::<Vec<_>>();
539 match retained.len() {
540 0 => None,
541 1 => retained.pop(),
542 _ => Some(Expr::And(retained)),
543 }
544 }
545 other => Some(other),
546 }
547}
548
549pub fn dynamic_json_value_to_teaql_value(value: &JsonValue) -> Value {
554 match value {
555 JsonValue::Null => Value::Null,
556 JsonValue::Bool(value) => Value::Bool(*value),
557 JsonValue::Number(value) => value
558 .as_i64()
559 .map(Value::I64)
560 .or_else(|| value.as_u64().map(Value::U64))
561 .or_else(|| value.as_f64().map(Value::F64))
562 .unwrap_or(Value::Null),
563 JsonValue::String(value) => Value::Text(value.trim().to_owned()),
564 JsonValue::Array(values) => Value::List(
565 values
566 .iter()
567 .map(dynamic_json_value_to_teaql_value)
568 .collect(),
569 ),
570 JsonValue::Object(object) => object
571 .get("id")
572 .map(dynamic_json_value_to_teaql_value)
573 .unwrap_or(Value::Null),
574 }
575}
576
577pub fn dynamic_json_values(value: &JsonValue) -> Vec<Value> {
578 match value {
579 JsonValue::Array(values) => values
580 .iter()
581 .map(dynamic_json_value_to_teaql_value)
582 .collect(),
583 value => vec![dynamic_json_value_to_teaql_value(value)],
584 }
585}
586
587pub fn dynamic_json_operator(value: &JsonValue) -> FieldOperator {
588 match value {
589 JsonValue::String(value) if value.eq_ignore_ascii_case("__is_null__") => {
590 FieldOperator::IsNull
591 }
592 JsonValue::String(value) if value.eq_ignore_ascii_case("__is_not_null__") => {
593 FieldOperator::IsNotNull
594 }
595 JsonValue::String(_) => FieldOperator::Contain,
596 JsonValue::Number(_) | JsonValue::Bool(_) => FieldOperator::Equal,
597 JsonValue::Array(values) if values.first().map(JsonValue::is_string).unwrap_or(false) => {
598 FieldOperator::In
599 }
600 JsonValue::Array(values) if values.first().map(JsonValue::is_object).unwrap_or(false) => {
601 FieldOperator::In
602 }
603 JsonValue::Array(values) if values.len() == 2 => FieldOperator::Between,
604 _ => FieldOperator::Equal,
605 }
606}
607
608pub fn dynamic_json_filter_expr(field: &str, value: &JsonValue) -> Expr {
609 let operator = dynamic_json_operator(value);
610 field_operator_expr(field, operator, dynamic_json_values(value))
611}
612
613pub fn dynamic_json_u64_field(
614 object: &serde_json::Map<String, JsonValue>,
615 field: &str,
616) -> Option<u64> {
617 object.get(field).and_then(|value| {
618 value
619 .as_u64()
620 .or_else(|| value.as_i64().and_then(|value| u64::try_from(value).ok()))
621 })
622}