Flutter中如何用Firebase服务器时间戳作为字段键存储记录?
问题:Firestore中如何用服务器时间戳作为字段键?
需求是在Firebase/Firestore文档中添加以服务器时间戳为键的字段,目前遇到两个问题:
- 先把
FieldValue.serverTimestamp()存为值,再读取出来当键,流程繁琐且不合理 - 直接调用
FieldValue.serverTimestamp().toString()作为键,得到的是无效字符串FieldValue(Instance of 'MethodChannelFieldValue')
当前使用的Dart代码:
saveUserAnswersToFirestore(List<RemoteAnswerModel> answers) async { User? user = FirebaseAuth.instance.currentUser; DocumentReference<Map<String, dynamic>> userAnswerDoc = FirebaseFirestore.instance .collection('answers') .doc('${user?.phoneNumber}'); final now = '${Timestamp.now().millisecondsSinceEpoch}'; //FieldValue.serverTimestamp().toString(); var answersMap = answers.map((e) => e.toJson()).toList(); try { await userAnswerDoc.update({ now : answersMap }); } catch (e) { await userAnswerDoc.set({ now : answersMap }); } }
预期存储效果:
解决方案
方案1:使用客户端生成的时间戳(简单高效,适合多数场景)
如果对时间精度要求不是绝对严格,直接用客户端生成的时间戳是最简便的方式,也就是你当前代码里的Timestamp.now().millisecondsSinceEpoch。可以优化代码,用set方法的merge: true参数替代try-catch逻辑,避免重复代码:
saveUserAnswersToFirestore(List<RemoteAnswerModel> answers) async { User? user = FirebaseAuth.instance.currentUser; if (user?.phoneNumber == null) return; // 增加空值判断 DocumentReference<Map<String, dynamic>> userAnswerDoc = FirebaseFirestore.instance .collection('answers') .doc(user!.phoneNumber!); final timestampKey = Timestamp.now().millisecondsSinceEpoch.toString(); final answersMap = answers.map((e) => e.toJson()).toList(); // 使用merge: true,存在则更新,不存在则创建 await userAnswerDoc.set( {timestampKey: answersMap}, SetOptions(merge: true), ); }
注意:客户端时间可能和Firestore服务器时间有几秒误差,如果业务要求必须严格用服务器时间,看方案2。
方案2:用事务+批量写入获取服务器时间戳作为键
如果必须使用Firestore服务器生成的时间戳作为字段键,可以通过事务先获取服务器时间,再写入目标字段:
saveUserAnswersToFirestore(List<RemoteAnswerModel> answers) async { User? user = FirebaseAuth.instance.currentUser; if (user?.phoneNumber == null) return; final userAnswerDoc = FirebaseFirestore.instance .collection('answers') .doc(user!.phoneNumber!); final answersMap = answers.map((e) => e.toJson()).toList(); // 通过事务获取服务器时间戳 await FirebaseFirestore.instance.runTransaction((transaction) async { // 第一步:写入临时字段获取服务器时间戳 transaction.update(userAnswerDoc, {'_temp_timestamp': FieldValue.serverTimestamp()}); // 提交事务后读取文档 final docSnapshot = await userAnswerDoc.get(); final serverTimestamp = docSnapshot.get('_temp_timestamp') as Timestamp; final timestampKey = serverTimestamp.millisecondsSinceEpoch.toString(); // 第二步:写入目标字段并删除临时字段 transaction.update(userAnswerDoc, { timestampKey: answersMap, '_temp_timestamp': FieldValue.delete(), }); }); }
这个方案通过事务保证操作的原子性,先获取服务器时间戳,再用它作为键写入数据,同时清理临时字段,完全使用Firestore服务器时间。
内容的提问来源于stack exchange,提问作者Vitaliy A
相关产品推荐
相关产品推荐

