[][src]Trait juniper_eager_loading::EagerLoadChildrenOfType

pub trait EagerLoadChildrenOfType<Child, QueryTrailT, Context, JoinModel: ?Sized = ()> where
    Self: GraphqlNodeForModel,
    Child: GraphqlNodeForModel<Model = Self::ChildModel, Connection = Self::Connection, Error = Self::Error> + EagerLoadAllChildren<QueryTrailT> + Clone,
    QueryTrailT: GenericQueryTrail<Child, Walked>,
    JoinModel: 'static + Clone
{ type ChildModel: Clone; type ChildId: Hash + Eq; fn child_ids(
        models: &[Self::Model],
        db: &Self::Connection
    ) -> Result<LoadResult<Self::ChildId, (Self::ChildModel, JoinModel)>, Self::Error>;
fn load_children(
        ids: &[Self::ChildId],
        db: &Self::Connection
    ) -> Result<Vec<Self::ChildModel>, Self::Error>;
fn is_child_of(parent: &Self, child: &(Child, &JoinModel)) -> bool;
fn loaded_child(node: &mut Self, child: Child);
fn assert_loaded_otherwise_failed(node: &mut Self); fn eager_load_children(
        nodes: &mut [Self],
        models: &[Self::Model],
        db: &Self::Connection,
        trail: &QueryTrailT
    ) -> 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(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

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.

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.

Loading content...

Required methods

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

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).

Loading content...

Provided methods

fn eager_load_children(
    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.

Loading content...

Implementors

Loading content...