Firebase DB warning: "Использование неопределенного индекса. Рассмотрим добавление".indexOn "для повышения производительности

При запуске моего приложения Android с использованием базы данных Firebase Realtime я получаю следующее предупреждение:

Использование неуказанного индекса. Подумайте о добавлении ".indexOn"... в свои правила безопасности и Firebase для лучшей производительности

Я полностью понимаю предупреждение. Но я не знаю, как сделать это лучше. Я действительно хочу запросить только индексированные поля!

Это моя БД:

{      
  "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"
    }
  }
}

Это мои правила безопасности:

{
  "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())"
      }
    }
  }
}

Проблема заключается в структуре groupUser. Он имеет ключи группы в качестве имен свойств. У меня нет поля для индексации, так как у меня нет постоянного имени свойства. Как изменить структуру, чтобы можно было проиндексировать все поля и чтобы все мои правила все еще работали?

Ответ 1

Я не эксперт в Firebase, но мне кажется, что есть проблема с вашими правилами безопасности. В частности, в разделе групп вы ссылаетесь на поле с именем "name" как индекс, это поле, однако, не является прямым полем группового объекта. "name" - это поле объекта properties, поэтому ваша группа должна была быть:

.indexOn ": [" properties/name "]

ИЛИ

.indexOn ":" properties/name "

Как вы можете видеть, Firebase зарезервировала символ '/' для перемещения внутри вложенных полей ваших объектов, которые будут использоваться в качестве индексов.

Если по каким-либо причинам по умолчанию открывается поле свойств, и это неявная характеристика FireBase, пропустите эту часть моего ответа.

Я думаю, что если мы хотим обозначить объект-отношение (user/group) как источник предупреждения, нам нужно хотя бы оптимизировать остальные индексы объектов правильно, а затем, возможно, мы сможем перепроектировать реляционная таблица немного, чтобы включить внутренние, индексируемые поля.

Оптимизация структуры Entity-Relation:

Как и для группы Groupity Entity-Relation, одно предложение может заключаться в том, что вы включаете его в таблицу Group. Добавьте поле groupUsers внутри группы. Это поле будет состоять из массива, и каждая запись массива будет объектом User. Единственная проблема с этим подходом заключается в том, что вам нужно как-то указать отношение внешнего ключа между пользователями и этими элементами (например, мы делаем в реляционных таблицах), чтобы использовать преимущества каскадных операций. Пример изменения:

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

или используя решение без массива:

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

Ответ 2

Попытался получить ваш вопрос, я понял, что у вас нет ключевого имени для установки индекса. По документации здесь мы можем используйте ".value" здесь, чтобы добавить индекс в значения,

Ниже может работать для вас, добавлено .value на уровне роли пользователя в groupUsers, поскольку это похоже на только строковое значение (роль пользователя), где вы можете установить индекс

"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"
  }
}