全新推出 Cloud Firestore(测试版):试用 Firebase 和 Google Cloud Platform 全新推出的规模可扩展且灵活的数据库。详细了解 Cloud Firestore

在 iOS 上处理数据列表

获取 FIRDatabaseReference

要从数据库读取数据或将数据写入到数据库中,您需要一个 FIRDatabaseReference 实例:

Swift

var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

@property (strong, nonatomic) FIRDatabaseReference *ref;

self.ref = [[FIRDatabase database] reference];

读取和写入列表

向数据列表附加数据

使用 childByAutoId 方法可将数据附加到多用户应用中的列表中。每次将新子项目添加到指定的 Firebase 引用时,childByAutoId 方法均会生成一个独一无二的键。如果为列表中的每个新元素使用这些自动生成的键,则多个客户端可以同时向同一位置添加子项目,而不会引起写入冲突。childByAutoId 生成的独一无二的键是以时间戳为基础的,因此列表项目会自动按时间顺序排列。

您可以使用 childByAutoId 方法返回的对新数据的引用获取子项目的自动生成的键的值或为子项目设置数据。对 childByAutoId 引用调用 getKey 将返回自动生成的键。

然后,您可以使用这些自动生成的键简化数据结构的平展过程。如需了解详细信息,请参阅数据扇出示例

侦听子项目事件

当因为某项操作(例如通过 childByAutoId 方法添加新的子项目,或通过 updateChildValues 方法更新子项目)而使得某个节点的子项目发生更改时,就会触发子项目事件。

事件类型 典型用法
FIRDataEventTypeChildAdded 检索项目列表,或侦听项目列表中是否添加了新项目。该事件将针对每个现有的子项目触发一次,并在每次向指定的路径添加新的子项目时再次触发。系统将向侦听器传递包含新子项目的数据的快照。
FIRDataEventTypeChildChanged 侦听对列表中的项目做出的更改。每次修改子节点时,均会触发此事件。这包括对子节点的后代所做的任何修改。传递给事件侦听器的快照包含子节点的更新数据。
FIRDataEventTypeChildRemoved 侦听将从列表中移除的项目。移除直接子项目将会触发此事件。传递给回调块的快照包含已移除的子项目的数据。
FIRDataEventTypeChildMoved 侦听经过排序的列表的项目顺序是否有更改。只要更新会引发子项目重新排序,就会触发此事件。此事件用于按 queryOrderedByChild queryOrderedByValue 排序的数据。

同时使用所有这些事件有助于侦听数据库中某个特定节点是否有更改。例如,社交博客应用可以结合使用这些方法来监控博文评论中的活动,如下所示:

Swift

// Listen for new comments in the Firebase database
commentsRef.observe(.childAdded, with: { (snapshot) -> Void in
  self.comments.append(snapshot)
  self.tableView.insertRows(at: [IndexPath(row: self.comments.count-1, section: self.kSectionComments)], with: UITableViewRowAnimation.automatic)
})
// Listen for deleted comments in the Firebase database
commentsRef.observe(.childRemoved, with: { (snapshot) -> Void in
  let index = self.indexOfMessage(snapshot)
  self.comments.remove(at: index)
  self.tableView.deleteRows(at: [IndexPath(row: index, section: self.kSectionComments)], with: UITableViewRowAnimation.automatic)
})

Objective-C

// Listen for new comments in the Firebase database
[_commentsRef
              observeEventType:FIRDataEventTypeChildAdded
              withBlock:^(FIRDataSnapshot *snapshot) {
                [self.comments addObject:snapshot];
                [self.tableView insertRowsAtIndexPaths:@[
                  [NSIndexPath indexPathForRow:self.comments.count - 1 inSection:kSectionComments]
                ]
                                      withRowAnimation:UITableViewRowAnimationAutomatic];
              }];
