sea_orm/entity/
link.rs

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