Salvare i dati con Firebase Realtime Database per C++

Inizia

Se non hai ancora configurato l'app e l'accesso al database, consulta prima la guida Get Started.

Recuperare un DatabaseReference

Per scrivere i dati nel database, devi disporre di un'istanza di DatabaseReference:

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

Risparmio dati

Esistono quattro metodi per scrivere dati in Firebase Realtime Database:

Metodo Utilizzi comuni
SetValue() Scrivi o sostituisci i dati in un percorso definito, ad esempio users/<user-id>/<username>.
PushChild() Aggiungere a un elenco di dati. Ogni volta che chiami Push(), Firebase genera una chiave univoca che può essere utilizzata anche come identificatore univoco, ad esempio user-scores/<user-id>/<unique-score-id>.
UpdateChildren() Aggiorna alcune chiavi per un percorso definito senza sostituire tutti i dati.
RunTransaction() Aggiorna dati complessi che potrebbero essere danneggiati da aggiornamenti simultanei.

Scrivere, aggiornare o eliminare dati in un riferimento

Operazioni di scrittura di base

Per le operazioni di scrittura di base, puoi utilizzare SetValue() per salvare i dati in un riferimento specificato, sostituendo tutti i dati esistenti nel percorso. Puoi utilizzare questo metodo per passare i tipi accettati da JSON tramite un tipo Variant che supporta:

  • Null (questo elimina i dati)
  • Numeri interi (64 bit)
  • Numeri in virgola mobile a precisione doppia
  • Booleani
  • Corde
  • Vettori di varianti
  • Mappe di stringhe alle varianti

L'utilizzo di SetValue() in questo modo sovrascrive i dati nella posizione specificata, inclusi tutti i nodi secondari. Tuttavia, puoi comunque aggiornare un figlio senza riscrivere l'intero oggetto. Se vuoi consentire agli utenti di aggiornare i propri profili, puoi aggiornare il nome utente nel seguente modo:

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

Aggiungere a un elenco di dati

Utilizza il metodo PushChild() per aggiungere dati a un elenco in applicazioni multiutente. Il metodo PushChild() genera una chiave univoca ogni volta che viene aggiunto un nuovo elemento secondario al riferimento Firebase specificato. Utilizzando queste chiavi generate automaticamente per ogni nuovo elemento dell'elenco, più client possono aggiungere figli alla stessa posizione contemporaneamente senza conflitti di scrittura. La chiave univoca generata da PushChild() si basa su un timestamp, quindi gli elementi dell'elenco vengono ordinati automaticamente in ordine cronologico.

Puoi utilizzare il riferimento ai nuovi dati restituiti dal metodo PushChild() per ottenere il valore della chiave generata automaticamente del figlio o impostare i dati per il figlio. La chiamata di GetKey() su un riferimento PushChild() restituisce il valore della chiave generata automaticamente.

Aggiornare campi specifici

Per scrivere contemporaneamente a nodi secondari specifici di un nodo senza sovrascrivere altri nodi secondari, utilizza il metodo UpdateChildren().

Quando chiami UpdateChildren(), puoi aggiornare i valori secondari di livello inferiore specificando un percorso per la chiave. Se i dati vengono archiviati in più posizioni per migliorare la scalabilità, puoi aggiornare tutte le istanze di questi dati utilizzando la distribuzione dei dati. Ad esempio, un gioco potrebbe avere una classe LeaderboardEntry come questa:

class LeaderboardEntry {
  std::string uid;
  int score = 0;

 public:
  LeaderboardEntry() {
  }

  LeaderboardEntry(std::string uid, int score) {
    this->uid = uid;
    this->score = score;
  }

  std::map&ltstd::string, Object&gt ToMap() {
    std::map&ltstring, Variant&gt result = new std::map&ltstring, Variant&gt();
    result["uid"] = Variant(uid);
    result["score"] = Variant(score);

    return result;
  }
}

Per creare un LeaderboardEntry e aggiornarlo contemporaneamente al feed dei punteggi recenti e all'elenco dei punteggi dell'utente, il gioco utilizza il seguente codice:

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&ltstd::string, Variant&gt entryValues = entry.ToMap();

  std::map&ltstring, Variant&gt childUpdates = new std::map&ltstring, Variant&gt();
  childUpdates["/scores/" + key] = entryValues;
  childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

  dbref.UpdateChildren(childUpdates);
}

