From e228ada74aeb36854bdfc0fcdf8bfed3fb619e20 Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sun, 8 Aug 2021 15:30:01 +0800 Subject: [PATCH] Restore example --- Cargo.toml | 1 + examples/sqlx/Cargo.toml | 13 + examples/sqlx/Readme.md | 141 +++++++++ {src/tests_cfg => examples/sqlx}/bakery.sql | 8 +- examples/sqlx/import.sh | 7 + examples/sqlx/src/entities.rs | 4 + examples/sqlx/src/example_cake.rs | 75 +++++ examples/sqlx/src/example_cake_filling.rs | 68 +++++ examples/sqlx/src/example_filling.rs | 65 ++++ examples/sqlx/src/example_fruit.rs | 71 +++++ examples/sqlx/src/main.rs | 34 +++ examples/sqlx/src/operation.rs | 94 ++++++ examples/sqlx/src/select.rs | 310 ++++++++++++++++++++ 13 files changed, 887 insertions(+), 4 deletions(-) create mode 100644 examples/sqlx/Cargo.toml create mode 100644 examples/sqlx/Readme.md rename {src/tests_cfg => examples/sqlx}/bakery.sql (89%) create mode 100644 examples/sqlx/import.sh create mode 100644 examples/sqlx/src/entities.rs create mode 100644 examples/sqlx/src/example_cake.rs create mode 100644 examples/sqlx/src/example_cake_filling.rs create mode 100644 examples/sqlx/src/example_filling.rs create mode 100644 examples/sqlx/src/example_fruit.rs create mode 100644 examples/sqlx/src/main.rs create mode 100644 examples/sqlx/src/operation.rs create mode 100644 examples/sqlx/src/select.rs diff --git a/Cargo.toml b/Cargo.toml index 7dc1c014..640db4d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "sea-orm-macros", "sea-orm-codegen", "sea-orm-cli", + "examples/sqlx", ] [package] diff --git a/examples/sqlx/Cargo.toml b/examples/sqlx/Cargo.toml new file mode 100644 index 00000000..e6286ae1 --- /dev/null +++ b/examples/sqlx/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sea-orm-sqlx-example" +version = "0.1.0" +edition = "2018" +publish = false + +[dependencies] +async-std = { version = "^1.9", features = [ "attributes" ] } +sea-orm = { path = "../../", features = [ "sqlx-all", "runtime-async-std-native-tls" ] } +serde_json = { version = "^1" } +futures = { version = "^0.3" } +async-stream = { version = "^0.3" } +futures-util = { version = "^0.3" } \ No newline at end of file diff --git a/examples/sqlx/Readme.md b/examples/sqlx/Readme.md new file mode 100644 index 00000000..98a14ada --- /dev/null +++ b/examples/sqlx/Readme.md @@ -0,0 +1,141 @@ +# SeaORM SQLx MySql example + +Prepare: + +Setup a test database and configure the connection string in `main.rs`. +Run `bakery.sql` to setup the test table and data. + +Running: + +```sh +cargo run +``` + +All about selects: + +```sh +SqlxMySqlPoolConnection + +===== ===== + +find all cakes: SELECT `cake`.`id`, `cake`.`name` FROM `cake` + +Model { id: 1, name: "New York Cheese" } + +Model { id: 2, name: "Chocolate Forest" } + +find all fruits: SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` + +Model { id: 1, name: "Blueberry", cake_id: Some(1) } + +Model { id: 2, name: "Rasberry", cake_id: Some(1) } + +Model { id: 3, name: "Strawberry", cake_id: Some(2) } + +Model { id: 4, name: "Apple", cake_id: None } + +Model { id: 5, name: "Banana", cake_id: None } + +Model { id: 6, name: "Cherry", cake_id: None } + +Model { id: 7, name: "Lemon", cake_id: None } + +Model { id: 8, name: "Orange", cake_id: None } + +Model { id: 9, name: "Pineapple", cake_id: None } + +===== ===== + +find cakes and fruits: SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id` FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` + +(Model { id: 1, name: "New York Cheese" }, Some(Model { id: 1, name: "Blueberry", cake_id: Some(1) })) + +(Model { id: 1, name: "New York Cheese" }, Some(Model { id: 2, name: "Rasberry", cake_id: Some(1) })) + +(Model { id: 2, name: "Chocolate Forest" }, Some(Model { id: 3, name: "Strawberry", cake_id: Some(2) })) + +===== ===== + +find one by primary key: SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 1 LIMIT 1 + +Model { id: 1, name: "New York Cheese" } + +find one by name: SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%chocolate%' LIMIT 1 + +Some(Model { id: 2, name: "Chocolate Forest" }) + +find models belong to: SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id` WHERE `cake`.`id` = 1 + +Model { id: 1, name: "Blueberry", cake_id: Some(1) } + +Model { id: 2, name: "Rasberry", cake_id: Some(1) } + +===== ===== + +count fruits by cake: SELECT `cake`.`name`, COUNT(`fruit`.`id`) AS `num_of_fruits` FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` GROUP BY `cake`.`name` + +SelectResult { name: "New York Cheese", num_of_fruits: 2 } + +SelectResult { name: "Chocolate Forest", num_of_fruits: 1 } + +===== ===== + +find cakes and fillings: SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `filling`.`id` AS `B_id`, `filling`.`name` AS `B_name` FROM `cake` LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id` ORDER BY `cake`.`id` ASC + +(Model { id: 1, name: "New York Cheese" }, [Model { id: 1, name: "Vanilla" }, Model { id: 2, name: "Lemon" }]) + +(Model { id: 2, name: "Chocolate Forest" }, [Model { id: 2, name: "Lemon" }, Model { id: 3, name: "Mango" }]) + +find fillings for cheese cake: SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 1 LIMIT 1 +SELECT `filling`.`id`, `filling`.`name` FROM `filling` INNER JOIN `cake_filling` ON `cake_filling`.`filling_id` = `filling`.`id` INNER JOIN `cake` ON `cake`.`id` = `cake_filling`.`cake_id` WHERE `cake`.`id` = 1 + +Model { id: 1, name: "Vanilla" } + +Model { id: 2, name: "Lemon" } + +find cakes for lemon: SELECT `filling`.`id`, `filling`.`name` FROM `filling` WHERE `filling`.`id` = 2 LIMIT 1 +SELECT `cake`.`id`, `cake`.`name` FROM `cake` INNER JOIN `cake_filling` ON `cake_filling`.`cake_id` = `cake`.`id` INNER JOIN `filling` ON `filling`.`id` = `cake_filling`.`filling_id` WHERE `filling`.`id` = 2 + +Model { id: 1, name: "New York Cheese" } + +Model { id: 2, name: "Chocolate Forest" } + +``` + +All about operations: + +```sh +INSERT INTO `fruit` (`name`) VALUES ('pear') + +Inserted: InsertResult { last_insert_id: 21 } + +SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = 21 LIMIT 1 + +Pear: Some(Model { id: 21, name: "pear", cake_id: None }) + +UPDATE `fruit` SET `name` = 'Sweet pear' WHERE `fruit`.`id` = 21 + +Updated: ActiveModel { id: ActiveValue { value: 21, state: Unchanged }, name: ActiveValue { value: "Sweet pear", state: Set }, cake_id: ActiveValue { value: None, state: Unchanged } } + +===== ===== + +INSERT INTO `fruit` (`name`) VALUES ('banana') +SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = 22 LIMIT 1 + +Inserted: ActiveModel { id: ActiveValue { value: 22, state: Unchanged }, name: ActiveValue { value: "banana", state: Unchanged }, cake_id: ActiveValue { value: None, state: Unchanged } } + +UPDATE `fruit` SET `name` = 'banana banana' WHERE `fruit`.`id` = 22 + +Updated: ActiveModel { id: ActiveValue { value: 22, state: Unchanged }, name: ActiveValue { value: "banana banana", state: Set }, cake_id: ActiveValue { value: None, state: Unchanged } } + +DELETE FROM `fruit` WHERE `fruit`.`id` = 22 + +Deleted: DeleteResult { rows_affected: 1 } + +===== ===== + +INSERT INTO `fruit` (`name`) VALUES ('pineapple') +SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = 23 LIMIT 1 + +Saved: ActiveModel { id: ActiveValue { value: 23, state: Unchanged }, name: ActiveValue { value: "pineapple", state: Unchanged } } +``` \ No newline at end of file diff --git a/src/tests_cfg/bakery.sql b/examples/sqlx/bakery.sql similarity index 89% rename from src/tests_cfg/bakery.sql rename to examples/sqlx/bakery.sql index 2335b7d9..763328dc 100644 --- a/src/tests_cfg/bakery.sql +++ b/examples/sqlx/bakery.sql @@ -4,7 +4,7 @@ CREATE TABLE `cake` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +); INSERT INTO `cake` (`id`, `name`) VALUES (1, 'New York Cheese'), @@ -18,7 +18,7 @@ CREATE TABLE `fruit` ( `cake_id` int DEFAULT NULL, PRIMARY KEY (`id`), CONSTRAINT `fk-fruit-cake` FOREIGN KEY (`cake_id`) REFERENCES `cake` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +); INSERT INTO `fruit` (`id`, `name`, `cake_id`) VALUES (1, 'Blueberry', 1), @@ -39,7 +39,7 @@ CREATE TABLE `filling` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +); INSERT INTO `filling` (`id`, `name`) VALUES (1, 'Vanilla'), @@ -54,7 +54,7 @@ CREATE TABLE `cake_filling` ( PRIMARY KEY (`cake_id`, `filling_id`), CONSTRAINT `fk-cake_filling-cake` FOREIGN KEY (`cake_id`) REFERENCES `cake` (`id`), CONSTRAINT `fk-cake_filling-filling` FOREIGN KEY (`filling_id`) REFERENCES `filling` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +); INSERT INTO `cake_filling` (`cake_id`, `filling_id`) VALUES (1, 1), diff --git a/examples/sqlx/import.sh b/examples/sqlx/import.sh new file mode 100644 index 00000000..7e4820c1 --- /dev/null +++ b/examples/sqlx/import.sh @@ -0,0 +1,7 @@ +cp ../../src/tests_cfg/cake.rs src/example_cake.rs +cp ../../src/tests_cfg/fruit.rs src/example_fruit.rs +cp ../../src/tests_cfg/filling.rs src/example_filling.rs +cp ../../src/tests_cfg/cake_filling.rs src/example_cake_filling.rs + +sed -i 's/^use crate::/use sea_orm::/g' src/*.rs +sed -i '/^use crate as sea_orm;/d' src/*.rs \ No newline at end of file diff --git a/examples/sqlx/src/entities.rs b/examples/sqlx/src/entities.rs new file mode 100644 index 00000000..40a4bd9a --- /dev/null +++ b/examples/sqlx/src/entities.rs @@ -0,0 +1,4 @@ +pub use super::cake::Entity as Cake; +pub use super::cake_filling::Entity as CakeFilling; +pub use super::filling::Entity as Filling; +pub use super::fruit::Entity as Fruit; diff --git a/examples/sqlx/src/example_cake.rs b/examples/sqlx/src/example_cake.rs new file mode 100644 index 00000000..475315e8 --- /dev/null +++ b/examples/sqlx/src/example_cake.rs @@ -0,0 +1,75 @@ +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "cake" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Name, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Fruit, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Name => ColumnType::String(None).def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Fruit => Entity::has_many(super::fruit::Entity).into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Fruit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Filling.def() + } + + fn via() -> Option { + Some(super::cake_filling::Relation::Cake.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/sqlx/src/example_cake_filling.rs b/examples/sqlx/src/example_cake_filling.rs new file mode 100644 index 00000000..19de83e4 --- /dev/null +++ b/examples/sqlx/src/example_cake_filling.rs @@ -0,0 +1,68 @@ +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "cake_filling" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub cake_id: i32, + pub filling_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + CakeId, + FillingId, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + CakeId, + FillingId, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Cake, + Filling, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::CakeId => ColumnType::Integer.def(), + Self::FillingId => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Cake => Entity::belongs_to(super::cake::Entity) + .from(Column::CakeId) + .to(super::cake::Column::Id) + .into(), + Self::Filling => Entity::belongs_to(super::filling::Entity) + .from(Column::FillingId) + .to(super::filling::Column::Id) + .into(), + } + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/sqlx/src/example_filling.rs b/examples/sqlx/src/example_filling.rs new file mode 100644 index 00000000..925b92fc --- /dev/null +++ b/examples/sqlx/src/example_filling.rs @@ -0,0 +1,65 @@ +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "filling" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Name, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation {} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Name => ColumnType::String(None).def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + panic!() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cake_filling::Relation::Cake.def() + } + + fn via() -> Option { + Some(super::cake_filling::Relation::Filling.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/sqlx/src/example_fruit.rs b/examples/sqlx/src/example_fruit.rs new file mode 100644 index 00000000..b875da24 --- /dev/null +++ b/examples/sqlx/src/example_fruit.rs @@ -0,0 +1,71 @@ +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "fruit" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub name: String, + pub cake_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Name, + CakeId, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Cake, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Name => ColumnType::String(None).def(), + Self::CakeId => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Cake => Entity::belongs_to(super::cake::Entity) + .from(Column::CakeId) + .to(super::cake::Column::Id) + .into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Cake.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/sqlx/src/main.rs b/examples/sqlx/src/main.rs new file mode 100644 index 00000000..5db015f2 --- /dev/null +++ b/examples/sqlx/src/main.rs @@ -0,0 +1,34 @@ +use sea_orm::Database; + +mod entities; +mod example_cake; +mod example_cake_filling; +mod example_filling; +mod example_fruit; +mod operation; +mod select; + +use entities::*; +use example_cake as cake; +use example_cake_filling as cake_filling; +use example_filling as filling; +use example_fruit as fruit; +use operation::*; +use select::*; + +#[async_std::main] +async fn main() { + let db = Database::connect("sql://sea:sea@localhost/bakery") + .await + .unwrap(); + + println!("{:?}\n", db); + + println!("===== =====\n"); + + all_about_select(&db).await.unwrap(); + + println!("===== =====\n"); + + all_about_operation(&db).await.unwrap(); +} diff --git a/examples/sqlx/src/operation.rs b/examples/sqlx/src/operation.rs new file mode 100644 index 00000000..b1273e10 --- /dev/null +++ b/examples/sqlx/src/operation.rs @@ -0,0 +1,94 @@ +use super::*; +use sea_orm::{entity::*, error::*, query::*, DbConn}; + +pub async fn all_about_operation(db: &DbConn) -> Result<(), DbErr> { + insert_and_update(db).await?; + + println!("===== =====\n"); + + save_active_model(db).await?; + + println!("===== =====\n"); + + save_custom_active_model(db).await?; + + Ok(()) +} + +pub async fn insert_and_update(db: &DbConn) -> Result<(), DbErr> { + let pear = fruit::ActiveModel { + name: Set("pear".to_owned()), + ..Default::default() + }; + let res: InsertResult = Fruit::insert(pear).exec(db).await?; + + println!(); + println!("Inserted: last_insert_id = {}\n", res.last_insert_id); + + let pear: Option = Fruit::find_by_id(res.last_insert_id).one(db).await?; + + println!(); + println!("Pear: {:?}\n", pear); + + let mut pear: fruit::ActiveModel = pear.unwrap().into(); + pear.name = Set("Sweet pear".to_owned()); + + let pear: fruit::ActiveModel = Fruit::update(pear).exec(db).await?; + + println!(); + println!("Updated: {:?}\n", pear); + + Ok(()) +} + +pub async fn save_active_model(db: &DbConn) -> Result<(), DbErr> { + let banana = fruit::ActiveModel { + name: Set("Banana".to_owned()), + ..Default::default() + }; + let mut banana = banana.save(db).await?; + + println!(); + println!("Inserted: {:?}\n", banana); + + banana.name = Set("Banana Mongo".to_owned()); + + let banana = banana.save(db).await?; + + println!(); + println!("Updated: {:?}\n", banana); + + let result = banana.delete(db).await?; + + println!(); + println!("Deleted: {:?}\n", result); + + Ok(()) +} + +mod form { + use super::fruit::*; + use sea_orm::entity::prelude::*; + + #[derive( + Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, DeriveActiveModelBehavior, + )] + pub struct Model { + pub id: i32, + pub name: String, + } +} + +async fn save_custom_active_model(db: &DbConn) -> Result<(), DbErr> { + let pineapple = form::ActiveModel { + id: Unset(None), + name: Set("Pineapple".to_owned()), + }; + + let pineapple = pineapple.save(db).await?; + + println!(); + println!("Saved: {:?}\n", pineapple); + + Ok(()) +} diff --git a/examples/sqlx/src/select.rs b/examples/sqlx/src/select.rs new file mode 100644 index 00000000..9b2cf15c --- /dev/null +++ b/examples/sqlx/src/select.rs @@ -0,0 +1,310 @@ +use super::*; +use sea_orm::{entity::*, error::*, query::*, DbConn, FromQueryResult}; + +pub async fn all_about_select(db: &DbConn) -> Result<(), DbErr> { + find_all(db).await?; + + println!("===== =====\n"); + + find_together(db).await?; + + println!("===== =====\n"); + + find_one(db).await?; + + println!("===== =====\n"); + + count_fruits_by_cake(db).await?; + + println!("===== =====\n"); + + find_many_to_many(db).await?; + + if false { + println!("===== =====\n"); + + all_about_select_json(db).await?; + } + + println!("===== =====\n"); + + find_all_stream(&db).await.unwrap(); + + println!("===== =====\n"); + + find_first_page(&db).await.unwrap(); + + println!("===== =====\n"); + + find_num_pages(&db).await.unwrap(); + + Ok(()) +} + +async fn find_all(db: &DbConn) -> Result<(), DbErr> { + print!("find all cakes: "); + + let cakes: Vec = Cake::find().all(db).await?; + + println!(); + for cc in cakes.iter() { + println!("{:?}\n", cc); + } + + print!("find all fruits: "); + + let fruits = Fruit::find().all(db).await?; + + println!(); + for ff in fruits.iter() { + println!("{:?}\n", ff); + } + + Ok(()) +} + +async fn find_together(db: &DbConn) -> Result<(), DbErr> { + print!("find cakes and fruits: "); + + let both = Cake::find().find_also_related(Fruit).all(db).await?; + + println!(); + for bb in both.iter() { + println!("{:?}\n", bb); + } + + Ok(()) +} + +impl Cake { + fn find_by_name(name: &str) -> Select { + Self::find().filter(cake::Column::Name.contains(name)) + } +} + +async fn find_one(db: &DbConn) -> Result<(), DbErr> { + print!("find one by primary key: "); + + let cheese: Option = Cake::find_by_id(1).one(db).await?; + let cheese = cheese.unwrap(); + + println!(); + println!("{:?}", cheese); + println!(); + + print!("find one by name: "); + + let chocolate = Cake::find_by_name("chocolate").one(db).await?; + + println!(); + println!("{:?}", chocolate); + println!(); + + print!("find models belong to: "); + + let fruits = cheese.find_related(Fruit).all(db).await?; + + println!(); + for ff in fruits.iter() { + println!("{:?}\n", ff); + } + + Ok(()) +} + +async fn count_fruits_by_cake(db: &DbConn) -> Result<(), DbErr> { + #[derive(Debug, FromQueryResult)] + struct SelectResult { + name: String, + num_of_fruits: i32, + } + + print!("count fruits by cake: "); + + let select = Cake::find() + .left_join(Fruit) + .select_only() + .column(cake::Column::Name) + .column_as(fruit::Column::Id.count(), "num_of_fruits") + .group_by(cake::Column::Name); + + let results = select.into_model::().all(db).await?; + + println!(); + for rr in results.iter() { + println!("{:?}\n", rr); + } + + Ok(()) +} + +async fn find_many_to_many(db: &DbConn) -> Result<(), DbErr> { + print!("find cakes and fillings: "); + + let both: Vec<(cake::Model, Vec)> = + Cake::find().find_with_related(Filling).all(db).await?; + + println!(); + for bb in both.iter() { + println!("{:?}\n", bb); + } + + print!("find fillings for cheese cake: "); + + let cheese = Cake::find_by_id(1).one(db).await?; + + if let Some(cheese) = cheese { + let fillings: Vec = cheese.find_related(Filling).all(db).await?; + + println!(); + for ff in fillings.iter() { + println!("{:?}\n", ff); + } + } + + print!("find cakes for lemon: "); + + let lemon = Filling::find_by_id(2).one(db).await?; + + if let Some(lemon) = lemon { + let cakes: Vec = lemon.find_related(Cake).all(db).await?; + + println!(); + for cc in cakes.iter() { + println!("{:?}\n", cc); + } + } + + Ok(()) +} + +async fn all_about_select_json(db: &DbConn) -> Result<(), DbErr> { + find_all_json(&db).await?; + + println!("===== =====\n"); + + find_together_json(&db).await?; + + println!("===== =====\n"); + + count_fruits_by_cake_json(&db).await?; + + Ok(()) +} + +async fn find_all_json(db: &DbConn) -> Result<(), DbErr> { + print!("find all cakes: "); + + let cakes = Cake::find().into_json().all(db).await?; + + println!("\n{}\n", serde_json::to_string_pretty(&cakes).unwrap()); + + print!("find all fruits: "); + + let fruits = Fruit::find().into_json().all(db).await?; + + println!("\n{}\n", serde_json::to_string_pretty(&fruits).unwrap()); + + Ok(()) +} + +async fn find_together_json(db: &DbConn) -> Result<(), DbErr> { + print!("find cakes and fruits: "); + + let cakes_fruits = Cake::find() + .find_with_related(Fruit) + .into_json() + .all(db) + .await?; + + println!( + "\n{}\n", + serde_json::to_string_pretty(&cakes_fruits).unwrap() + ); + + Ok(()) +} + +async fn count_fruits_by_cake_json(db: &DbConn) -> Result<(), DbErr> { + print!("count fruits by cake: "); + + let count = Cake::find() + .left_join(Fruit) + .select_only() + .column(cake::Column::Name) + .column_as(fruit::Column::Id.count(), "num_of_fruits") + .group_by(cake::Column::Name) + .into_json() + .all(db) + .await?; + + println!("\n{}\n", serde_json::to_string_pretty(&count).unwrap()); + + Ok(()) +} + +async fn find_all_stream(db: &DbConn) -> Result<(), DbErr> { + use async_std::task::sleep; + use futures::TryStreamExt; + use std::time::Duration; + + println!("find all cakes: "); + let mut cake_paginator = cake::Entity::find().paginate(db, 2); + while let Some(cake_res) = cake_paginator.fetch_and_next().await? { + for cake in cake_res { + println!("{:?}", cake); + } + } + + println!(); + println!("find all fruits: "); + let mut fruit_paginator = fruit::Entity::find().paginate(db, 2); + while let Some(fruit_res) = fruit_paginator.fetch_and_next().await? { + for fruit in fruit_res { + println!("{:?}", fruit); + } + } + + println!(); + println!("find all fruits with stream: "); + let mut fruit_stream = fruit::Entity::find().paginate(db, 2).into_stream(); + while let Some(fruits) = fruit_stream.try_next().await? { + for fruit in fruits { + println!("{:?}", fruit); + } + sleep(Duration::from_millis(250)).await; + } + + println!(); + println!("find all fruits in json with stream: "); + let mut json_stream = fruit::Entity::find() + .into_json() + .paginate(db, 2) + .into_stream(); + while let Some(jsons) = json_stream.try_next().await? { + for json in jsons { + println!("{:?}", json); + } + sleep(Duration::from_millis(250)).await; + } + + Ok(()) +} + +async fn find_first_page(db: &DbConn) -> Result<(), DbErr> { + println!("fruits first page: "); + let page = fruit::Entity::find().paginate(db, 2).fetch_page(0).await?; + for fruit in page { + println!("{:?}", fruit); + } + + Ok(()) +} + +async fn find_num_pages(db: &DbConn) -> Result<(), DbErr> { + println!("fruits number of page: "); + let num_pages = fruit::Entity::find().paginate(db, 2).num_pages().await?; + println!("{:?}", num_pages); + + Ok(()) +}