// Listen for deleted comments in the Firebase database
[_commentsRef
 observeEventType:FIRDataEventTypeChildRemoved
 withBlock:^(FIRDataSnapshot *snapshot) {
   int index = [self indexOfMessage:snapshot];
   [self.comments removeObjectAtIndex:index];
   [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:kSectionComments]]
                         withRowAnimation:UITableViewRowAnimationAutomatic];
 }];

侦听值事件

尽管侦听子项目事件是推荐的读取数据列表的方式,但有些情况下侦听某个列表引用中的值事件会很有用。

如果将一个 FIRDataEventTypeValue 观察者附加到某个数据列表,系统会以单个数据快照的形式返回整个数据列表,然后您可以遍历该数据快照来访问各个子项目。

即使查询仅存在一个匹配项时,该快照仍是一个仅包含单个项目的列表。要访问该项目,您需要遍历查询结果:

Swift

_commentsRef.observe(.value) { snapshot in
  for child in snapshot.children {
    ...
  }
}

Objective-C

[_commentsRef
              observeEventType:FIRDataEventTypeValue
              withBlock:^(FIRDataSnapshot *snapshot) {
                // Loop over children
                NSEnumerator *children = [snapshot children];
                FIRDataSnapshot *child;
                while (child = [children nextObject]) {
                  // ...
                }
              }];

当您希望通过一次操作提取某个列表的所有子项目(而不是侦听额外子项目添加事件)时,此模式可能会很有用。

排序和过滤数据

您可以使用实时数据库 FIRDatabaseQuery 类来检索按键、按值或按子项目的值排序的数据。您还可以对排序后的结果进行过滤,从而得到特定数量的结果或一系列键或值。

将数据排序

要检索经过排序的数据,请先指定一项排序依据方法,确定如何对结果排序:

方法 用法
queryOrderedByKey 按子键对结果排序。
queryOrderedByValue 按子值对结果排序。
queryOrderedByChild 按指定子键的值或嵌套子路径对结果排序。

您每次只能使用一种排序依据方法。对同一查询多次调用排序依据方法会引发错误。

以下示例演示了如何检索按所得星数排序的用户热门博文列表:

Swift

// My top posts by number of stars
let myTopPostsQuery = (ref.child("user-posts").child(getUid())).queryOrdered(byChild: "starCount")

Objective-C

// My top posts by number of stars
FIRDatabaseQuery *myTopPostsQuery = [[[self.ref child:@"user-posts"]
                                      child:[super getUid]]
                                     queryOrderedByChild:@"starCount"];

此查询根据用户 ID 检索数据库路径中的用户博文,并按每篇博文获得的星数排序。这种使用 ID 作为索引键的技术称为“数据扇出”,您可以在组织您的数据库中详加了解。

调用 queryOrderedByChild 方法可将子键指定为将结果排序的依据。在本例中,博文按各自 "starCount" 子项目的值进行排序。您具有与下例类似的数据,查询也可以按嵌套的子项目进行排序:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

在本例中,通过在 queryOrderedByChild 调用中指定嵌套子项目的相对路径,我们可以按嵌套在 metrics 键下的值对列表元素进行排序。

Swift

 
let postsByMostPopular = ref.child("posts").queryOrdered(byChild: "metrics/views")

Objective-C

 
FIRDatabaseQuery *postsByMostPopular = [[ref child:@"posts"] queryOrderedByChild:@"metrics/views"];

如需详细了解如何对其他数据类型进行排序,请参阅如何对查询数据进行排序

过滤数据

要过滤数据,您可以在构建查询时将某种限制方法或范围方法与排序依据方法结合使用。

方法 用法
queryLimitedToFirst 设置要返回的最大项目数:从经过排序的结果列表开头算起。
queryLimitedToLast 设置要从返回的最大项目数:经过排序的结果列表结尾算起。
queryStartingAtValue 返回大于或等于指定键或值的项目,具体取决于所选的排序依据方法。
queryEndingAtValue 返回小于或等于指定键或值的项目,具体取决于所选的排序依据方法。
queryEqualToValue 返回等于指定键或值的项目,具体取决于所选的排序依据方法。

