Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Structuring data for chat app in Firebase

Im following Firebase guide to structuring data for a chat app. They suggest the structure as seen below.

{
  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    },
    "two": { ... },
    "three": { ... }
  },

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": {
    // we'll talk about indices like this below
    "one": {
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    },
    "two": { ... },
    "three": { ... }
  },

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // converation ID
  "messages": {
    "one": {
      "m1": {
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... },
      "m3": { ... }
    },
    "two": { ... },
    "three": { ... }
  }
}

How do I structure my user data so that I can easily display a list of all of the chats they are part of and for each one of them display the last message and timestamp. If I do the following structure:

   "users": {
    "ghopper": {
      "name": "Gary Hopper",
      "chats": {
          "one: true",
          "two": true
      }
    },
    "alovelace" { ... }
  },

I can easily get a list of each chat group for a specific user, for example ghopper, by doing (in swift):

ref.child("users").child("ghopper").child("chats").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
  //do something with data
}

However I won't have the lastMessage and timestamp in this snapshot. What do I need to do to access this data?

  • Duplicate all this data for each user? i.e adding users/ghopper/chats/one/ {"lastMessage": "ghopper: Relay malfunction found. Cause: moth.", "timestamp" : 1459361875666}
  • Make a query for "chats/specificGroupId" for each chat that the user is part of (adding multiple listners)?
  • Some other way?
like image 867
Nilsymbol Avatar asked May 29 '16 20:05

Nilsymbol


People also ask

What data structure does Firebase use?

How data is structured: it's a JSON tree. All Firebase Realtime Database data is stored as JSON objects. You can think of the database as a cloud-hosted JSON tree. Unlike a SQL database, there are no tables or records.

How do you save chat messages on firestore?

Enable Cloud Firestore The web app uses Cloud Firestore to save chat messages and receive new chat messages. You'll need to enable Cloud Firestore: In the Firebase console's Build section, click Firestore Database. Click Create database in the Cloud Firestore pane.

Is Firebase free for chat app?

If you're new to Firebase, you'll need to create an account. Don't worry! It's easy and free.


2 Answers

How do I structure my user data so that I can easily display a list of all of the chats they are part of and for each one of them display the last message and timestamp.

Change the chats structure a tad by adding users who are in the chat node

"chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
      users
       uid_1: true
       uid_3: true
    },
    "two": { ... },

Then you can deep query for all chats a particular user is part of - this will return the chats uid_3 is involved in

chatsRef.queryOrderedByChild("users/uid_3").queryEqualToValue(true)
     .observeSingleEventOfType(.Value, withBlock: { snapshot in

     //.Value can return multiple nodes within the snapshot so iterate over them
     for child in snapshot.children {
          let lastmsg = child.value["lastMessage"] as! String
          let timestamp = child.value["timestamp"] as! String
          print(lastmsg)
          print(timestamp)
     }     
})

Note that each firebase user has a discreet user id obtained when the user is created via auth.uid. This should (generally) be used as the key for each user.

like image 126
Jay Avatar answered Oct 04 '22 19:10

Jay


In the block where you have a list of all the chats a user is in, can you do:

var dictionary: [String: Long]   
var lastMessage: String 
for chat in listOfChatsUserIsIn
    ref.child("chats").child("\(chat)").child("lastMessage").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
        lastMessage = snapshot
        ref.child("chats").child("\(chat)").child("timestamp").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
            //add timestamp and last message to dictionary
        }
    }

I don't know how correct my syntax is. Still learning firebase. But, I think this is basically your second suggestion. Don't know how else you would get the data. This would be O(2n) though which isn't bad.

[[Update 1]]

I was being lazy with my code. I put lastMessage = snapshot to save it so you could add it to the dictionary in the next block.

As for Firebase being asynchronous. I think this would still work as long as you use either the timestamp or message as the key and the other as the value in the dictionary. It may be populated out of order, but you could always sort it by timestamp later. Although, yes, this probably is not best practice.

Jay, I like your solution. Wouldn't you have to also list uid_2: false?

Unfortunately, it seems like both these database structures grow by n^2 as users -> inf and as chats -> inf.

like image 25
Sharud Avatar answered Oct 04 '22 20:10

Sharud