使用 Firebase 实时数据库检索数据 (C++)

本文档将介绍数据检索的基础知识以及如何对 Firebase 数据进行排序和过滤。

开始前的准备工作

确保您已设置好自己的应用,并且可以按照Get Started指南中介绍的方法访问数据库。

检索数据

您可以通过一次性调用 GetValue() 或将某个 ValueListener 附加到 FirebaseDatabase 引用来检索 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 检索项目列表,或侦听项目列表中是否添加了新项目。建议与 OnChildChangedOnChildRemoved 配合使用以监控列表的更改。
OnChildChanged 侦听对列表中的项目做出的更改。与 OnChildAddedOnChildRemoved 配合使用以监控列表的更改。
OnChildRemoved 侦听列表中是否有项目被移除。与 OnChildAddedOnChildChanged 配合使用以监控列表的更改。
OnChildMoved 侦听经过排序的列表的项目顺序是否有更改。 每当发生 OnChildChanged 回调并且项目的顺序发生更改(基于您当前的排序依据方法)时,就会触发 OnChildMoved 回调。

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&ltDataSnaphot&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 回调函数,包括对子节点的后代所做的任何修改。它通常与 OnChildAddedOnChildRemoved 调用结合使用,以响应对项目列表的更改。传递给侦听器的快照中将包含子节点更新后的数据。

移除直接子节点后就会触发 OnChildRemoved 回调函数。它通常与 OnChildAddedOnChildChanged 回调函数结合使用。传递给回调函数的快照中将包含已移除的子节点的数据。

每当引发 OnChildChanged 调用的子节点数据更新操作还会导致子节点重新排序时,系统就会触发 OnChildMoved 回调函数。当数据已通过 OrderByChildOrderByValue 排序后,就可以使用此回调函数。

排序和过滤数据

您可以使用实时数据库 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() 中相同,但这里使用的是节点本身的值而非指定子键的值。

后续步骤

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面