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

Kotlin中Firebase fetchArea返回空列表问题排查

问题描述

使用Kotlin开发应用,基于Firebase存储和读取数据:

  • 调用saveArea()函数可正常存储数据
  • 调用fetchData()函数能正常获取指定数据
  • 调用fetchArea()获取全量用户数据时,返回空列表

Firestore控制台初始显示:

this document does not exist, it will not appear in queries or snapshot

手动点击「Add field」添加数据再删除后,控制台提示变为:

This document has no data.

此时fetchArea()恢复正常,可获取Users下所有数据。

附相关代码:

class FireBaseSourceImpl @Inject constructor(private val database: FirebaseFirestore) : FireBaseSource {


    override suspend fun saveArea(dataModel: DataModel): Result<Unit> {

        return kotlin.runCatching {

            val post = hashMapOf(
                "message" to dataModel.message,
                "title" to dataModel.title,
                "photoUrl" to dataModel.photoUrl,
                "timeStamp" to dataModel.timestamp

            )

            database.collection("Users").document(dataModel.currentUser)
                .collection("construcitonName").document(dataModel.constructionArea).set(post)
                .await()
        }
    }


    override suspend fun fetchData(
        currentUser: String, constructionName: String
    ): Result<List<DataModel>> {
        return kotlin.runCatching {
            val dataModelList = mutableListOf<DataModel>()

            val documentSnapshot =
                database.collection("Users").document(currentUser).collection("construcitonName")
                    .document(constructionName).get().await()

            if (documentSnapshot.exists()) {
                val data = documentSnapshot.data
                val message = data?.get("message") as? String ?: ""
                val title = data?.get("title") as? String ?: ""
                val photoUrl = data?.get("photoUrl") as? String ?: ""
                val timestamp = data?.get("timeStamp") as? Long ?: 0L

                val retrievedDataModel = DataModel(
                    message, title, photoUrl, timestamp, currentUser, constructionName
                )
                dataModelList.add(retrievedDataModel)
            }

            dataModelList.toList()
        }
    }

    override suspend fun fetchArea(): Result<List<ConstructionName>> {
        return kotlin.runCatching {
            val constructionNames = mutableListOf<ConstructionName>()

            val usersSnapshot = database.collection("Users")
                .get().await()

            if (usersSnapshot.isEmpty){
                Log.d("FirebaseDebug", "usersSnapshot is empty")
            }

            for (userDocument in usersSnapshot.documents) {
                val userId = userDocument.id
                val constructionsSnapshot = userDocument.reference.collection("construcitonName").get().await()

                for (constructionDocument in constructionsSnapshot.documents) {
                    val data = constructionDocument.data
                    val message = data?.get("message") as? String ?: ""
                    val title = data?.get("title") as? String ?: ""
                    val photoUrl = data?.get("photoUrl") as? String ?: ""
                    val timestamp = data?.get("timeStamp") as? Number ?: 0

                    val constructionName = ConstructionName(
                        userId,
                        constructionDocument.id,
                        message,
                        title,
                        photoUrl,
                        timestamp.toLong()
                    )
                    constructionNames.add(constructionName)
                }
            }

            constructionNames.toList()
        }
    }

}
问题分析与解决

核心原因

Firestore中,仅创建子集合但未给父文档写入任何字段的情况下,父文档属于「幽灵文档」(ghost document),这类文档不会出现在集合查询结果中。

你的saveArea()函数直接在Users/{currentUser}/construcitonName/{constructionArea}路径写入数据,相当于跳过了Users/{currentUser}父文档的显式创建,此时Users/{currentUser}就是幽灵文档,导致database.collection("Users").get()无法查询到该文档,最终fetchArea()返回空列表。

而手动添加字段再删除后,Users/{currentUser}变成了「空数据但存在的文档」,这类文档会被集合查询捕获,所以fetchArea()恢复正常。

解决方案

修改saveArea()函数,在写入子集合数据前,先显式创建父文档(即使父文档没有任何业务字段):

override suspend fun saveArea(dataModel: DataModel): Result<Unit> {
    return kotlin.runCatching {
        val post = hashMapOf(
            "message" to dataModel.message,
            "title" to dataModel.title,
            "photoUrl" to dataModel.photoUrl,
            "timeStamp" to dataModel.timestamp
        )
        // 先显式创建用户文档(写入空数据即可)
        database.collection("Users").document(dataModel.currentUser).set(hashMapOf())
            .await()
        // 再写入子集合数据
        database.collection("Users").document(dataModel.currentUser)
            .collection("construcitonName").document(dataModel.constructionArea).set(post)
            .await()
    }
}

如果需要给用户文档添加基础信息,也可以在创建时写入:

database.collection("Users").document(dataModel.currentUser).set(
    hashMapOf("createdAt" to FieldValue.serverTimestamp())
).await()

补充说明

  • fetchData()能正常工作是因为它直接通过文档ID定位到子集合文档,不需要查询父集合,所以不受幽灵文档影响。
  • Firestore的集合查询只会返回存在的文档(包含空数据但已显式创建的文档),幽灵文档不会被包含在内。

内容的提问来源于stack exchange,提问作者Muhammed Ali ERKOÇ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 03:30:04