正在擷取資料

本文將說明擷取資料的基本概念,以及如何排序和篩選 Firebase 資料。

事前準備

使用 Realtime Database 前,請先完成下列事項:

  • 註冊 Unity 專案並設定使用 Firebase。

    • 如果 Unity 專案已使用 Firebase,代表已註冊並設定 Firebase。

    • 如果沒有 Unity 專案,可以下載範例應用程式。

  • 將 Firebase Unity SDK (具體來說是 FirebaseDatabase.unitypackage) 新增至 Unity 專案。

請注意,將 Firebase 新增至 Unity 專案時,您必須在 Firebase 控制台和開啟的 Unity 專案中執行工作 (例如從控制台下載 Firebase 設定檔,然後移至 Unity 專案)。

擷取資料

如要擷取 Firebase 資料,可以呼叫 GetValueAsync() 一次,或附加至 FirebaseDatabase 參照上的事件。系統會針對資料的初始狀態呼叫事件監聽器一次,並在資料變更時再次呼叫。

取得 DatabaseReference

如要從資料庫讀取資料,您需要 DatabaseReference 的執行個體:

using Firebase;
using Firebase.Database;
using Firebase.Extensions.TaskExtension; // for ContinueWithOnMainThread

public class MyScript: MonoBehaviour {
  void Start() {
    // Get the root reference location of the database.
    DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
  }
}

讀取資料一次

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

    FirebaseDatabase.DefaultInstance
      .GetReference("Leaders")
      .GetValueAsync().ContinueWithOnMainThread(task =&gt {
        if (task.IsFaulted) {
          // Handle the error...
        }
        else if (task.IsCompleted) {
          DataSnapshot snapshot = task.Result;
          // Do something with snapshot...
        }
      });

接聽事件

您可以新增事件監聽器,訂閱資料變更:

事件 一般用途
ValueChanged 讀取及監聽路徑中所有內容的變更。
ChildAdded 擷取項目清單,或監聽項目清單的新增內容。 建議與 ChildChanged 和 ChildRemoved 搭配使用,監控清單變更。
ChildChanged 監聽清單中項目的變更。與 ChildAdded 和 ChildRemoved 搭配使用,監控清單變更。
ChildRemoved 監聽從清單中移除的項目。與 ChildAdded 和 ChildChanged 搭配使用,監控清單變更。
ChildMoved 監聽已排序的清單中項目順序的變更。ChildMoved 事件一律會接在 ChildChanged 事件之後,而事件會導致項目順序變更 (根據目前的排序方法)。

ValueChanged 事件

您可以使用 ValueChanged 事件,訂閱特定路徑的內容變更。這個事件會在附加事件監聽器時觸發一次,並在每次資料 (包括子項) 變更時再次觸發。事件回呼會傳遞快照,其中包含該位置的所有資料,包括子項資料。如果沒有資料,傳回的快照會是 null。

以下範例說明遊戲如何從資料庫擷取排行榜分數:

      FirebaseDatabase.DefaultInstance
        .GetReference("Leaders")
        .ValueChanged += HandleValueChanged;
    }

    void HandleValueChanged(object sender, ValueChangedEventArgs args) {
      if (args.DatabaseError != null) {
        Debug.LogError(args.DatabaseError.Message);
        return;
      }
      // Do something with the data in args.Snapshot
    }

ValueChangedEventArgs 包含 DataSnapshot,其中含有事件發生時資料庫中指定位置的資料。對快照呼叫 Value 會傳回代表資料的 Dictionary<string, object>。如果該位置沒有任何資料,呼叫 Value 會傳回 null。

在這個範例中,系統也會檢查 args.DatabaseError,確認讀取作業是否已取消。舉例來說,如果用戶端沒有從 Firebase 資料庫位置讀取的權限,系統可能會取消讀取。DatabaseError 會指出失敗原因。

您之後可以使用任何具有相同路徑的 DatabaseReference 取消訂閱事件。DatabaseReference 執行個體是暫時性的,可視為存取任何路徑和查詢的方式。

      FirebaseDatabase.DefaultInstance
        .GetReference("Leaders")
        .ValueChanged -= HandleValueChanged; // unsubscribe from ValueChanged.
    }

子事件

