# 筛选、排序与分页

## query(datasetId, options?)

返回 `Promise<Record[]>`，不是带有 `records` 和 `total` 的对象。需要总数时，单独调用 `count`。

```js
const todos = await platform.data.get("todo");
const options = {
  where: { done: false },
  search: "快递",
  orderBy: { field: "created_at", dir: "desc" },
  limit: 20,
  offset: 0,
};
const rows = await platform.data.query(todos.id, options);
const total = await platform.data.count(todos.id, {
  where: options.where,
  search: options.search,
});
```

| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `where` | `{}` | 各字段条件以 AND 组合；每个字段最多一个比较符。 |
| `search` | 无 | 在 schema 中所有 string 字段进行文本匹配，与 where 同时生效。 |
| `orderBy.field` | `created_at` | 一个业务字段或系统字段。 |
| `orderBy.dir` | `desc` | `asc` 为升序，`desc` 为降序。 |
| `limit` | 100 | 当前范围 1 至 200，超出范围会限制，数值向下取整。 |
| `offset` | 0 | 跳过的记录数，负数按 0 处理，向下取整。 |

请传入有限的数字，不要使用 `NaN` 或无限大。调用 `query(id)` 也只返回最多 100 条，不会自动返回全部数据。

## where 比较符

```js
const rows = await platform.data.query(expenses.id, {
  where: {
    category: { $in: ["餐饮", "交通"] },
    amount: { $gte: 10 },
    note: { $contains: "午餐" },
  },
});
```

| 比较符 | 示例 | 含义 |
| --- | --- | --- |
| 直接值或 `$eq` | `{ done: false }` | 相等。 |
| `$ne` | `{ category: { $ne: "交通" } }` | 不相等。 |
| `$gt`、`$gte` | `{ amount: { $gte: 10 } }` | 大于、大于等于。 |
| `$lt`、`$lte` | `{ amount: { $lt: 100 } }` | 小于、小于等于。 |
| `$in` | `{ category: { $in: ["餐饮", "交通"] } }` | 命中数组中的任意一项；空数组不匹配记录。 |
| `$contains` | `{ title: { $contains: "快递" } }` | 文本包含匹配，不是 JSON 数组元素查询。 |

字段必须存在于 schema，或是系统字段 `id`、`created_at`、`updated_at`。每个字段只能使用一个比较符，当前不支持同字段双边范围、`$and`、`$or`、嵌套对象路径或数组比较。

`null` 条件目前不等价于 SQL 的 `IS NULL`，也没有专门的空值查询符。不要依赖它筛选空字段。`search` 和 `$contains` 使用文本包含匹配，`%`、`_` 和反斜杠会按照字面字符处理。

`search` 仅检索 string 字段。如果 schema 中没有 string 字段，这个条件不会产生筛选效果。

## count(datasetId, options?)

返回匹配的记录总数 `Promise<number>`。支持 `where` 和 `search`，不受分页参数影响。

```js
const remaining = await platform.data.count(todos.id, {
  where: { done: false },
});
```

## 分页

```js
const pageSize = 20;
const pageIndex = 1;
const rows = await platform.data.query(todos.id, {
  orderBy: { field: "created_at", dir: "desc" },
  limit: pageSize,
  offset: pageIndex * pageSize,
});
```

当前支持一个指定排序字段，并使用记录 ID 作为同值时的次级排序；分页采用 offset，不提供游标。数据在分页期间发生变化时，条目位置也可能变化；应用调用 `count` 和 `query` 时是两次独立请求，不是同一快照；宿主的单次分页查询会在同一事务中计算页内容与总数。

数字字段请使用数字条件，布尔字段请使用真正的 boolean。日期字段建议统一保存为同一时区的 ISO 8601 字符串后比较。