与排序依据方法不同,您可以结合使用多种限制或范围函数。例如,您可以结合使用 queryStartingAtValuequeryEndingAtValue 方法将结果限制为指定范围内的值。

限制结果数

您可以使用 queryLimitedToFirstqueryLimitedToLast 方法设置要针对给定回调同步的最大子项目数。例如,如果使用 queryLimitedToFirst 设置一个数量为 100 的限制,则起初最多会收到 100 个 FIRDataEventTypeChildAdded 回调。如果您在 Firebase 数据库中存储的项目不到 100 个,则每个项目均会触发一次 FIRDataEventTypeChildAdded 回调。

随着项目发生更改,对于落入查询范围的项目,您将收到 FIRDataEventTypeChildAdded 回调;对于不再属于查询范围的项目,您将收到 FIRDataEventTypeChildRemoved 回调,而总数始终保持为 100。

以下示例演示了示例博客应用如何检索所有用户最新的 100 篇博文:

Swift

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
let recentPostsQuery = (ref?.child("posts").queryLimited(toFirst: 100))!

Objective-C

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
FIRDatabaseQuery *recentPostsQuery = [[self.ref child:@"posts"] queryLimitedToFirst:100];

按键或值过滤

您可以使用 queryStartingAtValuequeryEndingAtValuequeryEqualToValue 为查询选择任意起点、终点和等当点。这对于将数据分页或查找子项目具有特定值的项目非常有用。

如何对查询数据进行排序

此部分将介绍如何通过 FIRDatabaseQuery 类中的每种排序依据方法对数据进行排序。

queryOrderedByKey

当使用 queryOrderedByKey 对数据进行排序时,系统会按键以升序返回数据。

  1. 键可以解析为 32 位整数的子项目显示在前面,按升序排序。
  2. 以字符串值作为键的子项目紧随其后,按字典顺序以升序排列。

queryOrderedByValue

使用 queryOrderedByValue 时,系统将按值对子项目进行排序。排序标准与 queryOrderedByChild 中相同,但这里使用节点的值而非指定子键的值。

queryOrderedByChild

使用 queryOrderedByChild 时,系统会按如下方式对包含指定子键的数据进行排序:

  1. 指定子键为 nil 值的子项目显示在前面。
  2. 接下来是指定子键为 false 值的子项目。如果多个子节点的值均为 false,则按照键以字典顺序对它们进行排序。
  3. 接下来是指定子键为 true 值的子项目。如果多个子项目的值均为 true,则按键以字典顺序对它们进行排序。
  4. 接下来是值为数字的子项目,按升序排序。如果指定子节点的多个子项目具有相同的数字值,则按键对它们进行排序。
  5. 字符串显示在数字后面并按字典顺序以升序排列。如果指定子节点的多个子项目具有相同的值,则按键以字典顺序对它们进行排序。
  6. 对象放在最后,并按键以字典顺序排列(升序)。

分离侦听器

当您退出 ViewController 时,观察者不会自动停止同步数据。如果未妥善移除,观察者会继续将数据同步到本地内存。当不再需要观察者时,您可以将关联的 FIRDatabaseHandle 传递给 removeObserverWithHandle 方法,以将其移除。

将回调块添加到引用时,系统会返回 FIRDatabaseHandle。这些句柄可用于移除回调块。

如果有多个侦听器添加到了一个数据库引用,则当发生某事件时,系统会调用每一个侦听器。要在该位置停止同步数据,必须通过调用 removeAllObservers 方法来移除其中的所有观察者。

对侦听器调用 removeObserverWithHandleremoveAllObservers 不会自动移除在子节点上注册的侦听器;您还必须跟踪这些引用或句柄才能将其移除。

后续步骤

发送以下问题的反馈:

此网页
Firebase 实时数据库
需要帮助?请访问我们的支持页面