Получение данных с помощью базы данных Firebase Realtime для C++

В этом документе описаны основы получения данных, а также способы упорядочивания и фильтрации данных Firebase.

Прежде чем вы начнете

Убедитесь, что вы настроили свое приложение и можете получить доступ к базе данных, как описано в руководстве Get Started .

Извлечение данных

Данные Firebase извлекаются либо путем однократного вызова GetValue() , либо путем присоединения к ValueListener по ссылке FirebaseDatabase . Прослушиватель значений вызывается один раз для исходного состояния данных и снова при каждом изменении данных.

Получить ссылку на базу данных

Чтобы записать данные в базу данных, вам понадобится экземпляр DatabaseReference :

    // Get the root reference location of the database.
    firebase::database::DatabaseReference dbref = database->GetReference();

Считайте данные один раз

Вы можете использовать метод GetValue() для однократного чтения статического снимка содержимого по заданному пути. Результат задачи будет содержать снимок, содержащий все данные в этом месте, включая дочерние данные. Если данных нет, возвращаемый снимок имеет значение null .

  firebase::Future<firebase::database::DataSnapshot> result =
    dbRef.GetReference("Leaders").GetValue();

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

  // In the game loop that polls for the result...

  if (result.status() != firebase::kFutureStatusPending) {
    if (result.status() != firebase::kFutureStatusComplete) {
      LogMessage("ERROR: GetValue() returned an invalid result.");
      // Handle the error...
    } else if (result.error() != firebase::database::kErrorNone) {
      LogMessage("ERROR: GetValue() returned error %d: %s", result.error(),
                 result.error_message());
      // Handle the error...
    } else {
      firebase::database::DataSnapshot snapshot = result.result();
      // Do something with the snapshot...
    }
  }

Здесь показаны базовые проверки ошибок. Дополнительную информацию о проверке ошибок и способах определения готовности результата см. в справочнике по firebase::Future .

Слушайте события

Вы можете добавить прослушиватели, чтобы подписаться на изменения данных:

Базовый класс ValueListener

Перезвонить Типичное использование
OnValueChanged Прочитайте и прослушайте изменения всего содержимого пути.

Базовый класс OnChildListener

OnChildAdded Получайте списки элементов или прослушивайте дополнения к списку элементов. Рекомендуется использовать с OnChildChanged и OnChildRemoved для отслеживания изменений в списках.
OnChildChanged Прослушивайте изменения элементов в списке. Используйте с OnChildAdded и OnChildRemoved для отслеживания изменений в списках.
OnChildRemoved Прослушивайте удаление элементов из списка. Используйте с OnChildAdded и OnChildChanged для отслеживания изменений в списках.
OnChildMoved Прослушивайте изменения порядка элементов в упорядоченном списке. Обратные вызовы OnChildMoved всегда следуют за обратными вызовами OnChildChanged из-за изменения порядка элемента (на основе текущего метода упорядочивания).

Класс ValueListener

Вы можете использовать обратные вызовы OnValueChanged , чтобы подписаться на изменения содержимого по заданному пути. Этот обратный вызов запускается один раз при подключении прослушивателя и снова каждый раз, когда изменяются данные, включая дочерние элементы. Обратному вызову передается снимок, содержащий все данные в этом месте, включая дочерние данные. Если данных нет, возвращаемый снимок имеет значение null .

В следующем примере показана игра, извлекающая результаты таблицы лидеров из базы данных:

  class LeadersValueListener : public firebase::database::ValueListener {
   public:
    void OnValueChanged(
        const firebase::database::DataSnapshot& snapshot) override {
      // Do something with the data in snapshot...
    }
    void OnCancelled(const firebase::database::Error& error_code,
                     const char* error_message) override {
      LogMessage("ERROR: LeadersValueListener canceled: %d: %s", error_code,
                 error_message);
    }
  };

  // Elsewhere in the code...

  LeadersValueListener* listener = new LeadersValueListener();
  firebase::Future<firebase::database::DataSnapshot> result =
    dbRef.GetReference("Leaders").AddValueListener(listener);

Результат Future&ltDataSnaphot&gt содержит данные в указанном месте базы данных на момент события. Вызов value() для снимка возвращает Variant , представляющий данные.

В этом примере метод OnCancelled также переопределяется, чтобы проверить, отменено ли чтение. Например, чтение можно отменить, если у клиента нет разрешения на чтение из местоположения базы данных Firebase. database::Error укажет, почему произошел сбой.

