# 实时数据推送

[2.8.1](/miniprogram/dev/framework/compatibility.md)

### 介绍

云开发数据库支持实时推送变更数据的能力，给定查询条件，每当数据库更新而导致查询条件对应的查询结果发生变更时，小程序可收到一个更新事件，其中可获取更新内容和更新后的查询结果快照。

实时数据推送有广泛应用场景，此处是一些示例：

1. 聊天/即时通信：小游戏内聊天、大厅广播、区服广播等；企业内部小程序中的即时通信能力等
1. 多人小游戏：使用状态同步的小游戏，如棋牌类等回合制游戏
1. 协作工具：如在线协作文档、团队任务管理等
1. 实时应用状态同步：以信息流为例，可以实时获取最新文章、以及最新评论、点赞、通知等内容，让交互更顺畅自然

> 工具中新建云开发快速启动模板，可以查看到使用实时数据推送能力搭建的即时通信 demo

### 监听

调用 `Collection` 上的 `watch` 方法即可监听给定查询条件对应的数据，支持搭配使用 `orderBy` 和 `limit`（从 `2.9.2` 起监听支持 `orderBy` 和 `limit`）。

示例：

```js
const db = wx.cloud.database()
const watcher = db.collection('todos')
  // 按 progress 降序
  .orderBy('progress', 'desc')
  // 取按 orderBy 排序之后的前 10 个
  .limit(10)
  .where({
    team: 'our dev team'
  })
  .watch({
    onChange: function(snapshot) {
      console.log('docs\'s changed events', snapshot.docChanges)
      console.log('query result snapshot after the event', snapshot.docs)
      console.log('is init data', snapshot.type === 'init')
    },
    onError: function(err) {
      console.error('the watch closed because of error', err)
    }
  })
// ...
// 等到需要关闭监听的时候调用 close() 方法
// watcher.close()
```

`onChange` 和 `onError` 是必传参数。`onChange` 用于接收变更快照，`onError` 用于处理监听错误。如果监听发起失败或监听过程中出现不可恢复的错误，则会终止监听并通过 `onError` 抛出异常。`onChange` 会在第一次监听初始化及后续数据变更时收到推送事件。第一次初始化时会收到的查询条件对应的查询结果（此处不会有默认 20 条上限，包含集合中所有满足查询条件的记录），后续变更事件会包含变更内容和变更后的查询结果快照。

`onChange` 收到的 `snapshot` 变更快照中带有如下字段：

| 字段 | 类型  | 说明                                                      |
|------|-------|-----------------------------------------------------------|
| docChanges | ChangeEvent[] | 更新事件数组 |
| docs | object[] | 数据快照，表示此更新事件发生后查询语句对应的查询结果 |
| type | string | 快照类型，仅在第一次初始化数据时有值为 init |
| id   | number | 变更事件 id |

`ChangeEvent` 记录变更事件的内容包括：

| 字段 | 类型  | 说明                                                      |
|------|-------|-----------------------------------------------------------|
| id   | number | 更新事件 id |
| queueType | string | 列表更新类型，表示更新事件对监听列表的影响，枚举值，定义见 QueueType |
| dataType  | string | 数据更新类型，表示记录的具体更新类型，枚举值，定义见 DataType |
| docId     | string | 更新的记录 id |
| doc       | object | 更新的完整记录 |
| updatedFields | object | 所有更新的字段及字段更新后的值，key 为更新的字段路径，value 为字段更新后的值，仅在 `update` 操作时有此信息 |
| removedFields | string[] | 所有被删除的字段，仅在 `update` 操作时有此信息 |

`DataType` 枚举值

| 枚举值 | 说明 |
| ----  | --- |
| init    | 初始化数据 |
| update  | 记录内容更新，对应 `update` 操作 |
| replace | 记录内容被替换，对应 `set` 操作 |
| add     | 记录新增，对应 `add` 操作 |
| remove  | 记录被删除，对应 `remove` 操作 |
| limit   | 记录因 `limit` 排序而被进入/离开列表 |

`QueueType` 枚举值

| 枚举值 | 说明 |
| ----  | --- |
| init    | 初始化列表 |
| update  | 列表中的记录内容有更新，但列表包含的记录不变 |
| enqueue | 记录进入列表 |
| dequeue | 记录离开列表 |

变更事件会细分记录数据变更类型 `dataType` 和监听列表变更类型 `queueType`，其可能的搭配和意义如下。

| dataType | queueType | 说明 |
| -------- | --------- | --- |
| init     | init      | 监听开始时的初始化数据 |
| update   | update    | 记录部分更新，更新后仍在查询结果列表中 |
| update   | enqueue   | 记录部分更新，更新后进入查询结果列表 |
| update   | dequeue   | 记录部分更新，更新后离开查询结果列表 |
| replace  | update    | 记录被替换，更新后仍在查询结果列表中 |
| replace  | enqueue   | 记录被替换，更新后进入查询结果列表 |
| replace  | dequeue   | 记录被替换，更新后离开查询结果列表 |
| add      | enqueue   | 记录是新增记录，新增后进入查询结果列表 |
| remove   | dequeue   | 记录被删除，离开查询结果列表 |

> 监听 [API 文档](/miniprogram/dev/wxcloudservice/wxcloud/reference-sdk-api/database/collection/Collection.watch)

### orderBy 与 limit

[2.9.2](/miniprogram/dev/framework/compatibility.md)

从 `2.9.2` 起，在监听时支持使用 `orderBy` 和 `limit`，如果不传或版本号低于 `2.9.2`，则默认按 `id` 降序排列（等同于 `orderBy('id', 'desc')`），`limit` 默认不存在即取所有数据。使用 `limit` 时，若记录因排序问题而非记录变更时进入或离开队列，此时变更事件的 `dataType` 为 `limit`。

在监听中，`orderBy` 最多可以指定 5 个排序字段，`limit` 最大值为 200。

### 注意事项与系统限制

**监听返回的数据不受默认 20 条限制**

监听返回的数据可能超过 20 条，不受小程序端默认 20 条上限限制。

**监听支持 where, orderBy, limit 语句**

监听支持 `where`, `orderBy` 和 `limit` 语句，暂不支持 `field` 语句。

**监听记录数限制**

一次监听的记录数上限为 5000，若超出上限会抛错并停止监听。监听过大量的数据时初始化会较慢，对监听效率也有影响，如果预期监听发起时少于 5000，但后续有可能超过 5000，请注意在即将超过时重新监听并保证不超过 5000。

**注意集合权限设置**

集合的读权限设置在实时数据推送里同样生效，如果权限是设置为仅可读用户自己的数据，则监听的时候无法监听到非用户自己创建的数据。

### 最佳实践

**只监听必要的数据**

监听时应明确查询条件，只监听必须用到的数据，避免监听不必要的数据，以此提高初次加载数据的性能以及接收数据变更的性能。
