Home > database >  Is there a way to map a sub-collection into a struct while querying the main collection? (Firestore)
Is there a way to map a sub-collection into a struct while querying the main collection? (Firestore)

Time:10-14

Is there a way to map a sub-collection into a struct while querying the main collection?

My sub-collection structure is:

-conversations (collection)
--- "users" : [array of two users]
--- messages (subcollection)
------- "created by": "sender"
------- "date" : timestamp
------- "msg" : "help me guys I'm stuck with this damn chat feature"

I read somewhere the query are shallow meaning you'll only get the fields of the collecting you're querying, but maybe there is another way besides nested query loops after querying the first collection

func getFilteredConversations(query: String) {
        
        if (user != nil) {
            db.collection("conversations").whereField("users", arrayContains: user!.displayName)
                .order(by: "createdTime")
                .addSnapshotListener { (querySnapshot, error) in
                    guard let documents = querySnapshot?.documents else {
                        print("no conversations found")
                        return
                    }
                    
                    //mapping
                    self.chats = documents.map {(queryDocumentSnapshot) -> Conversation in
                        
                        let data = queryDocumentSnapshot.data()
                        let docId = queryDocumentSnapshot.documentID
                        let users = data["users"] as? [String] ?? [""]
                        let unreadmsg = data["hasUnreadMessage"] as? Bool ?? false
                        
                        //MARK: - GET MESSAGES
                        self.db.collection("conversations").document(docId).collection("messages")
                            .order(by: "date")
                            .addSnapshotListener{ (querySnapshot, err) in
                                
                                guard let documents = querySnapshot?.documents else {
                                    print("no messages found")
                                    return
                                }
                                
                                var mensajes = [Message]()
                                
                                mensajes = documents.map {(queryDocumentSnapshot) -> Message in
                                    
                                    let data = queryDocumentSnapshot.data()
                                    let docId = queryDocumentSnapshot.documentID
                                    let createdby = data["created_by"] as? String ?? ""
                                    let msg = data["msg"] as? String ?? ""
                                    let date = data["date"] as? Timestamp ?? Timestamp()
                                    
                                    return Message(createdBy: createdby, msg: msg, date: date, id: docId)
                                    
                                }
                            }
                        
                        print("Users: \(users)")
                        
                        return Conversation(id: docId, users: users, messages: mensajes, hasUnreadMessage: unreadmsg)
                        
                    }
                }
        }
    }

this is the model

struct Conversation: Decodable, Identifiable {
    //var id: UUID { person.id }
    @DocumentID var id: String? = UUID().uuidString
    var users: [String] = [""]
    var messages: [Message] = []
    var hasUnreadMessage : Bool = false
}

struct Message: Decodable {
    var createdBy: String?
    var msg: String?
    @ServerTimestamp var date : Timestamp?
    var id : String?
}

CodePudding user response:

You've already found the answer yourself it seems: Firestore queries are shallow. There is no way to read from the subcollection while reading the parent document. The only way to query across collections is with a collection group query, which doesn't seem to apply here.

Consider if it's worth duplicating the most recent messages from the conversation in the parent document, either through the client-side code or in a Cloud Function. That way you can reduce the number of documents you have to read to get the initial messages to display to the user.

  • Related