本文档介绍了检索数据的基础知识以及如何排序和过滤 Firebase 数据。
在你开始之前
确保您已设置您的应用程序并且可以访问Get Started
指南中所述的数据库。
检索数据
通过一次性调用GetValue()
或附加到FirebaseDatabase
引用上的ValueListener
来检索 Firebase 数据。值侦听器为数据的初始状态调用一次,并在数据更改时再次调用。
获取数据库引用
要将数据写入数据库,您需要一个DatabaseReference
实例:
// Get the root reference location of the database. firebase::database::DatabaseReference dbref = database->GetReference();
一次读取数据
您可以使用GetValue()
方法一次读取给定路径中内容的静态快照。任务结果将包含一个快照,其中包含该位置的所有数据,包括子数据。如果没有数据,则返回的快照为null
。
firebase::Future<firebase::database::DataSnapshot> 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 回调。 |
值监听类
您可以使用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<firebase::database::DataSnapshot> result = dbRef.GetReference("Leaders").AddValueListener(listener);
Future<DataSnaphot>
结果包含事件发生时数据库中指定位置的数据。在快照上调用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<firebase::database::DataSnapshot> result = dbRef.GetReference("GameSessionComments").AddChildListener(listener);
OnChildAdded
回调通常用于检索 Firebase 数据库中的项目列表。 OnChildAdded
回调会为每个现有子项调用一次,然后在每次将新子项添加到指定路径时再次调用。向侦听器传递包含新孩子数据的快照。
每次修改子节点时都会调用OnChildChanged
回调。这包括对子节点后代的任何修改。它通常与OnChildAdded
和OnChildRemoved
调用结合使用,以响应对项目列表的更改。传递给侦听器的快照包含子项的更新数据。
OnChildRemoved
回调在删除直接子项时触发。它通常与OnChildAdded
和OnChildChanged
回调结合使用。传递给回调的快照包含已删除子项的数据。
每当OnChildChanged
调用由导致子项重新排序的更新引发时,就会触发OnChildMoved
回调。它与使用OrderByChild
或OrderByValue
排序的数据一起使用。
排序和过滤数据
您可以使用实时数据库Query
类来检索按键、值或子值排序的数据。您还可以将排序结果过滤为特定数量的结果或一定范围的键或值。
排序数据
要检索排序的数据,首先指定一种排序方法以确定结果的排序方式:
方法 | 用法 |
---|---|
OrderByChild() | 按指定子键的值排序结果。 | OrderByKey() | 按子键排序结果。 |
OrderByValue() | 按子值排序结果。 |
您一次只能使用一种排序方法。在同一查询中多次调用 order-by 方法会引发错误。
以下示例演示如何订阅按分数排序的分数排行榜。
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 firebase::Query
,当它与ValueListener结合使用时,将客户端与数据库中的排行榜同步,按每个条目的分数排序。您可以在构建您的数据库中阅读更多关于高效构建数据的信息。
对OrderByChild()
方法的调用指定了对结果进行排序所依据的子键。在这种情况下,结果按每个孩子的"score"
值排序。有关其他数据类型如何排序的更多信息,请参阅查询数据的排序方式。
过滤数据
要过滤数据,您可以在构造查询时将任何 limit 或 range 方法与 order-by 方法结合使用。
方法 | 用法 |
---|---|
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()
时,包含指定子键的数据按如下方式排序:
- 指定子键的
null
值的子项排在第一位。 - 接下来是指定子键值为
false
的子项。如果多个孩子的值为false
,则它们按字典顺序按键排序。 - 接下来是指定子键值为
true
的子项。如果多个孩子的值为true
,则它们按字典顺序按键排序。 - 接下来是具有数值的孩子,按升序排序。如果指定子节点的多个子节点的数值相同,则按key排序。
- 字符串位于数字之后,并按字典顺序升序排序。如果指定子节点的多个子节点具有相同的值,则它们按键按字典顺序排序。
- 对象排在最后,并按键按字典顺序升序排序。
OrderByKey
当使用OrderByKey()
对数据进行排序时,数据将按键按升序返回。
- 具有可以解析为 32 位整数的键的孩子排在第一位,按升序排序。
- 接下来是以字符串值作为键的子项,按字典顺序升序排序。
OrderByValue
使用OrderByValue()
时,子项按其值排序。排序标准与OrderByChild()
中的相同,除了使用节点的值而不是指定子键的值。