
# 쿼리 빌더

`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`는 범위 인덱스로 처리됩니다. 범위 인덱스 필드에 대한 `limit`를 포함한 `orderBy`는 인덱스 순서로 읽습니다. 그 외에는 모두 레코드 스캔으로 필터링됩니다. 플래너가 인덱스로 처리할 수 없는 필드에 대한 `orderBy`는 일치하는 레코드를 PHP에서 정렬하므로 대규모 컬렉션에서는 인덱스 조건과 함께 사용하세요. 필터가 단일 동등성 조건이면 `count()`는 `DocStore`의 인덱스 항목 수와 `SegmentedLog`의 메모리 내 카운터를 사용합니다.

`cursor($id)`는 해당 ID 이후 레코드로 결과를 제한하며, `page()`와 함께 사용하면 레코드 ID 순서로 안정적인 페이지네이션을 구현할 수 있습니다. 빌더 없이 일반 스트리밍 필터를 사용하려면 [쿼리](https://storh.inline0.dev/ko/docs/queries)를 참고하세요.