Класс ChildListener

Дочерние события запускаются в ответ на определенные операции, которые происходят с дочерними узлами в результате такой операции, как добавление нового дочернего элемента с помощью метода PushChild() или обновление дочернего элемента с помощью метода UpdateChildren() . Каждый из них вместе может быть полезен для прослушивания изменений определенного узла в базе данных. Например, игра может использовать эти методы вместе для отслеживания активности в комментариях игровой сессии, как показано ниже:

  class SessionCommentsChildListener : public firebase::database::ChildListener {
   public:
    void OnChildAdded(const firebase::database::DataSnapshot& snapshot,
                      const char* previous_sibling) override {
      // Do something with the data in snapshot ...
    }
    void OnChildChanged(const firebase::database::DataSnapshot& snapshot,
                        const char* previous_sibling) override {
      // Do something with the data in snapshot ...
    }
    void OnChildRemoved(
        const firebase::database::DataSnapshot& snapshot) override {
      // Do something with the data in snapshot ...
    }
    void OnChildMoved(const firebase::database::DataSnapshot& snapshot,
                      const char* previous_sibling) override {
      // Do something with the data in snapshot ...
    }
    void OnCancelled(const firebase::database::Error& error_code,
                     const char* error_message) override {
      LogMessage("ERROR: SessionCommentsChildListener canceled: %d: %s",
                 error_code, error_message);
    }
  };

  // elsewhere ....

  SessionCommentsChildListener* listener = new SessionCommentsChildListener();
  firebase::Future<firebase::database::DataSnapshot> result =
    dbRef.GetReference("GameSessionComments").AddChildListener(listener);

Обратный вызов OnChildAdded обычно используется для получения списка элементов в базе данных Firebase. Обратный вызов OnChildAdded вызывается один раз для каждого существующего дочернего элемента, а затем снова каждый раз, когда к указанному пути добавляется новый дочерний элемент. Слушателю передается снимок, содержащий данные нового дочернего элемента.

Обратный вызов OnChildChanged вызывается каждый раз при изменении дочернего узла. Сюда входят любые изменения потомков дочернего узла. Обычно он используется вместе с вызовами OnChildAdded и OnChildRemoved для реагирования на изменения в списке элементов. Снимок, передаваемый прослушивателю, содержит обновленные данные для дочернего процесса.

Обратный вызов OnChildRemoved запускается при удалении непосредственного дочернего элемента. Обычно он используется вместе с обратными вызовами OnChildAdded и OnChildChanged . Снимок, передаваемый обратному вызову, содержит данные об удаленном дочернем элементе.

Обратный вызов OnChildMoved запускается всякий раз, когда вызов OnChildChanged вызывается обновлением, которое вызывает изменение порядка дочернего элемента. Он используется с данными, упорядоченными с помощью OrderByChild или OrderByValue .

Сортировка и фильтрация данных

Вы можете использовать класс Query к базе данных в реальном времени для получения данных, отсортированных по ключу, по значению или по значению дочернего элемента. Вы также можете отфильтровать отсортированный результат по определенному количеству результатов или диапазону ключей или значений.

Сортировка данных

Чтобы получить отсортированные данные, начните с указания одного из методов упорядочивания, чтобы определить, как упорядочиваются результаты:

Метод Применение
OrderByChild() Упорядочите результаты по значению указанного дочернего ключа.
OrderByKey() Упорядочить результаты по дочерним ключам.
OrderByValue() Упорядочить результаты по дочерним значениям.

Одновременно можно использовать только один метод order-by. Вызов метода упорядочивания несколько раз в одном и том же запросе приводит к ошибке.

В следующем примере показано, как можно подписаться на таблицу лидеров результатов, упорядоченную по количеству очков.

  firebase::database::Query query =
    dbRef.GetReference("Leaders").OrderByChild("score");

  // To get the resulting DataSnapshot either use query.GetValue() and poll the
  // future, or use query.AddValueListener() and register to handle the
  // OnValueChanged callback.

Здесь определяется firebase::Query , который в сочетании с ValueListener синхронизирует клиента со списком лидеров в базе данных, упорядоченным по баллу каждой записи. Подробнее об эффективном структурировании данных можно прочитать в статье «Структурирование базы данных ».

