starfish_core/schema/
relation.rs

1//! Define relation schema
2
3use super::{format_node_table_name, Schema};
4use crate::{entities::relation, lang::RelationJson};
5use sea_orm::{
6    ActiveModelTrait, ConnectionTrait, DbConn, DbErr, DeriveIden, ForeignKeyAction, Set,
7};
8use sea_query::{Alias, ColumnDef, ForeignKey, Index, Table};
9
10impl Schema {
11    /// Insert metadata of relation into database and create a corresponding node table
12    pub async fn create_relation(db: &DbConn, relation_json: RelationJson) -> Result<(), DbErr> {
13        relation::ActiveModel {
14            name: Set(relation_json.name.clone()),
15            from_entity: Set(relation_json.from_entity.clone()),
16            to_entity: Set(relation_json.to_entity.clone()),
17            directed: Set(relation_json.directed),
18            ..Default::default()
19        }
20        .insert(db)
21        .await?;
22
23        let edge_name = relation_json.name.as_str();
24        let from_entity = format_node_table_name(&relation_json.from_entity);
25        let to_entity = format_node_table_name(&relation_json.to_entity);
26
27        Self::add_in_connectivity_columns(db, edge_name, to_entity.as_str()).await?;
28        Self::add_out_connectivity_columns(db, edge_name, from_entity.as_str()).await?;
29
30        Self::create_edge_table(db, &relation_json, from_entity, to_entity).await
31    }
32
33    async fn add_in_connectivity_columns(
34        db: &DbConn,
35        edge_name: &str,
36        node_table: &str,
37    ) -> Result<(), DbErr> {
38        for col in [
39            "in_conn",
40            "in_conn_compound",
41            "in_conn_complex03",
42            "in_conn_complex05",
43            "in_conn_complex07",
44        ] {
45            Self::add_connectivity_column(db, node_table, &format!("{}_{}", edge_name, col))
46                .await?;
47        }
48
49        Ok(())
50    }
51
52    async fn add_out_connectivity_columns(
53        db: &DbConn,
54        edge_name: &str,
55        node_table: &str,
56    ) -> Result<(), DbErr> {
57        Self::add_connectivity_column(db, node_table, &format!("{}_out_conn", edge_name)).await
58    }
59
60    async fn add_connectivity_column(
61        db: &DbConn,
62        node_table: &str,
63        col: &str,
64    ) -> Result<(), DbErr> {
65        let builder = db.get_database_backend();
66        let mut stmt = Table::alter();
67        stmt.table(Alias::new(node_table)).add_column(
68            ColumnDef::new(Alias::new(col))
69                .double()
70                .not_null()
71                .default(0.0f64),
72        );
73        db.execute(builder.build(&stmt)).await?;
74
75        let mut stmt = Index::create();
76        stmt.name(&format!("idx-{}-{}", node_table, col))
77            .table(Alias::new(node_table))
78            .col(Alias::new(col));
79        db.execute(builder.build(&stmt)).await?;
80
81        Ok(())
82    }
83
84    async fn create_edge_table(
85        db: &DbConn,
86        relation_json: &RelationJson,
87        from_entity: String,
88        to_entity: String,
89    ) -> Result<(), DbErr> {
90        let table = Alias::new(relation_json.get_table_name().as_str());
91        let mut stmt = Table::create();
92        stmt.table(table.clone())
93            .col(
94                ColumnDef::new(Alias::new("id"))
95                    .integer()
96                    .not_null()
97                    .auto_increment()
98                    .primary_key(),
99            )
100            .col(ColumnDef::new(Alias::new("from_node")).string().not_null())
101            .col(ColumnDef::new(Alias::new("to_node")).string().not_null())
102            .index(
103                Index::create()
104                    .name(&format!("idx-{}-{}", table.to_string(), "from_node"))
105                    .table(table.clone())
106                    .col(Alias::new("from_node")),
107            )
108            .index(
109                Index::create()
110                    .name(&format!("idx-{}-{}", table.to_string(), "to_node"))
111                    .table(table.clone())
112                    .col(Alias::new("to_node")),
113            )
114            .index(
115                Index::create()
116                    .unique()
117                    .name(&format!(
118                        "idx-{}-from_node-to_node",
119                        relation_json.get_table_name()
120                    ))
121                    .col(Alias::new("from_node"))
122                    .col(Alias::new("to_node")),
123            )
124            .foreign_key(
125                ForeignKey::create()
126                    .name(&format!(
127                        "fk-{}-from-{}",
128                        relation_json.get_table_name(),
129                        from_entity
130                    ))
131                    .from_tbl(Alias::new(relation_json.get_table_name().as_str()))
132                    .from_col(Alias::new("from_node"))
133                    .to_tbl(Alias::new(from_entity.as_str()))
134                    .to_col(Alias::new("name"))
135                    .on_delete(ForeignKeyAction::Cascade),
136            )
137            .foreign_key(
138                ForeignKey::create()
139                    .name(&format!(
140                        "fk-{}-to-{}",
141                        relation_json.get_table_name(),
142                        to_entity
143                    ))
144                    .from_tbl(Alias::new(relation_json.get_table_name().as_str()))
145                    .from_col(Alias::new("to_node"))
146                    .to_tbl(Alias::new(to_entity.as_str()))
147                    .to_col(Alias::new("name"))
148                    .on_delete(ForeignKeyAction::Cascade),
149            );
150
151        let builder = db.get_database_backend();
152        db.execute(builder.build(&stmt)).await?;
153
154        Ok(())
155    }
156}