You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 22:51:30