Вызов метода OrderByChild() указывает дочерний ключ, по которому упорядочиваются результаты. В этом случае результаты сортируются по значению "score" в каждом дочернем элементе. Дополнительные сведения о том, как упорядочиваются другие типы данных, см. в разделе Как упорядочиваются данные запроса .

Фильтрация данных

Чтобы фильтровать данные, вы можете комбинировать любой метод ограничения или диапазона с методом упорядочивания при построении запроса.

Метод Применение
LimitToFirst() Устанавливает максимальное количество элементов, возвращаемых с начала упорядоченного списка результатов.
LimitToLast() Устанавливает максимальное количество элементов, возвращаемых с конца упорядоченного списка результатов.
StartAt() Возвращает элементы, которые больше или равны указанному ключу или значению в зависимости от выбранного метода упорядочения.
EndAt() Возвращает элементы, меньшие или равные указанному ключу или значению, в зависимости от выбранного метода упорядочения.
EqualTo() Возвращает элементы, равные указанному ключу или значению, в зависимости от выбранного метода упорядочения.

В отличие от методов упорядочивания, вы можете комбинировать несколько функций ограничения или диапазона. Например, вы можете объединить методы StartAt() и EndAt() , чтобы ограничить результаты указанным диапазоном значений.

Даже если запросу соответствует только одно совпадение, снимок по-прежнему представляет собой список; он содержит только один элемент.

Ограничить количество результатов

Вы можете использовать методы LimitToFirst() и LimitToLast() , чтобы установить максимальное количество дочерних элементов, которые будут синхронизироваться для данного обратного вызова. Например, если вы используете LimitToFirst() , чтобы установить ограничение в 100, вы изначально получите только до 100 обратных вызовов OnChildAdded . Если в вашей базе данных Firebase хранится менее 100 элементов, для каждого элемента срабатывает обратный вызов OnChildAdded .

При изменении элементов вы получаете обратные вызовы OnChildAdded для элементов, которые входят в запрос, и обратные вызовы OnChildRemoved для элементов, которые исключаются из него, так что общее число остается равным 100.

Например, приведенный ниже код возвращает лучший результат из таблицы лидеров:

  firebase::database::Query query =
    dbRef.GetReference("Leaders").OrderByChild("score").LimitToLast(1);

  // To get the resulting DataSnapshot either use query.GetValue() and poll the
  // future, or use query.AddValueListener() and register to handle the
  // OnValueChanged callback.

Фильтровать по ключу или значению

Вы можете использовать StartAt() , EndAt() и EqualTo() для выбора произвольных начальных, конечных и эквивалентных точек для запросов. Это может быть полезно для разбивки данных на страницы или поиска элементов с дочерними элементами, имеющими определенное значение.

Как упорядочиваются данные запроса

В этом разделе объясняется, как данные сортируются каждым из методов упорядочивания в классе Query .

OrderByChild

При использовании OrderByChild() данные, содержащие указанный дочерний ключ, упорядочиваются следующим образом:

  1. Дочерние элементы с null значением для указанного дочернего ключа идут первыми.
  2. Следующими идут дочерние элементы со значением false для указанного дочернего ключа. Если несколько дочерних элементов имеют значение false , они сортируются лексикографически по ключу.
  3. Следующими идут дочерние элементы со значением true для указанного дочернего ключа. Если несколько дочерних элементов имеют значение true , они сортируются лексикографически по ключу.
  4. Следующими идут дети с числовым значением, отсортированные в порядке возрастания. Если несколько дочерних узлов имеют одинаковое числовое значение для указанного дочернего узла, они сортируются по ключу.
  5. Строки идут после чисел и сортируются лексикографически по возрастанию. Если несколько дочерних узлов имеют одинаковое значение для указанного дочернего узла, они упорядочиваются лексикографически по ключу.
  6. Объекты идут последними и сортируются лексикографически по ключу в порядке возрастания.

OrderByKey

При использовании OrderByKey() для сортировки данных данные возвращаются в порядке возрастания ключа.

  1. Первыми идут дочерние элементы с ключом, который можно проанализировать как 32-битное целое число, отсортированное в порядке возрастания.
  2. Следующими идут дочерние элементы со строковым значением в качестве ключа, отсортированные лексикографически в порядке возрастания.

OrderByValue

При использовании OrderByValue() дочерние элементы упорядочиваются по их значению. Критерии упорядочивания такие же, как и в OrderByChild() , за исключением того, что вместо значения указанного дочернего ключа используется значение узла.

Следующие шаги