带有 Firebase Firestore 的 GraphQL 数据加载器

GraphQL dataloader with Firebase Firestore

我正在为这个项目使用 Firebase Firestore,我正在尝试实施数据加载器以减少对服务器的请求。

在这种特殊情况下,我有一个结构如下的类别集合: (我将使用打字稿来更好地定义我的代码)

type Category = {
    name: String
    parent?: Category
}

当我查询 graphql 端点时,在解析器中我验证该类别是否有父级,如果存在,我查询数据库以获取详细信息。数据加载器来了。

这是我的 Firestore Dataloader 实现:

 private dataLoader = new DataLoader(async (ids: readonly string[]) => {
    // firestore limits batches to 10
    let batches: Category[] = [];
    const tmp = [...ids];
    while (tmp.length) {
        const batch = tmp.splice(0, 10);
        const result = await this.collection.where(FieldPath.documentId(), 'in', [...batch]).get();
        batches = [...batches, ...result.docs.map(d => ({ id: d.id, ...d.data() } as Category))];
    }

    return batches;
});

解析器本身调用此方法

public async getById(id: string): Promise<Category> {
    const foo = await this.dataLoader.load(id);
    console.log('required', id, 'received', foo.id);
    return foo;
}

但是正如您在此示例输出中看到的那样,它把时间搞得一团糟:

required ztdOcVv9JdNp6U54ySed received 4jXbScd1qTwPStXocaoc
required Qnb86GsOy0gSgGxmMqYR received NeuCCRrJyurQTxvlAVhs
required NeuCCRrJyurQTxvlAVhs received Qnb86GsOy0gSgGxmMqYR
required 4jXbScd1qTwPStXocaoc received ztdOcVv9JdNp6U54ySed

我的理解是异步会在某处造成一些麻烦,但不知道在哪里或如何解决这个问题。这就是为什么我在这里寻求帮助。

提前致谢

您需要 return DataLoader 函数中的 ids 循环,将获取的对象(在本例中为类别)映射到相应的 ID。

下面是我如何实现类似的东西:

async function getItemsByIds(ids: string[]) {
  // where in supports max 10 values
  // https://firebase.google.com/docs/firestore/query-data/queries
  const chunks = _.chunk(ids, 10);
  const snapshots = await Promise.all(
    chunks.map((chunk) =>
      admin
        .firestore()
        .collection('items')
        .where(admin.firestore.FieldPath.documentId(), 'in', chunk)
        .get()
    )
  );
  return snapshots.flatMap((snapshot) => {
    return snapshot.docs.map((doc) => ({
      id: doc.id,
      ...doc.data(),
    }));
  });
}

const itemDataLoader = new DataLoader(async (ids) => {
  const items = await getItemsByIds(ids);
  return ids.map((id) => {
    const item = items.find((item) => item.id === id);
    if (!item) {
      return new Error('Item not found');
    }
    return item;
  });
})

注意:我使用 lodash 来帮助每个请求分块 10 个项目并取消嵌套分块数组。