[][src]Macro juniper_eager_loading::impl_load_from_for_diesel_pg

macro_rules! impl_load_from_for_diesel_pg {
    ( $($token:tt)* ) => { ... };
}

This macro will implement LoadFrom for Diesel models using the Postgres backend.

It'll use an = ANY which is only supported by Postgres.

Example usage

#[macro_use]
extern crate diesel;

use diesel::pg::PgConnection;
use diesel::prelude::*;
use juniper_eager_loading::impl_load_from_for_diesel_pg;

table! {
    users (id) {
        id -> Integer,
    }
}

table! {
    companies (id) {
        id -> Integer,
    }
}

table! {
    employments (id) {
        id -> Integer,
        user_id -> Integer,
        company_id -> Integer,
    }
}

#[derive(Queryable)]
struct User {
    id: i32,
}

#[derive(Queryable)]
struct Company {
    id: i32,
}

#[derive(Queryable)]
struct Employment {
    id: i32,
    user_id: i32,
    company_id: i32,
}

struct Context {
    db: PgConnection,
}

impl Context {
    // The macro assumes this method exists
    fn db(&self) -> &PgConnection {
        &self.db
    }
}

impl_load_from_for_diesel_pg! {
    (
        error = diesel::result::Error,
        context = Context,
    ) => {
        i32 -> (users, User),
        i32 -> (companies, Company),
        i32 -> (employments, Employment),

        User.id -> (employments.user_id, Employment),
        Company.id -> (employments.company_id, Employment),

        Employment.company_id -> (companies.id, Company),
        Employment.user_id -> (users.id, User),
    }
}

Syntax

First you specify your error and connection type with

(
    error = diesel::result::Error,
    context = Context,
) => {
    // ...
}

Then you define each model type you want to implement LoadFrom for and which columns and tables to use. There are two possible syntaxes for different purposes.

i32 -> (users, User),

The first syntax implements LoadFrom<i32> for User, meaning from a Vec<i32> we can load a Vec<User>. It just takes the id type, the table, and the model struct.

User.id -> (employments.user_id, Employment),

This syntax is required when using HasMany and HasManyThrough. In this case it implements LoadFrom<User> for Employment, meaning from a Vec<User> we can get Vec<Employment>. It does this by loading the users, mapping the list to the user ids, then finding the employments with those ids.

Context::db

It is required that your context type has a method called db which returns a reference to a Diesel connection that can be passed to .load(_).

Example:

This example is not tested
struct Context {
    db: PgConnection,
}

impl Context {
    fn db(&self) -> &PgConnection {
        &self.db
    }
}

// Whatever the method returns has to work with Diesel's `load` method
users::table
    .filter(users::id.eq(any(user_ids)))
    .load::<User>(ctx.db())

What gets generated

The two syntaxes generates code like this:


// i32 -> (users, User),
impl juniper_eager_loading::LoadFrom<i32> for User {
    type Error = diesel::result::Error;
    type Context = Context;

    fn load(ids: &[i32], field_args: &(), ctx: &Self::Context) -> Result<Vec<Self>, Self::Error> {
        use diesel::pg::expression::dsl::any;

        users::table
            .filter(users::id.eq(any(ids)))
            .load::<User>(ctx.db())
            .map_err(From::from)
    }
}

// User.id -> (employments.user_id, Employment),
impl juniper_eager_loading::LoadFrom<User> for Employment {
    type Error = diesel::result::Error;
    type Context = Context;

    fn load(froms: &[User], field_args: &(), ctx: &Self::Context) -> Result<Vec<Self>, Self::Error> {
        use diesel::pg::expression::dsl::any;

        let from_ids = froms.iter().map(|other| other.id).collect::<Vec<_>>();
        employments::table
            .filter(employments::user_id.eq(any(from_ids)))
            .load(ctx.db())
            .map_err(From::from)
    }
}