我使用Firebase作为iOS应用程序的后端,不知道如何通过他们的云函数构建批量写入。
我的Firestore有两个收藏品,饮料和顾客。每个新饮料和每个新顾客都被分配了一个userId
属性,该属性对应于当前登录用户的uid。这个userId
与对Firestore的查询一起使用,只获取连接到登录用户的饮料和顾客,如下所示:Firestore.firestore().collection("customers").whereField("userId", isEqualTo: Auth.auth().currentUser.uid)
用户可以匿名登录,也可以匿名订阅。问题是,如果他们注销,就无法重新登录到同一个匿名uid。使用RevenueCat SDK,uid也存储为appUserID
,因此我仍然可以访问它,但由于我无法使用uid将用户重新登录到他们的匿名帐户,因此在恢复购买时帮助用户访问其数据的唯一方法是将其数据的userId
字段从旧uid更新为新uid。这就是批量写入的需求
一般来说,我对编程相对陌生,但在云函数、JavaScript和Node.js方面我非常新鲜。不过,我在网上浏览了一下,发现了一个解决方案,我制作了一个可调用的云函数,并将新旧userID与数据对象一起发送,用旧userID查询集合中的文档,并将其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)
}
}
}
}
这是来自云功能日志的错误消息:
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()
返回一个promise。
为了理解你需要什么,你应该首先熟悉这里的承诺概念: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