如何使用 DataLoader 与 Hot Chocolate GraphQL 进行连接

Posted

技术标签:

【中文标题】如何使用 DataLoader 与 Hot Chocolate GraphQL 进行连接【英文标题】:How to use DataLoader for Connections with Hot Chocolate GraphQL 【发布时间】:2021-01-07 02:37:41 【问题描述】:

我发现可以将数据加载器用于根查询,但也可以将数据加载器用于嵌套连接吗?在下面的示例中,我想为 rooms 属性使用数据加载器。在底部的示例请求中,将进行三个数据库查询。一个由数据加载器获取两座建筑物,一个用于获取建筑物 1 的房间,另一个用于获取建筑物 2 的房间。相反,我试图为房间使用数据加载器,因此只有两个数据库查询制作。

// Building DB table
ID | Name
1  | Main Campus
2  | Satellite Campus
// Rooms DB table
ID | BuildingId | Name
1  | 1          | Lab
2  | 1          | Dorm
3  | 2          | Theatre
4  | 2          | Gym
// Schema
type Building 
  id: Int!
  name: String!
  rooms(after: String before: String first: PaginationAmount last: PaginationAmount): RoomsConnection


type Room 
  id: Int!
  name: String!
  building: Building!

// Hot Chocolate
public class BuildingType: ObjectType<Building> 
  protected override void Configure(IObjectTypeDescriptor<Building> descriptor)
  
    // ... omitted other fields for brevity

    // Instead of using a resolver, can a data loader be used instead?
    descriptor.Field(b => b.rooms).UsePaging<RoomType>().Resolver(ctx => 
      var building = ctx.Parent<Building>();
      var roomsRepository = ctx.Service<IRoomsRepository>();
      return roomsRepository.GetRoomsByBuildingId(building.Id);
    );
  

// Example request
query 
  a: building(id: 1)
    id,
    name,
    rooms 
      nodes 
        id,
        name
      
    
  ,
  b: building(id: 2)
    id,
    name,
    rooms 
      nodes 
        id,
        name
      
    
  

【问题讨论】:

【参考方案1】:

这不仅是可能的,而且我会说这是 DataLoaders 避免跨对象树发出“N+1”请求的基本用例。 出于您的目的,您可以使用GroupDataLoader。组数据加载器是一个批处理数据加载器,即它收集在一次 graphql 请求往返中对类似实体的请求,并将它们作为单个请求发送到数据源。之后,它缓存结果 - 即在从对象树的任何位置请求时从缓存中返回实体。组数据加载器获取键列表作为输入参数 (IReadOnlyList) 并返回缓存实体列表作为查找 (ILookup)。查找是一种每个键保存多个值的数据类型(而不是每个键保存单个值的字典)。

根据您的具体情况,必须按建筑物 ID 对房间进行分组 - 这是关键。因此,您可以使用组数据加载器以下列方式缓存您的连接(考虑到更改 RoomsRepository 的接口以支持对它的批量请求是值得的,即它必须接受的不是单个建筑物 ID,而是其中一批,我还假设 Room 包含对其建筑 ID 的反向引用):

   public class BuildingType : ObjectType<Building>

    protected override void Configure(IObjectTypeDescriptor<Building> descriptor)
    
        descriptor.Field(b => h.rooms).UsePaging<RoomType>
.Resolver(async (ctx, t) => await ctx.GroupDataLoader<int, Room>("roomsByBuildingGroup",
                    async keys => ctx.Service<IRoomsRepository>().GetRoomsByBuildingIds(keys).ToLookup(r => r.BuildingId))
                .LoadAsync(ctx.Parent<Building>().Id, t));

“roomsByBuildingGroup”是加载程序的名称。为了共享一个缓存并将来自不同对象树位置的所有请求合并到一个单一的缓存中,您必须在通过构建 id 加载房间的所有其他位置使用相同的加载器(即使用相同的加载器名称)。

【讨论】:

以上是关于如何使用 DataLoader 与 Hot Chocolate GraphQL 进行连接的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Dataloader 中使用 Batchsampler

pytorch datasets与dataloader阐释说明

Pytorch的Dataset与Dataloader之间的关系

如何使用 PyTorch 的 DataLoader 确保批次包含来自所有工作人员的样本?

深入浅出 Dataset 与 DataLoader

PyTorch DataLoader 将批次作为列表返回,批次作为唯一条目。如何从我的 DataLoader 获取张量的最佳方式