
# Індексований пошук тексту

Індексований пошук тексту — це версія [пошуку тексту](https://greph.inline0.dev/uk/docs/modes/text) з прогрітим кешем. Під час першого виклику на диску створюється сховище триграм і списків входжень ідентифікаторів; наступні виклики використовують його, щоб пропускати файли, які очевидно не містять шуканого шаблону. Завдяки цьому повторні запити до великих репозиторіїв виконуються із значно меншою затримкою, без фонового процесу чи процесу спостереження.

Прогрітий текстовий пошук тепер включає:

- пошук в одному індексі
- пошук у кількох індексах із повторенням `--index-dir`
- пошук у наборах на основі маніфесту
- обробку застарілих індексів з урахуванням життєвого циклу
- діагностику планувальника через `--trace-plan`

Індекс доступний через `greph-index` CLI і методи фасаду `Greph::buildTextIndex()` / `Greph::searchTextIndexed()`.

## Створення індексу

```bash
# Full build at the current directory
./vendor/bin/greph-index build .

# Full build at an explicit root
./vendor/bin/greph-index build path/to/repo

# Use a non-default index directory
./vendor/bin/greph-index build . --index-dir /tmp/greph-index

# Build with an explicit lifecycle policy
./vendor/bin/greph-index build . \
  --lifecycle opportunistic-refresh \
  --auto-refresh-max-files 32 \
  --auto-refresh-max-bytes 1048576
```

За замовчуванням індекс зберігається в `<root>/.greph-index/`.

Після успішного створення виводиться однорядковий підсумок:

```text
Built index for 2547 files in .greph-index (193428 trigrams, +2547 ~0 -0 =0)
```

Чотири лічильники показують кількість доданих, оновлених, видалених і незмінених файлів. Під час першого створення індексу лічильник доданих дорівнює кількості файлів, а решта лічильників дорівнюють нулю.

## Оновлення індексу

```bash
./vendor/bin/greph-index refresh .
```

`refresh` повторно обходить індексований кореневий каталог, обчислює хеш кожного файлу та перезаписує лише записи зі зміненими метаданими. У виводі використовуються ті самі чотири лічильники, щоб точно показати зміни:

```text
Refreshed index for 2547 files in .greph-index (193428 trigrams, +3 ~7 -1 =2536)
```

Запускайте `refresh` після редагування відстежуваних файлів, якщо використовуєте `manual-refresh`. Для індексів `opportunistic-refresh` команда Greph може автоматично оновити індекс під час пошуку, якщо кількість змін достатньо мала.

## Профілі життєвого циклу

Greph працює без фонового процесу. Профілі життєвого циклу визначають, як поводиться прогрітий індекс після змін у дереві вихідного коду:

- `static`: ніколи автоматично не перевіряє актуальність і не вносить змін
- `manual-refresh`: показує відомості про застарілий стан у `stats`, але не оновлює індекс під час пошуку
- `opportunistic-refresh`: оновлює індекс під час пошуку, лише якщо кількість змін нижча за налаштовані пороги
- `strict-stale-check`: відхиляє пошук із застарілим прогрітим індексом замість його оновлення

Використовуйте `greph-index stats --dry-refresh`, щоб перед запуском перевірити, як спрацює прогрітий пошук:

```bash
./vendor/bin/greph-index stats . --dry-refresh
```

## Запити до індексу

```bash
# Fixed-string search
./vendor/bin/greph-index search -F "function" .

# Case-insensitive whole-word
./vendor/bin/greph-index search -F -i -w "function" .

# Counts
./vendor/bin/greph-index search -F -c "function" .

# Files only
./vendor/bin/greph-index search -F -l "function" .

# Multi-index search
./vendor/bin/greph-index search -F "apply_filters" . \
  --index-dir wordpress/.greph-index \
  --index-dir wp-content/plugins/my-plugin/.greph-index \
  --show-index-origin

# Regex search
./vendor/bin/greph-index search "function\s+\w+" .

# JSON output
./vendor/bin/greph-index search --json -F "function" .

# Planner trace to stderr
./vendor/bin/greph-index search --trace-plan -F "function" .
```

Набір прапорців такий самий, як у нативному пошуку тексту. Повний перелік дивіться в довідці [CLIдовідника](https://greph.inline0.dev/uk/docs/cli#greph-index).

Якщо індексу немає в запитаному розташуванні, `greph-index search` видає помилку. Ця команда автоматично не переходить до пошуку без індексу (для команд AST є необов’язковий параметр `--fallback scan`, але для текстової команди його немає). Спершу створіть індекс.

## Іменовані набори індексів

Якщо ви регулярно шукаєте в тій самій прогрітій структурі, використовуйте маніфест замість повторення прапорців `--index-dir`:

```json
{
  "name": "wordpress-local",
  "indexes": [
    {
      "name": "core-text",
      "root": "wordpress",
      "mode": "text",
      "lifecycle": "static",
      "priority": 100
    },
    {
      "name": "plugin-text",
      "root": "wp-content/plugins/my-plugin",
      "mode": "text",
      "lifecycle": "opportunistic-refresh",
      "max_changed_files": 16,
      "max_changed_bytes": 262144,
      "priority": 200
    }
  ]
}
```

Збережіть його як `.greph-index-set.json`, а потім виконайте:

```bash
./vendor/bin/greph-index set build
./vendor/bin/greph-index set stats --dry-refresh
./vendor/bin/greph-index set search --show-index-origin -F "apply_filters" .
```

## Що зберігає індекс

Текстовий індекс — це сховище триграм і списків входжень ідентифікаторів. Для кожного індексованого файлу Greph витягує:

- **Набір триграм**: набір послідовностей із трьох байтів у вмісті файлу. Використовується як грубий попередній фільтр для пошуку за буквальним текстом і регулярними виразами.
- **Списки входжень ідентифікаторів**: набір токенів, схожих на ідентифікатори (`[A-Za-z_][A-Za-z0-9_]*`), у файлі. Використовується для пришвидшення пошуку цілих слів.
- **Метадані життєвого циклу**: збережені порогові значення та параметри перевірки застарілого стану.
- **Метадані вмісту**: розмір і mtime для ухвалення рішень про оновлення та перевірки актуальності.

Коли надходить запит, Greph витягує з шаблону ті самі дані (триграми з буквальних фрагментів, токени-ідентифікатори зі шаблонів пошуку цілих слів), перетинає списки входжень і перечитує лише файли зі збігами. Файли, що залишилися після перетину, шукаються тим самим рушієм, що й у нативному текстовому режимі.

Для деяких узагальнювальних запитів із прогрітим індексом Greph може одразу повернути відповідь зі списків входжень:

- ціле слово `-l`
- ціле слово `-L`
- ціле слово `-q`

Так не потрібно повторно відкривати файли-кандидати, якщо списки входжень уже дають однозначну відповідь.

Це схоже на необов’язкове попереднє індексування в ripgrep і триграмний індекс codesearch, але спеціально побудоване для формату зберігання на диску, за якого прогрітий PHP працює найшвидше.

## Діагностика планувальника

`--trace-plan` виводить рішення планувальника прогрітого текстового пошуку в `stderr`, не змінюючи звичайний потік результатів.

Трасування містить:

- профіль життєвого циклу
- кількість вибраних файлів
- джерело кандидатів (`full-scan`, `trigram-postings`, `word-postings` або `invert-scan`)
- кількість термінів у списках входжень
- кількість файлів-кандидатів і перевірених файлів
- можливість прямого підсумкового результату
- можливість кешування запиту та рішення щодо його наповнення

## Коли використовувати

Режим з індексом — доречний інструмент, коли:

- Ви багато разів поспіль шукаєте в тому самому репозиторії (завдання CI, цикли агентів, інтеграції з IDE).
- Репозиторій достатньо великий, щоб власне сканування стало вузьким місцем.
- Шаблони містять буквальні підрядки або токени ідентифікаторів, за якими індекс може попередньо відфільтрувати результати.

Власне сканування — доречний інструмент, коли:

- Ви шукаєте в невеликому репозиторії, де витрати на побудову індексу переважають.
- Ви шукаєте в окремому шляху, до якого більше не повертатиметеся.
- Вам потрібні всі збіги за один разовий виклик і ви не хочете підтримувати індекс.

Опубліковані результати тестування в [READMEбенчмарку](https://github.com/inline0/greph#performance) показують, що прогрітий режим з індексом випереджає і `rg`, і `grep` для запитів із регулярними виразами, а також для запитів на підрахунок, файли зі збігами та файли без збігів у корпусі WordPress. Для звичайних буквальних запитів він швидший за `grep`, але трохи повільніший за `rg`.

## Програмне використання

```php
use Greph\Greph;
use Greph\Text\TextSearchOptions;
use Greph\Index\IndexLifecycleProfile;

// Build once
Greph::buildTextIndex('.', lifecycle: IndexLifecycleProfile::OpportunisticRefresh);

// Refresh after edits
Greph::refreshTextIndex('.');

// Query
$results = Greph::searchTextIndexed(
    'function',
    'src',
    new TextSearchOptions(fixedString: true),
);
```

`searchTextIndexed` приймає ті самі `TextSearchOptions`, що й власний текстовий пошук, і повертає результати тієї самої форми `list<TextFileResult>`. Для роботи з кількома індексами або маніфестом використовуйте `searchTextIndexedMany(...)` і `searchTextIndexedSet(...)`.
