Trait EagerLoadChildrenOfType

Source
pub trait EagerLoadChildrenOfType<'a, Child, ImplContext, JoinModel = ()>
where Self: GraphqlNodeForModel, Child: GraphqlNodeForModel<Context = Self::Context, Error = Self::Error> + EagerLoadAllChildren + Clone, JoinModel: 'static + Clone + ?Sized,
{ type FieldArguments; // Required methods 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>; // Provided method 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> { ... } }
Expand description

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.

Required Associated Types§

Source

type FieldArguments

The types of arguments the GraphQL field takes. The type used by the code generation can be customized with field_arguments = SomeType.

Required Methods§

Source

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.

Source

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.

Source

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§

Source

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.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§