[−][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(context = Context, 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(context = Context, error = Box<dyn std::error::Error>)] pub struct Country { country: models::Country, } #[allow(missing_docs, dead_code)] struct EagerLoadingContextUserForCountry; impl<'a> EagerLoadChildrenOfType< 'a, Country, EagerLoadingContextUserForCountry, > for User { type FieldArguments = (); fn load_children( models: &[Self::Model], field_args: &Self::FieldArguments, ctx: &Self::Context, ) -> Result< LoadChildrenOutput<<Country as juniper_eager_loading::GraphqlNodeForModel>::Model>, Self::Error, > { let ids = models .iter() .filter_map(|model| model.country_id) .map(|id| id.clone()) .collect::<Vec<_>>(); let ids = juniper_eager_loading::unique(ids); let children = < <Country as GraphqlNodeForModel>::Model as juniper_eager_loading::LoadFrom<Self::Id> >::load(&ids, field_args, ctx)?; Ok(juniper_eager_loading::LoadChildrenOutput::ChildModels(children)) } fn is_child_of( node: &Self, child: &Country, _join_model: &(), _field_args: &Self::FieldArguments, _ctx: &Self::Context, ) -> bool { node.user.country_id == Some(child.country.id) } fn association(node: &mut Self) -> &mut dyn Association<Country> { &mut node.country } }
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
Is the model type of the child. If your User
struct has a field of type OptionHasOne<Country>
,
this type will default to models::Country
.
ImplContext
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.
Note that this is not the Juniper GraphQL context.
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 FieldArguments
The types of arguments the GraphQL field takes. The code generation always sets this to ()
.
Required methods
fn load_children(
models: &[Self::Model],
field_args: &Self::FieldArguments,
ctx: &Self::Context
) -> Result<LoadChildrenOutput<Child::Model, JoinModel>, Self::Error>
models: &[Self::Model],
field_args: &Self::FieldArguments,
ctx: &Self::Context
) -> Result<LoadChildrenOutput<Child::Model, JoinModel>, Self::Error>
Load the children from the data store.
fn is_child_of(
parent: &Self,
child: &Child,
join_model: &JoinModel,
field_args: &Self::FieldArguments,
context: &Self::Context
) -> bool
parent: &Self,
child: &Child,
join_model: &JoinModel,
field_args: &Self::FieldArguments,
context: &Self::Context
) -> bool
Does this parent and this child belong together?
The join_model
is only used for HasManyThrough
associations.
fn association(node: &mut Self) -> &mut dyn Association<Child>
Return the particular association type.
In most cases the implementation will be something like
fn association(node: &mut User) -> &mut dyn Association<Country> { &mut node.country }
Provided methods
fn eager_load_children(
nodes: &mut [Self],
models: &[Self::Model],
ctx: &Self::Context,
trail: &QueryTrail<'a, Child, Walked>,
field_args: &Self::FieldArguments
) -> Result<(), Self::Error>
nodes: &mut [Self],
models: &[Self::Model],
ctx: &Self::Context,
trail: &QueryTrail<'a, Child, Walked>,
field_args: &Self::FieldArguments
) -> Result<(), Self::Error>
Combine all the methods above to eager load the children for a list of GraphQL values and models.