编写Firebase Cloud Function合并两个Firestore集合数据
合并Cloud Firestore两个集合数据的Cloud Function实现
我已经帮你梳理了需求:你有两个可以正常向Firestore提交数据的Cloud Function,分别用于创建排课记录和添加课程时间,现在需要一个新的Function,把这两个集合的数据通过course字段关联合并,返回你指定格式的JSON结果。
首先看一下你期望的返回格式:
[ { "course": "xxx", "client": "hello", "location": "lahore", "instructor": "ahsan", "bidding": true, "price": "400", "totalHours": "15", "maxStudents": "30", "listing": true, "closeDays": "20", "assistants": "hassan", "publicNotes": "nothing to say", "internalNotes": "nothing to say", "date": "25/9/2019", "hour": "3", "minute": "15", "timeofday": "PM", "tohour": "6", "tominute": "15", "totimeofday": "PM" } ]
接下来是你提供的现有代码:
现有提交数据的Cloud Function
1. 排课提交函数
// 排课 exports.scheduleClass = functions.https.onRequest((req, res) => { res.set('Access-Control-Allow-Origin', '*'); res.set('Access-Control-Allow-Methods', 'GET', 'POST'); res.set('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.end(); } else { if(req.body.course != null && req.body.client != null && req.body.location != null && req.body.instructor != null && req.body.bidding != null && req.body.price != null && req.body.totalHours != null && req.body.maxStudents != null && req.body.listing != null && req.body.closeDays != null && req.body.assistants != null && req.body.publicNotes != null && req.body.internalNotes != null || req.body.course != undefined && req.body.client != undefined && req.body.location != undefined && req.body.instructor != undefined && req.body.bidding != undefined && req.body.price != undefined && req.body.totalHours != undefined && req.body.maxStudents != undefined && req.body.listing != undefined && req.body.closeDays != undefined && req.body.assistants != undefined && req.body.publicNotes != undefined && req.body.internalNotes != undefined) { let docId = Math.floor(Math.random() * (99999 - 00000)); let newClass = { "course": req.body.course, "client": req.body.client, "location": req.body.location, "instructor": req.body.instructor, "bidding": req.body.bidding, "price": req.body.price, "totalHours": req.body.totalHours, "maxStudents": req.body.maxStudents, "listing": req.body.listing, "closeDays": req.body.closeDays, "assistants": req.body.assistants, "publicNotes": req.body.publicNotes, "internalNotes": req.body.internalNotes } usersClasses.add(newClass).then(snapshot => { res.send(200, { "message": "Class was successfully created" }) }); } else { res.send(400, { "message": "All fields are required" }) } } });
2. 添加课程时间提交函数
// 添加课程时间 exports.addTimes = functions.https.onRequest((req, res) => { res.set('Access-Control-Allow-Origin', '*'); res.set('Access-Control-Allow-Methods', 'GET', 'POST'); res.set('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.end(); } else { if(req.body.course != null && req.body.date != null && req.body.hour != null && req.body.minute != null && req.body.timeofday != null && req.body.tohour != null && req.body.tominute != null && req.body.totimeofday != null || req.body.course != undefined && req.body.date != undefined && req.body.hour != undefined && req.body.minute != undefined && req.body.timeofday != undefined && req.body.tohour != undefined && req.body.tominute != undefined && req.body.totimeofday != undefined) { let docId = Math.floor(Math.random() * (99999 - 00000)); let newTimes = { "course": req.body.course, "tohour": req.body.tohour, "tominute": req.body.tominute, "totimeofday": req.body.totimeofday, "date": req.body.date, "hour": req.body.hour, "minute": req.body.minute, "timeofday": req.body.timeofday, } classtimesCollection.add(newTimes).then(snapshot => { res.send(200, { "message":"Time has been added" }) }); } else { res.send(400, { "message": "All fields are required" }) } } });
单个集合数据获取示例
// 获取课程安排 exports.getClassesSchedule = functions.https.onRequest((req, res) => { res.set('Access-Control-Allow-Origin', '*'); res.set('Access-Control-Allow-Methods', 'GET', 'POST'); res.set('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.end(); } else { let allClasses = []; usersClasses.get() .then(snapshot => { snapshot.forEach(doc => { allClasses.push(doc.data()); }); res.send(allClasses); }) .catch(err => { console.log("Error getting documents", err); }); } });
合并两个集合数据的Cloud Function实现
这里我给你写好了实现代码,思路是并行获取两个集合的数据,先把时间数据按course分组,再和课程信息合并,完全匹配你要的返回格式:
// 合并课程与时间数据并返回 exports.getMergedClassData = functions.https.onRequest(async (req, res) => { // 设置CORS头部,修正了原代码中Methods的格式问题 res.set('Access-Control-Allow-Origin', '*'); res.set('Access-Control-Allow-Methods', 'GET, POST'); res.set('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.end(); return; } try { // 并行获取两个集合的数据,提升效率 const [classesSnapshot, timesSnapshot] = await Promise.all([ usersClasses.get(), classtimesCollection.get() ]); // 将时间数据按course分组,方便后续快速匹配 const timesByCourse = {}; timesSnapshot.forEach(doc => { const timeData = doc.data(); const courseKey = timeData.course; if (!timesByCourse[courseKey]) { timesByCourse[courseKey] = []; } timesByCourse[courseKey].push(timeData); }); // 合并课程信息和对应的时间数据 const mergedData = []; classesSnapshot.forEach(doc => { const classInfo = doc.data(); const course = classInfo.course; // 获取当前课程对应的所有时间记录 const relatedTimes = timesByCourse[course] || []; // 每个时间记录对应一个合并后的对象(匹配你要的格式) relatedTimes.forEach(timeInfo => { // 合并两个对象,重复的course字段会自动覆盖(值相同不影响) const mergedItem = { ...classInfo, ...timeInfo }; mergedData.push(mergedItem); }); // 可选:如果某个课程没有时间记录,也可以把课程信息加入结果 // if (relatedTimes.length === 0) { // mergedData.push(classInfo); // } }); // 返回合并后的结果 res.status(200).send(mergedData); } catch (error) { console.error("合并数据时出错:", error); res.status(500).send({ message: "获取并合并数据失败" }); } });
几个关键说明:
- 效率优化:用
Promise.all()并行获取两个集合的数据,比串行请求快很多; - 数据匹配:先把时间数据按
course分组,避免嵌套循环,提升合并效率; - 格式匹配:完全按照你要的格式,每个时间记录和课程信息合并成一个独立对象;
- 错误处理:加入了try-catch块,出现异常时返回500状态码和错误信息;
- CORS修正:原代码中
Access-Control-Allow-Methods的写法有误(用了多个参数),新函数里改成了逗号分隔的字符串,建议你同步修改现有函数的这个部分,避免前端调用时出现CORS错误。
内容的提问来源于stack exchange,提问作者Ehsan Nissar
相关产品推荐
相关产品推荐

