首页 > 解决方案 > 使用 Firebase 云功能将所有文档从 Firestore 中的主集合复制到新的子集合

问题描述

我在 Firestore 中有一个包含几百个文档的主集合(几个月后将增长到几千个)。

我有一个用例,每次在 /users/ 集合中创建新的用户文档时,我都希望将 master 中的所有文档复制到 /users/{userId}/。

为此,我创建了一个 Firebase 云功能,如下所示:

// setup for new user
exports.setupCollectionForUser = functions.firestore
  .document('users/{userId}')
  .onCreate((snap, context) => {

    const userId = context.params.userId;

    db.collection('master').get().then(snapshot => {

      if (snapshot.empty) {
        console.log('no docs found');
        return;
      }

      snapshot.forEach(function(doc) {

        return db.collection('users').doc(userId).collection('slave').doc(doc.get('uid')).set(doc.data());
      });
    });
 });

这行得通,唯一的问题是,只有大约 200 个文档需要很长时间(约 3-5 分钟)。这真是太糟糕了,因为很大程度上取决于这些文件被复制的速度。我希望这最多不超过几秒钟。此外,这些文件完全显示出来,而不是像它们写的那样,或者至少看起来是这样。

我做错什么了吗?为什么要花这么长时间?

有没有办法可以将此操作分解为多次读取和写入,以便我可以保证在几秒钟内获得最少的文档,而不是等到所有文档都被复制过来?

请指教。

标签: node.jsfirebasegoogle-cloud-firestoregoogle-cloud-functions

解决方案


如果我没记错的话,通过正确管理并行写入Promise.all()并返回 Promises 链,它通常应该会提高速度。

尝试按如下方式调整您的代码:

exports.setupCollectionForUser = functions.firestore
    .document('users/{userId}')
    .onCreate((snap, context) => {

        const userId = context.params.userId;

        return db.collection('master').get().then(snapshot => {  

            if (snapshot.empty) {
                console.log('no docs found');
                return null;
            } else {

                const promises = [];
                const slaveRef = db.collection('users').doc(userId).collection('slave');
                snapshot.forEach(doc => {

                      promises.push(slaveRef.doc(doc.get('uid')).set(doc.data()))

                });

                return Promise.all(promises);

            }

        });

    });

我建议您观看Firebase 视频系列中关于“JavaScript Promises”的 3 个视频,这些视频解释了为什么在后台触发的云函数中返回 Promise 或值是关键。


另请注意,如果您确定要保存在集合中的文档少于 500 个slave,则可以使用批量写入。(您可以将它用于 500 多个文档,但是您必须管理不同批次的批量写入......)


推荐阅读