使用Dataloader处理GraphQL字段参数?

时间:2019-05-23 00:15:59

标签: javascript sql graphql apollo-server

我想知道是否在使用Dataloader时如何最好地处理GraphQL字段参数方面存在共识。 Dataloader需要的batchFn批处理函数预期会收到Array<key>并返回一个Array<Promise>,通常只需调用load( parent.id ),其中parent是第一个参数。给定字段的解析器。在大多数情况下,这很好,但是如果您需要为嵌套字段提供参数呢?

例如,假设我有一个SQL数据库,其中包含UsersBooks的表,以及一个名为BooksRead的关系表,它们表示Users:Books之间的1:许多关系。 / p>

我可能会运行以下查询,以便为所有用户查看他们阅读过哪些书:

query {
  users {
    id
    first_name
    books_read {
      title
      author {
        name
      }
      year_published
    }
  }
}

让我们说BooksReadLoader中有一个context可用,因此books_read的解析器可能看起来像这样:

const UserResolvers = {
  books_read: async function getBooksRead( user, args, context ) {
    return await context.loaders.booksRead.load( user.id );
  }
};

BooksReadLoader的批处理加载函数将对数据访问层方法进行async调用,该方法将运行一些SQL,如:

SELECT B.* FROM Books B INNER JOIN BooksRead BR ON B.id = BR.book_id WHERE BR.user_id IN(?);

我们将从结果行中创建一些Book实例,并按user_id分组,然后返回keys.map(fn)以确保我们为每个user_id键分配正确的书本加载程序的缓存。

现在假设我向books_read添加一个参数,询问用户已阅读的所有1950年以前出版的书:

query {
  users {
    id
    first_name
    books_read(published_before: 1950) {
      title
      author {
        name
      }
      year_published
    }
  }
}

理论上,我们可以运行相同的SQL语句,并在解析器中处理参数:

const UserResolvers = {
  books_read: async function getBooksRead( user, args, context ) {
    const books_read = await context.loaders.booksRead.load( user.id );
    return books_read.filter( function ( book ) { 
      return book.year_published < args.published_before; 
    });
  }
};

但是,这并不理想,因为当实际上只有少数几行实际上满足该参数时,我们仍然从Books表中获取潜在的大量行。相反,执行此SQL语句会更好:

SELECT B.* FROM Books B INNER JOIN BooksRead BR ON B.id = BR.book_id WHERE BR.user_id IN(?) AND B.year_published < ?;

我的问题是,通过cacheKeyFn可用的new DataLoader( batchFn[, options] )选项是否允许将字段的参数向下传递以在数据访问层中构造动态SQL语句?我已经审查过https://github.com/graphql/dataloader/issues/75,但仍不清楚cacheKeyFn是否是可行的方法。我正在使用apollo-server-express。还有另外一个问题:Passing down arguments using Facebook's DataLoader,但没有答案,我很难找到其他来源。

谢谢!

1 个答案:

答案 0 :(得分:0)

将id和params作为单个对象传递给load函数,如下所示:

const UserResolvers = {
  books_read: async function getBooksRead( user, args, context ) {
    return context.loaders.booksRead.load({id: user.id, ...args});
  }
};

然后让批处理加载函数找出如何以最佳方式满足它。

您还需要对对象的构造做一些说明,因为否则,数据加载器的缓存将无法正常工作(我认为它是基于身份而不是深度平等的)。