
# 查询构建器

`DocStore` 和 `SegmentedLog` 提供 `query()`。`DocStore` 可以使用二级索引；`SegmentedLog` 则对流式读取的记录应用相同谓词。

```php
$records = $docs
    ->query()
    ->where('kind')->eq('page')
    ->where('status')->in(['draft', 'published'])
    ->where('publishedAt')->between($from, $until)
    ->orderBy('publishedAt', 'desc')
    ->limit(50)
    ->get();
```

常用终结方法：

```php
$docs->query()->where('slug')->eq('home')->first();
$docs->query()->where('kind')->eq('page')->count();
$docs->query()->cursor($lastId)->page(100)->get();
$docs->query()->where('slug')->eq('home')->explain();
```

在 `DocStore` 上，针对非唯一相等索引字段的两个 `eq` 条件可以使用自动复合分桶。单字段相等、`in` 和范围运算符仍会使用已配置的字段索引。

运算符：

- `eq`, `neq`
- `in`, `notIn`
- `gt`, `gte`, `lt`, `lte`, `between`
- `exists`, `missing`
- `prefix`
- `andWhere`, `orWhere`
- `orderBy`, `limit`, `cursor`, `page`

`andWhere` 和 `orWhere` 使用回调，因为 `and` 和 `or` 是保留的 PHP 关键字：

```php
$docs->query()
    ->where('kind')->eq('page')
    ->orWhere(fn ($q) => $q->where('kind')->eq('post')->where('featured')->eq(true))
    ->get();
```

链式调用 `where()` 会以 AND 组合；每个 `orWhere()` 分支都会添加一个备选条件。

## 执行方式说明

每个条件都适用于任何集合；索引只会改变执行开销。在 `DocStore` 上，`eq` 和 `in` 通过相等索引解析，范围运算符和 `prefix` 通过范围索引解析；在范围索引字段上使用 `orderBy` 和 `limit` 时，会按索引顺序读取。其他情况都会扫描记录并筛选。对于规划器无法通过索引处理的字段，`orderBy` 会在 PHP 中对匹配记录排序，因此在大型集合中应将其与索引条件配合使用。当筛选条件是单个相等条件时，`count()` 会在 `DocStore` 上使用索引条目计数，在 `SegmentedLog` 上使用内存计数器。

`cursor($id)` 会将结果限制为指定 ID 之后的记录，可与 `page()` 组合，按记录 ID 排序实现稳定分页。若要在不使用构建器时执行普通流式筛选，请参阅[查询](https://storh.inline0.dev/zh/docs/queries)。
