Guardar datos con Firebase Realtime Database para C++

Empezar

Consulte primero la guía Get Started si aún no ha configurado su aplicación ni ha accedido a la base de datos.

Obtener una referencia de base de datos

Para escribir datos en la base de datos, necesita una instancia de DatabaseReference :

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

Guardar datos

Existen cuatro métodos para escribir datos en Firebase Realtime Database:

Método Usos comunes
SetValue() Escriba o reemplace datos en una ruta definida, como users/<user-id>/<username> .
PushChild() Agregar a una lista de datos. Cada vez que llamas Push() , Firebase genera una clave única que también se puede usar como identificador único, como user-scores/<user-id>/<unique-score-id> .
UpdateChildren() Actualice algunas de las claves para una ruta definida sin reemplazar todos los datos.
RunTransaction() Actualice datos complejos que podrían dañarse con actualizaciones simultáneas.

Escribir, actualizar o eliminar datos en una referencia

Operaciones básicas de escritura

Para operaciones de escritura básicas, puede usar SetValue() para guardar datos en una referencia específica, reemplazando cualquier dato existente en esa ruta. Puede utilizar este método para pasar tipos aceptados por JSON a través de un tipo Variant que admita:

  • Nulo (esto elimina los datos)
  • Enteros (64 bits)
  • Números de punto flotante de doble precisión
  • booleanos
  • Instrumentos de cuerda
  • Vectores de variantes
  • Mapas de cadenas a variantes.

El uso SetValue() de esta manera sobrescribe los datos en la ubicación especificada, incluidos los nodos secundarios. Sin embargo, aún puedes actualizar un elemento secundario sin tener que reescribir todo el objeto. Si desea permitir que los usuarios actualicen sus perfiles, puede actualizar el nombre de usuario de la siguiente manera:

dbref.Child("users").Child(userId).Child("username").SetValue(name);

Agregar a una lista de datos

Utilice el método PushChild() para agregar datos a una lista en aplicaciones multiusuario. El método PushChild() genera una clave única cada vez que se agrega un nuevo elemento secundario a la referencia de Firebase especificada. Al utilizar estas claves generadas automáticamente para cada elemento nuevo de la lista, varios clientes pueden agregar elementos secundarios a la misma ubicación al mismo tiempo sin conflictos de escritura. La clave única generada por PushChild() se basa en una marca de tiempo, por lo que los elementos de la lista se ordenan automáticamente cronológicamente.

Puede utilizar la referencia a los nuevos datos devueltos por el método PushChild() para obtener el valor de la clave generada automáticamente por el niño o establecer datos para el niño. Llamar GetKey() en una referencia PushChild() devuelve el valor de la clave generada automáticamente.

Actualizar campos específicos

Para escribir simultáneamente en hijos específicos de un nodo sin sobrescribir otros nodos hijos, utilice el método UpdateChildren() .

Al llamar UpdateChildren() , puede actualizar los valores secundarios de nivel inferior especificando una ruta para la clave. Si los datos se almacenan en varias ubicaciones para escalar mejor, puede actualizar todas las instancias de esos datos mediante la distribución en abanico de datos . Por ejemplo, un juego podría tener una clase LeaderboardEntry como esta:

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

Para crear un LeaderboardEntry y actualizarlo simultáneamente con la fuente de puntuación reciente y la lista de puntuación del usuario, el juego utiliza el siguiente código:

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);
}

Este ejemplo utiliza PushChild() para crear una entrada en el nodo que contiene entradas para todos los usuarios en /scores/$key y recuperar simultáneamente la clave con key() . Luego, la clave se puede usar para crear una segunda entrada en las puntuaciones del usuario en /user-scores/$userid/$key .

Con estas rutas, puede realizar actualizaciones simultáneas en varias ubicaciones en el árbol JSON con una sola llamada a UpdateChildren() , como en este ejemplo se crea la nueva entrada en ambas ubicaciones. Las actualizaciones simultáneas realizadas de esta manera son atómicas: o todas las actualizaciones tienen éxito o todas fallan.

Borrar datos

La forma más sencilla de eliminar datos es llamar RemoveValue() en una referencia a la ubicación de esos datos.

También puede eliminar especificando una Variant null como valor para otra operación de escritura como SetValue() o UpdateChildren() . Puede utilizar esta técnica con UpdateChildren() para eliminar varios elementos secundarios en una única llamada API.

Sepa cuándo se confirman sus datos.

Para saber cuándo sus datos se envían al servidor de Firebase Realtime Database, verifique que el resultado Futuro sea exitoso.

Guardar datos como transacciones

Cuando trabaje con datos que podrían dañarse debido a modificaciones simultáneas, como contadores incrementales, puede utilizar una operación de transacción . A esta operación le asignas una función DoTransaction . Esta función de actualización toma el estado actual de los datos como argumento y devuelve el nuevo estado deseado que le gustaría escribir. Si otro cliente escribe en la ubicación antes de que su nuevo valor se escriba correctamente, se llama nuevamente a su función de actualización con el nuevo valor actual y se vuelve a intentar la escritura.

Por ejemplo, en un juego podrías permitir a los usuarios actualizar una tabla de clasificación con las cinco puntuaciones más altas:

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;
  });
}

El uso de una transacción evita que la tabla de clasificación sea incorrecta si varios usuarios registran puntuaciones al mismo tiempo o si el cliente tenía datos obsoletos. Si la transacción es rechazada, el servidor devuelve el valor actual al cliente, que ejecuta la transacción nuevamente con el valor actualizado. Esto se repite hasta que se acepta la transacción o se han realizado demasiados intentos.

Escribir datos sin conexión

Si un cliente pierde su conexión de red, su aplicación seguirá funcionando correctamente.

Cada cliente conectado a una base de datos de Firebase mantiene su propia versión interna de cualquier dato activo. Cuando se escriben datos, primero se escriben en esta versión local. Luego, el cliente de Firebase sincroniza esos datos con los servidores de bases de datos remotos y con otros clientes al "mejor esfuerzo".

Como resultado, todas las escrituras en la base de datos desencadenan eventos locales inmediatamente, antes de que se escriba cualquier dato en el servidor. Esto significa que su aplicación sigue respondiendo independientemente de la latencia o la conectividad de la red.

Una vez que se restablece la conectividad, su aplicación recibe el conjunto apropiado de eventos para que el cliente se sincronice con el estado actual del servidor, sin tener que escribir ningún código personalizado.

Próximos pasos