From e1fa9938366c893de52484892cc7f3fddc7388eb Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 27 Jun 2023 16:06:11 +0800 Subject: [PATCH 1/8] API DEMO: `find_with_linked` --- src/query/join.rs | 9 +++++ tests/relational_tests.rs | 77 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/query/join.rs b/src/query/join.rs index 09f2e3857..c57b50c40 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -107,6 +107,15 @@ where } select_two } + + pub fn find_with_linked(self, l: L) -> SelectTwoMany + where + L: Linked, + T: EntityTrait, + { + // TODO: Should construct the select and join expression below + self.select_with(T::default()) + } } #[cfg(test)] diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 959f05577..2527a81b7 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -2,6 +2,7 @@ pub mod common; pub use chrono::offset::Utc; pub use common::{bakery_chain::*, setup::*, TestContext}; +use pretty_assertions::assert_eq; pub use rust_decimal::prelude::*; pub use rust_decimal_macros::dec; use sea_orm::{entity::*, query::*, DbErr, DerivePartialModel, FromQueryResult}; @@ -747,6 +748,82 @@ pub async fn linked() -> Result<(), DbErr> { }] ); + let select_baker_with_customer = Baker::find().find_with_linked(baker::BakedForCustomer); + + assert_eq!( + select_baker_with_customer + .build(sea_orm::DatabaseBackend::MySql) + .to_string(), + [ + // FIXME: This might be faulty! + "SELECT `baker`.`id` AS `A_id`,", + "`baker`.`name` AS `A_name`,", + "`baker`.`contact_details` AS `A_contact_details`,", + "`baker`.`bakery_id` AS `A_bakery_id`,", + "`r4`.`id` AS `B_id`,", + "`r4`.`name` AS `B_name`,", + "`r4`.`notes` AS `B_notes`", + "FROM `baker`", + "INNER JOIN `cakes_bakers` AS `r0` ON `r0`.`baker_id` = `baker`.`id`", + "INNER JOIN `cake` AS `r1` ON `r1`.`id` = `r0`.`cake_id`", + "INNER JOIN `lineitem` AS `r2` ON `r2`.`cake_id` = `r1`.`id`", + "INNER JOIN `order` AS `r3` ON `r3`.`id` = `r2`.`order_id`", + "INNER JOIN `customer` AS `r4` ON `r4`.`id` = `r3`.`customer_id`", + "ORDER BY `baker`.`id` ASC", + ] + .join(" ") + ); + + assert_eq!( + select_baker_with_customer.all(&ctx.db).await?, + [ + ( + baker::Model { + id: 1, + name: "Baker Bob".into(), + contact_details: serde_json::json!({ + "mobile": "+61424000000", + "home": "0395555555", + "address": "12 Test St, Testville, Vic, Australia", + }), + bakery_id: Some(1), + }, + vec![customer::Model { + id: 2, + name: "Kara".into(), + notes: Some("Loves all cakes".into()), + }] + ), + ( + baker::Model { + id: 1, + name: "Baker Bobby".into(), + contact_details: serde_json::json!({ + "mobile": "+85212345678", + }), + bakery_id: Some(1), + }, + vec![ + customer::Model { + id: 1, + name: "Kate".into(), + notes: Some("Loves cheese cake".into()), + }, + customer::Model { + id: 1, + name: "Kate".into(), + notes: Some("Loves cheese cake".into()), + }, + customer::Model { + id: 2, + name: "Kara".into(), + notes: Some("Loves all cakes".into()), + }, + ] + ), + ] + ); + ctx.delete().await; Ok(()) From a0045d710f2b07766c884746225a74f645711fb8 Mon Sep 17 00:00:00 2001 From: Yiu Tin Cheung Ivan Date: Thu, 29 Jun 2023 15:05:53 +0800 Subject: [PATCH 2/8] implemented find_with_linked --- src/query/combine.rs | 7 ++++++- src/query/join.rs | 41 +++++++++++++++++++++++++++++++++++++-- tests/relational_tests.rs | 12 ++++++------ 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/query/combine.rs b/src/query/combine.rs index 9ffb8e6ed..a96c9e447 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -119,11 +119,16 @@ where F: EntityTrait, { pub(crate) fn new(query: SelectStatement) -> Self { + Self::new_without_prepare(query) + .prepare_select() + .prepare_order_by() + } + + pub(crate) fn new_without_prepare(query: SelectStatement) -> Self { Self { query, entity: PhantomData, } - .prepare_select() .prepare_order_by() } diff --git a/src/query/join.rs b/src/query/join.rs index c57b50c40..906de45da 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -108,13 +108,50 @@ where select_two } + /// Left Join with a Linked Entity and select Entity as a `Vec`. pub fn find_with_linked(self, l: L) -> SelectTwoMany where L: Linked, T: EntityTrait, { - // TODO: Should construct the select and join expression below - self.select_with(T::default()) + let mut slf = self; + for (i, mut rel) in l.link().into_iter().enumerate() { + let to_tbl = Alias::new(format!("r{i}")).into_iden(); + let from_tbl = if i > 0 { + Alias::new(format!("r{}", i - 1)).into_iden() + } else { + unpack_table_ref(&rel.from_tbl) + }; + let table_ref = rel.to_tbl; + + let mut condition = Condition::all().add(join_tbl_on_condition( + SeaRc::clone(&from_tbl), + SeaRc::clone(&to_tbl), + rel.from_col, + rel.to_col, + )); + if let Some(f) = rel.on_condition.take() { + condition = condition.add(f(SeaRc::clone(&from_tbl), SeaRc::clone(&to_tbl))); + } + + slf.query() + .join_as(JoinType::InnerJoin, table_ref, to_tbl, condition); + } + slf = slf.apply_alias(SelectA.as_str()); + let mut select_two_many = SelectTwoMany::new_without_prepare(slf.query); + for col in ::iter() { + let alias = format!("{}{}", SelectB.as_str(), col.as_str()); + let expr = Expr::col(( + Alias::new(format!("r{}", l.link().len() - 1)).into_iden(), + col.into_iden(), + )); + select_two_many.query().expr(SelectExpr { + expr: col.select_as(expr), + alias: Some(SeaRc::new(Alias::new(alias))), + window: None, + }); + } + select_two_many } } diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 2527a81b7..61b17eaa4 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -764,11 +764,11 @@ pub async fn linked() -> Result<(), DbErr> { "`r4`.`name` AS `B_name`,", "`r4`.`notes` AS `B_notes`", "FROM `baker`", - "INNER JOIN `cakes_bakers` AS `r0` ON `r0`.`baker_id` = `baker`.`id`", - "INNER JOIN `cake` AS `r1` ON `r1`.`id` = `r0`.`cake_id`", - "INNER JOIN `lineitem` AS `r2` ON `r2`.`cake_id` = `r1`.`id`", - "INNER JOIN `order` AS `r3` ON `r3`.`id` = `r2`.`order_id`", - "INNER JOIN `customer` AS `r4` ON `r4`.`id` = `r3`.`customer_id`", + "LEFT JOIN `cakes_bakers` AS `r0` ON `baker`.`id` = `r0`.`baker_id`", + "LEFT JOIN `cake` AS `r1` ON `r0`.`cake_id` = `r1`.`id`", + "LEFT JOIN `lineitem` AS `r2` ON `r1`.`id` = `r2`.`cake_id`", + "LEFT JOIN `order` AS `r3` ON `r2`.`order_id` = `r3`.`id`", + "LEFT JOIN `customer` AS `r4` ON `r3`.`customer_id` = `r4`.`id`", "ORDER BY `baker`.`id` ASC", ] .join(" ") @@ -796,7 +796,7 @@ pub async fn linked() -> Result<(), DbErr> { ), ( baker::Model { - id: 1, + id: 2, name: "Baker Bobby".into(), contact_details: serde_json::json!({ "mobile": "+85212345678", From 7a428bb63fcb4435bba70ead03d2318bf3041ea8 Mon Sep 17 00:00:00 2001 From: Yiu Tin Cheung Ivan Date: Thu, 29 Jun 2023 16:03:27 +0800 Subject: [PATCH 3/8] adjusted test cases --- src/query/combine.rs | 4 ++-- src/query/join.rs | 2 +- tests/relational_tests.rs | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/query/combine.rs b/src/query/combine.rs index a96c9e447..7450ba4bd 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -120,8 +120,8 @@ where { pub(crate) fn new(query: SelectStatement) -> Self { Self::new_without_prepare(query) - .prepare_select() - .prepare_order_by() + .prepare_select() + .prepare_order_by() } pub(crate) fn new_without_prepare(query: SelectStatement) -> Self { diff --git a/src/query/join.rs b/src/query/join.rs index 906de45da..6ed56de0c 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -135,7 +135,7 @@ where } slf.query() - .join_as(JoinType::InnerJoin, table_ref, to_tbl, condition); + .join_as(JoinType::LeftJoin, table_ref, to_tbl, condition); } slf = slf.apply_alias(SelectA.as_str()); let mut select_two_many = SelectTwoMany::new_without_prepare(slf.query); diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 61b17eaa4..39f7671d3 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -804,6 +804,11 @@ pub async fn linked() -> Result<(), DbErr> { bakery_id: Some(1), }, vec![ + customer::Model { + id: 2, + name: "Kara".into(), + notes: Some("Loves all cakes".into()), + }, customer::Model { id: 1, name: "Kate".into(), @@ -814,11 +819,6 @@ pub async fn linked() -> Result<(), DbErr> { name: "Kate".into(), notes: Some("Loves cheese cake".into()), }, - customer::Model { - id: 2, - name: "Kara".into(), - notes: Some("Loves all cakes".into()), - }, ] ), ] From 47d91a33749bcfa43b5ea4cf102d0dae2a8d64e4 Mon Sep 17 00:00:00 2001 From: Yiu Tin Cheung Ivan Date: Fri, 30 Jun 2023 19:19:00 +0800 Subject: [PATCH 4/8] WIP for hashmap in consoldate --- src/executor/select.rs | 24 ++++++++++++++++++++++++ tests/relational_tests.rs | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/executor/select.rs b/src/executor/select.rs index 73815f1e2..fee32734e 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -5,6 +5,7 @@ use crate::{ }; use futures::{Stream, TryStreamExt}; use sea_query::SelectStatement; +use std::collections::HashMap; use std::marker::PhantomData; use std::pin::Pin; @@ -997,6 +998,29 @@ where L: EntityTrait, R: EntityTrait, { + + let keys: Vec = rows + .iter() + .map(|row| row.0.to_owned()) + .collect(); + + let col = ::iter().next().unwrap_or_default().into_column(); + + let hashmap: HashMap> = rows.into_iter().fold( + HashMap::>::new(), + |mut acc: HashMap>, + value: (L::Model, Option)| { + { + let key = value.0.get(L::PrimaryKey); + + acc.insert(format!("{key:?}"), value.1); + } + + acc + }, + ); + + let mut acc: Vec<(L::Model, Vec)> = Vec::new(); for (l, r) in rows { if let Some((last_l, last_r)) = acc.last_mut() { diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 39f7671d3..729e04a47 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -774,6 +774,30 @@ pub async fn linked() -> Result<(), DbErr> { .join(" ") ); + assert_eq!( + select_baker_with_customer + .build(sea_orm::DatabaseBackend::Sqlite) + .to_string(), + [ + // FIXME: This might be faulty! + "SELECT \"baker\".\"id\" AS \"A_id\",", + "\"baker\".\"name\" AS \"A_name\",", + "\"baker\".\"contact_details\" AS \"A_contact_details\",", + "\"baker\".\"bakery_id\" AS \"A_bakery_id\",", + "\"r4\".\"id\" AS \"B_id\",", + "\"r4\".\"name\" AS \"B_name\",", + "\"r4\".\"notes\" AS \"B_notes\"", + "FROM \"baker\"", + "LEFT JOIN \"cakes_bakers\" AS \"r0\" ON \"baker\".\"id\" = \"r0\".\"baker_id\"", + "LEFT JOIN \"cake\" AS \"r1\" ON \"r0\".\"cake_id\" = \"r1\".\"id\"", + "LEFT JOIN \"lineitem\" AS \"r2\" ON \"r1\".\"id\" = \"r2\".\"cake_id\"", + "LEFT JOIN \"order\" AS \"r3\" ON \"r2\".\"order_id\" = \"r3\".\"id\"", + "LEFT JOIN \"customer\" AS \"r4\" ON \"r3\".\"customer_id\" = \"r4\".\"id\"", + "ORDER BY \"baker\".\"id\" ASC", + ] + .join(" ") + ); + assert_eq!( select_baker_with_customer.all(&ctx.db).await?, [ From c7d7a882eb8fbfd79eb60e7aa3aa261e60ee62d9 Mon Sep 17 00:00:00 2001 From: Yiu Tin Cheung Ivan Date: Mon, 3 Jul 2023 10:57:49 +0800 Subject: [PATCH 5/8] hashable issue --- src/executor/select.rs | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/executor/select.rs b/src/executor/select.rs index fee32734e..719edb7ae 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -4,7 +4,7 @@ use crate::{ SelectB, SelectTwo, SelectTwoMany, Statement, StreamTrait, TryGetableMany, }; use futures::{Stream, TryStreamExt}; -use sea_query::SelectStatement; +use sea_query::{SelectStatement, Value}; use std::collections::HashMap; use std::marker::PhantomData; use std::pin::Pin; @@ -998,28 +998,26 @@ where L: EntityTrait, R: EntityTrait, { + // #[cfg(feature = "hashable-value")] + { + let pkcol = ::iter() + .next() + .expect("should have primary key") + .into_column(); + + let hashmap: HashMap> = rows.into_iter().fold( + HashMap::>::new(), + |mut acc: HashMap>, value: (L::Model, Option)| { + { + let key = value.0.get(pkcol); + + acc.insert(key, value.1); + } - let keys: Vec = rows - .iter() - .map(|row| row.0.to_owned()) - .collect(); - - let col = ::iter().next().unwrap_or_default().into_column(); - - let hashmap: HashMap> = rows.into_iter().fold( - HashMap::>::new(), - |mut acc: HashMap>, - value: (L::Model, Option)| { - { - let key = value.0.get(L::PrimaryKey); - - acc.insert(format!("{key:?}"), value.1); - } - - acc - }, - ); - + acc + }, + ); + } let mut acc: Vec<(L::Model, Vec)> = Vec::new(); for (l, r) in rows { From 7002e7c057db809be6e44fcb1b98587835885334 Mon Sep 17 00:00:00 2001 From: Yiu Tin Cheung Ivan Date: Mon, 3 Jul 2023 17:28:46 +0800 Subject: [PATCH 6/8] temp_commit --- Cargo.toml | 2 +- src/executor/select.rs | 30 ++++++++++++++++++++++++++---- tests/relational_tests.rs | 24 ------------------------ 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc5dffa02..a10d74ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", rust_decimal = { version = "1", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true } sea-orm-macros = { version = "0.12.0-rc.3", path = "sea-orm-macros", default-features = false, features = ["strum"] } -sea-query = { version = "0.29.0-rc.2", features = ["thread-safe"] } +sea-query = { version = "0.29.0-rc.2", features = ["thread-safe", "hashable-value"] } sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } strum = { version = "0.24", default-features = false } serde = { version = "1.0", default-features = false } diff --git a/src/executor/select.rs b/src/executor/select.rs index 719edb7ae..79cb657be 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -998,9 +998,19 @@ where L: EntityTrait, R: EntityTrait, { - // #[cfg(feature = "hashable-value")] { - let pkcol = ::iter() + + let lpkcol = ::iter() + .next() + .expect("should have primary key") + .into_column(); + + let keys: Vec = rows + .iter() + .map(|row| row.0.get(lpkcol)) + .collect(); + + let rpkcol = ::iter() .next() .expect("should have primary key") .into_column(); @@ -1009,14 +1019,26 @@ where HashMap::>::new(), |mut acc: HashMap>, value: (L::Model, Option)| { { - let key = value.0.get(pkcol); + let key = value.0.get(lpkcol); - acc.insert(key, value.1); + acc.insert(key, Vec::new()); } acc }, ); + + rows.into_iter().for_each(|row| { + let key = + }) + + let result: Vec = keys + .iter() + .map(|key| + hashmap.get(key).cloned() + ) + .collect(); + } let mut acc: Vec<(L::Model, Vec)> = Vec::new(); diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 729e04a47..39f7671d3 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -774,30 +774,6 @@ pub async fn linked() -> Result<(), DbErr> { .join(" ") ); - assert_eq!( - select_baker_with_customer - .build(sea_orm::DatabaseBackend::Sqlite) - .to_string(), - [ - // FIXME: This might be faulty! - "SELECT \"baker\".\"id\" AS \"A_id\",", - "\"baker\".\"name\" AS \"A_name\",", - "\"baker\".\"contact_details\" AS \"A_contact_details\",", - "\"baker\".\"bakery_id\" AS \"A_bakery_id\",", - "\"r4\".\"id\" AS \"B_id\",", - "\"r4\".\"name\" AS \"B_name\",", - "\"r4\".\"notes\" AS \"B_notes\"", - "FROM \"baker\"", - "LEFT JOIN \"cakes_bakers\" AS \"r0\" ON \"baker\".\"id\" = \"r0\".\"baker_id\"", - "LEFT JOIN \"cake\" AS \"r1\" ON \"r0\".\"cake_id\" = \"r1\".\"id\"", - "LEFT JOIN \"lineitem\" AS \"r2\" ON \"r1\".\"id\" = \"r2\".\"cake_id\"", - "LEFT JOIN \"order\" AS \"r3\" ON \"r2\".\"order_id\" = \"r3\".\"id\"", - "LEFT JOIN \"customer\" AS \"r4\" ON \"r3\".\"customer_id\" = \"r4\".\"id\"", - "ORDER BY \"baker\".\"id\" ASC", - ] - .join(" ") - ); - assert_eq!( select_baker_with_customer.all(&ctx.db).await?, [ From 886093c0b2f0913136c43bfb21b1c7a9a88105b8 Mon Sep 17 00:00:00 2001 From: Yiu Tin Cheung Ivan Date: Wed, 5 Jul 2023 19:32:53 +0800 Subject: [PATCH 7/8] completed hashmap implementation --- sea-orm-macros/src/derives/partial_model.rs | 22 +++- src/executor/select.rs | 130 ++++++++++++-------- src/query/combine.rs | 1 - tests/relational_tests.rs | 17 +-- 4 files changed, 104 insertions(+), 66 deletions(-) diff --git a/sea-orm-macros/src/derives/partial_model.rs b/sea-orm-macros/src/derives/partial_model.rs index 567e633ac..79ad9ea69 100644 --- a/sea-orm-macros/src/derives/partial_model.rs +++ b/sea-orm-macros/src/derives/partial_model.rs @@ -43,7 +43,14 @@ impl DerivePartialModel { return Err(Error::NotSupportGeneric(input.generics.params.span())); } - let syn::Data::Struct(syn::DataStruct{fields:syn::Fields::Named(syn::FieldsNamed{named:fields,..}),..},..) = input.data else{ + let syn::Data::Struct( + syn::DataStruct { + fields: syn::Fields::Named(syn::FieldsNamed { named: fields, .. }), + .. + }, + .., + ) = input.data + else { return Err(Error::InputNotStruct); }; @@ -196,11 +203,18 @@ mod util { impl GetAsKVMeta for Meta { fn get_as_kv(&self, k: &str) -> Option { - let Meta::NameValue(MetaNameValue{path, value: syn::Expr::Lit(exprlit), ..}) = self else { - return None; + let Meta::NameValue(MetaNameValue { + path, + value: syn::Expr::Lit(exprlit), + .. + }) = self + else { + return None; }; - let syn::Lit::Str(litstr) = &exprlit.lit else { return None; }; + let syn::Lit::Str(litstr) = &exprlit.lit else { + return None; + }; if path.is_ident(k) { Some(litstr.value()) diff --git a/src/executor/select.rs b/src/executor/select.rs index 79cb657be..a5600f1c7 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -999,73 +999,95 @@ where R: EntityTrait, { { - - let lpkcol = ::iter() + let pkcol = ::iter() .next() .expect("should have primary key") .into_column(); - - let keys: Vec = rows - .iter() - .map(|row| row.0.get(lpkcol)) - .collect(); - let rpkcol = ::iter() - .next() - .expect("should have primary key") - .into_column(); + let keys: Vec = rows.iter().map(|row| row.0.get(pkcol)).collect(); + let mut unique_keys: Vec = Vec::new(); + for key in keys { + let mut existed = false; + for existing_key in unique_keys.clone() { + if key == existing_key { + existed = true; + break; + } + } + if !existed { + unique_keys.push(key) + } + } - let hashmap: HashMap> = rows.into_iter().fold( - HashMap::>::new(), - |mut acc: HashMap>, value: (L::Model, Option)| { - { - let key = value.0.get(lpkcol); + let key_values: Vec<(Value, L::Model, R::Model)> = rows + .iter() + .map(|row| { + ( + row.0.clone().get(pkcol), + row.0.clone(), + row.1.clone().expect("should have linked entity"), + ) + }) + .collect(); - acc.insert(key, Vec::new()); - } + let mut hashmap: HashMap)> = key_values.into_iter().fold( + HashMap::)>::new(), + |mut acc: HashMap)>, + key_value: (Value, L::Model, R::Model)| { + acc.insert(key_value.0, (key_value.1, Vec::new())); acc }, ); rows.into_iter().for_each(|row| { - let key = - }) - - let result: Vec = keys - .iter() - .map(|key| - hashmap.get(key).cloned() - ) + let key = row.0.get(pkcol); + + let vec = &mut hashmap + .get_mut(&key) + .expect("Failed at finding key on hashmap") + .1; + + vec.push(row.1.expect("should have value in row")); + }); + + let result: Vec<(L::Model, Vec)> = unique_keys + .into_iter() + .map(|key: Value| { + hashmap + .get(&key) + .cloned() + .expect("should have key in hashmap") + }) .collect(); - + result } - let mut acc: Vec<(L::Model, Vec)> = Vec::new(); - for (l, r) in rows { - if let Some((last_l, last_r)) = acc.last_mut() { - let mut same_l = true; - for pk_col in ::iter() { - let col = pk_col.into_column(); - let val = l.get(col); - let last_val = last_l.get(col); - if !val.eq(&last_val) { - same_l = false; - break; - } - } - if same_l { - if let Some(r) = r { - last_r.push(r); - continue; - } - } - } - let rows = match r { - Some(r) => vec![r], - None => vec![], - }; - acc.push((l, rows)); - } - acc + // let mut acc: Vec<(L::Model, Vec)> = Vec::new(); + // for (l, r) in rows { + // if let Some((last_l, last_r)) = acc.last_mut() { + // let mut same_l = true; + // for pk_col in ::iter() { + // let col = pk_col.into_column(); + // let val = l.get(col); + // let last_val = last_l.get(col); + // if !val.eq(&last_val) { + // same_l = false; + // break; + // } + // } + // if same_l { + // if let Some(r) = r { + // last_r.push(r); + // continue; + // } + // } + // } + // let rows = match r { + // Some(r) => vec![r], + // None => vec![], + // }; + // acc.push((l, rows)); + // } + // acc } diff --git a/src/query/combine.rs b/src/query/combine.rs index 7450ba4bd..61d957f74 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -129,7 +129,6 @@ where query, entity: PhantomData, } - .prepare_order_by() } fn prepare_select(mut self) -> Self { diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 39f7671d3..6b0c6950b 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -748,7 +748,10 @@ pub async fn linked() -> Result<(), DbErr> { }] ); - let select_baker_with_customer = Baker::find().find_with_linked(baker::BakedForCustomer); + let select_baker_with_customer = Baker::find() + .find_with_linked(baker::BakedForCustomer) + .order_by_asc(baker::Column::Id) + .order_by_asc(Expr::col((Alias::new("r4"), customer::Column::Id))); assert_eq!( select_baker_with_customer @@ -769,7 +772,7 @@ pub async fn linked() -> Result<(), DbErr> { "LEFT JOIN `lineitem` AS `r2` ON `r1`.`id` = `r2`.`cake_id`", "LEFT JOIN `order` AS `r3` ON `r2`.`order_id` = `r3`.`id`", "LEFT JOIN `customer` AS `r4` ON `r3`.`customer_id` = `r4`.`id`", - "ORDER BY `baker`.`id` ASC", + "ORDER BY `baker`.`id` ASC, `r4`.`id` ASC" ] .join(" ") ); @@ -804,11 +807,6 @@ pub async fn linked() -> Result<(), DbErr> { bakery_id: Some(1), }, vec![ - customer::Model { - id: 2, - name: "Kara".into(), - notes: Some("Loves all cakes".into()), - }, customer::Model { id: 1, name: "Kate".into(), @@ -819,6 +817,11 @@ pub async fn linked() -> Result<(), DbErr> { name: "Kate".into(), notes: Some("Loves cheese cake".into()), }, + customer::Model { + id: 2, + name: "Kara".into(), + notes: Some("Loves all cakes".into()), + }, ] ), ] From 3a3a335d1dd49a9d38fdc1edbc728761d81459f0 Mon Sep 17 00:00:00 2001 From: Yiu Tin Cheung Ivan Date: Fri, 7 Jul 2023 15:35:50 +0800 Subject: [PATCH 8/8] optimized consolidate process --- src/executor/select.rs | 119 ++++++++++------------------------------- 1 file changed, 28 insertions(+), 91 deletions(-) diff --git a/src/executor/select.rs b/src/executor/select.rs index a5600f1c7..42c994dcc 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -992,102 +992,39 @@ where } fn consolidate_query_result( - rows: Vec<(L::Model, Option)>, + mut rows: Vec<(L::Model, Option)>, ) -> Vec<(L::Model, Vec)> where L: EntityTrait, R: EntityTrait, { - { - let pkcol = ::iter() - .next() - .expect("should have primary key") - .into_column(); - - let keys: Vec = rows.iter().map(|row| row.0.get(pkcol)).collect(); - let mut unique_keys: Vec = Vec::new(); - for key in keys { - let mut existed = false; - for existing_key in unique_keys.clone() { - if key == existing_key { - existed = true; - break; - } - } - if !existed { - unique_keys.push(key) - } - } - - let key_values: Vec<(Value, L::Model, R::Model)> = rows - .iter() - .map(|row| { - ( - row.0.clone().get(pkcol), - row.0.clone(), - row.1.clone().expect("should have linked entity"), - ) - }) - .collect(); - - let mut hashmap: HashMap)> = key_values.into_iter().fold( - HashMap::)>::new(), - |mut acc: HashMap)>, - key_value: (Value, L::Model, R::Model)| { - acc.insert(key_value.0, (key_value.1, Vec::new())); - - acc - }, - ); - - rows.into_iter().for_each(|row| { + //todo: could take not iter + let pkcol = ::iter() + .next() + .expect("should have primary key") + .into_column(); + + let mut hashmap: HashMap> = rows.iter_mut().fold( + HashMap::>::new(), + |mut acc: HashMap>, row: &mut (L::Model, Option)| { let key = row.0.get(pkcol); + let value = row.1.take().expect("should have a linked entity"); + let vec: Option<&mut Vec> = acc.get_mut(&key); + if let Some(vec) = vec { + vec.push(value) + } else { + acc.insert(key, vec![value]); + } - let vec = &mut hashmap - .get_mut(&key) - .expect("Failed at finding key on hashmap") - .1; - - vec.push(row.1.expect("should have value in row")); - }); - - let result: Vec<(L::Model, Vec)> = unique_keys - .into_iter() - .map(|key: Value| { - hashmap - .get(&key) - .cloned() - .expect("should have key in hashmap") - }) - .collect(); - result - } - - // let mut acc: Vec<(L::Model, Vec)> = Vec::new(); - // for (l, r) in rows { - // if let Some((last_l, last_r)) = acc.last_mut() { - // let mut same_l = true; - // for pk_col in ::iter() { - // let col = pk_col.into_column(); - // let val = l.get(col); - // let last_val = last_l.get(col); - // if !val.eq(&last_val) { - // same_l = false; - // break; - // } - // } - // if same_l { - // if let Some(r) = r { - // last_r.push(r); - // continue; - // } - // } - // } - // let rows = match r { - // Some(r) => vec![r], - // None => vec![], - // }; - // acc.push((l, rows)); - // } - // acc + acc + }, + ); + + rows.into_iter() + .filter_map(|(l_model, _)| { + let l_pk = l_model.get(pkcol); + let r_models = hashmap.remove(&l_pk); + r_models.map(|r_models| (l_model, r_models)) + }) + .collect() }