
# Построитель запросов

`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)` ограничивает результаты записями после указанного идентификатора и сочетается с `page()` для стабильной постраничной навигации, отсортированной по идентификатору записи. Обычные потоковые фильтры без построителя запросов описаны в разделе [Запросы](https://storh.inline0.dev/ru/docs/queries).
