[][src]Trait juniper_eager_loading::EagerLoadChildrenOfType

pub trait EagerLoadChildrenOfType<'a, Child, ImplContext, JoinModel: ?Sized = ()> where
    Self: GraphqlNodeForModel,
    Child: GraphqlNodeForModel<Context = Self::Context, Error = Self::Error> + EagerLoadAllChildren + Clone,
    JoinModel: 'static + Clone
{ type FieldArguments; fn load_children(
        models: &[Self::Model],
        field_args: &Self::FieldArguments,
        ctx: &Self::Context
    ) -> Result<LoadChildrenOutput<Child::Model, JoinModel>, Self::Error>;
fn is_child_of(
        parent: &Self,
        child: &Child,
        join_model: &JoinModel,
        field_args: &Self::FieldArguments,
        context: &Self::Context
    ) -> bool;
fn association(node: &mut Self) -> &mut dyn Association<Child>; 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> { ... } }

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

This example is not tested
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 ().

Loading content...

Required methods

fn load_children(
    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

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

This example is not tested
fn association(node: &mut User) -> &mut dyn Association<Country> {
    &mut node.country
}
Loading content...

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>

Combine all the methods above to eager load the children for a list of GraphQL values and models.

Loading content...

Implementors

Loading content...