使用 Firebase Cloud Functions 批量写入

Batch write with Firebase Cloud Functions

我正在使用 Firebase 作为我的 iOS 应用程序的后端,但不知道如何通过他们的 Cloud Functions 构建批量写入。

我的 Firestore 中有两个集合,饮品和顾客。每个新饮料和每个新客户都分配了一个 userId 属性 对应于当前登录用户的 uid。此 userId 与对 Firestore 的查询一起使用,以仅获取与登录用户相关联的饮料和顾客,如下所示:Firestore.firestore().collection("customers").whereField("userId", isEqualTo: Auth.auth().currentUser.uid)

用户可以匿名登录,也可以匿名订阅。问题是,如果他们注销,就没有办法重新登录到同一个匿名 uid。 uid 也存储为 RevenueCat SDK 的 appUserID,因此我仍然可以访问它,但由于我无法使用 uid 将用户重新登录到他们的匿名帐户,这是帮助用户访问他们的唯一方法恢复购买时的数据是将其数据的 userId 字段从旧 uid 更新为新 uid。这就是需要批量写入的地方。

总的来说,我对编程还比较陌生,但在云函数方面我非常新鲜,JavaScript 和 Node.js。我在网上四处游荡,认为我找到了一个解决方案,我创建了一个可调用的 Cloud Function 并将新旧用户 ID 与数据对象一起发送,查询集合以查找具有旧用户 ID 的文档并将其 userId 字段更新为新的。不幸的是它不起作用,我不知道为什么。

我的代码如下所示:

// Cloud Function

exports.transferData = functions.https.onCall((data, context) => {
  const firestore = admin.firestore();
  const customerQuery = firestore.collection('customers').where('userId', '==', `${data.oldUser}`);
  const drinkQuery = firestore.collection('drinks').where('userId', '==', `${data.oldUser}`);

  const customerSnapshot = customerQuery.get();
  const drinkSnapshot = drinkQuery.get();
  const batch = firestore.batch();

  for (const documentSnapshot of customerSnapshot.docs) {
    batch.update(documentSnapshot.ref, { 'userId': `${data.newUser}` });
  };
  for (const documentSnapshot of drinkSnapshot.docs) {
    batch.update(documentSnapshot.ref, { 'userId': `${data.newUser}` });
  };
  return batch.commit();
});
// Call from app

func transferData(from oldUser: String, to newUser: String) {
    let functions = Functions.functions()
    
    functions.httpsCallable("transferData").call(["oldUser": oldUser, "newUser": newUser]) { _, error in
        if let error = error as NSError? {
            if error.domain == FunctionsErrorDomain {
                let code = FunctionsErrorCode(rawValue: error.code)
                let message = error.localizedDescription
                let details = error.userInfo[FunctionsErrorDetailsKey]
                print(code)
                print(message)
                print(details)
            }
        }
    }
}

这是 Cloud Functions 日志中的错误消息:

Unhandled error TypeError: customerSnapshot.docs is not iterable
    at /workspace/index.js:22:51
    at fixedLen (/workspace/node_modules/firebase-functions/lib/providers/https.js:66:41)
    at /workspace/node_modules/firebase-functions/lib/common/providers/https.js:385:32
    at processTicksAndRejections (internal/process/task_queues.js:95:5)

据我所知,customerSnapshot 是一种叫做 Promise 的东西,我猜这就是为什么我不能迭代它。到目前为止,我的知识太少了,不知道如何处理查询返回的这些 Promise。

我想我可以强制用户在订阅之前创建一个登录名,但现在我已经走到这一步了,这感觉像是一个胆小鬼的出路。我宁愿两种选择都可用并做出决定,而不是走强制路径。另外,如果我弄明白了,我会学到更多 JavaScript!

非常感谢任何帮助!

编辑:

解决方案:

// Cloud Function

exports.transferData = functions.https.onCall(async(data, context) => {
  const firestore = admin.firestore();
  const customerQuery = firestore.collection('customers').where('userId', '==', `${data.oldUser}`);
  const drinkQuery = firestore.collection('drinks').where('userId', '==', `${data.oldUser}`);

  const customerSnapshot = await customerQuery.get();
  const drinkSnapshot = await drinkQuery.get();
  const batch = firestore.batch();

  for (const documentSnapshot of customerSnapshot.docs.concat(drinkSnapshot.docs)) {
    batch.update(documentSnapshot.ref, { 'userId': `${data.newUser}` });
  };
  
  return batch.commit();
});

正如您已经猜到的那样,调用 customerQuery.get() returns 一个承诺。

为了理解你需要什么,你应该首先在这里熟悉承诺的概念:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

对于您的用例,您可能最终会使用 then 回调:

customerQuery.get().then((result) => {
    // now you can access the result
}

或者通过使用 await 语句使方法调用同步:

const result = await customerQuery.get()
// now you can access the result