Kotlin使用merge更新Firestore文档时意外新建文档的修复方法
问题描述
需求:不新增文档的前提下更新/合并Firestore已有文档,通过merge方法将图片URL写入现有文档的对应字段。实际运行时没有更新目标文档,反而创建了带自动生成ID的新文档。
原有问题代码:
val uuid = UUID.randomUUID() val imageName = "$uuid.jpg" val reference = storage.reference val imagereference = reference.child("images").child(imageName) if (selectedPicture != null) { imagereference.putFile(selectedPicture!!).addOnSuccessListener { val uid = FirebaseAuth.getInstance().currentUser!!.uid val uploadPictureReference = storage.reference.child("images").child(imageName) uploadPictureReference.downloadUrl.addOnSuccessListener { val downloadUrl = it.toString() if (uid != null) { val postMap = hashMapOf<String, Any>() postMap.put("downloadUs", downloadUrl) postMap.put("Email", auth.currentUser!!.email!!) postMap.put("date", Timestamp.now()) val new = firestore.collection("ProfPic").document(uid) new.set(postMap, SetOptions.merge()).addOnSuccessListener { }. addOnFailureListener { Toast.makeText(this@MainActivity, it.localizedMessage, Toast .LENGTH_LONG).show() }
问题根因
代码中调用set(SetOptions.merge())时指定的文档路径为ProfPic/[当前登录用户uid],但集合内现有文档使用Firestore自动生成的随机字符串作为ID,和用户uid完全不匹配。Firestore的set操作规则为:指定路径下不存在对应文档时,即使传入merge参数也会创建新文档。当前代码操作的路径和目标存量文档路径完全不一致,自然不会更新原有文档,只会在新路径生成新文档。
此外代码中写入图片地址的字段名为downloadUs,如果原有文档存储图片链接的字段名与此不一致,即使路径匹配,merge操作也只会新增downloadUs字段,不会更新预期的原有图片字段。
修复方案
方案1(长期推荐):统一文档ID规则
用户个人相关的数据(比如头像资料),从创建文档开始就固定用用户uid作为文档ID,不要使用自动生成ID:
- 先迁移现有ProfPic集合里的存量文档:查询每个文档对应的用户uid,将文档ID替换为对应用户的uid,删除原来的随机ID文档
- 后续所有对ProfPic文档的读写,都直接用
firestore.collection("ProfPic").document(uid)作为路径,不需要额外做查询匹配,路径完全对齐就不会出现新建错误文档的问题。
方案2(兼容存量随机ID文档):先查询目标文档再更新
如果暂时不想迁移存量数据,就先根据用户唯一标识(比如已存储的Email、uid字段)查询到对应的老文档,拿到它的文档ID之后再执行merge更新:
// 替换原代码中直接指定uid为文档ID的逻辑 firestore.collection("ProfPic") .whereEqualTo("Email", auth.currentUser!!.email!!) // 用文档中存储的用户唯一标识做匹配 .get() .addOnSuccessListener { querySnapshot -> val targetDoc = querySnapshot.documents.firstOrNull() if (targetDoc != null) { val postMap = hashMapOf<String, Any>() // 注意字段名必须和原有文档对齐,比如原字段是downloadUrl就不要写为downloadUs postMap["downloadUrl"] = downloadUrl postMap["date"] = Timestamp.now() // 对查询到的正确文档路径执行merge更新 targetDoc.reference.set(postMap, SetOptions.merge()) .addOnSuccessListener { /* 编写更新成功后的业务逻辑 */ } .addOnFailureListener { e -> Toast.makeText(this@MainActivity, e.localizedMessage, Toast.LENGTH_LONG).show() } } }
注意:
SetOptions.merge()只会更新传入map中包含的字段,不会覆盖文档内其他未传入的原有字段,符合合并更新的需求。
内容的提问来源于stack exchange,提问作者terlan abaszade
相关产品推荐
相关产品推荐

