You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编写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: "获取并合并数据失败" });
    }
});

几个关键说明:

  1. 效率优化:用Promise.all()并行获取两个集合的数据,比串行请求快很多;
  2. 数据匹配:先把时间数据按course分组,避免嵌套循环,提升合并效率;
  3. 格式匹配:完全按照你要的格式,每个时间记录和课程信息合并成一个独立对象;
  4. 错误处理:加入了try-catch块,出现异常时返回500状态码和错误信息;
  5. CORS修正:原代码中Access-Control-Allow-Methods的写法有误(用了多个参数),新函数里改成了逗号分隔的字符串,建议你同步修改现有函数的这个部分,避免前端调用时出现CORS错误。

内容的提问来源于stack exchange,提问作者Ehsan Nissar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:13:10