1use crate::{EntityTrait, QuerySelect, RelationDef, Select, join_tbl_on_condition};
2use sea_query::{
3 Alias, CommonTableExpression, Condition, ConditionType, IntoIden, IntoTableRef, JoinType,
4 UnionType,
5};
6
7pub type LinkDef = RelationDef;
9
10pub trait Linked {
19 type FromEntity: EntityTrait;
21
22 type ToEntity: EntityTrait;
24
25 fn link(&self) -> Vec<LinkDef>;
27
28 fn find_linked(&self) -> Select<Self::ToEntity> {
30 find_linked(self.link().into_iter().rev(), JoinType::InnerJoin)
31 }
32}
33
34pub(crate) fn find_linked<I, E>(links: I, join: JoinType) -> Select<E>
35where
36 I: Iterator<Item = LinkDef>,
37 E: EntityTrait,
38{
39 let mut select = Select::new();
40 for (i, mut rel) in links.enumerate() {
41 let from_tbl = format!("r{i}").into_iden();
42 let to_tbl = if i > 0 {
43 format!("r{}", i - 1).into_iden()
44 } else {
45 rel.to_tbl.sea_orm_table().clone()
46 };
47 let table_ref = rel.from_tbl;
48
49 let mut condition = match rel.condition_type {
50 ConditionType::All => Condition::all(),
51 ConditionType::Any => Condition::any(),
52 };
53 condition = condition.add(join_tbl_on_condition(
54 from_tbl.clone(),
55 to_tbl.clone(),
56 rel.from_col,
57 rel.to_col,
58 ));
59 if let Some(f) = rel.on_condition.take() {
60 condition = condition.add(f(from_tbl.clone(), to_tbl.clone()));
61 }
62
63 select.query().join_as(join, table_ref, from_tbl, condition);
64 }
65 select
66}
67
68pub(crate) fn find_linked_recursive<E>(
69 mut initial_query: Select<E>,
70 mut link: Vec<LinkDef>,
71) -> Select<E>
72where
73 E: EntityTrait,
74{
75 let cte_name = Alias::new("cte");
76
77 let Some(first) = link.first_mut() else {
78 return initial_query;
79 };
80 first.from_tbl = cte_name.clone().into_table_ref();
81 let mut recursive_query: Select<E> =
82 find_linked(link.into_iter().rev(), JoinType::InnerJoin).select_only();
83 initial_query.query.exprs_mut_for_each(|expr| {
84 recursive_query.query.expr(expr.clone());
85 });
86
87 let mut cte_query = initial_query.query.clone();
88 cte_query.union(UnionType::All, recursive_query.query);
89
90 let cte = CommonTableExpression::new()
91 .table_name(cte_name.clone())
92 .query(cte_query)
93 .to_owned();
94
95 let mut select = E::find().select_only();
96 initial_query.query.exprs_mut_for_each(|expr| {
97 select.query.expr(expr.clone());
98 });
99 select
100 .query
101 .from_clear()
102 .from_as(cte_name, E::default())
103 .with_cte(cte);
104 select
105}