带有 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 个项目并取消嵌套分块数组。
我正在为这个项目使用 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 个项目并取消嵌套分块数组。