TL; DR 提取少量文档需要很多时间
场景:
我为每个帐户都有一个集合,每个帐户都包含一个projects
子集合和tasks
子集合。任务子集合中的每个文档可以进一步包含checkLists
子集合中的清单
注意:
插图:
someTopLevelDB
|
|____ accountId1
| |______projects
| | |_______ projectId1
| |
| |______tasks
| |________taskId1 (belongs to projectId1)
| | |
| | |________checkLists
| | |
| | |_____checkListId1
| |
| |________taskId2 (standalone)
用例: 当用户单击重复的项目(从用户界面)时,我必须创建整个项目的副本,即所有任务,清单等。
代码: 这样做的过程很慢,当我分析代码时,此代码段花了很多时间来执行。该代码段提取了所有任务及其清单
let db = admin.firestore();
function getTasks(accountId) {
return db.collection('someTopLevelDB')
.doc(accountId)
.collection('tasks')
.where('deleted', '==', false)
.get();
}
function getCheckLists(accountId, taskId) {
return db.collection('someTopLevelDB')
.doc(accountId)
.collection('tasks')
.doc(taskId)
.collection('checkLists')
.where('deleted', '==', false)
.get();
}
async function getTasksAndCheckLists(accountId) {
try {
let records = { tasks: [], checkLists: [] };
// prepare tasks details
const tasks = await getTasks(accountId);
const tasksQueryDocumentSnapshot = tasks.docs;
for (let taskDocumentSnapshot of tasksQueryDocumentSnapshot) {
const taskId = taskDocumentSnapshot.id;
const taskData = taskDocumentSnapshot.data();
const taskDetails = {
id: taskId,
...taskData
};
records.tasks.push(taskDetails);
// prepare check list details
checkListQueryDocumentSnapshot = (await getCheckLists(accountId, taskId)).docs;
for (let checkListDocumentSnapshot of checkListQueryDocumentSnapshot) {
const checkListId = checkListDocumentSnapshot.id;
const checkListData = checkListDocumentSnapshot.data();
const checkListDetails = {
id: checkListId,
...checkListData
};
records.checkLists.push(checkListDetails);
}
}
console.log(`successfully fetched ${records.tasks.length} tasks and ${records.checkLists.length} checklists`);
return records;
} catch (error) {
console.log('Error fetching docs ====>', error);
}
}
// Call the function to fetch records
getTasksAndCheckLists('someAccountId')
.then(result => {
console.log(result);
return true;
})
.catch(error => {
console.error('Error fetching docs ===>', error);
return false;
});
执行状态:
在220.532秒内成功获取627个任务和51个清单
我得出的结论是,检索清单会拖慢整个过程的速度,因为任务的检索相当快。
所以我的问题如下:
谢谢。
答案 0 :(得分:3)
此问题是由于在此处的for循环中使用await
引起的:
checkListQueryDocumentSnapshot = (await getCheckLists(accountId, taskId)).docs;
这会导致您的for循环停滞不前,直到获取该特定任务的检查清单为止。
避免这种情况的方法是使用Promise链异步处理检查表。遍历任务时,将为该任务的检查清单创建请求,向其结果添加侦听器,然后将其发送并立即移至下一个任务。
在您的数据结构中,检查表与服务器上的特定任务相关,但在上面的代码中它们与它们无关。如果您仅使用带有push()
的标准数组,则在使用相同的数据结构异步工作时,这将意味着它们将与您的任务不协调(例如,任务B的清单提取可能会在任务A之前完成)。为了解决这个问题,在下面的代码中,我已将清单嵌套在taskDetails对象下,以便它们仍保持链接。
async function getTasksAndCheckLists(accountId) {
try {
let taskDetailsArray = [];
// fetch task details
const tasks = await getTasks(accountId);
// init Promise holder
const getCheckListsPromises = [];
tasks.forEach((taskDocumentSnapshot) => {
const taskId = taskDocumentSnapshot.id;
const taskData = taskDocumentSnapshot.data();
const taskDetails = {
id: taskId,
checkLists: [], // for storing this task's checklists
...taskData
};
taskDetailsArray.push(taskDetails);
// asynchronously get check lists for this task
let getCheckListPromise = getCheckLists(accountId, taskId)
.then((checkListQuerySnapshot) => {
checkListQuerySnapshot.forEach((checkListDocumentSnapshot) => {
const checkListId = checkListDocumentSnapshot.id;
const checkListData = checkListDocumentSnapshot.data();
const checkListDetails = {
id: checkListId,
...checkListData
};
taskDetails.checkLists.push(checkListDetails);
});
});
// add this task to the promise holder
getCheckListsPromises.push(getCheckListPromise);
});
// wait for all check list fetches - this is an all-or-nothing operation
await Promise.all(getCheckListsPromises);
// calculate the checklist count for all tasks
let checkListsCount = taskDetailsArray.reduce((acc, v) => acc+v.checkLists.length, 0);
console.log(`successfully fetched ${taskDetailsArray.length} tasks and ${checkListsCount} checklists`);
return taskDetailsArray;
} catch (error) {
console.log('Error fetching docs ====>', error);
}
}
通过这些更改,您应该看到函数运行的时间大大减少了。根据您提供的时间,我估计它将下降到大约2-3秒。