[−][src]Trait juniper_eager_loading::EagerLoadChildrenOfType
Perform eager loading for a single association of a GraphQL struct.
#[derive(EagerLoading)] will implement this trait for each association field your GraphQL
struct has.
Manual implementation
Sometimes you might have a setup that #[derive(EagerLoading)] doesn't support. In those cases
you have to implement this trait yourself for those struct fields. Here is an example of how to
do that:
#[derive(Clone, EagerLoading)] #[eager_loading(connection = "DbConnection", error = "Box<dyn std::error::Error>")] pub struct User { user: models::User, // Add `#[option_has_one(default, print)]` to get a good starting point for your // manual implementaion. #[option_has_one(skip)] country: OptionHasOne<Country>, } #[derive(Clone, EagerLoading)] #[eager_loading(connection = "DbConnection", error = "Box<dyn std::error::Error>")] pub struct Country { country: models::Country, } #[allow(missing_docs, dead_code)] struct EagerLoadingContextUserForCountry; impl<'a> EagerLoadChildrenOfType< Country, QueryTrail<'a, Country, juniper_from_schema::Walked>, EagerLoadingContextUserForCountry, (), > for User { type ChildModel = models::Country; type ChildId = Option<Self::Id>; fn child_ids( models: &[Self::Model], db: &Self::Connection, ) -> Result< juniper_eager_loading::LoadResult<Self::ChildId, (Self::ChildModel, ())>, Self::Error > { let ids = models .iter() .map(|model| model.country_id.clone()) .collect::<Vec<_>>(); let ids = juniper_eager_loading::unique(ids); Ok(juniper_eager_loading::LoadResult::Ids(ids)) } fn load_children( ids: &[Self::ChildId], db: &Self::Connection, ) -> Result<Vec<Self::ChildModel>, Self::Error> { let ids = ids .into_iter() .filter_map(|id| id.as_ref()) .cloned() .collect::<Vec<_>>(); let ids = juniper_eager_loading::unique(ids); <Self::ChildModel as juniper_eager_loading::LoadFrom<Self::Id>>::load(&ids, db) } fn is_child_of(node: &Self, child: &(Country, &())) -> bool { node.user.country_id == Some((child.0).country.id) } fn loaded_child(node: &mut Self, child: Country) { node.country.loaded(child) } fn assert_loaded_otherwise_failed(node: &mut Self) { node.country.assert_loaded_otherwise_failed(); } }
Generic parameters
The number of generic parameters to this trait might look scary, but in the vast majority of cases you shouldn't have to worry about them.
Child
If model type of the child. If your User struct has a field of type OptionHasOne<Country>,
this type will default to models::Country.
QueryTrailT
Since we cannot depend directly on QueryTrail we have to
depend on this generic version instead.
The generic constraint enforces that .walk() must to have been called on the QueryTrail to
ensure the field we're trying to eager load is actually part of the incoming GraphQL query.
Otherwise the field will not be eager loaded. This is how the compiler can guarantee that we
don't eager load too much.
Context
This "context" type is needed in case your GraphQL type has multiple assocations to values of the same type. Could for example be something like this
struct User { home_country: HasOne<Country>, current_country: HasOne<Country>, }
If we didn't have this we wouldn't be able to implement EagerLoadChildrenOfType<Country>
twice for User, because you cannot implement the same trait twice for the same type.
JoinModel
This type defaults to () and is only need for HasManyThrough. In the other associations
there are only two types involved (such as models::User and models::Country) and one of
them will have a foreign key pointing to the other one. But consider this scenario instead
where users can work for many companies, and companies can have many employees:
mod models { struct User { id: i32, } struct Company { id: i32, } struct Employment { id: i32, user_id: i32, company_id: i32, } }
Imagine now we need to eager load the list of companies a given user works at. That means
LoadFrom would return Vec<models::Company>. However that isn't enough information once
we need to pair users up with the correct companies. User doesn't have company_id and
Company doesn't have user_id.
Instead we need LoadFrom to return Vec<(models::Company, models::Employment)>. We say
"users have many companies through employments", because models::Employment is necessary for
pairing things up at the end of EagerLoadChildrenOfType.
In this case JoinModel would be models::Employment.
Associated Types
type ChildModel: Clone
The model type backing your GraphQL type.
type ChildId: Hash + Eq
The id type the child uses. This will be different for the different association types.
Required methods
fn child_ids(
models: &[Self::Model],
db: &Self::Connection
) -> Result<LoadResult<Self::ChildId, (Self::ChildModel, JoinModel)>, Self::Error>
models: &[Self::Model],
db: &Self::Connection
) -> Result<LoadResult<Self::ChildId, (Self::ChildModel, JoinModel)>, Self::Error>
Given a list of models, load either the list of child ids or child models associated.
fn load_children(
ids: &[Self::ChildId],
db: &Self::Connection
) -> Result<Vec<Self::ChildModel>, Self::Error>
ids: &[Self::ChildId],
db: &Self::Connection
) -> Result<Vec<Self::ChildModel>, Self::Error>
Load a list of children from a list of ids.
fn is_child_of(parent: &Self, child: &(Child, &JoinModel)) -> bool
Does this parent and this child belong together?
fn loaded_child(node: &mut Self, child: Child)
Store the loaded child on the association.
fn assert_loaded_otherwise_failed(node: &mut Self)
The association should have been loaded by now, if not store an error inside the association (if applicable for the particular association).
Provided methods
fn eager_load_children(
nodes: &mut [Self],
models: &[Self::Model],
db: &Self::Connection,
trail: &QueryTrailT
) -> Result<(), Self::Error>
nodes: &mut [Self],
models: &[Self::Model],
db: &Self::Connection,
trail: &QueryTrailT
) -> Result<(), Self::Error>
Combine all the methods above to eager load the children for a list of GraphQL values and models.