Questo esempio utilizza PushChild() per creare una voce nel nodo contenente le voci per tutti gli utenti all'indirizzo /scores/$key e recuperare contemporaneamente la chiave con key(). La chiave può essere utilizzata per creare una seconda voce nei punteggi dell'utente all'indirizzo /user-scores/$userid/$key.

Utilizzando questi percorsi, puoi eseguire aggiornamenti simultanei a più posizioni nell'albero JSON con una singola chiamata a UpdateChildren(), ad esempio come questo esempio crea la nuova voce in entrambe le posizioni. Gli aggiornamenti simultanei eseguiti in questo modo sono atomici: o tutti gli aggiornamenti vanno a buon fine o tutti gli aggiornamenti non vanno a buon fine.

Elimina dati

Il modo più semplice per eliminare i dati è chiamare RemoveValue() su un riferimento alla posizione di questi dati.

Puoi anche eliminare specificando un null Variant come valore per un'altra operazione di scrittura, ad esempio SetValue() o UpdateChildren(). Puoi utilizzare questa tecnica con UpdateChildren() per eliminare più figli in una singola chiamata API.

Scopri quando i tuoi dati vengono salvati.

Per sapere quando i dati vengono inviati al server Firebase Realtime Database, controlla il risultato Futuro per verificare che l'operazione sia riuscita.

Salvare i dati come transazioni

Quando lavori con dati che potrebbero essere danneggiati da modifiche simultanee, come i contatori incrementali, puoi utilizzare un'operazione di transazione. Assegni a questa operazione una funzione DoTransaction. Questa funzione di aggiornamento accetta lo stato attuale dei dati come argomento e restituisce il nuovo stato desiderato che vuoi scrivere. Se un altro client scrive nella posizione prima che il nuovo valore venga scritto correttamente, la funzione di aggiornamento viene chiamata di nuovo con il nuovo valore corrente e la scrittura viene ritentata.

Ad esempio, in un gioco potresti consentire agli utenti di aggiornare una classifica con i cinque punteggi più alti:

void AddScoreToLeaders(std::string email,
                       long score,
                       DatabaseReference leaderBoardRef) {
  leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) {
    if (mutableData.children_count() &gt= MaxScores) {
      long minScore = LONG_MAX;
      MutableData *minVal = null;
      std::vector&ltMutableData&gt children = mutableData.children();
      std::vector&ltMutableData&gt::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 &lt minScore) {
          minScore = childScore;
          minVal = &amp*it;
        }
      }
      if (minScore &gt 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&ltstd::string, Variant&gt newScoreMap =
      new std::map&ltstd::string, Variant&gt();
    newScoreMap["score"] = score;
    newScoreMap["email"] = email;
    children.Add(newScoreMap);
    mutableData->set_value(children);
    return kTransactionResultSuccess;
  });
}

L'utilizzo di una transazione impedisce che la classifica sia errata se più utenti registrano punteggi contemporaneamente o se il client dispone di dati obsoleti. Se la transazione viene rifiutata, il server restituisce il valore corrente al client, che esegue nuovamente la transazione con il valore aggiornato. Questa operazione si ripete finché la transazione non viene accettata o non vengono effettuati troppi tentativi.

Scrivere dati offline

Se un client perde la connessione di rete, la tua app continuerà a funzionare correttamente.

Ogni client connesso a un database Firebase mantiene la propria versione interna di tutti i dati attivi. Quando i dati vengono scritti, vengono scritti prima in questa versione locale. Il client Firebase sincronizza quindi i dati con i server di database remoti e con altri client in base al "miglior sforzo".

Di conseguenza, tutte le scritture nel database attivano immediatamente gli eventi locali, prima che i dati vengano scritti sul server. Ciò significa che la tua app rimane reattiva indipendentemente dalla latenza o dalla connettività di rete.

Una volta ripristinata la connettività, la tua app riceve il set appropriato di eventi in modo che il client si sincronizzi con lo stato attuale del server, senza dover scrivere codice personalizzato.

Passaggi successivi