
# Индексирано търсене в текст

Индексираното търсене в текст е вариантът с предварително загрят кеш на [търсенето в текст](https://greph.inline0.dev/bg/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/bg/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_]*`), които се срещат във файла. Използва се за ускоряване на търсенията по цели думи.
- **Метаданни за жизнения цикъл**: съхранени прагове и поведение при проверка за остарели данни.
- **Метаданни за съдържанието**: размер и време на последна промяна за решения при обновяване и проверка за остарели данни.

При постъпване на заявка 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(...)`.
