Adding and ".indexOn": ".value" into Firebase Rules

Marco Cavanna picture Marco Cavanna · Apr 6, 2017 · Viewed 16.7k times · Source

I'm studying AngularJS and Firebase and I'm playing with a simple ChatApp, just to understand the code.

My Firebase DB structure is like this:

"chat": {
    "messages": {
        "chat1": { [ list of messages ] },
        "chat2": { [ list of messages ] }
    },
    "rooms": {
        "chat1": {
            "users": {
                "user1": true,
                "user2": true
            }
        },
        "chat2": {
            "users": {
                "user2": true,
                "user3": true
            }
        }
    }
}

Due to Firebase protection Rules I can't add chat link in the user node, than I necessary have to do a Firebase query to retrieve chat rooms for user, using orderByChild() query on App Start Up.

Using Firebase is simple than I can retrieve all chat using:

var chatLink = firebase.database().ref('chat/rooms');
chatLink
  .orderByChild('users/' + userId)
  .equalTo(true)
  .once('value', function(chatSnap){

      /* Do Some Stuff Here */

  });

Every thing work well, but I'm receiving a warning in the console:

FIREBASE WARNING: Using an unspecified index. Consider adding ".indexOn": "users/{userId}" at /chat/rooms to your security rules for better performance

Searching on the web, I found that for variable orderByChild() query, I can user .indexOn: .value in my Firebase Rules.

I've tried to add this Rule:

{
    "rules": {
        "chat": {
            "rooms": {
                ".indexOn": ".value"
            }
        }
    }
}

But I'm still receive always that warning. How can i fix it?

Thank you very much!

Answer

Frank van Puffelen picture Frank van Puffelen · Apr 6, 2017

To index this structure to allow efficient querying, you have to add an index for each user:

{
    "rules": {
        "chat": {
            "rooms": {
                ".indexOn": ["users/user1", "users/user2"]
            }
        }
    }
}

That won't be maintainable, since you're likely adding users dynamically.

As usual with NoSQL databases, this means you have to expand/adapt your data model to allow the use-case you want. Instead of querying the rooms for their users, keep a list of the rooms for each user (in addition to your current data):

"user_rooms": {
  "user1": {
    "chat1": true
  },
  "user2": {
    "chat1": true,
    "chat2": true
  }
  "user3": {
    "chat2": true
  }

Now you can look up the chat rooms for a user without even needing to query.

Also see my answer about this categorization problem in Firebase query if child of child contains a value.