使用 Dataloader 处理 GraphQL 字段参数?

handling GraphQL field arguments using Dataloader?

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

例如,假设我有一个 SQL 数据库,其中 table 用于 UsersBooks,以及一个名为 [=23] 的关系 table =] 表示 Users:Books.

之间的 1:many 关系

我可能会 运行 以下查询,以查看所有用户都读过哪些书:

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

假设 context 中有一个 BooksReadLoader 可用,这样 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 分组,然后 return 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 table 中获取潜在的大量行,而实际上可能只有少数行满足参数。执行此 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 < ?;

我的问题是,通过 new DataLoader( batchFn[, options] ) 提供的 cacheKeyFn 选项是否允许传递字段的参数以在数据访问层中构造动态 SQL 语句?我已经查看了 https://github.com/graphql/dataloader/issues/75 but I'm still unclear if cacheKeyFn is the way to go. I'm using apollo-server-express. There is this other SO question: ,但没有答案,而且我很难找到与此相关的其他来源。

谢谢!

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

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

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

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