子事件會因應節點子項發生的特定作業而觸發,例如透過 Push() 方法新增子項,或透過 UpdateChildrenAsync() 方法更新子項。這些函式搭配使用,可用於監聽資料庫中特定節點的變更。舉例來說,遊戲可能會同時使用這些方法,監控遊戲工作階段留言中的活動,如下所示:

      var ref = FirebaseDatabase.DefaultInstance
      .GetReference("GameSessionComments");

      ref.ChildAdded += HandleChildAdded;
      ref.ChildChanged += HandleChildChanged;
      ref.ChildRemoved += HandleChildRemoved;
      ref.ChildMoved += HandleChildMoved;
    }

    void HandleChildAdded(object sender, ChildChangedEventArgs args) {
      if (args.DatabaseError != null) {
        Debug.LogError(args.DatabaseError.Message);
        return;
      }
      // Do something with the data in args.Snapshot
    }

    void HandleChildChanged(object sender, ChildChangedEventArgs args) {
      if (args.DatabaseError != null) {
        Debug.LogError(args.DatabaseError.Message);
        return;
      }
      // Do something with the data in args.Snapshot
    }

    void HandleChildRemoved(object sender, ChildChangedEventArgs args) {
      if (args.DatabaseError != null) {
        Debug.LogError(args.DatabaseError.Message);
        return;
      }
      // Do something with the data in args.Snapshot
    }

    void HandleChildMoved(object sender, ChildChangedEventArgs args) {
      if (args.DatabaseError != null) {
        Debug.LogError(args.DatabaseError.Message);
        return;
      }
      // Do something with the data in args.Snapshot
    }

ChildAdded 事件通常用於擷取 Firebase 資料庫中的項目清單。系統會為每個現有子項觸發一次 ChildAdded 事件,之後每當有新子項新增至指定路徑時,就會再次觸發。事件監聽器會收到包含新子項資料的快照。

每當子節點遭到修改,系統就會引發 ChildChanged 事件。包括對子節點後代所做的任何修改。通常會與 ChildAdded 和 ChildRemoved 事件搭配使用,以回應項目清單的變更。傳遞至事件監聽器的快照包含子項的更新資料。

移除直接子項時,會觸發 ChildRemoved 事件。通常會與 ChildAdded 和 ChildChanged 回呼搭配使用。傳遞至事件回呼的快照包含已移除子項的資料。

每當更新導致子項重新排序而引發 ChildChanged 事件時,系統就會觸發 ChildMoved 事件。適用於以 OrderByChild 或 OrderByValue 排序的資料。

排序及篩選資料

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

排序資料

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

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

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

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

      FirebaseDatabase.DefaultInstance
        .GetReference("Leaders").OrderByChild("score")
        .ValueChanged += HandleValueChanged;
    }

    void HandleValueChanged(object sender, ValueChangedEventArgs args) {
      if (args.DatabaseError != null) {
        Debug.LogError(args.DatabaseError.Message);
        return;
      }
      // Do something with the data in args.Snapshot
    }

這會定義查詢,與 valuechanged 事件監聽器結合時,會依據每個項目的分數排序,將用戶端與資料庫中的排行榜同步。如要進一步瞭解如何有效率地建構資料,請參閱「建構資料庫」。

呼叫 OrderByChild() 方法時,會指定要用來排序結果的子項鍵。在這種情況下,系統會依據每個子項的 "score" 值排序結果。如要進一步瞭解其他資料類型的排序方式,請參閱「查詢資料的排序方式」。

篩選資料

如要篩選資料,您可以在建構查詢時,將任何限制或範圍方法與排序方法結合。

方法 用量
LimitToFirst() 設定從已排序的清單結果開頭傳回的項目數量上限。
LimitToLast() 設定從已排序結果清單結尾傳回的項目數量上限。
StartAt() 根據所選排序方法,傳回大於或等於指定鍵或值的項目。
EndAt() 根據所選排序方法,傳回小於或等於指定鍵或值的項目。
EqualTo() 根據所選排序方法,傳回與指定鍵或值相等的項目。

與排序方法不同,您可以合併多個限制或範圍函式。舉例來說,您可以結合 StartAt() 和 EndAt() 方法,將結果限制在指定的值範圍內。

即使查詢只有一個相符項目,快照仍會是清單,只是其中只有一個項目。

限制結果數量

您可以使用 LimitToFirst() 和 LimitToLast() 方法,設定指定回呼要同步處理的子項數量上限。舉例來說,如果您使用 LimitToFirst() 將限制設為 100,一開始最多只會收到 100 個 ChildAdded 回呼。如果 Firebase 資料庫中儲存的項目少於 100 個,系統會為每個項目觸發 ChildAdded 回呼。

當項目變更時,您會收到進入查詢項目的 ChildAdded 回呼,以及從查詢中刪除項目的 ChildRemoved 回呼,因此總數會維持在 100。

舉例來說,下列程式碼會傳回排行榜的最高分數:

      FirebaseDatabase.DefaultInstance
        .GetReference("Leaders").OrderByChild("score").LimitToLast(1)
        .ValueChanged += HandleValueChanged;
    }

    void HandleValueChanged(object sender, ValueChangedEventArgs args) {
      if (args.DatabaseError != null) {
        Debug.LogError(args.DatabaseError.Message);
        return;
      }
      // Do something with the data in args.Snapshot
    }

依鍵或值篩選

您可以使用 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() 相同,但會使用節點的值,而非指定子項索引鍵的值。