Home » Android » java – Firebase DB warning: "Using an unspecified index. Consider adding '".indexOn" for better performance

java – Firebase DB warning: "Using an unspecified index. Consider adding '".indexOn" for better performance

Posted by: admin May 14, 2020 Leave a comment

Questions:

When running my Android app using a Firebase Realtime Database I get the following warning:

Using an unspecified index. Consider adding “.indexOn” … to your security and Firebase Database rules for better performance

I totally understand the warning. But I don’t know how to make it better. I really want to query only on indexed fields!

This is my DB:

{      
  "groupUsers" : {
    "g1" : {
      "u1" : "admin"
    },
    "g2" : {
      "u1" : "admin",
      "u2" : "readonly"
    }
  },
  "groups" : {    
    "g1" : {
      "areas" : {
        "a1" : {
          "groupId" : "g1",
          "name" : "My Group"
        }
      },
      "interests" : {
        "i1" : {
          "groupId" : "g1",
          "name" : "My Interest"
        }
      },
      "points" : {
        "p1" : {
          "address" : "First Street",
          "areaId" : "a1",
          "groupId" : "g1",
          "latitude" : -25,
          "longitude" : -55,
          "name" : "Harry"
        }
      },
      "properties" : {
        "name" : "My Group Name"
      },
      "waypoints" : {
        "w1" : {
          "areaId" : "a1",
          "groupId" : "g1"
        }
      }
    }
  }
  "users" : {
    "u1" : {
      "email" : "[email protected]",
      "firstName" : "Peter",
      "lastName" : "Smith"
    },
    "u2" : {
      "email" : "[email protected]",
      "firstName" : "John",
      "lastName" : "Wayne"
    }
  }
}

These are my security rules:

{
  "rules": {       
    "groups": {          
      "$groupId": {
        ".read":  "root.child('groupUsers').child($groupId).child(auth.uid).exists()",
        ".write": "! root.child('groupUsers').child($groupId).exists() || root.child('groupUsers').child($groupId).child(auth.uid).val() === 'admin'",
        "$child": {
          ".write": "root.child('groupUsers').child($groupId).child(auth.uid).exists() && root.child('groupUsers').child($groupId).child(auth.uid).val() !== 'readonly' && ($child === 'points' || $child === 'visits')"
        }
      },

      "areas": {        
        ".indexOn": ["groupId", "name"]
      },
        "waypoints": {
        ".indexOn": ["groupId", "areaId", "sequenceNumber"]
      },
      "interests": {
        ".indexOn": ["groupId", "rank", "name"]
      },
      "points": {        
        ".indexOn": ["groupId", "areaId", "name"]
      },
      "visits": {
        ".indexOn": ["groupId", "pointId", "interestId", "userId"] 
      }
    },
    "users": {
      ".read": "auth != null",
      "$userId": {        
        ".write": "auth != null && $userId === auth.uid && newData.val() != null",
        ".indexOn": ["email", "firstName", "lastName"]
      }
    },
    "groupUsers": {
      ".read": "auth != null",
      "$groupId": {
        ".write": "auth != null && (root.child('groupUsers').child($groupId).child(auth.uid).val() === 'admin' || !root.child('groupUsers').child($groupId).exists())"
      }
    }
  }
}

The problem is the groupUser structure. It has group keys as property names. I do not have a field to index on since I do not have a constant property name.
How to change the structure to make it possible that all fields are indexed and that all my rules still work?

How to&Answers:

I am not an expert in Firebase but it seems to me like there is an issue with your security rules. Specifically in the groups section you are referencing a field named “name” as index, this field however is not a direct field of the group object. ‘name’ is a field of the properties object so your groups configuration should had been :

.indexOn”: [“properties/name”]

OR

.indexOn”: “properties/name”

As you can see Firebase has reserved the ‘/’ character to navigate inside the nested fields of your objects to use as indexes.

If by any chance the properties field is accessed by default and this is an implicit characteristic of FireBase, please skip this part of my response.

I think that if we are to denote the relation-object(user/group) as the source of the warning, we need to at least optimize the rest of the object indexes correctly and then maybe we can redesign the relational table a little bit to incorporate internal, indexable fields.

Optimization of the Entity-Relation structure:

As for the Entity-Relation table groupUser, one suggestion could be that you incorporate it in the Group table. Add a groupUsers field inside the group. This field would consist of an array and every array entry is an User object. The only problem with this approach is that you need to somehow specify the foreign key relation between the users and these entries (like we do in relational tables) in order to take advantage of cascade operations. Example change:

    "groups" : {    
        "g1" : {
          "areas" : {
            "a1" : {
              "groupId" : "g1",
              "name" : "My Group"
            }
          },
          "groupUsers" : [
             {"userID" : "u1"},
             {"userID" : "u2"}],...
}

or using a non-array solution:

    "groups" : {    
        "g1" : {
          "areas" : {
            "a1" : {
              "groupId" : "g1",
              "name" : "My Group"
            }
          },
          "groupUsers" : {
              "r1" : {"userID" : "u1"},
              "r2" : {"userID" : "u2"}
           },...
}

Answer:

Tried getting your question, I got your point that you don’t have key name to set index, As per documentation here we can use ".value" here to add index on values,

Below might work for you, added .value at user role level in groupUsers, as this seems to be only string value(user role) where you can set index

"groupUsers": {
  ".read": "auth != null",
  "$groupId": {
    ".write": "auth != null && (root.child('groupUsers').child($groupId).child(auth.uid).val() === 'admin' || !root.child('groupUsers').child($groupId).exists())"
    ".indexOn": ".value"
  }
}