1use super::{Aggregation, EncodeColumn, Entity, IntoSqlValue, ModelColumn, Schema, Window};
100use regex::{Captures, Regex};
101use std::{borrow::Cow, fmt::Display, marker::PhantomData};
102use zino_core::{
103 JsonValue, LazyLock, Map, SharedString,
104 extension::{JsonObjectExt, JsonValueExt},
105 model::{Query, QueryOrder},
106};
107
108#[derive(Debug, Clone)]
129pub struct QueryBuilder<E: Entity> {
130 fields: Vec<String>,
132 group_by_fields: Vec<String>,
134 having_conditions: Vec<Map>,
136 filters: Map,
138 logical_and: Vec<Map>,
140 logical_or: Vec<Map>,
142 sort_order: Vec<QueryOrder>,
144 offset: usize,
146 limit: usize,
148 extra: Map,
150 phantom: PhantomData<E>,
152}
153
154impl<E: Entity> QueryBuilder<E> {
155 #[inline]
157 pub fn new() -> Self {
158 Self {
159 fields: Vec::new(),
160 group_by_fields: Vec::new(),
161 having_conditions: Vec::new(),
162 filters: Map::new(),
163 logical_and: Vec::new(),
164 logical_or: Vec::new(),
165 sort_order: Vec::new(),
166 offset: 0,
167 limit: 0,
168 extra: Map::new(),
169 phantom: PhantomData,
170 }
171 }
172
173 #[inline]
175 pub fn table_name(mut self, table_name: String) -> Self {
176 self.extra.upsert("table_name", table_name);
177 self
178 }
179
180 #[inline]
182 pub fn field<C: ModelColumn<E>>(mut self, col: C) -> Self {
183 self.fields.push(col.into_column_expr());
184 self
185 }
186
187 #[inline]
189 pub fn fields<C, V>(mut self, cols: V) -> Self
190 where
191 C: ModelColumn<E>,
192 V: Into<Vec<C>>,
193 {
194 let mut fields = cols
195 .into()
196 .into_iter()
197 .map(|col| col.into_column_expr())
198 .collect();
199 self.fields.append(&mut fields);
200 self
201 }
202
203 pub fn alias<C, F>(mut self, col: C, alias: F) -> Self
205 where
206 C: ModelColumn<E>,
207 F: AsRef<str>,
208 {
209 let col_name = col.into_column_expr();
210 let field = Query::format_field(&col_name);
211 let field_alias = [alias.as_ref(), ":", &field].concat();
212 self.fields.push(field_alias);
213 self
214 }
215
216 pub fn aggregate<F: AsRef<str>>(
218 mut self,
219 aggregation: Aggregation<E>,
220 alias: Option<F>,
221 ) -> Self {
222 let expr = aggregation.expr();
223 let field_alias = if let Some(alias) = alias {
224 [alias.as_ref(), ":", &expr].concat()
225 } else {
226 let mut field_alias = aggregation.default_alias();
227 field_alias.push(':');
228 field_alias.push_str(&expr);
229 field_alias
230 };
231 self.fields.push(field_alias);
232 self
233 }
234
235 pub fn window<F: AsRef<str>>(mut self, window: Window<E>, alias: Option<F>) -> Self {
237 let expr = window.expr();
238 let field_alias = if let Some(alias) = alias {
239 [alias.as_ref(), ":", &expr].concat()
240 } else {
241 let mut field_alias = window.default_alias();
242 field_alias.push(':');
243 field_alias.push_str(&expr);
244 field_alias
245 };
246 self.fields.push(field_alias);
247 self
248 }
249
250 pub fn group_by<C, F>(mut self, col: C, alias: Option<F>) -> Self
252 where
253 C: ModelColumn<E>,
254 F: AsRef<str>,
255 {
256 let expr = col.into_column_expr();
257 let field = if let Some(alias) = alias {
258 [alias.as_ref(), ":", &expr].concat()
259 } else {
260 expr.clone()
261 };
262 if !self.fields.contains(&field) {
263 self.fields.push(field);
264 }
265 self.group_by_fields.push(expr);
266 self
267 }
268
269 #[inline]
271 pub fn having_filter<V: IntoSqlValue>(mut self, aggregation: Aggregation<E>, value: V) -> Self {
272 let condition = Map::from_entry(aggregation.expr(), value.into_sql_value());
273 self.having_conditions.push(condition);
274 self
275 }
276
277 #[inline]
279 pub fn having_eq<V: IntoSqlValue>(self, aggregation: Aggregation<E>, value: V) -> Self {
280 self.push_having_condition(aggregation, "$eq", value.into_sql_value())
281 }
282
283 #[inline]
285 pub fn having_ne<V: IntoSqlValue>(self, aggregation: Aggregation<E>, value: V) -> Self {
286 self.push_having_condition(aggregation, "$ne", value.into_sql_value())
287 }
288
289 #[inline]
291 pub fn having_lt<V: IntoSqlValue>(self, aggregation: Aggregation<E>, value: V) -> Self {
292 self.push_having_condition(aggregation, "$lt", value.into_sql_value())
293 }
294
295 #[inline]
297 pub fn having_le<V: IntoSqlValue>(self, aggregation: Aggregation<E>, value: V) -> Self {
298 self.push_having_condition(aggregation, "$le", value.into_sql_value())
299 }
300
301 #[inline]
303 pub fn having_gt<V: IntoSqlValue>(self, aggregation: Aggregation<E>, value: V) -> Self {
304 self.push_having_condition(aggregation, "$gt", value.into_sql_value())
305 }
306
307 #[inline]
309 pub fn having_ge<V: IntoSqlValue>(self, aggregation: Aggregation<E>, value: V) -> Self {
310 self.push_having_condition(aggregation, "$ge", value.into_sql_value())
311 }
312
313 #[inline]
315 pub fn primary_key<V: IntoSqlValue>(mut self, value: V) -> Self {
316 let field = E::format_column(&E::PRIMARY_KEY);
317 self.filters.upsert(field, value.into_sql_value());
318 self
319 }
320
321 #[inline]
323 pub fn rand<V: IntoSqlValue>(mut self, value: V) -> Self {
324 self.filters.upsert("$rand", value.into_sql_value());
325 self
326 }
327
328 pub fn and<M: Entity>(mut self, mut other: QueryBuilder<M>) -> Self {
330 let mut logical_and = other.logical_and;
331 let logical_or = other.logical_or;
332 if !logical_or.is_empty() {
333 logical_and.push(Map::from_entry("$or", logical_or));
334 }
335 if !logical_and.is_empty() {
336 self.logical_and.push(Map::from_entry("$and", logical_and));
337 }
338 self.fields.append(&mut other.fields);
339 self.group_by_fields.append(&mut other.group_by_fields);
340 self
341 }
342
343 pub fn and_not<M: Entity>(mut self, mut other: QueryBuilder<M>) -> Self {
345 let mut logical_and = other.logical_and;
346 let logical_or = other.logical_or;
347 if !logical_or.is_empty() {
348 logical_and.push(Map::from_entry("$or", logical_or));
349 }
350 if !logical_and.is_empty() {
351 self.logical_and.push(Map::from_entry("$not", logical_and));
352 }
353 self.fields.append(&mut other.fields);
354 self.group_by_fields.append(&mut other.group_by_fields);
355 self
356 }
357
358 #[inline]
360 pub fn and_filter<C, V>(mut self, col: C, value: V) -> Self
361 where
362 C: ModelColumn<E>,
363 V: IntoSqlValue,
364 {
365 let condition = Map::from_entry(col.into_column_expr(), value.into_sql_value());
366 self.logical_and.push(condition);
367 self
368 }
369
370 #[inline]
372 pub fn and_eq<C, V>(self, col: C, value: V) -> Self
373 where
374 C: ModelColumn<E>,
375 V: IntoSqlValue,
376 {
377 self.push_logical_and(col, "$eq", value.into_sql_value())
378 }
379
380 #[inline]
382 pub fn and_eq_if_not_null<C, V>(self, col: C, value: V) -> Self
383 where
384 C: ModelColumn<E>,
385 V: IntoSqlValue,
386 {
387 let value = value.into_sql_value();
388 if !value.is_null() {
389 self.push_logical_and(col, "$eq", value)
390 } else {
391 self
392 }
393 }
394
395 #[inline]
397 pub fn and_eq_if_some<C, V>(self, col: C, value: Option<V>) -> Self
398 where
399 C: ModelColumn<E>,
400 V: IntoSqlValue,
401 {
402 if let Some(value) = value {
403 self.push_logical_and(col, "$eq", value.into_sql_value())
404 } else {
405 self
406 }
407 }
408
409 #[inline]
411 pub fn and_ne<C, V>(self, col: C, value: V) -> Self
412 where
413 C: ModelColumn<E>,
414 V: IntoSqlValue,
415 {
416 self.push_logical_and(col, "$ne", value.into_sql_value())
417 }
418
419 #[inline]
421 pub fn and_ne_if_not_null<C, V>(self, col: C, value: V) -> Self
422 where
423 C: ModelColumn<E>,
424 V: IntoSqlValue,
425 {
426 let value = value.into_sql_value();
427 if !value.is_null() {
428 self.push_logical_and(col, "$ne", value)
429 } else {
430 self
431 }
432 }
433
434 #[inline]
436 pub fn and_ne_if_some<C, V>(self, col: C, value: Option<V>) -> Self
437 where
438 C: ModelColumn<E>,
439 V: IntoSqlValue,
440 {
441 if let Some(value) = value {
442 self.push_logical_and(col, "$ne", value.into_sql_value())
443 } else {
444 self
445 }
446 }
447
448 #[inline]
450 pub fn and_lt<C, V>(self, col: C, value: V) -> Self
451 where
452 C: ModelColumn<E>,
453 V: IntoSqlValue,
454 {
455 self.push_logical_and(col, "$lt", value.into_sql_value())
456 }
457
458 #[inline]
460 pub fn and_le<C, V>(self, col: C, value: V) -> Self
461 where
462 C: ModelColumn<E>,
463 V: IntoSqlValue,
464 {
465 self.push_logical_and(col, "$le", value.into_sql_value())
466 }
467
468 #[inline]
470 pub fn and_gt<C, V>(self, col: C, value: V) -> Self
471 where
472 C: ModelColumn<E>,
473 V: IntoSqlValue,
474 {
475 self.push_logical_and(col, "$gt", value.into_sql_value())
476 }
477
478 #[inline]
480 pub fn and_ge<C, V>(self, col: C, value: V) -> Self
481 where
482 C: ModelColumn<E>,
483 V: IntoSqlValue,
484 {
485 self.push_logical_and(col, "$ge", value.into_sql_value())
486 }
487
488 #[inline]
490 pub fn and_in<C, T, V>(self, col: C, values: V) -> Self
491 where
492 C: ModelColumn<E>,
493 T: IntoSqlValue,
494 V: Into<Vec<T>>,
495 {
496 self.push_logical_and(col, "$in", values.into().into_sql_value())
497 }
498
499 #[inline]
501 pub fn and_not_in<C, T, V>(self, col: C, values: V) -> Self
502 where
503 C: ModelColumn<E>,
504 T: IntoSqlValue,
505 V: Into<Vec<T>>,
506 {
507 self.push_logical_and(col, "$nin", values.into().into_sql_value())
508 }
509
510 #[inline]
513 pub fn and_in_if_nonempty<C, T, V>(self, col: C, values: V) -> Self
514 where
515 C: ModelColumn<E>,
516 T: IntoSqlValue,
517 V: Into<Vec<T>>,
518 {
519 let values = values.into();
520 if values.is_empty() {
521 self
522 } else {
523 self.push_logical_and(col, "$in", values.into_sql_value())
524 }
525 }
526
527 #[inline]
530 pub fn and_not_in_if_nonempty<C, T, V>(self, col: C, values: V) -> Self
531 where
532 C: ModelColumn<E>,
533 T: IntoSqlValue,
534 V: Into<Vec<T>>,
535 {
536 let values = values.into();
537 if values.is_empty() {
538 self
539 } else {
540 self.push_logical_and(col, "$nin", values.into_sql_value())
541 }
542 }
543
544 pub fn and_in_subquery<C, V, M>(mut self, cols: V, subquery: QueryBuilder<M>) -> Self
546 where
547 C: ModelColumn<E>,
548 V: Into<Vec<C>>,
549 M: Entity + Schema,
550 {
551 let cols = cols
552 .into()
553 .into_iter()
554 .map(|col| {
555 let col_name = col.into_column_expr();
556 Query::format_field(&col_name).into_owned()
557 })
558 .collect::<Vec<_>>()
559 .join(", ");
560 let field = format!("({cols})");
561 let condition = Map::from_entry("$in", subquery.into_sql_value());
562 self.logical_and.push(Map::from_entry(field, condition));
563 self
564 }
565
566 pub fn and_not_in_subquery<C, V, M>(mut self, cols: V, subquery: QueryBuilder<M>) -> Self
568 where
569 C: ModelColumn<E>,
570 V: Into<Vec<C>>,
571 M: Entity + Schema,
572 {
573 let cols = cols
574 .into()
575 .into_iter()
576 .map(|col| {
577 let col_name = col.into_column_expr();
578 Query::format_field(&col_name).into_owned()
579 })
580 .collect::<Vec<_>>()
581 .join(", ");
582 let field = format!("({cols})");
583 let condition = Map::from_entry("$nin", subquery.into_sql_value());
584 self.logical_and.push(Map::from_entry(field, condition));
585 self
586 }
587
588 pub fn and_in_range<C, V>(mut self, col: C, min: V, max: V) -> Self
590 where
591 C: ModelColumn<E>,
592 V: IntoSqlValue,
593 {
594 let field = col.into_column_expr();
595 let mut condition = Map::new();
596 condition.upsert("$ge", min.into_sql_value());
597 condition.upsert("$lt", max.into_sql_value());
598 self.logical_and.push(Map::from_entry(field, condition));
599 self
600 }
601
602 #[inline]
604 pub fn and_between<C, V>(self, col: C, min: V, max: V) -> Self
605 where
606 C: ModelColumn<E>,
607 V: IntoSqlValue,
608 {
609 self.push_logical_and(col, "$betw", [min, max].into_sql_value())
610 }
611
612 #[inline]
614 pub fn and_like<C: ModelColumn<E>>(self, col: C, value: String) -> Self {
615 self.push_logical_and(col, "$like", value.into_sql_value())
616 }
617
618 #[inline]
620 pub fn and_ilike<C: ModelColumn<E>>(self, col: C, value: String) -> Self {
621 self.push_logical_and(col, "$ilike", value.into_sql_value())
622 }
623
624 #[inline]
626 pub fn and_rlike<C: ModelColumn<E>>(self, col: C, value: String) -> Self {
627 self.push_logical_and(col, "$rlike", value.into_sql_value())
628 }
629
630 #[inline]
632 pub fn and_contains<C: ModelColumn<E>>(self, col: C, value: &str) -> Self {
633 let value = ["%", value, "%"].concat();
634 self.push_logical_and(col, "$like", value.into_sql_value())
635 }
636
637 #[inline]
639 pub fn and_starts_with<C: ModelColumn<E>>(self, col: C, value: &str) -> Self {
640 let value = [value, "%"].concat();
641 self.push_logical_and(col, "$like", value.into_sql_value())
642 }
643
644 #[inline]
646 pub fn and_ends_with<C: ModelColumn<E>>(self, col: C, value: &str) -> Self {
647 let value = ["%", value].concat();
648 self.push_logical_and(col, "$like", value.into_sql_value())
649 }
650
651 #[inline]
653 pub fn and_null<C: ModelColumn<E>>(self, col: C) -> Self {
654 self.and_filter(col, JsonValue::Null)
655 }
656
657 #[inline]
659 pub fn and_not_null<C: ModelColumn<E>>(self, col: C) -> Self {
660 self.and_filter(col, "not_null")
661 }
662
663 #[inline]
665 pub fn and_empty<C: ModelColumn<E>>(self, col: C) -> Self {
666 self.and_filter(col, "empty")
667 }
668
669 #[inline]
671 pub fn and_nonempty<C: ModelColumn<E>>(self, col: C) -> Self {
672 self.and_filter(col, "nonempty")
673 }
674
675 pub fn and_overlaps<L, R, V>(mut self, cols: (L, R), values: (V, V)) -> Self
677 where
678 L: ModelColumn<E>,
679 R: ModelColumn<E>,
680 V: IntoSqlValue,
681 {
682 let mut condition = Map::new();
683 condition.upsert(
684 cols.0.into_column_expr(),
685 Map::from_entry("$le", values.1.into_sql_value()),
686 );
687 condition.upsert(
688 cols.1.into_column_expr(),
689 Map::from_entry("$ge", values.0.into_sql_value()),
690 );
691 self.logical_and.push(condition);
692 self
693 }
694
695 pub fn or<M: Entity>(mut self, mut other: QueryBuilder<M>) -> Self {
697 let mut logical_and = other.logical_and;
698 let logical_or = other.logical_or;
699 if !logical_or.is_empty() {
700 logical_and.push(Map::from_entry("$or", logical_or));
701 }
702 if !logical_and.is_empty() {
703 self.logical_or.push(Map::from_entry("$and", logical_and));
704 }
705 self.fields.append(&mut other.fields);
706 self.group_by_fields.append(&mut other.group_by_fields);
707 self
708 }
709
710 pub fn or_not<M: Entity>(mut self, mut other: QueryBuilder<M>) -> Self {
712 let mut logical_and = other.logical_and;
713 let logical_or = other.logical_or;
714 if !logical_or.is_empty() {
715 logical_and.push(Map::from_entry("$or", logical_or));
716 }
717 if !logical_and.is_empty() {
718 self.logical_or.push(Map::from_entry("$not", logical_and));
719 }
720 self.fields.append(&mut other.fields);
721 self.group_by_fields.append(&mut other.group_by_fields);
722 self
723 }
724
725 #[inline]
727 pub fn or_filter<C, V>(mut self, col: C, value: V) -> Self
728 where
729 C: ModelColumn<E>,
730 V: IntoSqlValue,
731 {
732 let condition = Map::from_entry(col.into_column_expr(), value.into_sql_value());
733 self.logical_or.push(condition);
734 self
735 }
736
737 #[inline]
739 pub fn or_eq<C, V>(self, col: C, value: V) -> Self
740 where
741 C: ModelColumn<E>,
742 V: IntoSqlValue,
743 {
744 self.push_logical_or(col, "$eq", value.into_sql_value())
745 }
746
747 #[inline]
749 pub fn or_eq_if_not_null<C, V>(self, col: C, value: V) -> Self
750 where
751 C: ModelColumn<E>,
752 V: IntoSqlValue,
753 {
754 let value = value.into_sql_value();
755 if !value.is_null() {
756 self.push_logical_or(col, "$eq", value)
757 } else {
758 self
759 }
760 }
761
762 #[inline]
764 pub fn or_eq_if_some<C, V>(self, col: C, value: Option<V>) -> Self
765 where
766 C: ModelColumn<E>,
767 V: IntoSqlValue,
768 {
769 if let Some(value) = value {
770 self.push_logical_or(col, "$eq", value.into_sql_value())
771 } else {
772 self
773 }
774 }
775
776 #[inline]
778 pub fn or_ne<C, V>(self, col: C, value: V) -> Self
779 where
780 C: ModelColumn<E>,
781 V: IntoSqlValue,
782 {
783 self.push_logical_or(col, "$ne", value.into_sql_value())
784 }
785
786 #[inline]
788 pub fn or_ne_if_not_null<C, V>(self, col: C, value: V) -> Self
789 where
790 C: ModelColumn<E>,
791 V: IntoSqlValue,
792 {
793 let value = value.into_sql_value();
794 if !value.is_null() {
795 self.push_logical_or(col, "$ne", value)
796 } else {
797 self
798 }
799 }
800
801 #[inline]
803 pub fn or_ne_if_some<C, V>(self, col: C, value: Option<V>) -> Self
804 where
805 C: ModelColumn<E>,
806 V: IntoSqlValue,
807 {
808 if let Some(value) = value {
809 self.push_logical_or(col, "$ne", value.into_sql_value())
810 } else {
811 self
812 }
813 }
814
815 #[inline]
817 pub fn or_lt<C, V>(self, col: C, value: V) -> Self
818 where
819 C: ModelColumn<E>,
820 V: IntoSqlValue,
821 {
822 self.push_logical_or(col, "$lt", value.into_sql_value())
823 }
824
825 #[inline]
827 pub fn or_le<C, V>(self, col: C, value: V) -> Self
828 where
829 C: ModelColumn<E>,
830 V: IntoSqlValue,
831 {
832 self.push_logical_or(col, "$le", value.into_sql_value())
833 }
834
835 #[inline]
837 pub fn or_gt<C, V>(self, col: C, value: V) -> Self
838 where
839 C: ModelColumn<E>,
840 V: IntoSqlValue,
841 {
842 self.push_logical_or(col, "$gt", value.into_sql_value())
843 }
844
845 #[inline]
847 pub fn or_ge<C, V>(self, col: C, value: V) -> Self
848 where
849 C: ModelColumn<E>,
850 V: IntoSqlValue,
851 {
852 self.push_logical_or(col, "$ge", value.into_sql_value())
853 }
854
855 #[inline]
857 pub fn or_in<C, T, V>(self, col: C, values: V) -> Self
858 where
859 C: ModelColumn<E>,
860 T: IntoSqlValue,
861 V: Into<Vec<T>>,
862 {
863 self.push_logical_or(col, "$in", values.into().into_sql_value())
864 }
865
866 #[inline]
868 pub fn or_not_in<C, T, V>(self, col: C, values: V) -> Self
869 where
870 C: ModelColumn<E>,
871 T: IntoSqlValue,
872 V: Into<Vec<T>>,
873 {
874 self.push_logical_or(col, "$nin", values.into().into_sql_value())
875 }
876
877 #[inline]
880 pub fn or_in_if_nonempty<C, T, V>(self, col: C, values: V) -> Self
881 where
882 C: ModelColumn<E>,
883 T: IntoSqlValue,
884 V: Into<Vec<T>>,
885 {
886 let values = values.into();
887 if values.is_empty() {
888 self
889 } else {
890 self.push_logical_or(col, "$in", values.into_sql_value())
891 }
892 }
893
894 #[inline]
897 pub fn or_not_in_if_nonempty<C, T, V>(self, col: C, values: V) -> Self
898 where
899 C: ModelColumn<E>,
900 T: IntoSqlValue,
901 V: Into<Vec<T>>,
902 {
903 let values = values.into();
904 if values.is_empty() {
905 self
906 } else {
907 self.push_logical_or(col, "$nin", values.into_sql_value())
908 }
909 }
910
911 pub fn or_in_subquery<C, V, M>(mut self, cols: V, subquery: QueryBuilder<M>) -> Self
913 where
914 C: ModelColumn<E>,
915 V: Into<Vec<C>>,
916 M: Entity + Schema,
917 {
918 let cols = cols
919 .into()
920 .into_iter()
921 .map(|col| {
922 let col_name = col.into_column_expr();
923 Query::format_field(&col_name).into_owned()
924 })
925 .collect::<Vec<_>>()
926 .join(", ");
927 let field = format!("({cols})");
928 let condition = Map::from_entry("$in", subquery.into_sql_value());
929 self.logical_or.push(Map::from_entry(field, condition));
930 self
931 }
932
933 pub fn or_not_in_subquery<C, V, M>(mut self, cols: V, subquery: QueryBuilder<M>) -> Self
935 where
936 C: ModelColumn<E>,
937 V: Into<Vec<C>>,
938 M: Entity + Schema,
939 {
940 let cols = cols
941 .into()
942 .into_iter()
943 .map(|col| {
944 let col_name = col.into_column_expr();
945 Query::format_field(&col_name).into_owned()
946 })
947 .collect::<Vec<_>>()
948 .join(", ");
949 let field = format!("({cols})");
950 let condition = Map::from_entry("$nin", subquery.into_sql_value());
951 self.logical_or.push(Map::from_entry(field, condition));
952 self
953 }
954
955 pub fn or_in_range<C, V>(mut self, col: C, min: V, max: V) -> Self
957 where
958 C: ModelColumn<E>,
959 V: IntoSqlValue,
960 {
961 let field = col.into_column_expr();
962 let mut condition = Map::new();
963 condition.upsert("$ge", min.into_sql_value());
964 condition.upsert("$lt", max.into_sql_value());
965 self.logical_or.push(Map::from_entry(field, condition));
966 self
967 }
968
969 #[inline]
971 pub fn or_between<C, V>(self, col: C, min: V, max: V) -> Self
972 where
973 C: ModelColumn<E>,
974 V: IntoSqlValue,
975 {
976 self.push_logical_or(col, "$betw", [min, max].into_sql_value())
977 }
978
979 #[inline]
981 pub fn or_like<C: ModelColumn<E>>(self, col: C, value: String) -> Self {
982 self.push_logical_or(col, "$like", value.into_sql_value())
983 }
984
985 #[inline]
987 pub fn or_ilike<C: ModelColumn<E>>(self, col: C, value: String) -> Self {
988 self.push_logical_or(col, "$ilike", value.into_sql_value())
989 }
990
991 #[inline]
993 pub fn or_rlike<C: ModelColumn<E>>(self, col: C, value: String) -> Self {
994 self.push_logical_or(col, "$rlike", value.into_sql_value())
995 }
996
997 #[inline]
999 pub fn or_contains<C: ModelColumn<E>>(self, col: C, value: &str) -> Self {
1000 let value = ["%", value, "%"].concat();
1001 self.push_logical_or(col, "$like", value.into_sql_value())
1002 }
1003
1004 #[inline]
1006 pub fn or_starts_with<C: ModelColumn<E>>(self, col: C, value: &str) -> Self {
1007 let value = [value, "%"].concat();
1008 self.push_logical_or(col, "$like", value.into_sql_value())
1009 }
1010
1011 #[inline]
1013 pub fn or_ends_with<C: ModelColumn<E>>(self, col: C, value: &str) -> Self {
1014 let value = ["%", value].concat();
1015 self.push_logical_or(col, "$like", value.into_sql_value())
1016 }
1017
1018 #[inline]
1020 pub fn or_null<C: ModelColumn<E>>(self, col: C) -> Self {
1021 self.or_filter(col, JsonValue::Null)
1022 }
1023
1024 #[inline]
1026 pub fn or_not_null<C: ModelColumn<E>>(self, col: C) -> Self {
1027 self.or_filter(col, "not_null")
1028 }
1029
1030 #[inline]
1032 pub fn or_empty<C: ModelColumn<E>>(self, col: C) -> Self {
1033 self.or_filter(col, "empty")
1034 }
1035
1036 #[inline]
1038 pub fn or_nonempty<C: ModelColumn<E>>(self, col: C) -> Self {
1039 self.or_filter(col, "nonempty")
1040 }
1041
1042 pub fn or_overlaps<L, R, V>(mut self, cols: (L, R), values: (V, V)) -> Self
1044 where
1045 L: ModelColumn<E>,
1046 R: ModelColumn<E>,
1047 V: IntoSqlValue,
1048 {
1049 let mut condition = Map::new();
1050 condition.upsert(
1051 cols.0.into_column_expr(),
1052 Map::from_entry("$le", values.1.into_sql_value()),
1053 );
1054 condition.upsert(
1055 cols.1.into_column_expr(),
1056 Map::from_entry("$ge", values.0.into_sql_value()),
1057 );
1058 self.logical_or.push(condition);
1059 self
1060 }
1061
1062 #[inline]
1064 pub fn order_by<C: ModelColumn<E>>(mut self, col: C, descending: bool) -> Self {
1065 self.sort_order
1066 .push(QueryOrder::new(col.into_column_expr(), descending));
1067 self
1068 }
1069
1070 #[inline]
1072 pub fn order_by_with_nulls<C: ModelColumn<E>>(
1073 mut self,
1074 col: C,
1075 descending: bool,
1076 nulls_first: bool,
1077 ) -> Self {
1078 let mut order = QueryOrder::new(col.into_column_expr(), descending);
1079 if nulls_first {
1080 order.set_nulls_first();
1081 } else {
1082 order.set_nulls_last();
1083 }
1084 self.sort_order.push(order);
1085 self
1086 }
1087
1088 #[inline]
1090 pub fn order_asc<C: ModelColumn<E>>(mut self, col: C) -> Self {
1091 self.sort_order
1092 .push(QueryOrder::new(col.into_column_expr(), false));
1093 self
1094 }
1095
1096 #[inline]
1098 pub fn order_desc<C: ModelColumn<E>>(mut self, col: C) -> Self {
1099 self.sort_order
1100 .push(QueryOrder::new(col.into_column_expr(), true));
1101 self
1102 }
1103
1104 #[inline]
1106 pub fn offset(mut self, offset: usize) -> Self {
1107 self.offset = offset;
1108 self
1109 }
1110
1111 #[inline]
1113 pub fn limit(mut self, limit: usize) -> Self {
1114 self.limit = limit;
1115 self
1116 }
1117
1118 pub fn build(mut self) -> Query {
1120 let mut filters = self.filters;
1121 let group_by_fields = self.group_by_fields;
1122 let having_conditions = self.having_conditions;
1123 let logical_and = self.logical_and;
1124 let logical_or = self.logical_or;
1125 if !group_by_fields.is_empty() {
1126 filters.upsert("$group", group_by_fields);
1127 }
1128 if !having_conditions.is_empty() {
1129 filters.upsert("$having", having_conditions);
1130 }
1131 if !logical_and.is_empty() {
1132 filters.upsert("$and", logical_and);
1133 }
1134 if !logical_or.is_empty() {
1135 filters.upsert("$or", logical_or);
1136 }
1137
1138 let mut query = Query::new(filters);
1139 query.set_fields(self.fields);
1140 query.set_order(self.sort_order);
1141 query.set_offset(self.offset);
1142 query.set_limit(self.limit);
1143 query.append_extra_flags(&mut self.extra);
1144 query
1145 }
1146
1147 fn push_having_condition(
1149 mut self,
1150 aggregation: Aggregation<E>,
1151 operator: &str,
1152 value: JsonValue,
1153 ) -> Self {
1154 let condition = Map::from_entry(operator, value);
1155 self.having_conditions
1156 .push(Map::from_entry(aggregation.expr(), condition));
1157 self
1158 }
1159
1160 fn push_logical_and<C: ModelColumn<E>>(
1162 mut self,
1163 col: C,
1164 operator: &str,
1165 value: JsonValue,
1166 ) -> Self {
1167 let condition = Map::from_entry(operator, value);
1168 self.logical_and
1169 .push(Map::from_entry(col.into_column_expr(), condition));
1170 self
1171 }
1172
1173 fn push_logical_or<C: ModelColumn<E>>(
1175 mut self,
1176 col: C,
1177 operator: &str,
1178 value: JsonValue,
1179 ) -> Self {
1180 let condition = Map::from_entry(operator, value);
1181 self.logical_or
1182 .push(Map::from_entry(col.into_column_expr(), condition));
1183 self
1184 }
1185}
1186
1187impl<E: Entity + Schema> QueryBuilder<E> {
1188 #[inline]
1190 pub fn build_subquery(self) -> String {
1191 let query = self.build();
1192 let table_name = query.format_table_name::<E>();
1193 let projection = query.format_table_fields::<E>();
1194 let filters = query.format_filters::<E>();
1195 let sort = query.format_sort();
1196 let pagination = query.format_pagination();
1197 format!("(SELECT {projection} FROM {table_name} {filters} {sort} {pagination})")
1198 }
1199}
1200
1201impl<E: Entity> Default for QueryBuilder<E> {
1202 #[inline]
1203 fn default() -> Self {
1204 Self::new()
1205 }
1206}
1207
1208pub(super) trait QueryExt<DB> {
1210 type QueryResult;
1212
1213 fn parse_query_result(query_result: Self::QueryResult) -> (Option<i64>, u64);
1215
1216 fn query_fields(&self) -> &[String];
1218
1219 fn query_filters(&self) -> ⤅
1221
1222 fn query_order(&self) -> &[QueryOrder];
1224
1225 fn query_offset(&self) -> usize;
1227
1228 fn query_limit(&self) -> usize;
1230
1231 fn placeholder(n: usize) -> SharedString;
1233
1234 fn prepare_query<'a>(
1236 query: &'a str,
1237 params: Option<&'a Map>,
1238 ) -> (Cow<'a, str>, Vec<&'a JsonValue>);
1239
1240 fn format_field(field: &str) -> Cow<'_, str>;
1242
1243 fn format_table_fields<M: Schema>(&self) -> Cow<'_, str>;
1245
1246 fn format_table_name<M: Schema>(&self) -> String;
1248
1249 fn escape_table_name(table_name: &str) -> String;
1251
1252 fn parse_text_search(filter: &Map) -> Option<String>;
1254
1255 #[inline]
1257 fn escape_string(value: impl Display) -> String {
1258 format!("'{}'", value.to_string().replace('\'', "''"))
1259 }
1260
1261 fn format_projection(&self) -> Cow<'_, str> {
1263 let fields = self.query_fields();
1264 if fields.is_empty() {
1265 "*".into()
1266 } else {
1267 fields
1268 .iter()
1269 .map(|field| {
1270 if let Some((alias, expr)) = field.split_once(':') {
1271 let alias = Self::format_field(alias.trim());
1272 format!(r#"{expr} AS {alias}"#).into()
1273 } else {
1274 Self::format_field(field)
1275 }
1276 })
1277 .collect::<Vec<_>>()
1278 .join(", ")
1279 .into()
1280 }
1281 }
1282
1283 fn format_filters<M: Schema>(&self) -> String {
1285 let filters = self.query_filters();
1286 if filters.is_empty() {
1287 return String::new();
1288 }
1289
1290 let mut expression = String::new();
1291 let mut logical_and_conditions = Vec::with_capacity(filters.len());
1292 for (key, value) in filters {
1293 match key.as_str() {
1294 "$and" => {
1295 if let Some(filters) = value.as_array() {
1296 let condition = Self::format_logical_filters::<M>(filters, " AND ");
1297 logical_and_conditions.push(condition);
1298 }
1299 }
1300 "$not" => {
1301 if let Some(filters) = value.as_array() {
1302 let condition = Self::format_logical_filters::<M>(filters, " AND ");
1303 logical_and_conditions.push(format!("(NOT {condition})"));
1304 }
1305 }
1306 "$or" => {
1307 if let Some(filters) = value.as_array() {
1308 let condition = Self::format_logical_filters::<M>(filters, " OR ");
1309 logical_and_conditions.push(condition);
1310 }
1311 }
1312 "$rand" => {
1313 if let Some(Ok(value)) = value.parse_f64() {
1314 let condition = if cfg!(any(
1315 feature = "orm-mariadb",
1316 feature = "orm-mysql",
1317 feature = "orm-tidb"
1318 )) {
1319 format!("rand() < {value}")
1320 } else if cfg!(feature = "orm-postgres") {
1321 format!("random() < {value}")
1322 } else {
1323 let value = (value * i64::MAX as f64) as i64;
1324 format!("abs(random()) < {value}")
1325 };
1326 logical_and_conditions.push(condition);
1327 }
1328 }
1329 "$text" => {
1330 if let Some(condition) = value.as_object().and_then(Self::parse_text_search) {
1331 logical_and_conditions.push(condition);
1332 }
1333 }
1334 _ => {
1335 if let Some(col) = M::get_column(key) {
1336 let condition = if let Some(subquery) =
1337 value.as_object().and_then(|m| m.get_str("$subquery"))
1338 {
1339 let key = Self::format_field(key);
1340 format!(r#"{key} = {subquery}"#)
1341 } else {
1342 col.format_filter(key, value)
1343 };
1344 if !condition.is_empty() {
1345 logical_and_conditions.push(condition);
1346 }
1347 } else if key.contains('.') {
1348 let condition = Self::format_query_filter::<M>(key, value);
1349 if !condition.is_empty() {
1350 logical_and_conditions.push(condition);
1351 }
1352 }
1353 }
1354 }
1355 }
1356 if !logical_and_conditions.is_empty() {
1357 expression += &format!("WHERE {}", logical_and_conditions.join(" AND "));
1358 };
1359 if let Some(groups) = filters.parse_str_array("$group") {
1360 let groups = groups
1361 .into_iter()
1362 .map(Self::format_field)
1363 .collect::<Vec<_>>()
1364 .join(", ");
1365 expression += &format!(" GROUP BY {groups}");
1366 if let Some(filters) = filters.get_array("$having") {
1367 let condition = Self::format_logical_filters::<M>(filters, " AND ");
1368 expression += &format!(" HAVING {condition}");
1369 }
1370 }
1371 expression
1372 }
1373
1374 fn format_logical_filters<M: Schema>(filters: &[JsonValue], operator: &str) -> String {
1376 let mut conditions = Vec::with_capacity(filters.len());
1377 for filter in filters {
1378 if let JsonValue::Object(filter) = filter {
1379 let mut logical_and_conditions = Vec::with_capacity(filter.len());
1380 for (key, value) in filter {
1381 match key.as_str() {
1382 "$and" => {
1383 if let Some(filters) = value.as_array() {
1384 let condition = Self::format_logical_filters::<M>(filters, " AND ");
1385 logical_and_conditions.push(condition);
1386 }
1387 }
1388 "$not" => {
1389 if let Some(filters) = value.as_array() {
1390 let condition = Self::format_logical_filters::<M>(filters, " AND ");
1391 logical_and_conditions.push(format!("(NOT {condition})"));
1392 }
1393 }
1394 "$nor" => {
1395 if let Some(filters) = value.as_array() {
1396 let condition = Self::format_logical_filters::<M>(filters, " OR ");
1397 logical_and_conditions.push(format!("(NOT {condition})"));
1398 }
1399 }
1400 "$or" => {
1401 if let Some(filters) = value.as_array() {
1402 let condition = Self::format_logical_filters::<M>(filters, " OR ");
1403 logical_and_conditions.push(condition);
1404 }
1405 }
1406 _ => {
1407 if let Some(col) = M::get_column(key) {
1408 let condition = if let Some(subquery) =
1409 value.as_object().and_then(|m| m.get_str("$subquery"))
1410 {
1411 let key = Self::format_field(key);
1412 format!(r#"{key} = {subquery}"#)
1413 } else {
1414 col.format_filter(key, value)
1415 };
1416 if !condition.is_empty() {
1417 logical_and_conditions.push(condition);
1418 }
1419 } else if key.contains('.') {
1420 let condition = Self::format_query_filter::<M>(key, value);
1421 if !condition.is_empty() {
1422 logical_and_conditions.push(condition);
1423 }
1424 }
1425 }
1426 }
1427 }
1428 if !logical_and_conditions.is_empty() {
1429 let condition = Self::join_conditions(logical_and_conditions, " AND ");
1430 conditions.push(condition);
1431 }
1432 }
1433 }
1434 Self::join_conditions(conditions, operator)
1435 }
1436
1437 fn format_query_filter<M: Schema>(key: &str, value: &JsonValue) -> String {
1439 let json_field = key.split_once('.').and_then(|(key, path)| {
1440 M::get_column(key)
1441 .filter(|col| col.type_name() == "Map")
1442 .map(|col| {
1443 let key = [M::model_name(), ".", col.name()].concat();
1444 let field = Self::format_field(&key);
1445 if cfg!(feature = "orm-postgres") {
1446 let path = path.replace('.', ", ");
1447 format!(r#"({field} #> '{{{path}}}')"#)
1448 } else {
1449 format!(r#"json_extract({field}, '$.{path}')"#)
1450 }
1451 })
1452 });
1453 let requires_json_value = json_field.is_some();
1454 let field = json_field
1455 .map(|s| s.into())
1456 .unwrap_or_else(|| Self::format_field(key));
1457 if let Some(filter) = value.as_object() {
1458 let mut conditions = Vec::with_capacity(filter.len());
1459 for (name, value) in filter {
1460 let operator = match name.as_str() {
1461 "$eq" => "=",
1462 "$ne" => "<>",
1463 "$lt" => "<",
1464 "$le" => "<=",
1465 "$gt" => ">",
1466 "$ge" => ">=",
1467 "$in" => "IN",
1468 "$nin" => "NOT IN",
1469 _ => "=",
1470 };
1471 let condition = if let Some(subquery) =
1472 value.as_object().and_then(|m| m.get_str("$subquery"))
1473 {
1474 format!(r#"{field} {operator} {subquery}"#)
1475 } else if requires_json_value {
1476 Self::format_json_filter(&field, operator, value)
1477 } else if let Some(s) = value.as_str() {
1478 if name == "$subquery" {
1479 format!(r#"{field} {operator} {s}"#)
1480 } else {
1481 let value = Self::escape_string(s);
1482 format!(r#"{field} {operator} {value}"#)
1483 }
1484 } else {
1485 format!(r#"{field} {operator} {value}"#)
1486 };
1487 conditions.push(condition);
1488 }
1489 Self::join_conditions(conditions, " AND ")
1490 } else if requires_json_value {
1491 Self::format_json_filter(&field, "=", value)
1492 } else if let Some(s) = value.as_str() {
1493 let value = Self::escape_string(s);
1494 format!(r#"{field} = {value}"#)
1495 } else {
1496 format!(r#"{field} = {value}"#)
1497 }
1498 }
1499
1500 fn format_json_filter(field: &str, operator: &str, value: &JsonValue) -> String {
1502 match value {
1503 JsonValue::Null => format!(r#"{field} IS NULL"#),
1504 JsonValue::Bool(b) => {
1505 let value = if *b { "TRUE" } else { "FALSE" };
1506 if cfg!(feature = "orm-postgres") {
1507 format!(r#"({field})::boolean IS {value}"#)
1508 } else {
1509 format!(r#"{field} = {value}"#)
1510 }
1511 }
1512 JsonValue::Number(n) => {
1513 if cfg!(feature = "orm-postgres") {
1514 format!(r#"{field} {operator} '{n}'"#)
1515 } else {
1516 format!(r#"{field} {operator} {n}"#)
1517 }
1518 }
1519 JsonValue::String(s) => {
1520 if s == "null" {
1521 format!(r#"{field} IS NULL"#)
1522 } else if s == "not_null" {
1523 format!(r#"{field} IS NOT NULL"#)
1524 } else if s == "true" || s == "false" {
1525 if cfg!(feature = "orm-postgres") {
1526 format!(r#"({field})::boolean IS {s}"#)
1527 } else {
1528 format!(r#"{field} = {s}"#)
1529 }
1530 } else if let Ok(n) = s.parse::<serde_json::Number>() {
1531 if cfg!(feature = "orm-postgres") {
1532 format!(r#"{field} {operator} '{n}'"#)
1533 } else {
1534 format!(r#"{field} {operator} {n}"#)
1535 }
1536 } else {
1537 let value = if cfg!(feature = "orm-postgres") {
1538 Self::escape_string(value)
1539 } else {
1540 Self::escape_string(s)
1541 };
1542 format!(r#"{field} {operator} {value}"#)
1543 }
1544 }
1545 _ => {
1546 let value = Self::escape_string(value);
1547 format!(r#"{field} {operator} {value}"#)
1548 }
1549 }
1550 }
1551
1552 fn format_sort(&self) -> String {
1554 let sort_order = self.query_order();
1555 if sort_order.is_empty() {
1556 String::new()
1557 } else {
1558 let sort_order = sort_order
1559 .iter()
1560 .map(|order| {
1561 let sort_field = Self::format_field(order.field());
1562 let mut expr = if order.is_descending() {
1563 format!("{sort_field} DESC")
1564 } else {
1565 format!("{sort_field} ASC")
1566 };
1567 if order.nulls_first() {
1568 expr.push_str(" NULLS FIRST");
1569 } else if order.nulls_last() {
1570 expr.push_str(" NULLS LAST");
1571 }
1572 expr
1573 })
1574 .collect::<Vec<_>>();
1575 format!("ORDER BY {}", sort_order.join(", "))
1576 }
1577 }
1578
1579 fn format_pagination(&self) -> String {
1581 let limit = self.query_limit();
1582 if limit == 0 || limit == usize::MAX {
1583 return String::new();
1584 }
1585
1586 let offset = self.query_offset();
1587 format!("LIMIT {limit} OFFSET {offset}")
1588 }
1589
1590 fn join_conditions(mut conditions: Vec<String>, operator: &str) -> String {
1592 match conditions.len() {
1593 0 => String::new(),
1594 1 => conditions.remove(0),
1595 _ => format!("({})", conditions.join(operator)),
1596 }
1597 }
1598}
1599
1600pub(crate) fn format_query<'a>(query: &'a str, params: Option<&'a Map>) -> Cow<'a, str> {
1605 if let Some(params) = params.filter(|_| query.contains('$')) {
1606 INTERPOLATION_PATTERN.replace_all(query, |captures: &Captures| {
1607 let key = &captures[1];
1608 params
1609 .get(key)
1610 .map(|value| match value {
1611 JsonValue::String(s) => s.to_owned(),
1612 _ => value.to_string(),
1613 })
1614 .unwrap_or_else(|| ["${", key, "}"].concat())
1615 })
1616 } else {
1617 Cow::Borrowed(query)
1618 }
1619}
1620
1621pub(crate) fn prepare_sql_query<'a>(
1627 query: &'a str,
1628 params: Option<&'a Map>,
1629 placeholder: char,
1630) -> (Cow<'a, str>, Vec<&'a JsonValue>) {
1631 let sql = format_query(query, params);
1632 if let Some(params) = params.filter(|_| sql.contains('#')) {
1633 let mut values = Vec::new();
1634 let sql = STATEMENT_PATTERN.replace_all(&sql, |captures: &Captures| {
1635 let key = &captures[1];
1636 let value = params.get(key).unwrap_or(&JsonValue::Null);
1637 values.push(value);
1638 if placeholder == '$' {
1639 Cow::Owned(format!("${}", values.len()))
1640 } else {
1641 Cow::Borrowed("?")
1642 }
1643 });
1644 (sql.into_owned().into(), values)
1645 } else {
1646 (sql, Vec::new())
1647 }
1648}
1649
1650static INTERPOLATION_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
1652 Regex::new(r"\$\{\s*([a-zA-Z]+[\w\.]*)\s*\}")
1653 .expect("fail to create a regex for the interpolation parameter")
1654});
1655
1656static STATEMENT_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
1658 Regex::new(r"\#\{\s*([a-zA-Z]+[\w\.]*)\s*\}")
1659 .expect("fail to create a regex for the prepared statement")
1660});
1661
1662#[cfg(test)]
1663mod tests {
1664 use zino_core::{Map, extension::JsonObjectExt};
1665
1666 #[test]
1667 fn it_formats_query_params() {
1668 let query = "SELECT ${fields} FROM users WHERE name = 'alice' AND age >= #{age};";
1669 let mut params = Map::new();
1670 params.upsert("fields", "id, name, age");
1671 params.upsert("age", 18);
1672
1673 let sql = super::format_query(query, Some(¶ms));
1674 assert_eq!(
1675 sql,
1676 "SELECT id, name, age FROM users WHERE name = 'alice' AND age >= #{age};"
1677 );
1678 }
1679
1680 #[test]
1681 fn it_formats_sql_query_params() {
1682 let query = "SELECT ${fields} FROM users WHERE name = 'alice' AND age >= #{age};";
1683 let mut params = Map::new();
1684 params.upsert("fields", "id, name, age");
1685 params.upsert("age", 18);
1686
1687 let (sql, values) = super::prepare_sql_query(query, Some(¶ms), '?');
1688 assert_eq!(
1689 sql,
1690 "SELECT id, name, age FROM users WHERE name = 'alice' AND age >= ?;"
1691 );
1692 assert_eq!(values[0], 18);
1693
1694 let (sql, values) = super::prepare_sql_query(query, Some(¶ms), '$');
1695 assert_eq!(
1696 sql,
1697 "SELECT id, name, age FROM users WHERE name = 'alice' AND age >= $1;"
1698 );
1699 assert_eq!(values[0], 18);
1700 }
1701}