Начать
Если вы ещё не настроили приложение и доступ к базе данных, сначала ознакомьтесь с руководством по Get Started.
Как получить DatabaseReference
Чтобы записывать данные в базу данных, вам понадобится экземпляр DatabaseReference:
// Get the root reference location of the database. firebase::database::DatabaseReference dbref = database->GetReference();
Сохранение данных
Существует четыре способа записи данных в свойство Firebase Realtime Database:
| Метод | Примеры использования |
|---|---|
SetValue() |
Записывать или заменять данные в указанном пути, например users/<user-id>/<username>. |
PushChild() |
добавлять в список данных; Каждый раз, когда вы вызываете функцию Push(), Firebase создает уникальный ключ, который также можно использовать в качестве уникального идентификатора, например user-scores/<user-id>/<unique-score-id>. |
UpdateChildren() |
Обновить некоторые ключи для определенного пути, не заменяя все данные. |
RunTransaction() |
Обновлять сложные данные, которые могут быть повреждены при одновременном обновлении. |
записывать, обновлять или удалять данные по ссылке;
Основные операции записи
Для базовых операций записи можно использовать SetValue(), чтобы сохранить данные в указанной ссылке, заменив все существующие данные по этому пути. Этот метод позволяет передавать типы, принимаемые JSON, через тип Variant, который поддерживает:
- Null (данные будут удалены).
- Целые числа (64-разрядные)
- Числа с плавающей запятой двойной точности
- Логические значения
- Струны
- Векторы вариантов
- Сопоставления строк с вариантами
При использовании SetValue() таким образом данные в указанном местоположении, включая все дочерние узлы, будут перезаписаны. Однако вы можете обновить дочерний объект, не переписывая весь объект. Если вы хотите разрешить пользователям изменять свои профили, вы можете обновить имя пользователя следующим образом:
dbref.Child("users").Child(userId).Child("username").SetValue(name);
Добавить данные в список
Используйте метод PushChild(), чтобы добавлять данные в список в многопользовательских приложениях.
Метод PushChild() создает уникальный ключ каждый раз, когда в указанную ссылку Firebase добавляется новый дочерний элемент. Используя эти автоматически созданные ключи для каждого нового элемента в списке, несколько клиентов могут одновременно добавлять дочерние элементы в одно и то же местоположение без конфликтов записи. Уникальный ключ, созданный PushChild(), основан на временной метке, поэтому элементы списка автоматически упорядочиваются по хронологии.
Вы можете использовать ссылку на новые данные, возвращенные методом PushChild(), чтобы получить значение автоматически созданного ключа дочернего объекта или задать данные для дочернего объекта.
При вызове GetKey() для ссылки на PushChild() возвращается значение автоматически созданного ключа.
Как изменить определенные поля
Чтобы одновременно записать данные в определенные дочерние узлы, не перезаписывая другие дочерние узлы, используйте метод UpdateChildren().
При вызове UpdateChildren() можно обновить дочерние значения более низкого уровня, указав путь к ключу. Если данные хранятся в нескольких местах для лучшего масштабирования, вы можете обновить все экземпляры этих данных с помощью веерной рассылки данных. Например, в игре может быть класс LeaderboardEntry, который выглядит следующим образом:
class LeaderboardEntry { std::string uid; int score = 0; public: LeaderboardEntry() { } LeaderboardEntry(std::string uid, int score) { this->uid = uid; this->score = score; } std::map<std::string, Object> ToMap() { std::map<string, Variant> result = new std::map<string, Variant>(); result["uid"] = Variant(uid); result["score"] = Variant(score); return result; } }
Чтобы создать LeaderboardEntry и одновременно обновить его до недавней ленты результатов и списка результатов пользователя, игра использует следующий код:
void WriteNewScore(std::string userId, int score) { // Create new entry at /user-scores/$userid/$scoreid and at // /leaderboard/$scoreid simultaneously std::string key = dbref.Child("scores").PushChild().GetKey(); LeaderBoardEntry entry = new LeaderBoardEntry(userId, score); std::map<std::string, Variant> entryValues = entry.ToMap(); std::map<string, Variant> childUpdates = new std::map<string, Variant>(); childUpdates["/scores/" + key] = entryValues; childUpdates["/user-scores/" + userId + "/" + key] = entryValues; dbref.UpdateChildren(childUpdates); }
В этом примере PushChild() используется для создания записи в узле, содержащей записи для всех пользователей в /scores/$key, и одновременного получения ключа с помощью key(). Затем этот ключ можно использовать, чтобы создать вторую запись в таблице результатов пользователя по адресу /user-scores/$userid/$key.
Используя эти пути, вы можете одновременно обновлять несколько местоположений в дереве JSON с помощью одного вызова UpdateChildren(), как в этом примере, где новая запись создается в обоих местоположениях. Одновременные обновления, выполненные таким образом, являются атомарными: либо все обновления выполняются успешно, либо ни одно из них не выполняется.
Удаление данных
Самый простой способ удалить данные – вызвать метод RemoveValue() для ссылки на местоположение этих данных.
Вы также можете удалить значение, указав null Variant в качестве значения для другой операции записи, например SetValue() или UpdateChildren(). Этот метод можно использовать с UpdateChildren(), чтобы удалить несколько дочерних элементов за один вызов API.
Сведения о готовности данных
Чтобы узнать, когда данные будут переданы на сервер Firebase Realtime Database, проверьте результат Future.
Как сохранять данные в виде транзакций
При работе с данными, которые могут быть повреждены в результате одновременных изменений, например с инкрементными счетчиками, можно использовать транзакцию.
Вы передаете этой операции функцию DoTransaction. Эта функция обновления принимает текущее состояние данных в качестве аргумента и возвращает новое желаемое состояние, которое вы хотите записать. Если другой клиент запишет данные в то же место до того, как ваше новое значение будет успешно записано, функция обновления будет вызвана снова с новым текущим значением, и запись будет повторена.
Например, в игре можно разрешить пользователям обновлять таблицу лидеров, добавляя в нее пять лучших результатов:
void AddScoreToLeaders(std::string email, long score, DatabaseReference leaderBoardRef) { leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) { if (mutableData.children_count() >= MaxScores) { long minScore = LONG_MAX; MutableData *minVal = null; std::vector<MutableData> children = mutableData.children(); std::vector<MutableData>::iterator it; for (it = children.begin(); it != children.end(); ++it) { if (!it->value().is_map()) continue; long childScore = (long)it->Child("score").value().int64_value(); if (childScore < minScore) { minScore = childScore; minVal = &*it; } } if (minScore > score) { // The new score is lower than the existing 5 scores, abort. return kTransactionResultAbort; } // Remove the lowest score. children.Remove(minVal); } // Add the new high score. std::map<std::string, Variant> newScoreMap = new std::map<std::string, Variant>(); newScoreMap["score"] = score; newScoreMap["email"] = email; children.Add(newScoreMap); mutableData->set_value(children); return kTransactionResultSuccess; }); }
Транзакция предотвращает ошибки в таблице лидеров, если несколько пользователей записывают результаты одновременно или клиент использует устаревшие данные. Если транзакция отклонена, сервер возвращает клиенту текущее значение, и клиент снова выполняет транзакцию с обновленным значением. Это повторяется, пока транзакция не будет принята или не будет предпринято слишком много попыток.
Как записывать данные в офлайн-режиме
Если клиент потеряет подключение к сети, ваше приложение продолжит работать корректно.
Каждый клиент, подключенный к базе данных Firebase, хранит собственную внутреннюю версию активных данных. При записи данных сначала используется локальная версия. Затем клиент Firebase синхронизирует эти данные с удаленными серверами базы данных и другими клиентами по мере возможности.
В результате все записи в базу данных немедленно активируют локальные события, прежде чем какие-либо данные будут записаны на сервер. Это значит, что приложение будет работать без сбоев независимо от задержки сети или качества подключения.
После восстановления подключения приложение получит нужный набор событий, чтобы клиент синхронизировался с текущим состоянием сервера без необходимости писать какой-либо код.