使用 C++ 適用的 Firebase 即時資料庫擷取資料

本文將介紹如何擷取資料,以及如何排序和篩選 Firebase 資料。

事前準備

請確認您已設定應用程式,且可以存取資料庫,如Get Started指南所述。

正在擷取資料

您可以呼叫 GetValue() 一次,或附加至 FirebaseDatabase 參照的 ValueListener,即可擷取 Firebase 資料。系統會針對資料的初始狀態呼叫一次值監聽器,並在資料變更時再次呼叫。

取得 DatabaseReference

如要將資料寫入資料庫,您需要 DatabaseReference 的執行個體:

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

讀取資料一次

您可以使用 GetValue() 方法,讀取指定路徑內容的靜態快照一次。工作結果會包含快照,其中含有該位置的所有資料,包括子項資料。如果沒有資料,傳回的快照會是 null。

  firebase::Future&ltfirebase::database::DataSnapshot&gt 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&ltfirebase::database::DataSnapshot&gt result =
    dbRef.GetReference("Leaders").AddValueListener(listener);

Future&ltDataSnapshot&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&ltfirebase::database::DataSnapshot&gt result =
    dbRef.GetReference("GameSessionComments").AddChildListener(listener);

OnChildAdded 回呼通常用於擷取 Firebase 資料庫中的項目清單。系統會為每個現有子項呼叫一次 OnChildAdded 回呼,然後每當有新子項新增至指定路徑時,系統也會再次呼叫這個回呼。系統會將含有新子項資料的快照傳遞給接聽程式。

每當子節點遭到修改,系統就會呼叫 OnChildChanged 回呼。 包括對子節點後代的任何修改。通常會與 OnChildAdded 和 OnChildRemoved 呼叫搭配使用,以回應項目清單的變更。傳遞至接聽程式的快照包含子項的更新資料。

移除直接子項時,系統會觸發 OnChildRemoved 回呼。 通常會與 OnChildAdded 和 OnChildChanged 回呼搭配使用。傳遞至回呼的快照包含已移除子項的資料。

每當更新導致子項重新排序時,系統就會觸發 OnChildMoved 回呼。OnChildChanged這項功能適用於以 OrderByChild 或 OrderByValue 排序的資料。

排序及篩選資料

您可以使用 Realtime Database Query 類別,依鍵、值或子項的值擷取排序後的資料。您也可以將排序結果篩選為特定數量的結果,或是特定範圍的鍵或值。

排序資料

如要擷取排序後的資料,請先指定其中一個排序依據方法,決定結果的排序方式:

方法 用量
OrderByChild() 依指定子項鍵的值排序結果。
OrderByKey() 依子項鍵排序結果。
OrderByValue() 依子項值排序結果。

一次只能使用一種排序方法。在同一項查詢中多次呼叫排序方法會擲回錯誤。

以下範例說明如何訂閱依分數排序的分數排行榜。

  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() 相同,但會使用節點的值,而不是指定子項鍵的值。

後續步驟