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

添加.whereField后无法从Firestore检索Gemini ChatBot对话文档

问题排查:Firestore查询userID无法获取文档,但删除逻辑正常

我在Firebase项目中集成了Gemini ChatBot扩展,通过sendMessage函数将profile!.uid写入userID字段,实现仅查询当前用户的聊天文档。目前能成功添加文档、扩展也能生成响应,但fetchMessages函数添加.whereField("userID", isEqualTo: profile!.uid)条件后无法获取文档,相同条件在removeAllMessages中却能正常删除对应用户的所有文档。

相关代码

sendMessage函数

func sendMessage(_ message: String) {
    guard let profileId = profile?.uid else {
        print("Profile ID not found")
        return
    }
    
    let data: [String: Any] = [
        "prompt": message,
        "userID": profileId,
    ]
    
    db.collection(collectionPath).addDocument(data: data) { error in
        if let error = error {
            print("Error adding document: \(error)")
        } else {
            print("Document added for userID: \(profileId)")
        }
    }
}

fetchMessages函数

func fetchMessages() {
    print("Fetch messages called for userID: \(profile!.uid)")
    db.collection(collectionPath)
        .whereField("userID", isEqualTo: profile!.uid)
        .order(by: "createTime", descending: false)
        .addSnapshotListener { [weak self] querySnapshot, error in
            guard let self else { return }
            guard let documents = querySnapshot?.documents else {
                print("No documents found")
                return
            }
            
            self.messages = documents.compactMap { queryDocumentSnapshot -> [ChatMessage]? in
                do {
                    let document = try queryDocumentSnapshot.data(as: ChatDocument.self)
                    
                    let prompt = ChatMessage(text: document.prompt, isUser: true, state: .COMPLETED)
                    let response = ChatMessage(text: document.response ?? "", isUser: false, state: document.status.chatState)
                    print("RESPONSE FROM DOCUMENTS")
                    print("fetched \(response)")
                    
                    return [prompt, response]
                } catch {
                    print(error.localizedDescription)
                    return nil
                }
            }.flatMap { $0 }
        }
}

数据模型定义

struct ChatDocument: Codable {
    let createTime: Timestamp
    let prompt: String
    let response: String?
    let status: Status
    let userID: String

    struct Status: Codable {
        let startTime: Timestamp?
        let completeTime: Timestamp?
        let updateTime: Timestamp
        let state: String
        let error: String?
        
        var chatState: ChatState {
            return ChatState(rawValue: state) ?? .PROCESSING
        }
    }
}

struct ChatMessage: Hashable {
    
    @ServerTimestamp var createdTime: Timestamp?
    @DocumentID var uid: String?
    
    private(set) var id: UUID = .init()
    var text: String?
    var isUser: Bool
    var state: ChatState = .PROCESSING
    
    
    var message: String {
        switch state {
        case .COMPLETED:
            return text ?? ""
        case .ERROR:
            return "Something went wrong. Please try again."
        case .PROCESSING:
            return "..."
        }
    }
    
}

enum ChatState: String, Codable {
    case COMPLETED
    case ERROR
    case PROCESSING
}

removeAllMessages函数

func removeAllMessages() async {
    
    do {
        let querySnapshot = try await db.collection(collectionPath)
            .whereField("userID", isEqualTo: profile!.uid)
            .getDocuments()
        
        for document in querySnapshot.documents {
            do {
                try await db.collection(collectionPath).document(document.documentID).delete()
                self.messages.removeAll()
                print("Document with ID \(document.documentID) successfully removed!")
            } catch {
                print("Error removing document with ID \(document.documentID): \(error)")
            }
        }
    } catch {
        print("Error querying documents for profileId \(profile!.uid): \(error)")
    }
}

Firestore数据截图

Firestore数据截图

可能原因及解决方法

1. 复合索引缺失

Firestore中同时使用where和order(by)查询时,必须创建对应复合索引。删除操作不需要排序,因此能正常执行,但带排序的查询会因缺少索引失败。

解决方法:

  • 查看Xcode控制台输出,会自动生成一个指向Firebase控制台的索引创建链接,点击即可自动生成所需索引。
  • 手动到Firebase控制台,找到目标集合,创建包含userID(等于条件)和createTime(排序字段)的复合索引。

2. 字段大小写不匹配

Firestore字段名区分大小写,若文档中实际字段名(如userid)与代码中userID大小写不一致,会导致查询无结果。可对照截图确认字段名完全匹配。

3. 解码失败过滤数据

fetchMessages中使用data(as: ChatDocument.self)解码时,若模型结构与Firestore文档不匹配会抛出错误,导致文档被compactMap过滤掉。

解决方法:

  • 检查ChatDocument的字段与Firestore文档完全对齐,比如createTime是否存在、status子字段是否一致。
  • 给可选字段添加默认值,或通过CodingKeys映射字段名差异。

4. 强制解包导致查询条件错误

若调用fetchMessages时profile未完全加载,profile!.uid可能取到错误值,导致查询无结果。

解决方法:

  • 修改fetchMessages为可选绑定版本,避免强制解包:
func fetchMessages() {
    guard let userId = profile?.uid else {
        print("Profile not loaded yet")
        return
    }
    print("Fetch messages called for userID: \(userId)")
    db.collection(collectionPath)
        .whereField("userID", isEqualTo: userId)
        .order(by: "createTime", descending: false)
        .addSnapshotListener { [weak self] querySnapshot, error in
            // 剩余逻辑不变
        }
}

内容的提问来源于stack exchange,提问作者RileyDev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 19:33:12