From ae8a23c38a63492c59951bb32bd1bd298d903909 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Wed, 26 Oct 2022 22:07:52 +0300 Subject: [PATCH 01/18] WIP: towards related queries filtering, ordering++ * Add more functions to common libraries * Enforce stricter traits --- derive/src/filter.rs | 59 +++++----- derive/src/relation.rs | 220 ++++++++++++++++++++++++------------- derive/src/root_query.rs | 66 +++-------- examples/sqlite/src/lib.rs | 1 + src/lib.rs | 128 +++++++++++++++++---- 5 files changed, 303 insertions(+), 171 deletions(-) diff --git a/derive/src/filter.rs b/derive/src/filter.rs index 4dc58db4..4c6d2db2 100644 --- a/derive/src/filter.rs +++ b/derive/src/filter.rs @@ -38,6 +38,12 @@ pub fn filter_fn(item: syn::DataStruct, attrs: SeaOrm) -> Result Result stmt.order_by(Column::#column, sea_orm::query::Order::Asc), seaography::OrderByEnum::Desc => stmt.order_by(Column::#column, sea_orm::query::Order::Desc), @@ -134,13 +140,12 @@ pub fn order_by_fn(fields: &[IdentTypeTuple]) -> Result, order_by_struct: Option) -> sea_orm::Select { - use sea_orm::QueryOrder; + impl seaography::EntityOrderBy for OrderBy { + fn order_by(&self, stmt: sea_orm::Select) -> sea_orm::Select { + use sea_orm::QueryOrder; - if let Some(order_by_struct) = order_by_struct { #(#fields)* - stmt - } else { + stmt } } @@ -156,40 +161,40 @@ pub fn recursive_filter_fn(fields: &[IdentTypeTuple]) -> Result Result) -> sea_orm::Condition { - let mut condition = sea_orm::Condition::all(); + impl seaography::EntityFilter for Filter { + fn filter_condition(&self) -> sea_orm::Condition { + let mut condition = sea_orm::Condition::all(); - if let Some(current_filter) = root_filter { - if let Some(or_filters) = current_filter.or { + if let Some(or_filters) = &self.or { let or_condition = or_filters - .into_iter() + .iter() .fold( sea_orm::Condition::any(), - |fold_condition, filter| fold_condition.add(filter_recursive(Some(*filter))) + |fold_condition, filter| fold_condition.add(filter.filter_condition()) ); condition = condition.add(or_condition); } - if let Some(and_filters) = current_filter.and { + if let Some(and_filters) = &self.and { let and_condition = and_filters - .into_iter() + .iter() .fold( sea_orm::Condition::all(), - |fold_condition, filter| fold_condition.add(filter_recursive(Some(*filter))) + |fold_condition, filter| fold_condition.add(filter.filter_condition()) ); condition = condition.add(and_condition); } #(#columns_filters)* - } - condition + condition + } } }) } diff --git a/derive/src/relation.rs b/derive/src/relation.rs index a72f3d9e..05393a73 100644 --- a/derive/src/relation.rs +++ b/derive/src/relation.rs @@ -211,91 +211,163 @@ pub fn relation_fn( quote! { self } }; - let (return_type, extra_imports, map_method) = if has_many.is_some() { - ( - quote! { Vec<#path::Model> }, - quote! { use seaography::itertools::Itertools; }, - quote! { .into_group_map() }, - ) - } else if belongs_to.is_some() { - (quote! { #path::Model }, quote! {}, quote! { .collect() }) - } else { - return Err(crate::error::Error::Internal( - "Cannot map relation: neither one-many or many-one".into(), - )); - }; - let relation_enum = quote! {Relation::#relation_ident}; let foreign_key_name = format_ident!("{}FK", relation_name).to_token_stream(); - Ok(( - quote! { - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct #foreign_key_name(pub seaography::RelationKeyStruct, Option<#path::OrderBy>>); - - #[async_trait::async_trait] - impl async_graphql::dataloader::Loader<#foreign_key_name> for crate::OrmDataloader { - type Value = #return_type; - type Error = std::sync::Arc; + if has_many.is_some() && belongs_to.is_some() { + Err(crate::error::Error::Internal( + "Cannot map relation: cannot be both one-many and many-one".into(), + )) + } else if has_many.is_some() { + Ok(( + quote! { + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct #foreign_key_name(pub seaography::RelationKeyStruct<#path::Entity>); + + #[async_trait::async_trait] + impl async_graphql::dataloader::Loader<#foreign_key_name> for crate::OrmDataloader { + type Value = Vec<#path::Model>; + type Error = std::sync::Arc; + + async fn load( + &self, + keys: &[#foreign_key_name], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys + .into_iter() + .map(|key| key.0.to_owned()) + .collect(); + + use seaography::itertools::Itertools; + + let data: std::collections::HashMap<#foreign_key_name, Self::Value> = seaography + ::fetch_relation_data::<#path::Entity>( + keys, + #relation_enum.def(), + #reverse, + &self.db, + ).await? + .into_iter() + .map(|(key, model)| (#foreign_key_name(key), model)) + .into_group_map(); + + + Ok(data) + } + } + }, + quote! { + pub async fn #relation_name<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option<#path::Filter>, + order_by: Option<#path::OrderBy>, + ) -> Option> { + use seaography::heck::ToSnakeCase; + use ::std::str::FromStr; - async fn load( + let data_loader = ctx + .data::>() + .unwrap(); + + let from_column: Column = Column::from_str( + #relation_enum + .def() + .#column_type + .to_string() + .to_snake_case() + .as_str() + ).unwrap(); + + let key = #foreign_key_name(seaography::RelationKeyStruct(self.get(from_column), filters, order_by)); + + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + + if let Some(nodes) = option_nodes { + // TODO pagination + Some( + seaography::data_to_connection::<#path::Entity>( + nodes, + false, + false, + Some(1), + Some(1) + ) + ) + } else { + None + } + } + }, + )) + } else if belongs_to.is_some() { + Ok(( + quote! { + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct #foreign_key_name(pub seaography::RelationKeyStruct<#path::Entity>); + + #[async_trait::async_trait] + impl async_graphql::dataloader::Loader<#foreign_key_name> for crate::OrmDataloader { + type Value = #path::Model; + type Error = std::sync::Arc; + + async fn load( + &self, + keys: &[#foreign_key_name], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys + .into_iter() + .map(|key| key.0.to_owned()) + .collect(); + + let data: std::collections::HashMap<#foreign_key_name, Self::Value> = seaography + ::fetch_relation_data::<#path::Entity>( + keys, + #relation_enum.def(), + #reverse, + &self.db, + ).await? + .into_iter() + .map(|(key, model)| (#foreign_key_name(key), model)) + .collect(); + + + Ok(data) + } + } + }, + quote! { + pub async fn #relation_name<'a>( &self, - keys: &[#foreign_key_name], - ) -> Result, Self::Error> { + ctx: &async_graphql::Context<'a>, + ) -> Option<#path::Model> { use seaography::heck::ToSnakeCase; use ::std::str::FromStr; - let keys: Vec<_> = keys - .into_iter() - .map(|key| key.0.to_owned()) - .collect(); + let data_loader = ctx + .data::>() + .unwrap(); - #extra_imports + let from_column: Column = Column::from_str( + #relation_enum + .def() + .#column_type + .to_string() + .to_snake_case() + .as_str() + ).unwrap(); - let data: std::collections::HashMap<#foreign_key_name, Self::Value> = seaography - ::fetch_relation_data::<#path::Entity, #path::Filter, #path::OrderBy>( - keys, - #relation_enum.def(), - #reverse, - &self.db, - ).await? - .into_iter() - .map(|(key, model)| (#foreign_key_name(key), model)) - #map_method; + let key = #foreign_key_name(seaography::RelationKeyStruct(self.get(from_column), None, None)); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); - Ok(data) + data } - } - }, - quote! { - pub async fn #relation_name<'a>( - &self, - ctx: &async_graphql::Context<'a>, - ) -> Option<#return_type> { - use seaography::heck::ToSnakeCase; - use ::std::str::FromStr; - - let data_loader = ctx - .data::>() - .unwrap(); - - // TODO support multiple value keys - let target_column: Column = Column::from_str( - #relation_enum - .def() - .#column_type - .to_string() - .to_snake_case() - .as_str() - ).unwrap(); - - let key = #foreign_key_name(seaography::RelationKeyStruct(self.get(target_column), None, None)); - - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - - data - } - }, - )) + }, + )) + } else { + return Err(crate::error::Error::Internal( + "Cannot map relation: neither one-many or many-one".into(), + )) + } } diff --git a/derive/src/root_query.rs b/derive/src/root_query.rs index af6dbe5f..f4a03a85 100644 --- a/derive/src/root_query.rs +++ b/derive/src/root_query.rs @@ -89,61 +89,25 @@ pub fn basic_query(name: &Ident, path: &TokenStream) -> TokenStream { use sea_orm::prelude::*; use sea_orm::Iterable; use seaography::itertools::Itertools; + use seaography::{EntityOrderBy, EntityFilter}; use async_graphql::types::connection::CursorType; println!("filters: {:?}", filters); let db: &crate::DatabaseConnection = ctx.data::().unwrap(); - let stmt = #path::Entity::find() - .filter(#path::filter_recursive(filters)); - - let stmt = #path::order_by(stmt, order_by); - - fn get_result( - data: Vec<#path::Model>, - has_previous_page: bool, - has_next_page: bool, - pages: Option, - current: Option - ) -> async_graphql::types::connection::Connection< - String, - #path::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields - > { - let edges: Vec> = data - .into_iter() - .map(|node| { - let values: Vec = #path::PrimaryKey::iter() - .map(|variant| { - node.get(variant.into_column()) - }) - .collect(); - - let cursor_string = seaography::CursorValues(values).encode_cursor(); - - async_graphql::types::connection::Edge::new(cursor_string, node) - }) - .collect(); - - let mut result = async_graphql::types::connection::Connection::< - String, - #path::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields - >::with_additional_fields( - has_previous_page, - has_next_page, - seaography::ExtraPaginationFields { - pages, - current - } - ); + let stmt = #path::Entity::find(); - result.edges.extend(edges); + let stmt: sea_orm::Select<#path::Entity> = if let Some(filters) = filters { + stmt.filter(filters.filter_condition()) + } else { + stmt + }; - result - } + let stmt: sea_orm::Select<#path::Entity> = if let Some(order_by) = order_by { + order_by.order_by(stmt) + } else { + stmt + }; if let Some(pagination) = pagination { @@ -161,7 +125,7 @@ pub fn basic_query(name: &Ident, path: &TokenStream) -> TokenStream { .await .unwrap(); - get_result(data, pagination.page != 1, pagination.page < pages, Some(pages), Some(pagination.page)) + seaography::data_to_connection::<#path::Entity>(data, pagination.page != 1, pagination.page < pages, Some(pages), Some(pagination.page)) }, seaography::Pagination::Cursor(cursor) => { let next_stmt = stmt.clone(); @@ -252,13 +216,13 @@ pub fn basic_query(name: &Ident, path: &TokenStream) -> TokenStream { } }; - get_result(data, has_previous_page, has_next_page, None, None) + seaography::data_to_connection::<#path::Entity>(data, has_previous_page, has_next_page, None, None) } } } else { let data: Vec<#path::Model> = stmt.all(db).await.unwrap(); - get_result(data, false, false, Some(1), Some(1)) + seaography::data_to_connection::<#path::Entity>(data, false, false, Some(1), Some(1)) } } } diff --git a/examples/sqlite/src/lib.rs b/examples/sqlite/src/lib.rs index c08fce51..e059ff40 100644 --- a/examples/sqlite/src/lib.rs +++ b/examples/sqlite/src/lib.rs @@ -1,3 +1,4 @@ +#![recursion_limit = "1024"] use sea_orm::prelude::*; pub mod entities; diff --git a/src/lib.rs b/src/lib.rs index ca77d743..6e962699 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,12 +127,8 @@ //! Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. //! //! Seaography is a community driven project. We welcome you to participate, contribute and together build for Rust's future. - -use std::{fmt::Debug, str::FromStr}; - pub use heck; pub use itertools; -use itertools::Itertools; pub use seaography_derive as macros; pub mod type_filter; @@ -352,6 +348,8 @@ impl async_graphql::types::connection::CursorType for CursorValues { } fn encode_cursor(&self) -> String { + use itertools::Itertools; + self.0 .iter() .map(|value| -> String { @@ -451,9 +449,9 @@ impl async_graphql::types::connection::CursorType for CursorValues { } #[derive(Debug, Clone)] -pub struct RelationKeyStruct(pub sea_orm::Value, pub Filter, pub Order); +pub struct RelationKeyStruct(pub sea_orm::Value, pub Option, pub Option); -impl PartialEq for RelationKeyStruct { +impl PartialEq for RelationKeyStruct { fn eq(&self, other: &Self) -> bool { // TODO temporary hack to solve the following problem // let v1 = TestFK(sea_orm::Value::TinyInt(Some(1))); @@ -476,9 +474,9 @@ impl PartialEq for RelationKeyStruct { } } -impl Eq for RelationKeyStruct {} +impl Eq for RelationKeyStruct {} -impl std::hash::Hash for RelationKeyStruct { +impl std::hash::Hash for RelationKeyStruct { fn hash(&self, state: &mut H) { // TODO this is a hack @@ -509,49 +507,74 @@ impl std::hash::Hash for RelationKeyStruct { } } -pub async fn fetch_relation_data( - keys: Vec, Option>>, +pub async fn fetch_relation_data( + keys: Vec>, relation: sea_orm::RelationDef, reverse: bool, db: &sea_orm::DatabaseConnection, ) -> std::result::Result< Vec<( - RelationKeyStruct, Option>, + RelationKeyStruct, ::Model, )>, sea_orm::error::DbErr, > where - Entity: sea_orm::EntityTrait, - ::Err: Debug, + Entity: sea_orm::EntityTrait + EnchantedEntity, + ::Err: core::fmt::Debug, { use heck::ToSnakeCase; use sea_orm::prelude::*; + let filters = if keys.len() > 0 { + keys[0].clone().1 + } else { + None + }; + + let order_by = if keys.len() > 0 { + keys[0].clone().2 + } else { + None + }; + let keys: Vec = keys.into_iter().map(|key| key.0).collect(); // TODO support multiple columns let to_column = if reverse { - ::from_str( + ::from_str( relation.from_col.to_string().to_snake_case().as_str(), ) .unwrap() } else { - ::from_str(relation.to_col.to_string().to_snake_case().as_str()) + ::from_str(relation.to_col.to_string().to_snake_case().as_str()) .unwrap() }; let stmt = ::find(); - let stmt = - as sea_orm::QueryFilter>::filter(stmt, to_column.is_in(keys)); + let filter = sea_orm::Condition::all().add(to_column.is_in(keys)); + + let filter = if let Some(filters) = filters { + filter.add(filters.filter_condition()) + } else { + filter + }; + + let stmt = sea_orm::QueryFilter::filter(stmt, filter); + + let stmt = if let Some(order_by) = order_by { + order_by.order_by(stmt) + } else { + stmt + }; let data = stmt.all(db).await?.into_iter().map( |model: ::Model| -> ( - RelationKeyStruct, Option>, + RelationKeyStruct, ::Model, ) { - let key = RelationKeyStruct::, Option>( + let key = RelationKeyStruct::( model.get(to_column), None, None, @@ -563,3 +586,70 @@ where Ok(data.collect()) } + +pub trait EntityFilter { + fn filter_condition(&self) -> sea_orm::Condition; +} + +pub trait EntityOrderBy +where + Entity: sea_orm::EntityTrait +{ + fn order_by(&self, stmt: sea_orm::Select) -> sea_orm::Select; +} + +pub trait EnchantedEntity { + type Entity: sea_orm::EntityTrait; + + type Filter: EntityFilter + Clone; + + type OrderBy: EntityOrderBy + Clone; +} + +pub fn data_to_connection( + data: Vec, + has_previous_page: bool, + has_next_page: bool, + pages: Option, + current: Option, +) -> async_graphql::types::connection::Connection +where + T: sea_orm::EntityTrait, + ::Model: async_graphql::OutputType, +{ + use sea_orm::{Iterable, PrimaryKeyToColumn, ModelTrait}; + use async_graphql::connection::CursorType; + + let edges: Vec> = data + .into_iter() + .map(|node| { + let values: Vec = T::PrimaryKey::iter() + .map(|variant| { + node.get(variant.into_column()) + }) + .collect(); + + let cursor_string = CursorValues(values).encode_cursor(); + + async_graphql::types::connection::Edge::new(cursor_string, node) + }) + .collect(); + + let mut result = async_graphql::types::connection::Connection::< + String, + T::Model, + ExtraPaginationFields, + async_graphql::types::connection::EmptyFields + >::with_additional_fields( + has_previous_page, + has_next_page, + ExtraPaginationFields { + pages, + current + } + ); + + result.edges.extend(edges); + + result +} \ No newline at end of file From 180bc2bd8f60f9389ace07216a4b91629ab9ae73 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Mon, 31 Oct 2022 02:00:29 +0200 Subject: [PATCH 02/18] Fix warnings on relation derive --- derive/src/relation.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/derive/src/relation.rs b/derive/src/relation.rs index 05393a73..eca89e78 100644 --- a/derive/src/relation.rs +++ b/derive/src/relation.rs @@ -1,4 +1,4 @@ -use heck::ToUpperCamelCase; +use heck::{ToUpperCamelCase, ToSnakeCase}; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; @@ -215,11 +215,13 @@ pub fn relation_fn( let foreign_key_name = format_ident!("{}FK", relation_name).to_token_stream(); if has_many.is_some() && belongs_to.is_some() { - Err(crate::error::Error::Internal( + return Err(crate::error::Error::Internal( "Cannot map relation: cannot be both one-many and many-one".into(), )) - } else if has_many.is_some() { - Ok(( + } + + let (global_scope, object_scope) = if has_many.is_some() { + ( quote! { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct #foreign_key_name(pub seaography::RelationKeyStruct<#path::Entity>); @@ -299,9 +301,9 @@ pub fn relation_fn( } } }, - )) + ) } else if belongs_to.is_some() { - Ok(( + ( quote! { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct #foreign_key_name(pub seaography::RelationKeyStruct<#path::Entity>); @@ -364,10 +366,12 @@ pub fn relation_fn( data } }, - )) + ) } else { return Err(crate::error::Error::Internal( "Cannot map relation: neither one-many or many-one".into(), )) - } + }; + + Ok((global_scope, object_scope)) } From 1268acc5643d3e1cbd95188086ade58bfa73f8d8 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Mon, 31 Oct 2022 02:01:20 +0200 Subject: [PATCH 03/18] WIP found where recursion bug occurs --- examples/sqlite/src/entities/actor.rs | 83 ++++- examples/sqlite/src/entities/address.rs | 265 +++++++++++++- examples/sqlite/src/entities/category.rs | 85 ++++- examples/sqlite/src/entities/city.rs | 6 +- examples/sqlite/src/entities/country.rs | 6 +- examples/sqlite/src/entities/customer.rs | 250 +++++++++++++- examples/sqlite/src/entities/film.rs | 325 +++++++++++++++++- examples/sqlite/src/entities/film_actor.rs | 104 +++++- examples/sqlite/src/entities/film_category.rs | 6 +- examples/sqlite/src/entities/film_text.rs | 2 +- examples/sqlite/src/entities/inventory.rs | 177 +++++++++- examples/sqlite/src/entities/language.rs | 2 +- examples/sqlite/src/entities/payment.rs | 6 +- examples/sqlite/src/entities/rental.rs | 226 +++++++++++- examples/sqlite/src/entities/staff.rs | 249 +++++++++++++- examples/sqlite/src/entities/store.rs | 6 +- 16 files changed, 1777 insertions(+), 21 deletions(-) diff --git a/examples/sqlite/src/entities/actor.rs b/examples/sqlite/src/entities/actor.rs index f4844a2d..03d1d6d1 100644 --- a/examples/sqlite/src/entities/actor.rs +++ b/examples/sqlite/src/entities/actor.rs @@ -19,7 +19,14 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive( + Copy, + Clone, + Debug, + EnumIter, + DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm(has_many = "super::film_actor::Entity")] FilmActor, @@ -32,3 +39,77 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FilmActorFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[FilmActorFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::FilmActor.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (FilmActorFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn film_actor<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::film_actor::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::FilmActor + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = FilmActorFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } +} \ No newline at end of file diff --git a/examples/sqlite/src/entities/address.rs b/examples/sqlite/src/entities/address.rs index e41a877a..4e5422b5 100644 --- a/examples/sqlite/src/entities/address.rs +++ b/examples/sqlite/src/entities/address.rs @@ -23,7 +23,9 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::city::Entity", @@ -66,3 +68,264 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CityFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::city::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[CityFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::City.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (CityFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CustomerFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[CustomerFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Customer.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (CustomerFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StaffFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[StaffFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Staff.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (StaffFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StoreFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[StoreFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Store.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (StoreFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn city<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::City + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = CityFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn customer<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::customer::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Customer + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = CustomerFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } + pub async fn staff<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::staff::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Staff + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = StaffFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } + pub async fn store<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::store::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Store + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = StoreFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } +} diff --git a/examples/sqlite/src/entities/category.rs b/examples/sqlite/src/entities/category.rs index 66ee88d3..6b3e4db2 100644 --- a/examples/sqlite/src/entities/category.rs +++ b/examples/sqlite/src/entities/category.rs @@ -18,7 +18,14 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive( + Copy, + Clone, + Debug, + EnumIter, + DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm(has_many = "super::film_category::Entity")] FilmCategory, @@ -31,3 +38,79 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FilmCategoryFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[FilmCategoryFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::FilmCategory.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (FilmCategoryFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn film_category<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::film_category::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::FilmCategory + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = FilmCategoryFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some( + seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + ), + ) + } else { + None + } + } +} diff --git a/examples/sqlite/src/entities/city.rs b/examples/sqlite/src/entities/city.rs index 15691770..89864f73 100644 --- a/examples/sqlite/src/entities/city.rs +++ b/examples/sqlite/src/entities/city.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "city")] -#[graphql(complex)] +// #[graphql(complex)] #[graphql(name = "City")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -19,7 +19,9 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation +// , seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::country::Entity", diff --git a/examples/sqlite/src/entities/country.rs b/examples/sqlite/src/entities/country.rs index 8f2d16ba..9d4502c7 100644 --- a/examples/sqlite/src/entities/country.rs +++ b/examples/sqlite/src/entities/country.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "country")] -#[graphql(complex)] +// #[graphql(complex)] #[graphql(name = "Country")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -18,7 +18,9 @@ pub struct Model { pub last_update: Option, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation +// , seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm(has_many = "super::city::Entity")] City, diff --git a/examples/sqlite/src/entities/customer.rs b/examples/sqlite/src/entities/customer.rs index 2294dbc8..5ff8e865 100644 --- a/examples/sqlite/src/entities/customer.rs +++ b/examples/sqlite/src/entities/customer.rs @@ -24,7 +24,14 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive( + Copy, + Clone, + Debug, + EnumIter, + DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::address::Entity", @@ -73,3 +80,244 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AddressFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::address::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[AddressFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Address.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (AddressFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StoreFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::store::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[StoreFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Store.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (StoreFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PaymentFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[PaymentFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Payment.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (PaymentFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RentalFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[RentalFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Rental.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (RentalFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn address<'a>( + &self, + ctx: &async_graphql::Context<'a>, + ) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Address + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = AddressFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn store<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Store + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = StoreFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn payment<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::payment::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Payment + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = PaymentFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } + pub async fn rental<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::rental::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Rental + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = RentalFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } +} diff --git a/examples/sqlite/src/entities/film.rs b/examples/sqlite/src/entities/film.rs index 0703949a..93e2a35c 100644 --- a/examples/sqlite/src/entities/film.rs +++ b/examples/sqlite/src/entities/film.rs @@ -30,7 +30,14 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive( + Copy, + Clone, + Debug, + EnumIter, + DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::language::Entity", @@ -75,3 +82,319 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Language2FK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::language::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[Language2FK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Language2.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (Language2FK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Language1FK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::language::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[Language1FK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Language1.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (Language1FK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FilmActorFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[FilmActorFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::FilmActor.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (FilmActorFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FilmCategoryFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[FilmCategoryFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::FilmCategory.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (FilmCategoryFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct InventoryFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[InventoryFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Inventory.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (InventoryFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn language2<'a>( + &self, + ctx: &async_graphql::Context<'a>, + ) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Language2 + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = Language2FK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn language1<'a>( + &self, + ctx: &async_graphql::Context<'a>, + ) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Language1 + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = Language1FK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn film_actor<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::film_actor::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::FilmActor + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = FilmActorFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } + pub async fn film_category<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::film_category::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::FilmCategory + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = FilmCategoryFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some( + seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + ), + ) + } else { + None + } + } + pub async fn inventory<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::inventory::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Inventory + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = InventoryFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } +} diff --git a/examples/sqlite/src/entities/film_actor.rs b/examples/sqlite/src/entities/film_actor.rs index 429587c6..571e43d1 100644 --- a/examples/sqlite/src/entities/film_actor.rs +++ b/examples/sqlite/src/entities/film_actor.rs @@ -19,7 +19,9 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::film::Entity", @@ -52,3 +54,103 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + + + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FilmFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::film::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[FilmFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Film.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (FilmFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ActorFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::actor::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[ActorFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Actor.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (ActorFK(key), model)) + .collect(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn film<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Film + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = FilmFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn actor<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Actor + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = ActorFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } +} \ No newline at end of file diff --git a/examples/sqlite/src/entities/film_category.rs b/examples/sqlite/src/entities/film_category.rs index b61e4933..463c44c8 100644 --- a/examples/sqlite/src/entities/film_category.rs +++ b/examples/sqlite/src/entities/film_category.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "film_category")] -#[graphql(complex)] +// #[graphql(complex)] #[graphql(name = "FilmCategory")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -19,7 +19,9 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::category::Entity", diff --git a/examples/sqlite/src/entities/film_text.rs b/examples/sqlite/src/entities/film_text.rs index 2f6bea1b..a200b260 100644 --- a/examples/sqlite/src/entities/film_text.rs +++ b/examples/sqlite/src/entities/film_text.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "film_text")] -#[graphql(complex)] +// #[graphql(complex)] #[graphql(name = "FilmText")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] diff --git a/examples/sqlite/src/entities/inventory.rs b/examples/sqlite/src/entities/inventory.rs index 265983e1..a68b0fe7 100644 --- a/examples/sqlite/src/entities/inventory.rs +++ b/examples/sqlite/src/entities/inventory.rs @@ -19,7 +19,14 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive( + Copy, + Clone, + Debug, + EnumIter, + DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::film::Entity", @@ -60,3 +67,171 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FilmFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::film::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[FilmFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Film.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (FilmFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StoreFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::store::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[StoreFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Store.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (StoreFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RentalFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[RentalFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Rental.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (RentalFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn film<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Film + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = FilmFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn store<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Store + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = StoreFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn rental<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::rental::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Rental + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = RentalFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } +} diff --git a/examples/sqlite/src/entities/language.rs b/examples/sqlite/src/entities/language.rs index 094fd81a..1e88f545 100644 --- a/examples/sqlite/src/entities/language.rs +++ b/examples/sqlite/src/entities/language.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "language")] -#[graphql(complex)] +// #[graphql(complex)] #[graphql(name = "Language")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] diff --git a/examples/sqlite/src/entities/payment.rs b/examples/sqlite/src/entities/payment.rs index e1b6949e..88b432c2 100644 --- a/examples/sqlite/src/entities/payment.rs +++ b/examples/sqlite/src/entities/payment.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "payment")] -#[graphql(complex)] +// #[graphql(complex)] #[graphql(name = "Payment")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -23,7 +23,9 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation +// , seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::staff::Entity", diff --git a/examples/sqlite/src/entities/rental.rs b/examples/sqlite/src/entities/rental.rs index 7917cd37..c10e1b0f 100644 --- a/examples/sqlite/src/entities/rental.rs +++ b/examples/sqlite/src/entities/rental.rs @@ -22,7 +22,9 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::customer::Entity", @@ -77,3 +79,225 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CustomerFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::customer::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[CustomerFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Customer.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (CustomerFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct InventoryFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::inventory::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[InventoryFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Inventory.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (InventoryFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StaffFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::staff::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[StaffFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Staff.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (StaffFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PaymentFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[PaymentFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Payment.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (PaymentFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn customer<'a>( + &self, + ctx: &async_graphql::Context<'a>, + ) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Customer + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = CustomerFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn inventory<'a>( + &self, + ctx: &async_graphql::Context<'a>, + ) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Inventory + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = InventoryFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn staff<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Staff + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = StaffFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn payment<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::payment::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Payment + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = PaymentFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } +} \ No newline at end of file diff --git a/examples/sqlite/src/entities/staff.rs b/examples/sqlite/src/entities/staff.rs index 80046439..4481ad7e 100644 --- a/examples/sqlite/src/entities/staff.rs +++ b/examples/sqlite/src/entities/staff.rs @@ -27,7 +27,13 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive( + Copy, + Clone, + Debug, + EnumIter, + DeriveRelation, // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::address::Entity", @@ -84,3 +90,244 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AddressFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::address::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[AddressFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Address.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (AddressFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StoreFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = super::store::Model; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[StoreFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Store.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (StoreFK(key), model)) + .collect(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PaymentFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[PaymentFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Payment.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (PaymentFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RentalFK(pub seaography::RelationKeyStruct); +#[async_trait::async_trait] +impl async_graphql::dataloader::Loader for crate::OrmDataloader { + type Value = Vec; + type Error = std::sync::Arc; + async fn load( + &self, + keys: &[RentalFK], + ) -> Result, Self::Error> { + let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); + use seaography::itertools::Itertools; + let data: std::collections::HashMap = + seaography::fetch_relation_data::( + keys, + Relation::Rental.def(), + &self.db, + ) + .await? + .into_iter() + .map(|(key, model)| (RentalFK(key), model)) + .into_group_map(); + Ok(data) + } +} +#[async_graphql::ComplexObject] +impl Model { + pub async fn address<'a>( + &self, + ctx: &async_graphql::Context<'a>, + ) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Address + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = AddressFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn store<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Store + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = StoreFK(seaography::RelationKeyStruct( + self.get(from_column), + None, + None, + )); + let data: Option<_> = data_loader.load_one(key).await.unwrap(); + data + } + pub async fn payment<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::payment::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + > + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Payment + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = PaymentFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } + pub async fn rental<'a>( + &self, + ctx: &async_graphql::Context<'a>, + filters: Option, + order_by: Option, + ) -> Option< + async_graphql::types::connection::Connection< + String, + super::rental::Model, + seaography::ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, + >, + > { + use ::std::str::FromStr; + use seaography::heck::ToSnakeCase; + let data_loader = ctx + .data::>() + .unwrap(); + let from_column: Column = Column::from_str( + Relation::Rental + .def() + .from_col + .to_string() + .to_snake_case() + .as_str(), + ) + .unwrap(); + let key = RentalFK(seaography::RelationKeyStruct( + self.get(from_column), + filters, + order_by, + )); + let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); + if let Some(nodes) = option_nodes { + Some(seaography::data_to_connection::( + nodes, + false, + false, + Some(1), + Some(1), + )) + } else { + None + } + } +} diff --git a/examples/sqlite/src/entities/store.rs b/examples/sqlite/src/entities/store.rs index 01a92f99..41811e4f 100644 --- a/examples/sqlite/src/entities/store.rs +++ b/examples/sqlite/src/entities/store.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "store")] -#[graphql(complex)] +// #[graphql(complex)] #[graphql(name = "Store")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -19,7 +19,9 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, + // seaography::macros::RelationsCompact +)] pub enum Relation { #[sea_orm( belongs_to = "super::address::Entity", From eb951beb2f55da0d11bea3aa8b8b997f6b002228 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Mon, 7 Nov 2022 11:10:04 +0200 Subject: [PATCH 04/18] Fix sqlite example --- examples/sqlite/src/entities/actor.rs | 83 +---- examples/sqlite/src/entities/address.rs | 265 +------------- examples/sqlite/src/entities/category.rs | 85 +---- examples/sqlite/src/entities/city.rs | 6 +- examples/sqlite/src/entities/country.rs | 6 +- examples/sqlite/src/entities/customer.rs | 250 +------------- examples/sqlite/src/entities/film.rs | 325 +----------------- examples/sqlite/src/entities/film_actor.rs | 104 +----- examples/sqlite/src/entities/film_category.rs | 6 +- examples/sqlite/src/entities/film_text.rs | 2 +- examples/sqlite/src/entities/inventory.rs | 177 +--------- examples/sqlite/src/entities/language.rs | 2 +- examples/sqlite/src/entities/payment.rs | 6 +- examples/sqlite/src/entities/rental.rs | 226 +----------- examples/sqlite/src/entities/staff.rs | 249 +------------- examples/sqlite/src/entities/store.rs | 6 +- 16 files changed, 21 insertions(+), 1777 deletions(-) diff --git a/examples/sqlite/src/entities/actor.rs b/examples/sqlite/src/entities/actor.rs index 03d1d6d1..f4844a2d 100644 --- a/examples/sqlite/src/entities/actor.rs +++ b/examples/sqlite/src/entities/actor.rs @@ -19,14 +19,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive( - Copy, - Clone, - Debug, - EnumIter, - DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm(has_many = "super::film_actor::Entity")] FilmActor, @@ -39,77 +32,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FilmActorFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[FilmActorFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::FilmActor.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (FilmActorFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn film_actor<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::film_actor::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::FilmActor - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = FilmActorFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } -} \ No newline at end of file diff --git a/examples/sqlite/src/entities/address.rs b/examples/sqlite/src/entities/address.rs index 4e5422b5..e41a877a 100644 --- a/examples/sqlite/src/entities/address.rs +++ b/examples/sqlite/src/entities/address.rs @@ -23,9 +23,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::city::Entity", @@ -68,264 +66,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CityFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::city::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[CityFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::City.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (CityFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CustomerFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[CustomerFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Customer.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (CustomerFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StaffFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[StaffFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Staff.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (StaffFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StoreFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[StoreFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Store.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (StoreFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn city<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::City - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = CityFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn customer<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::customer::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Customer - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = CustomerFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } - pub async fn staff<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::staff::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Staff - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = StaffFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } - pub async fn store<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::store::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Store - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = StoreFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } -} diff --git a/examples/sqlite/src/entities/category.rs b/examples/sqlite/src/entities/category.rs index 6b3e4db2..66ee88d3 100644 --- a/examples/sqlite/src/entities/category.rs +++ b/examples/sqlite/src/entities/category.rs @@ -18,14 +18,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive( - Copy, - Clone, - Debug, - EnumIter, - DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm(has_many = "super::film_category::Entity")] FilmCategory, @@ -38,79 +31,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FilmCategoryFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[FilmCategoryFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::FilmCategory.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (FilmCategoryFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn film_category<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::film_category::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::FilmCategory - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = FilmCategoryFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some( - seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - ), - ) - } else { - None - } - } -} diff --git a/examples/sqlite/src/entities/city.rs b/examples/sqlite/src/entities/city.rs index 89864f73..15691770 100644 --- a/examples/sqlite/src/entities/city.rs +++ b/examples/sqlite/src/entities/city.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "city")] -// #[graphql(complex)] +#[graphql(complex)] #[graphql(name = "City")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -19,9 +19,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation -// , seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::country::Entity", diff --git a/examples/sqlite/src/entities/country.rs b/examples/sqlite/src/entities/country.rs index 9d4502c7..8f2d16ba 100644 --- a/examples/sqlite/src/entities/country.rs +++ b/examples/sqlite/src/entities/country.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "country")] -// #[graphql(complex)] +#[graphql(complex)] #[graphql(name = "Country")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -18,9 +18,7 @@ pub struct Model { pub last_update: Option, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation -// , seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm(has_many = "super::city::Entity")] City, diff --git a/examples/sqlite/src/entities/customer.rs b/examples/sqlite/src/entities/customer.rs index 5ff8e865..2294dbc8 100644 --- a/examples/sqlite/src/entities/customer.rs +++ b/examples/sqlite/src/entities/customer.rs @@ -24,14 +24,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive( - Copy, - Clone, - Debug, - EnumIter, - DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::address::Entity", @@ -80,244 +73,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct AddressFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::address::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[AddressFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Address.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (AddressFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StoreFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::store::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[StoreFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Store.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (StoreFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct PaymentFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[PaymentFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Payment.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (PaymentFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct RentalFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[RentalFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Rental.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (RentalFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn address<'a>( - &self, - ctx: &async_graphql::Context<'a>, - ) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Address - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = AddressFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn store<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Store - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = StoreFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn payment<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::payment::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Payment - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = PaymentFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } - pub async fn rental<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::rental::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Rental - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = RentalFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } -} diff --git a/examples/sqlite/src/entities/film.rs b/examples/sqlite/src/entities/film.rs index 93e2a35c..0703949a 100644 --- a/examples/sqlite/src/entities/film.rs +++ b/examples/sqlite/src/entities/film.rs @@ -30,14 +30,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive( - Copy, - Clone, - Debug, - EnumIter, - DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::language::Entity", @@ -82,319 +75,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Language2FK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::language::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[Language2FK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Language2.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (Language2FK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Language1FK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::language::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[Language1FK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Language1.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (Language1FK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FilmActorFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[FilmActorFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::FilmActor.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (FilmActorFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FilmCategoryFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[FilmCategoryFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::FilmCategory.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (FilmCategoryFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct InventoryFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[InventoryFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Inventory.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (InventoryFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn language2<'a>( - &self, - ctx: &async_graphql::Context<'a>, - ) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Language2 - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = Language2FK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn language1<'a>( - &self, - ctx: &async_graphql::Context<'a>, - ) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Language1 - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = Language1FK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn film_actor<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::film_actor::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::FilmActor - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = FilmActorFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } - pub async fn film_category<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::film_category::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::FilmCategory - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = FilmCategoryFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some( - seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - ), - ) - } else { - None - } - } - pub async fn inventory<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::inventory::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Inventory - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = InventoryFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } -} diff --git a/examples/sqlite/src/entities/film_actor.rs b/examples/sqlite/src/entities/film_actor.rs index 571e43d1..429587c6 100644 --- a/examples/sqlite/src/entities/film_actor.rs +++ b/examples/sqlite/src/entities/film_actor.rs @@ -19,9 +19,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::film::Entity", @@ -54,103 +52,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - - - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FilmFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::film::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[FilmFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Film.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (FilmFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ActorFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::actor::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[ActorFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Actor.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (ActorFK(key), model)) - .collect(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn film<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Film - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = FilmFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn actor<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Actor - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = ActorFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } -} \ No newline at end of file diff --git a/examples/sqlite/src/entities/film_category.rs b/examples/sqlite/src/entities/film_category.rs index 463c44c8..b61e4933 100644 --- a/examples/sqlite/src/entities/film_category.rs +++ b/examples/sqlite/src/entities/film_category.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "film_category")] -// #[graphql(complex)] +#[graphql(complex)] #[graphql(name = "FilmCategory")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -19,9 +19,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::category::Entity", diff --git a/examples/sqlite/src/entities/film_text.rs b/examples/sqlite/src/entities/film_text.rs index a200b260..2f6bea1b 100644 --- a/examples/sqlite/src/entities/film_text.rs +++ b/examples/sqlite/src/entities/film_text.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "film_text")] -// #[graphql(complex)] +#[graphql(complex)] #[graphql(name = "FilmText")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] diff --git a/examples/sqlite/src/entities/inventory.rs b/examples/sqlite/src/entities/inventory.rs index a68b0fe7..265983e1 100644 --- a/examples/sqlite/src/entities/inventory.rs +++ b/examples/sqlite/src/entities/inventory.rs @@ -19,14 +19,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive( - Copy, - Clone, - Debug, - EnumIter, - DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::film::Entity", @@ -67,171 +60,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FilmFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::film::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[FilmFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Film.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (FilmFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StoreFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::store::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[StoreFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Store.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (StoreFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct RentalFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[RentalFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Rental.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (RentalFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn film<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Film - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = FilmFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn store<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Store - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = StoreFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn rental<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::rental::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Rental - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = RentalFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } -} diff --git a/examples/sqlite/src/entities/language.rs b/examples/sqlite/src/entities/language.rs index 1e88f545..094fd81a 100644 --- a/examples/sqlite/src/entities/language.rs +++ b/examples/sqlite/src/entities/language.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "language")] -// #[graphql(complex)] +#[graphql(complex)] #[graphql(name = "Language")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] diff --git a/examples/sqlite/src/entities/payment.rs b/examples/sqlite/src/entities/payment.rs index 88b432c2..e1b6949e 100644 --- a/examples/sqlite/src/entities/payment.rs +++ b/examples/sqlite/src/entities/payment.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "payment")] -// #[graphql(complex)] +#[graphql(complex)] #[graphql(name = "Payment")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -23,9 +23,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation -// , seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::staff::Entity", diff --git a/examples/sqlite/src/entities/rental.rs b/examples/sqlite/src/entities/rental.rs index c10e1b0f..7917cd37 100644 --- a/examples/sqlite/src/entities/rental.rs +++ b/examples/sqlite/src/entities/rental.rs @@ -22,9 +22,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::customer::Entity", @@ -79,225 +77,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CustomerFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::customer::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[CustomerFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Customer.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (CustomerFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct InventoryFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::inventory::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[InventoryFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Inventory.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (InventoryFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StaffFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::staff::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[StaffFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Staff.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (StaffFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct PaymentFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[PaymentFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Payment.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (PaymentFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn customer<'a>( - &self, - ctx: &async_graphql::Context<'a>, - ) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Customer - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = CustomerFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn inventory<'a>( - &self, - ctx: &async_graphql::Context<'a>, - ) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Inventory - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = InventoryFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn staff<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Staff - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = StaffFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn payment<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::payment::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Payment - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = PaymentFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } -} \ No newline at end of file diff --git a/examples/sqlite/src/entities/staff.rs b/examples/sqlite/src/entities/staff.rs index 4481ad7e..80046439 100644 --- a/examples/sqlite/src/entities/staff.rs +++ b/examples/sqlite/src/entities/staff.rs @@ -27,13 +27,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive( - Copy, - Clone, - Debug, - EnumIter, - DeriveRelation, // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::address::Entity", @@ -90,244 +84,3 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct AddressFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::address::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[AddressFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Address.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (AddressFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StoreFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = super::store::Model; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[StoreFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Store.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (StoreFK(key), model)) - .collect(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct PaymentFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[PaymentFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Payment.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (PaymentFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct RentalFK(pub seaography::RelationKeyStruct); -#[async_trait::async_trait] -impl async_graphql::dataloader::Loader for crate::OrmDataloader { - type Value = Vec; - type Error = std::sync::Arc; - async fn load( - &self, - keys: &[RentalFK], - ) -> Result, Self::Error> { - let keys: Vec<_> = keys.into_iter().map(|key| key.0.to_owned()).collect(); - use seaography::itertools::Itertools; - let data: std::collections::HashMap = - seaography::fetch_relation_data::( - keys, - Relation::Rental.def(), - &self.db, - ) - .await? - .into_iter() - .map(|(key, model)| (RentalFK(key), model)) - .into_group_map(); - Ok(data) - } -} -#[async_graphql::ComplexObject] -impl Model { - pub async fn address<'a>( - &self, - ctx: &async_graphql::Context<'a>, - ) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Address - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = AddressFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn store<'a>(&self, ctx: &async_graphql::Context<'a>) -> Option { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Store - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = StoreFK(seaography::RelationKeyStruct( - self.get(from_column), - None, - None, - )); - let data: Option<_> = data_loader.load_one(key).await.unwrap(); - data - } - pub async fn payment<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::payment::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - > - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Payment - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = PaymentFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } - pub async fn rental<'a>( - &self, - ctx: &async_graphql::Context<'a>, - filters: Option, - order_by: Option, - ) -> Option< - async_graphql::types::connection::Connection< - String, - super::rental::Model, - seaography::ExtraPaginationFields, - async_graphql::types::connection::EmptyFields, - >, - > { - use ::std::str::FromStr; - use seaography::heck::ToSnakeCase; - let data_loader = ctx - .data::>() - .unwrap(); - let from_column: Column = Column::from_str( - Relation::Rental - .def() - .from_col - .to_string() - .to_snake_case() - .as_str(), - ) - .unwrap(); - let key = RentalFK(seaography::RelationKeyStruct( - self.get(from_column), - filters, - order_by, - )); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - if let Some(nodes) = option_nodes { - Some(seaography::data_to_connection::( - nodes, - false, - false, - Some(1), - Some(1), - )) - } else { - None - } - } -} diff --git a/examples/sqlite/src/entities/store.rs b/examples/sqlite/src/entities/store.rs index 41811e4f..01a92f99 100644 --- a/examples/sqlite/src/entities/store.rs +++ b/examples/sqlite/src/entities/store.rs @@ -9,7 +9,7 @@ use sea_orm::entity::prelude::*; seaography::macros::Filter, )] #[sea_orm(table_name = "store")] -// #[graphql(complex)] +#[graphql(complex)] #[graphql(name = "Store")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -19,9 +19,7 @@ pub struct Model { pub last_update: DateTimeUtc, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, - // seaography::macros::RelationsCompact -)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, seaography::macros::RelationsCompact)] pub enum Relation { #[sea_orm( belongs_to = "super::address::Entity", From b760d8f164e768325aa2fc9fc2863d9de263b358 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Mon, 7 Nov 2022 11:13:53 +0200 Subject: [PATCH 05/18] Fix related query pagination and recursion --- derive/src/relation.rs | 67 +++++++++++++++++++++++++++++++---------- generator/src/writer.rs | 2 +- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/derive/src/relation.rs b/derive/src/relation.rs index eca89e78..1147e99b 100644 --- a/derive/src/relation.rs +++ b/derive/src/relation.rs @@ -263,8 +263,9 @@ pub fn relation_fn( &self, ctx: &async_graphql::Context<'a>, filters: Option<#path::Filter>, + pagination: Option, order_by: Option<#path::OrderBy>, - ) -> Option> { + ) -> async_graphql::types::connection::Connection { use seaography::heck::ToSnakeCase; use ::std::str::FromStr; @@ -283,22 +284,56 @@ pub fn relation_fn( let key = #foreign_key_name(seaography::RelationKeyStruct(self.get(from_column), filters, order_by)); - let option_nodes: Option<_> = data_loader.load_one(key).await.unwrap(); - - if let Some(nodes) = option_nodes { - // TODO pagination - Some( - seaography::data_to_connection::<#path::Entity>( - nodes, - false, - false, - Some(1), - Some(1) - ) - ) - } else { - None + let nodes: Vec<#path::Model> = data_loader + .load_one(key) + .await + .unwrap() + .unwrap(); + + if let Some(pagination) = pagination { + return match pagination { + seaography::Pagination::Pages(pagination) => { + let nodes_size = nodes.len(); + + let nodes = nodes + .into_iter() + .skip(pagination.page * pagination.limit) + .take(pagination.limit) + .collect(); + + let has_previous_page = pagination.page * pagination.limit > 0 && nodes_size != 0; + let has_next_page = ((nodes_size / pagination.limit) as i64) - (pagination.page as i64) - 1 > 0; + let pages: usize = nodes_size / pagination.limit; + let current = pagination.page; + + seaography::data_to_connection::<#path::Entity>( + nodes, + has_previous_page, + has_next_page, + Some(pages), + Some(current) + ) + }, + seaography::Pagination::Cursor(cursor) => { + // TODO fix cursor related query pagination + seaography::data_to_connection::<#path::Entity>( + nodes, + false, + false, + Some(1), + Some(1) + ) + } + } } + + seaography::data_to_connection::<#path::Entity>( + nodes, + false, + false, + Some(1), + Some(1) + ) } }, ) diff --git a/generator/src/writer.rs b/generator/src/writer.rs index ad6f6487..7bd75917 100644 --- a/generator/src/writer.rs +++ b/generator/src/writer.rs @@ -92,7 +92,7 @@ pub fn write_lib>(path: &P) -> std::io::Result<()> { let file_name = path.as_ref().join("lib.rs"); - std::fs::write(file_name, add_line_break(tokens))?; + std::fs::write(file_name, format!("#![recursion_limit = \"1024\"]\n{}", add_line_break(tokens)))?; Ok(()) } From e315c538ef6a17766dda00b0f563c7b3d5cdfad7 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Mon, 7 Nov 2022 11:34:56 +0200 Subject: [PATCH 06/18] Format --- derive/src/relation.rs | 6 +++--- generator/src/writer.rs | 5 ++++- src/lib.rs | 33 +++++++++++++++++++-------------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/derive/src/relation.rs b/derive/src/relation.rs index 1147e99b..b3b9307b 100644 --- a/derive/src/relation.rs +++ b/derive/src/relation.rs @@ -1,4 +1,4 @@ -use heck::{ToUpperCamelCase, ToSnakeCase}; +use heck::{ToSnakeCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; @@ -217,7 +217,7 @@ pub fn relation_fn( if has_many.is_some() && belongs_to.is_some() { return Err(crate::error::Error::Internal( "Cannot map relation: cannot be both one-many and many-one".into(), - )) + )); } let (global_scope, object_scope) = if has_many.is_some() { @@ -405,7 +405,7 @@ pub fn relation_fn( } else { return Err(crate::error::Error::Internal( "Cannot map relation: neither one-many or many-one".into(), - )) + )); }; Ok((global_scope, object_scope)) diff --git a/generator/src/writer.rs b/generator/src/writer.rs index 7bd75917..38007424 100644 --- a/generator/src/writer.rs +++ b/generator/src/writer.rs @@ -92,7 +92,10 @@ pub fn write_lib>(path: &P) -> std::io::Result<()> { let file_name = path.as_ref().join("lib.rs"); - std::fs::write(file_name, format!("#![recursion_limit = \"1024\"]\n{}", add_line_break(tokens)))?; + std::fs::write( + file_name, + format!("#![recursion_limit = \"1024\"]\n{}", add_line_break(tokens)), + )?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 6e962699..dec939ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -449,7 +449,11 @@ impl async_graphql::types::connection::CursorType for CursorValues { } #[derive(Debug, Clone)] -pub struct RelationKeyStruct(pub sea_orm::Value, pub Option, pub Option); +pub struct RelationKeyStruct( + pub sea_orm::Value, + pub Option, + pub Option, +); impl PartialEq for RelationKeyStruct { fn eq(&self, other: &Self) -> bool { @@ -593,7 +597,7 @@ pub trait EntityFilter { pub trait EntityOrderBy where - Entity: sea_orm::EntityTrait + Entity: sea_orm::EntityTrait, { fn order_by(&self, stmt: sea_orm::Select) -> sea_orm::Select; } @@ -617,16 +621,20 @@ where T: sea_orm::EntityTrait, ::Model: async_graphql::OutputType, { - use sea_orm::{Iterable, PrimaryKeyToColumn, ModelTrait}; use async_graphql::connection::CursorType; - - let edges: Vec> = data + use sea_orm::{Iterable, ModelTrait, PrimaryKeyToColumn}; + + let edges: Vec< + async_graphql::types::connection::Edge< + String, + T::Model, + async_graphql::types::connection::EmptyFields, + >, + > = data .into_iter() .map(|node| { let values: Vec = T::PrimaryKey::iter() - .map(|variant| { - node.get(variant.into_column()) - }) + .map(|variant| node.get(variant.into_column())) .collect(); let cursor_string = CursorValues(values).encode_cursor(); @@ -639,17 +647,14 @@ where String, T::Model, ExtraPaginationFields, - async_graphql::types::connection::EmptyFields + async_graphql::types::connection::EmptyFields, >::with_additional_fields( has_previous_page, has_next_page, - ExtraPaginationFields { - pages, - current - } + ExtraPaginationFields { pages, current }, ); result.edges.extend(edges); result -} \ No newline at end of file +} From 4a65d385cbf782e0f63ed4f6dc15dbc98eb64b1b Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Mon, 7 Nov 2022 11:39:49 +0200 Subject: [PATCH 07/18] Fix linter --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dec939ab..131690ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -530,13 +530,13 @@ where use heck::ToSnakeCase; use sea_orm::prelude::*; - let filters = if keys.len() > 0 { + let filters = if !keys.is_empty() { keys[0].clone().1 } else { None }; - let order_by = if keys.len() > 0 { + let order_by = if !keys.is_empty() { keys[0].clone().2 } else { None From 85a651a5dd7b0452e86380a7f6e493bf9ee12d95 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Mon, 7 Nov 2022 12:14:10 +0200 Subject: [PATCH 08/18] Update README --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 46721216..4f700162 100644 --- a/README.md +++ b/README.md @@ -64,9 +64,11 @@ Go to http://localhost:8000/ and try out the following queries: description releaseYear filmActor { - actor { - firstName - lastName + nodes { + actor { + firstName + lastName + } } } } @@ -135,6 +137,46 @@ Go to http://localhost:8000/ and try out the following queries: } ``` +### Complex query with filters on relations +Find all inactive customers, include their address, and their payments with amount greater than 7 ordered by amount the second result +```graphql +{ + customer( + filters: { active: { eq: 0 } } + ) { + nodes { + customerId + lastName + email + address { + address + } + payment( + filters: { amount: { gt: "7" } } + orderBy: { amount: ASC } + pagination: { pages: { limit: 1, page: 1 } } + ) { + nodes { + paymentId + amount + } + pages + current + pageInfo { + hasPreviousPage + hasNextPage + } + } + } + pageInfo { + hasPreviousPage + hasNextPage + endCursor + } + } +} +``` + ### Postgres Setup the [sakila](https://github.com/SeaQL/seaography/blob/main/examples/postgres/sakila-schema.sql) sample database. From 462793a9d27e23f0d1594932b52570042b8eae6b Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Fri, 11 Nov 2022 17:12:50 +0200 Subject: [PATCH 09/18] Fix usizes --- derive/src/relation.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/derive/src/relation.rs b/derive/src/relation.rs index b3b9307b..d3e28ea9 100644 --- a/derive/src/relation.rs +++ b/derive/src/relation.rs @@ -293,17 +293,19 @@ pub fn relation_fn( if let Some(pagination) = pagination { return match pagination { seaography::Pagination::Pages(pagination) => { - let nodes_size = nodes.len(); + let nodes_size = nodes.len() as u64; + let skip_size: usize = (pagination.page * pagination.limit).try_into().unwrap(); + let take_size: usize = pagination.limit.try_into().unwrap(); let nodes = nodes .into_iter() - .skip(pagination.page * pagination.limit) - .take(pagination.limit) + .skip(skip_size) + .take(take_size) .collect(); let has_previous_page = pagination.page * pagination.limit > 0 && nodes_size != 0; - let has_next_page = ((nodes_size / pagination.limit) as i64) - (pagination.page as i64) - 1 > 0; - let pages: usize = nodes_size / pagination.limit; + let has_next_page = (nodes_size / pagination.limit) - pagination.page - 1 > 0; + let pages = nodes_size / pagination.limit; let current = pagination.page; seaography::data_to_connection::<#path::Entity>( From 440d7c520fe256acf695894d79ccc88389644920 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Fri, 11 Nov 2022 17:45:53 +0200 Subject: [PATCH 10/18] Fix errors --- examples/mysql/src/entities/actor.rs | 228 ++++++++++++++++++++++++++- 1 file changed, 227 insertions(+), 1 deletion(-) diff --git a/examples/mysql/src/entities/actor.rs b/examples/mysql/src/entities/actor.rs index f4844a2d..60608f8b 100644 --- a/examples/mysql/src/entities/actor.rs +++ b/examples/mysql/src/entities/actor.rs @@ -6,7 +6,6 @@ use sea_orm::entity::prelude::*; PartialEq, DeriveEntityModel, async_graphql::SimpleObject, - seaography::macros::Filter, )] #[sea_orm(table_name = "actor")] #[graphql(complex)] @@ -32,3 +31,230 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +// Recursive expansion of seaography::macros::Filter! macro +// ========================================================= + +#[derive(Debug, Clone, async_graphql::InputObject)] +#[graphql(name = "ActorFilter")] +pub struct Filter { + pub or: Option>>, + pub and: Option>>, + actor_id: Option<::Filter>, + first_name: Option<::Filter>, + last_name: Option<::Filter>, + last_update: Option<::Filter>, +} +impl seaography::EntityFilter for Filter { + fn filter_condition(&self) -> sea_orm::Condition { + let mut condition = sea_orm::Condition::all(); + if let Some(or_filters) = &self.or { + let or_condition = or_filters + .iter() + .fold(sea_orm::Condition::any(), |fold_condition, filter| { + fold_condition.add(filter.filter_condition()) + }); + condition = condition.add(or_condition); + } + if let Some(and_filters) = &self.and { + let and_condition = and_filters + .iter() + .fold(sea_orm::Condition::all(), |fold_condition, filter| { + fold_condition.add(filter.filter_condition()) + }); + condition = condition.add(and_condition); + } + if let Some(actor_id) = &self.actor_id { + if let Some(eq_value) = seaography::FilterTrait::eq(actor_id) { + condition = condition.add(Column::ActorId.eq(eq_value)) + } + if let Some(ne_value) = seaography::FilterTrait::ne(actor_id) { + condition = condition.add(Column::ActorId.ne(ne_value)) + } + if let Some(gt_value) = seaography::FilterTrait::gt(actor_id) { + condition = condition.add(Column::ActorId.gt(gt_value)) + } + if let Some(gte_value) = seaography::FilterTrait::gte(actor_id) { + condition = condition.add(Column::ActorId.gte(gte_value)) + } + if let Some(lt_value) = seaography::FilterTrait::lt(actor_id) { + condition = condition.add(Column::ActorId.lt(lt_value)) + } + if let Some(lte_value) = seaography::FilterTrait::lte(actor_id) { + condition = condition.add(Column::ActorId.lte(lte_value)) + } + if let Some(is_in_value) = seaography::FilterTrait::is_in(actor_id) { + condition = condition.add(Column::ActorId.is_in(is_in_value)) + } + if let Some(is_not_in_value) = seaography::FilterTrait::is_not_in(actor_id) { + condition = condition.add(Column::ActorId.is_not_in(is_not_in_value)) + } + if let Some(is_null_value) = seaography::FilterTrait::is_null(actor_id) { + if is_null_value { + condition = condition.add(Column::ActorId.is_null()) + } + } + } + if let Some(first_name) = &self.first_name { + if let Some(eq_value) = seaography::FilterTrait::eq(first_name) { + condition = condition.add(Column::FirstName.eq(eq_value)) + } + if let Some(ne_value) = seaography::FilterTrait::ne(first_name) { + condition = condition.add(Column::FirstName.ne(ne_value)) + } + if let Some(gt_value) = seaography::FilterTrait::gt(first_name) { + condition = condition.add(Column::FirstName.gt(gt_value)) + } + if let Some(gte_value) = seaography::FilterTrait::gte(first_name) { + condition = condition.add(Column::FirstName.gte(gte_value)) + } + if let Some(lt_value) = seaography::FilterTrait::lt(first_name) { + condition = condition.add(Column::FirstName.lt(lt_value)) + } + if let Some(lte_value) = seaography::FilterTrait::lte(first_name) { + condition = condition.add(Column::FirstName.lte(lte_value)) + } + if let Some(is_in_value) = seaography::FilterTrait::is_in(first_name) { + condition = condition.add(Column::FirstName.is_in(is_in_value)) + } + if let Some(is_not_in_value) = seaography::FilterTrait::is_not_in(first_name) { + condition = condition.add(Column::FirstName.is_not_in(is_not_in_value)) + } + if let Some(is_null_value) = seaography::FilterTrait::is_null(first_name) { + if is_null_value { + condition = condition.add(Column::FirstName.is_null()) + } + } + } + if let Some(last_name) = &self.last_name { + if let Some(eq_value) = seaography::FilterTrait::eq(last_name) { + condition = condition.add(Column::LastName.eq(eq_value)) + } + if let Some(ne_value) = seaography::FilterTrait::ne(last_name) { + condition = condition.add(Column::LastName.ne(ne_value)) + } + if let Some(gt_value) = seaography::FilterTrait::gt(last_name) { + condition = condition.add(Column::LastName.gt(gt_value)) + } + if let Some(gte_value) = seaography::FilterTrait::gte(last_name) { + condition = condition.add(Column::LastName.gte(gte_value)) + } + if let Some(lt_value) = seaography::FilterTrait::lt(last_name) { + condition = condition.add(Column::LastName.lt(lt_value)) + } + if let Some(lte_value) = seaography::FilterTrait::lte(last_name) { + condition = condition.add(Column::LastName.lte(lte_value)) + } + if let Some(is_in_value) = seaography::FilterTrait::is_in(last_name) { + condition = condition.add(Column::LastName.is_in(is_in_value)) + } + if let Some(is_not_in_value) = seaography::FilterTrait::is_not_in(last_name) { + condition = condition.add(Column::LastName.is_not_in(is_not_in_value)) + } + if let Some(is_null_value) = seaography::FilterTrait::is_null(last_name) { + if is_null_value { + condition = condition.add(Column::LastName.is_null()) + } + } + } + if let Some(last_update) = &self.last_update { + if let Some(eq_value) = seaography::FilterTrait::eq(last_update) { + condition = condition.add(Column::LastUpdate.eq(eq_value)) + } + if let Some(ne_value) = seaography::FilterTrait::ne(last_update) { + condition = condition.add(Column::LastUpdate.ne(ne_value)) + } + if let Some(gt_value) = seaography::FilterTrait::gt(last_update) { + condition = condition.add(Column::LastUpdate.gt(gt_value)) + } + if let Some(gte_value) = seaography::FilterTrait::gte(last_update) { + condition = condition.add(Column::LastUpdate.gte(gte_value)) + } + if let Some(lt_value) = seaography::FilterTrait::lt(last_update) { + condition = condition.add(Column::LastUpdate.lt(lt_value)) + } + if let Some(lte_value) = seaography::FilterTrait::lte(last_update) { + condition = condition.add(Column::LastUpdate.lte(lte_value)) + } + if let Some(is_in_value) = seaography::FilterTrait::is_in(last_update) { + condition = condition.add(Column::LastUpdate.is_in(is_in_value)) + } + if let Some(is_not_in_value) = seaography::FilterTrait::is_not_in(last_update) { + condition = condition.add(Column::LastUpdate.is_not_in(is_not_in_value)) + } + if let Some(is_null_value) = seaography::FilterTrait::is_null(last_update) { + if is_null_value { + condition = condition.add(Column::LastUpdate.is_null()) + } + } + } + condition + } +} +#[derive(Debug, Clone, async_graphql::InputObject)] +#[graphql(name = "ActorOrderBy")] +pub struct OrderBy { + actor_id: Option, + first_name: Option, + last_name: Option, + last_update: Option, +} +impl seaography::EntityOrderBy for OrderBy { + fn order_by(&self, stmt: sea_orm::Select) -> sea_orm::Select { + use sea_orm::QueryOrder; + let stmt = if let Some(order_by) = self.actor_id { + match order_by { + seaography::OrderByEnum::Asc => { + stmt.order_by(Column::ActorId, sea_orm::query::Order::Asc) + } + seaography::OrderByEnum::Desc => { + stmt.order_by(Column::ActorId, sea_orm::query::Order::Desc) + } + } + } else { + stmt + }; + let stmt = if let Some(order_by) = self.first_name { + match order_by { + seaography::OrderByEnum::Asc => { + stmt.order_by(Column::FirstName, sea_orm::query::Order::Asc) + } + seaography::OrderByEnum::Desc => { + stmt.order_by(Column::FirstName, sea_orm::query::Order::Desc) + } + } + } else { + stmt + }; + let stmt = if let Some(order_by) = self.last_name { + match order_by { + seaography::OrderByEnum::Asc => { + stmt.order_by(Column::LastName, sea_orm::query::Order::Asc) + } + seaography::OrderByEnum::Desc => { + stmt.order_by(Column::LastName, sea_orm::query::Order::Desc) + } + } + } else { + stmt + }; + let stmt = if let Some(order_by) = self.last_update { + match order_by { + seaography::OrderByEnum::Asc => { + stmt.order_by(Column::LastUpdate, sea_orm::query::Order::Asc) + } + seaography::OrderByEnum::Desc => { + stmt.order_by(Column::LastUpdate, sea_orm::query::Order::Desc) + } + } + } else { + stmt + }; + stmt + } +} +impl seaography::EnchantedEntity for Entity { + type Entity = Entity; + type Filter = Filter; + type OrderBy = OrderBy; +} \ No newline at end of file From 8309a907f356434b4b4c54c0d5415c4ca426c384 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Tue, 15 Nov 2022 19:27:20 +0200 Subject: [PATCH 11/18] Fix existing tests --- derive/src/relation.rs | 7 ++- examples/mysql/tests/query_tests.rs | 72 ++++++++++++++------------ examples/postgres/tests/query_tests.rs | 72 ++++++++++++++------------ examples/sqlite/tests/query_tests.rs | 72 ++++++++++++++------------ 4 files changed, 120 insertions(+), 103 deletions(-) diff --git a/derive/src/relation.rs b/derive/src/relation.rs index d3e28ea9..e7b05b9c 100644 --- a/derive/src/relation.rs +++ b/derive/src/relation.rs @@ -1,4 +1,4 @@ -use heck::{ToSnakeCase, ToUpperCamelCase}; +use heck::{ToUpperCamelCase}; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; @@ -253,7 +253,6 @@ pub fn relation_fn( .map(|(key, model)| (#foreign_key_name(key), model)) .into_group_map(); - Ok(data) } } @@ -287,8 +286,8 @@ pub fn relation_fn( let nodes: Vec<#path::Model> = data_loader .load_one(key) .await - .unwrap() - .unwrap(); + .expect("cannot unwrap load_one") + .unwrap_or_else(|| vec![]); if let Some(pagination) = pagination { return match pagination { diff --git a/examples/mysql/tests/query_tests.rs b/examples/mysql/tests/query_tests.rs index 02915bd8..8286f7f5 100644 --- a/examples/mysql/tests/query_tests.rs +++ b/examples/mysql/tests/query_tests.rs @@ -453,51 +453,57 @@ async fn test_self_ref() { .execute( r#" { - staff { - nodes { - firstName - reportsToId - selfRefReverse { - staffId - firstName - } - selfRef { - staffId + staff { + nodes { firstName + reportsToId + selfRefReverse { + nodes { + staffId + firstName + } + } + selfRef { + staffId + firstName + } } } } - } "#, ) .await, r#" { - "staff": { - "nodes": [ - { - "firstName": "Mike", - "reportsToId": null, - "selfRefReverse": [ - { - "staffId": 2, - "firstName": "Jon" + "staff": { + "nodes": [ + { + "firstName": "Mike", + "reportsToId": null, + "selfRefReverse": { + "nodes": [ + { + "staffId": 2, + "firstName": "Jon" + } + ] + }, + "selfRef": null + }, + { + "firstName": "Jon", + "reportsToId": 1, + "selfRefReverse": { + "nodes": [] + }, + "selfRef": { + "staffId": 1, + "firstName": "Mike" } - ], - "selfRef": null - }, - { - "firstName": "Jon", - "reportsToId": 1, - "selfRefReverse": null, - "selfRef": { - "staffId": 1, - "firstName": "Mike" } - } - ] + ] + } } - } "#, ) } diff --git a/examples/postgres/tests/query_tests.rs b/examples/postgres/tests/query_tests.rs index db69df7c..b83aeea3 100644 --- a/examples/postgres/tests/query_tests.rs +++ b/examples/postgres/tests/query_tests.rs @@ -453,51 +453,57 @@ async fn test_self_ref() { .execute( r#" { - staff { - nodes { - firstName - reportsToId - selfRefReverse { - staffId - firstName - } - selfRef { - staffId + staff { + nodes { firstName + reportsToId + selfRefReverse { + nodes { + staffId + firstName + } + } + selfRef { + staffId + firstName + } } } } - } "#, ) .await, r#" { - "staff": { - "nodes": [ - { - "firstName": "Mike", - "reportsToId": null, - "selfRefReverse": [ - { - "staffId": 2, - "firstName": "Jon" + "staff": { + "nodes": [ + { + "firstName": "Mike", + "reportsToId": null, + "selfRefReverse": { + "nodes": [ + { + "staffId": 2, + "firstName": "Jon" + } + ] + }, + "selfRef": null + }, + { + "firstName": "Jon", + "reportsToId": 1, + "selfRefReverse": { + "nodes": [] + }, + "selfRef": { + "staffId": 1, + "firstName": "Mike" } - ], - "selfRef": null - }, - { - "firstName": "Jon", - "reportsToId": 1, - "selfRefReverse": null, - "selfRef": { - "staffId": 1, - "firstName": "Mike" } - } - ] + ] + } } - } "#, ) } diff --git a/examples/sqlite/tests/query_tests.rs b/examples/sqlite/tests/query_tests.rs index bb320992..09803004 100644 --- a/examples/sqlite/tests/query_tests.rs +++ b/examples/sqlite/tests/query_tests.rs @@ -451,51 +451,57 @@ async fn test_self_ref() { .execute( r#" { - staff { - nodes { - firstName - reportsToId - selfRefReverse { - staffId - firstName - } - selfRef { - staffId + staff { + nodes { firstName + reportsToId + selfRefReverse { + nodes { + staffId + firstName + } + } + selfRef { + staffId + firstName + } } } } - } "#, ) .await, r#" { - "staff": { - "nodes": [ - { - "firstName": "Mike", - "reportsToId": null, - "selfRefReverse": [ - { - "staffId": 2, - "firstName": "Jon" + "staff": { + "nodes": [ + { + "firstName": "Mike", + "reportsToId": null, + "selfRefReverse": { + "nodes": [ + { + "staffId": 2, + "firstName": "Jon" + } + ] + }, + "selfRef": null + }, + { + "firstName": "Jon", + "reportsToId": 1, + "selfRefReverse": { + "nodes": [] + }, + "selfRef": { + "staffId": 1, + "firstName": "Mike" } - ], - "selfRef": null - }, - { - "firstName": "Jon", - "reportsToId": 1, - "selfRefReverse": null, - "selfRef": { - "staffId": 1, - "firstName": "Mike" } - } - ] + ] + } } - } "#, ) } From 4c6df58c8e97e18a49ce48448b4e3b5b6934f6cc Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Wed, 16 Nov 2022 13:11:27 +0200 Subject: [PATCH 12/18] Add tests --- examples/mysql/tests/query_tests.rs | 238 +++++++++++++++++++++++++ examples/postgres/tests/query_tests.rs | 238 +++++++++++++++++++++++++ examples/sqlite/tests/query_tests.rs | 238 +++++++++++++++++++++++++ 3 files changed, 714 insertions(+) diff --git a/examples/mysql/tests/query_tests.rs b/examples/mysql/tests/query_tests.rs index 8286f7f5..874603f2 100644 --- a/examples/mysql/tests/query_tests.rs +++ b/examples/mysql/tests/query_tests.rs @@ -507,3 +507,241 @@ async fn test_self_ref() { "#, ) } + +#[tokio::test] +async fn related_queries_filters() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + customer( + filters: { active: { eq: 0 } } + pagination: { cursor: { limit: 3, cursor: "Int[3]:271" } } + ) { + nodes { + customerId + lastName + email + address { + address + } + payment(filters: { amount: { gt: "8" } }, orderBy: { amount: DESC }) { + nodes { + paymentId + } + } + } + pageInfo { + hasPreviousPage + hasNextPage + endCursor + } + } + } + "#, + ) + .await, + r#" + { + "customer": { + "nodes": [ + { + "customerId": 315, + "lastName": "GOODEN", + "email": "KENNETH.GOODEN@sakilacustomer.org", + "address": { + "address": "1542 Lubumbashi Boulevard" + }, + "payment": { + "nodes": [ + { + "paymentId": 8547 + }, + { + "paymentId": 8537 + } + ] + } + }, + { + "customerId": 368, + "lastName": "ARCE", + "email": "HARRY.ARCE@sakilacustomer.org", + "address": { + "address": "1922 Miraj Way" + }, + "payment": { + "nodes": [ + { + "paymentId": 9945 + }, + { + "paymentId": 9953 + }, + { + "paymentId": 9962 + }, + { + "paymentId": 9967 + } + ] + } + }, + { + "customerId": 406, + "lastName": "RUNYON", + "email": "NATHAN.RUNYON@sakilacustomer.org", + "address": { + "address": "264 Bhimavaram Manor" + }, + "payment": { + "nodes": [ + { + "paymentId": 10998 + } + ] + } + } + ], + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "endCursor": "Int[3]:406" + } + } + } + "#, + ) +} + +#[tokio::test] +async fn related_queries_pagination() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + customer( + filters: { active: { eq: 0 } } + pagination: { cursor: { limit: 3, cursor: "Int[3]:271" } } + ) { + nodes { + customerId + lastName + email + address { + address + } + payment( + filters: { amount: { gt: "7" } } + orderBy: { amount: ASC } + pagination: { pages: { limit: 1, page: 1 } } + ) { + nodes { + paymentId + amount + } + pages + current + pageInfo { + hasPreviousPage + hasNextPage + } + } + } + pageInfo { + hasPreviousPage + hasNextPage + endCursor + } + } + } + "#, + ) + .await, + r#" + { + "customer": { + "nodes": [ + { + "customerId": 315, + "lastName": "GOODEN", + "email": "KENNETH.GOODEN@sakilacustomer.org", + "address": { + "address": "1542 Lubumbashi Boulevard" + }, + "payment": { + "nodes": [ + { + "paymentId": 8547, + "amount": "9.99" + } + ], + "pages": 2, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false + } + } + }, + { + "customerId": 368, + "lastName": "ARCE", + "email": "HARRY.ARCE@sakilacustomer.org", + "address": { + "address": "1922 Miraj Way" + }, + "payment": { + "nodes": [ + { + "paymentId": 9972, + "amount": "7.99" + } + ], + "pages": 6, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true + } + } + }, + { + "customerId": 406, + "lastName": "RUNYON", + "email": "NATHAN.RUNYON@sakilacustomer.org", + "address": { + "address": "264 Bhimavaram Manor" + }, + "payment": { + "nodes": [ + { + "paymentId": 10989, + "amount": "7.99" + } + ], + "pages": 3, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true + } + } + } + ], + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "endCursor": "Int[3]:406" + } + } + } + "#, + ) +} diff --git a/examples/postgres/tests/query_tests.rs b/examples/postgres/tests/query_tests.rs index b83aeea3..75afc6c1 100644 --- a/examples/postgres/tests/query_tests.rs +++ b/examples/postgres/tests/query_tests.rs @@ -507,3 +507,241 @@ async fn test_self_ref() { "#, ) } + +#[tokio::test] +async fn related_queries_filters() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + customer( + filters: { active: { eq: 0 } } + pagination: { cursor: { limit: 3, cursor: "Int[3]:271" } } + ) { + nodes { + customerId + lastName + email + address { + address + } + payment(filters: { amount: { gt: "8" } }, orderBy: { amount: DESC }) { + nodes { + paymentId + } + } + } + pageInfo { + hasPreviousPage + hasNextPage + endCursor + } + } + } + "#, + ) + .await, + r#" + { + "customer": { + "nodes": [ + { + "customerId": 315, + "lastName": "GOODEN", + "email": "KENNETH.GOODEN@sakilacustomer.org", + "address": { + "address": "1542 Lubumbashi Boulevard" + }, + "payment": { + "nodes": [ + { + "paymentId": 8547 + }, + { + "paymentId": 8537 + } + ] + } + }, + { + "customerId": 368, + "lastName": "ARCE", + "email": "HARRY.ARCE@sakilacustomer.org", + "address": { + "address": "1922 Miraj Way" + }, + "payment": { + "nodes": [ + { + "paymentId": 9945 + }, + { + "paymentId": 9953 + }, + { + "paymentId": 9962 + }, + { + "paymentId": 9967 + } + ] + } + }, + { + "customerId": 406, + "lastName": "RUNYON", + "email": "NATHAN.RUNYON@sakilacustomer.org", + "address": { + "address": "264 Bhimavaram Manor" + }, + "payment": { + "nodes": [ + { + "paymentId": 10998 + } + ] + } + } + ], + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "endCursor": "Int[3]:406" + } + } + } + "#, + ) +} + +#[tokio::test] +async fn related_queries_pagination() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + customer( + filters: { active: { eq: 0 } } + pagination: { cursor: { limit: 3, cursor: "Int[3]:271" } } + ) { + nodes { + customerId + lastName + email + address { + address + } + payment( + filters: { amount: { gt: "7" } } + orderBy: { amount: ASC } + pagination: { pages: { limit: 1, page: 1 } } + ) { + nodes { + paymentId + amount + } + pages + current + pageInfo { + hasPreviousPage + hasNextPage + } + } + } + pageInfo { + hasPreviousPage + hasNextPage + endCursor + } + } + } + "#, + ) + .await, + r#" + { + "customer": { + "nodes": [ + { + "customerId": 315, + "lastName": "GOODEN", + "email": "KENNETH.GOODEN@sakilacustomer.org", + "address": { + "address": "1542 Lubumbashi Boulevard" + }, + "payment": { + "nodes": [ + { + "paymentId": 8547, + "amount": "9.99" + } + ], + "pages": 2, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false + } + } + }, + { + "customerId": 368, + "lastName": "ARCE", + "email": "HARRY.ARCE@sakilacustomer.org", + "address": { + "address": "1922 Miraj Way" + }, + "payment": { + "nodes": [ + { + "paymentId": 9972, + "amount": "7.99" + } + ], + "pages": 6, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true + } + } + }, + { + "customerId": 406, + "lastName": "RUNYON", + "email": "NATHAN.RUNYON@sakilacustomer.org", + "address": { + "address": "264 Bhimavaram Manor" + }, + "payment": { + "nodes": [ + { + "paymentId": 10989, + "amount": "7.99" + } + ], + "pages": 3, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true + } + } + } + ], + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "endCursor": "Int[3]:406" + } + } + } + "#, + ) +} diff --git a/examples/sqlite/tests/query_tests.rs b/examples/sqlite/tests/query_tests.rs index 09803004..3265c7a3 100644 --- a/examples/sqlite/tests/query_tests.rs +++ b/examples/sqlite/tests/query_tests.rs @@ -505,3 +505,241 @@ async fn test_self_ref() { "#, ) } + +#[tokio::test] +async fn related_queries_filters() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + customer( + filters: { active: { eq: 0 } } + pagination: { cursor: { limit: 3, cursor: "Int[3]:271" } } + ) { + nodes { + customerId + lastName + email + address { + address + } + payment(filters: { amount: { gt: "8" } }, orderBy: { amount: DESC }) { + nodes { + paymentId + } + } + } + pageInfo { + hasPreviousPage + hasNextPage + endCursor + } + } + } + "#, + ) + .await, + r#" + { + "customer": { + "nodes": [ + { + "customerId": 315, + "lastName": "GOODEN", + "email": "KENNETH.GOODEN@sakilacustomer.org", + "address": { + "address": "1542 Lubumbashi Boulevard" + }, + "payment": { + "nodes": [ + { + "paymentId": 8547 + }, + { + "paymentId": 8537 + } + ] + } + }, + { + "customerId": 368, + "lastName": "ARCE", + "email": "HARRY.ARCE@sakilacustomer.org", + "address": { + "address": "1922 Miraj Way" + }, + "payment": { + "nodes": [ + { + "paymentId": 9945 + }, + { + "paymentId": 9953 + }, + { + "paymentId": 9962 + }, + { + "paymentId": 9967 + } + ] + } + }, + { + "customerId": 406, + "lastName": "RUNYON", + "email": "NATHAN.RUNYON@sakilacustomer.org", + "address": { + "address": "264 Bhimavaram Manor" + }, + "payment": { + "nodes": [ + { + "paymentId": 10998 + } + ] + } + } + ], + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "endCursor": "Int[3]:406" + } + } + } + "#, + ) +} + +#[tokio::test] +async fn related_queries_pagination() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + customer( + filters: { active: { eq: 0 } } + pagination: { cursor: { limit: 3, cursor: "Int[3]:271" } } + ) { + nodes { + customerId + lastName + email + address { + address + } + payment( + filters: { amount: { gt: "7" } } + orderBy: { amount: ASC } + pagination: { pages: { limit: 1, page: 1 } } + ) { + nodes { + paymentId + amount + } + pages + current + pageInfo { + hasPreviousPage + hasNextPage + } + } + } + pageInfo { + hasPreviousPage + hasNextPage + endCursor + } + } + } + "#, + ) + .await, + r#" + { + "customer": { + "nodes": [ + { + "customerId": 315, + "lastName": "GOODEN", + "email": "KENNETH.GOODEN@sakilacustomer.org", + "address": { + "address": "1542 Lubumbashi Boulevard" + }, + "payment": { + "nodes": [ + { + "paymentId": 8547, + "amount": "9.99" + } + ], + "pages": 2, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false + } + } + }, + { + "customerId": 368, + "lastName": "ARCE", + "email": "HARRY.ARCE@sakilacustomer.org", + "address": { + "address": "1922 Miraj Way" + }, + "payment": { + "nodes": [ + { + "paymentId": 9972, + "amount": "7.99" + } + ], + "pages": 6, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true + } + } + }, + { + "customerId": 406, + "lastName": "RUNYON", + "email": "NATHAN.RUNYON@sakilacustomer.org", + "address": { + "address": "264 Bhimavaram Manor" + }, + "payment": { + "nodes": [ + { + "paymentId": 10989, + "amount": "7.99" + } + ], + "pages": 3, + "current": 1, + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true + } + } + } + ], + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "endCursor": "Int[3]:406" + } + } + } + "#, + ) +} From 44f1ea593484257923d14dd6d116b77896a9fbfd Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Wed, 16 Nov 2022 13:53:23 +0200 Subject: [PATCH 13/18] Fix format and tests --- derive/src/relation.rs | 2 +- examples/postgres/tests/query_tests.rs | 6 +++--- src/lib.rs | 13 ++++++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/derive/src/relation.rs b/derive/src/relation.rs index e7b05b9c..cd97b92a 100644 --- a/derive/src/relation.rs +++ b/derive/src/relation.rs @@ -1,4 +1,4 @@ -use heck::{ToUpperCamelCase}; +use heck::ToUpperCamelCase; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; diff --git a/examples/postgres/tests/query_tests.rs b/examples/postgres/tests/query_tests.rs index 75afc6c1..0f49847d 100644 --- a/examples/postgres/tests/query_tests.rs +++ b/examples/postgres/tests/query_tests.rs @@ -679,7 +679,7 @@ async fn related_queries_pagination() { "nodes": [ { "paymentId": 8547, - "amount": "9.99" + "amount": "9.9900" } ], "pages": 2, @@ -701,7 +701,7 @@ async fn related_queries_pagination() { "nodes": [ { "paymentId": 9972, - "amount": "7.99" + "amount": "7.9900" } ], "pages": 6, @@ -723,7 +723,7 @@ async fn related_queries_pagination() { "nodes": [ { "paymentId": 10989, - "amount": "7.99" + "amount": "7.9900" } ], "pages": 3, diff --git a/src/lib.rs b/src/lib.rs index 131690ce..49a439a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -551,8 +551,10 @@ where ) .unwrap() } else { - ::from_str(relation.to_col.to_string().to_snake_case().as_str()) - .unwrap() + ::from_str( + relation.to_col.to_string().to_snake_case().as_str(), + ) + .unwrap() }; let stmt = ::find(); @@ -616,7 +618,12 @@ pub fn data_to_connection( has_next_page: bool, pages: Option, current: Option, -) -> async_graphql::types::connection::Connection +) -> async_graphql::types::connection::Connection< + String, + T::Model, + ExtraPaginationFields, + async_graphql::types::connection::EmptyFields, +> where T: sea_orm::EntityTrait, ::Model: async_graphql::OutputType, From 116e2319f388a8159505d403fae3e05e64dd1800 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Wed, 16 Nov 2022 14:22:24 +0200 Subject: [PATCH 14/18] Fix pgsql test --- examples/postgres/tests/query_tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/postgres/tests/query_tests.rs b/examples/postgres/tests/query_tests.rs index 0f49847d..15a69417 100644 --- a/examples/postgres/tests/query_tests.rs +++ b/examples/postgres/tests/query_tests.rs @@ -578,14 +578,14 @@ async fn related_queries_filters() { { "paymentId": 9945 }, - { - "paymentId": 9953 - }, { "paymentId": 9962 }, { "paymentId": 9967 + }, + { + "paymentId": 9953 } ] } From 2b345d4a92206cfaa95919707291ca25e2705ac0 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 19 Nov 2022 09:38:10 +0800 Subject: [PATCH 15/18] Refactor: Related queries enchantments (#101) * Make RelationKeyStruct into struct with named fields * Add RelationParams * Fix clippy --- derive/src/relation.rs | 96 +++++++++++++++++++++++++++--------------- src/lib.rs | 32 +++++++------- 2 files changed, 78 insertions(+), 50 deletions(-) diff --git a/derive/src/relation.rs b/derive/src/relation.rs index cd97b92a..37ef11ef 100644 --- a/derive/src/relation.rs +++ b/derive/src/relation.rs @@ -12,27 +12,38 @@ pub struct SeaOrm { on_delete: Option, } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct RelationParams { + relation_name: String, + belongs_to: Option, + has_many: Option, + reverse: bool, +} + pub fn compact_relation_fn(item: &syn::DataEnum) -> Result { - let relations_parameters: Vec<(String, Option, Option, bool)> = item + let relations_parameters: Vec = item .variants .iter() - .map( - |variant| -> Result<(String, Option, Option, bool), crate::error::Error> { - let attrs = SeaOrm::from_attributes(&variant.attrs)?; + .map(|variant| -> Result { + let attrs = SeaOrm::from_attributes(&variant.attrs)?; - let belongs_to = match attrs.belongs_to { - Some(syn::Lit::Str(belongs_to)) => Some(belongs_to.value()), - _ => None, - }; + let belongs_to = match attrs.belongs_to { + Some(syn::Lit::Str(belongs_to)) => Some(belongs_to.value()), + _ => None, + }; - let has_many = match attrs.has_many { - Some(syn::Lit::Str(has_many)) => Some(has_many.value()), - _ => None, - }; + let has_many = match attrs.has_many { + Some(syn::Lit::Str(has_many)) => Some(has_many.value()), + _ => None, + }; - Ok((variant.ident.to_string(), belongs_to, has_many, false)) - }, - ) + Ok(RelationParams { + relation_name: variant.ident.to_string(), + belongs_to, + has_many, + reverse: false, + }) + }) .collect::, crate::error::Error>>()?; produce_relations(relations_parameters) @@ -112,9 +123,9 @@ pub fn expanded_relation_fn(item: &syn::ItemImpl) -> Result, crate::error::Error>>()?; - let relations_parameters: Vec<(String, Option, Option, bool)> = expanded_params + let relations_parameters: Vec = expanded_params .iter() - .map(|params| -> Result<(String, Option, Option, bool), crate::error::Error> { + .map(|params| -> Result { let belongs_to = if params.relation_type.to_string().eq("belongs_to") { Some(params.related_type.to_token_stream().to_string()) } else { @@ -129,7 +140,12 @@ pub fn expanded_relation_fn(item: &syn::ItemImpl) -> Result, crate::error::Error>>()?; @@ -137,27 +153,27 @@ pub fn expanded_relation_fn(item: &syn::ItemImpl) -> Result, Option, bool)>, + relations_parameters: Vec, ) -> Result { let relations_copy = relations_parameters.clone(); let reverse_self_references_parameters = relations_copy .into_iter() - .filter(|(_, belongs_to, has_one, _)| { - belongs_to.eq(&Some("Entity".into())) || has_one.eq(&Some("Entity".into())) + .filter(|rel_param| { + rel_param.belongs_to.eq(&Some("Entity".into())) + || rel_param.has_many.eq(&Some("Entity".into())) }) - .map(|(relation_name, belongs_to, has_many, _)| { - (relation_name, has_many, belongs_to, true) + .map(|rel_param| RelationParams { + relation_name: rel_param.relation_name, + belongs_to: rel_param.has_many, + has_many: rel_param.belongs_to, + reverse: true, }); let (loaders, functions): (Vec<_>, Vec<_>) = relations_parameters .into_iter() .chain(reverse_self_references_parameters) - .map( - |(relation_name, belongs_to, has_many, reverse)| -> Result<(TokenStream, TokenStream), crate::error::Error> { - relation_fn(relation_name, belongs_to, has_many, reverse) - }, - ) + .map(relation_fn) .collect::, crate::error::Error>>()? .into_iter() .map(|(loader, func)| (loader, func)) @@ -174,11 +190,15 @@ pub fn produce_relations( } pub fn relation_fn( - relation_name: String, - belongs_to: Option, - has_many: Option, - reverse: bool, + relations_parameters: RelationParams, ) -> Result<(TokenStream, TokenStream), crate::error::Error> { + let RelationParams { + relation_name, + belongs_to, + has_many, + reverse, + } = relations_parameters; + let relation_ident = format_ident!("{}", relation_name.to_upper_camel_case()); let relation_name = if reverse { @@ -281,7 +301,11 @@ pub fn relation_fn( .as_str() ).unwrap(); - let key = #foreign_key_name(seaography::RelationKeyStruct(self.get(from_column), filters, order_by)); + let key = #foreign_key_name(seaography::RelationKeyStruct { + val: self.get(from_column), + filter: filters, + order_by, + }); let nodes: Vec<#path::Model> = data_loader .load_one(key) @@ -395,7 +419,11 @@ pub fn relation_fn( .as_str() ).unwrap(); - let key = #foreign_key_name(seaography::RelationKeyStruct(self.get(from_column), None, None)); + let key = #foreign_key_name(seaography::RelationKeyStruct { + val: self.get(from_column), + filter: None, + order_by: None, + }); let data: Option<_> = data_loader.load_one(key).await.unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 49a439a6..92aee13b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -449,11 +449,11 @@ impl async_graphql::types::connection::CursorType for CursorValues { } #[derive(Debug, Clone)] -pub struct RelationKeyStruct( - pub sea_orm::Value, - pub Option, - pub Option, -); +pub struct RelationKeyStruct { + pub val: sea_orm::Value, + pub filter: Option, + pub order_by: Option, +} impl PartialEq for RelationKeyStruct { fn eq(&self, other: &Self) -> bool { @@ -468,8 +468,8 @@ impl PartialEq for RelationKeyStruct { .map(|(index, _)| s.split_at(index)) } - let a = format!("{:?}", self.0); - let b = format!("{:?}", other.0); + let a = format!("{:?}", self.val); + let b = format!("{:?}", other.val); let a = split_at_nth_char(a.as_str(), '(', 1).map(|v| v.1); let b = split_at_nth_char(b.as_str(), '(', 1).map(|v| v.1); @@ -490,7 +490,7 @@ impl std::hash::Hash for RelationKeyStruct { .map(|(index, _)| s.split_at(index)) } - let a = format!("{:?}", self.0); + let a = format!("{:?}", self.val); let a = split_at_nth_char(a.as_str(), '(', 1).map(|v| v.1); a.hash(state) @@ -531,18 +531,18 @@ where use sea_orm::prelude::*; let filters = if !keys.is_empty() { - keys[0].clone().1 + keys[0].clone().filter } else { None }; let order_by = if !keys.is_empty() { - keys[0].clone().2 + keys[0].clone().order_by } else { None }; - let keys: Vec = keys.into_iter().map(|key| key.0).collect(); + let keys: Vec = keys.into_iter().map(|key| key.val).collect(); // TODO support multiple columns let to_column = if reverse { @@ -580,11 +580,11 @@ where RelationKeyStruct, ::Model, ) { - let key = RelationKeyStruct::( - model.get(to_column), - None, - None, - ); + let key = RelationKeyStruct:: { + val: model.get(to_column), + filter: None, + order_by: None, + }; (key, model) }, From f72cc4c4ee758167556f4a8646cc02d2ba1e2839 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Sat, 19 Nov 2022 10:08:33 +0200 Subject: [PATCH 16/18] Fix typo --- derive/src/filter.rs | 2 +- examples/mysql/src/entities/actor.rs | 2 +- src/lib.rs | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/derive/src/filter.rs b/derive/src/filter.rs index 4c6d2db2..952219d0 100644 --- a/derive/src/filter.rs +++ b/derive/src/filter.rs @@ -39,7 +39,7 @@ pub fn filter_fn(item: syn::DataStruct, attrs: SeaOrm) -> Result for OrderBy { stmt } } -impl seaography::EnchantedEntity for Entity { +impl seaography::EnhancedEntity for Entity { type Entity = Entity; type Filter = Filter; type OrderBy = OrderBy; diff --git a/src/lib.rs b/src/lib.rs index 92aee13b..e6e03def 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -449,13 +449,13 @@ impl async_graphql::types::connection::CursorType for CursorValues { } #[derive(Debug, Clone)] -pub struct RelationKeyStruct { +pub struct RelationKeyStruct { pub val: sea_orm::Value, pub filter: Option, pub order_by: Option, } -impl PartialEq for RelationKeyStruct { +impl PartialEq for RelationKeyStruct { fn eq(&self, other: &Self) -> bool { // TODO temporary hack to solve the following problem // let v1 = TestFK(sea_orm::Value::TinyInt(Some(1))); @@ -478,9 +478,9 @@ impl PartialEq for RelationKeyStruct { } } -impl Eq for RelationKeyStruct {} +impl Eq for RelationKeyStruct {} -impl std::hash::Hash for RelationKeyStruct { +impl std::hash::Hash for RelationKeyStruct { fn hash(&self, state: &mut H) { // TODO this is a hack @@ -511,7 +511,7 @@ impl std::hash::Hash for RelationKeyStruct { } } -pub async fn fetch_relation_data( +pub async fn fetch_relation_data( keys: Vec>, relation: sea_orm::RelationDef, reverse: bool, @@ -524,7 +524,7 @@ pub async fn fetch_relation_data( sea_orm::error::DbErr, > where - Entity: sea_orm::EntityTrait + EnchantedEntity, + Entity: sea_orm::EntityTrait + EnhancedEntity, ::Err: core::fmt::Debug, { use heck::ToSnakeCase; @@ -604,7 +604,7 @@ where fn order_by(&self, stmt: sea_orm::Select) -> sea_orm::Select; } -pub trait EnchantedEntity { +pub trait EnhancedEntity { type Entity: sea_orm::EntityTrait; type Filter: EntityFilter + Clone; From 5afcb2d06f65f6cbb9fbdca1a281e718c24be330 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Sat, 19 Nov 2022 10:11:30 +0200 Subject: [PATCH 17/18] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5ddb848..b1c78c92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## Next + +## What's Changed +* Add pagination, filtering and ordering for related queries https://github.com/SeaQL/seaography/pull/84 + ## 0.2.0 - 2022-10-31 ## What's Changed From 402fdbffc0c6b97c36e9a32222e42ee6f2cc9a73 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Sat, 19 Nov 2022 10:21:05 +0200 Subject: [PATCH 18/18] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1c78c92..ca3d7244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## What's Changed * Add pagination, filtering and ordering for related queries https://github.com/SeaQL/seaography/pull/84 + Note: Pagination is WIP, currently in memory only pagination ## 0.2.0 - 2022-10-31