0

So I've come across this thread.

Structuring data for chat app in Firebase

I would just like to give a sample for my firebase db structure ( similar in firebase docs).

{
  "chatMetadata": {
    "chatroom_id_1": {
      "title": "Some Chatroom Title",
      "lastMessage": "Some Message",
      "timestamp": 1459361875666
      "users": {
          "uid_1": true,
          "uid_3": true
      }
    },
    "chatroom_id_2": { ... }
  },

  "users": {
    "uid_1": {
      "chatRooms": {
        "chatroom_id_1": true,
        "chatroom_id_2": true
      },
    },
    "uid_3": { ... }
  },

  "chatrooms": {
    "chatroom_id_1": {
      "m1": {
        "name": "uid_1",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... }
    },
    "chatroom_id_2": { ... }
  }
}

For me to implement like a recent chat messages. I'll probably need the "/chatMetadata/" data since it has the lastMessage

To query it in client we need the answer in the link above to get it in one reference alone.

//chatMetaDataRef = root of "chatMetadata"
chatMetaDataRef.queryOrderedByChild("users/uid_3").queryEqualToValue(true)
             .observeSingleEventOfType(.Value, withBlock: { snapshot in
             // Do something here
})

Problem with this is all users should have read access to "/chatMetadata" node for that to work.

Here is my security rule for it

{
  "rules": {
    ".read": "auth.uid === 'someServiceWorker'",
    ".write": "auth.uid === 'someServiceWorker'",
    "chatMetadata": {
      "$chatroomId":{
        ".read": "data.child('users/'+auth.uid).exists()"
      },
      ".read": "auth != null"
    },
    "chatrooms": {
      "$chatroomId": {
        ".read": "root.child('chatMetadata/'+ $chatroomId + '/users/'+auth.uid).exists()",
      }
    },
    "users": {
      "$userId": {
        ".read": "auth.uid === $userId"
      }
    }
  }
}

Now my question is how should we implement the security rule for this one such that users shall not have access to all chatMetadata(because if i remove "auth!=null", my query above will not work. Or if it is not possible how should we restructure the data such that we can query it with one reference alone?

P.S.

Reason for the question is I didn't like the idea that I will loop over "users/$uid/chatrooms" and query each reference I receive from it. Like the 2nd answer in the link above.

for chat in listOfChatsUserIsIn
    ref.child("chatMetadata").child("\(chat)").child("lastMessage").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
        lastMessage = snapshot
        // do something here
    }
Community
  • 1
  • 1
bumblebeen
  • 642
  • 8
  • 21
  • While querying for all chat rooms that contain a user may work in the above snippet, it will not scale. For each user you add, you will need to define an extra index. Since indexes can't be added through the regular API, this ends up being a manual operation or a maintenance nightmare. Instead, you should also store the list of chat rooms for each user. This essentially is a categorization problem as I described here: http://stackoverflow.com/questions/40656589/firebase-query-if-child-of-child-contains-a-value – Frank van Puffelen Feb 28 '17 at 05:24
  • Thanks @FrankvanPuffelen, I do understand the reason for inverting the data around. I just think the idea of looping to all chatrooms given the reference of "userRooms/$uid/chatrooms" is slow in client side. Because i need to loop all of them(lets say a few thousand query to firebase) before i can filter/do things like I want to get last 3 recent chatrooms. – bumblebeen Feb 28 '17 at 06:49

0 Answers0