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