
# Indexované textové vyhledávání

Indexované textové vyhledávání je varianta [textového vyhledávání](https://greph.inline0.dev/cs/docs/modes/text) využívající zahřátou mezipaměť. První volání vytvoří na disku úložiště trigramů a výskytů identifikátorů; další volání ho využijí k přeskočení souborů, které zjevně nemohou obsahovat hledaný vzor. Opakované dotazy ve velkých repozitářích tak mají mnohem nižší latenci, aniž by bylo nutné zavádět démona nebo proces sledující změny.

Zahřáté textové vyhledávání nyní zahrnuje:

- vyhledávání v jediném indexu
- opakované vyhledávání `--index-dir` ve více indexech
- vyhledávání v sadě určené manifestem
- práci se zastaralými daty s ohledem na životní cyklus
- diagnostiku plánovače pomocí `--trace-plan`

Index je zpřístupněn pod `greph-index` CLI a metodami fasády `Greph::buildTextIndex()` / `Greph::searchTextIndexed()`.

## Vytvoření indexu

```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
```

Index se ve výchozím nastavení ukládá do `<root>/.greph-index/`.

Úspěšné vytvoření vypíše jednořádkové shrnutí:

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

Čtyři čítače udávají počet přidaných, aktualizovaných, smazaných a nezměněných souborů. Při prvním sestavení se hodnota čítače přidaných rovná počtu souborů a všechny ostatní jsou nulové.

## Obnovení indexu

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

`refresh` znovu projde indexovaný kořenový adresář, vypočítá hash každého souboru a přepíše pouze položky, jejichž metadata se změnila. Výstup používá stejné čtyři čítače, takže přesně uvidíte, co se změnilo:

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

Pokud používáte `manual-refresh`, spusťte `refresh` po úpravě sledovaných souborů. U indexů `opportunistic-refresh` může Greph index automaticky obnovit během vyhledávání, pokud je sada změněných souborů stále dostatečně malá.

## Profily životního cyklu

Greph funguje bez démona. Profily životního cyklu určují chování zahřátého indexu při změně zdrojového stromu:

- `static`: nikdy automaticky nekontrolovat aktuálnost ani neměnit data
- `manual-refresh`: zobrazit informace o zastaralých datech v `stats`, ale během vyhledávání je nikdy neobnovovat
- `opportunistic-refresh`: obnovovat během vyhledávání pouze tehdy, když počet změněných souborů nepřekračuje nastavené limity
- `strict-stale-check`: místo obnovení odmítnout vyhledávání v zastaralém zahřátém indexu

Pomocí `greph-index stats --dry-refresh` si před spuštěním zahřátého vyhledávání prohlédněte, co by provedlo:

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

## Dotazování indexu

```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" .
```

Sada příznaků je stejná jako u nativního textového vyhledávání. Úplný seznam najdete v [referenční příručce CLI](https://greph.inline0.dev/cs/docs/cli#greph-index).

Pokud index v požadovaném umístění neexistuje, `greph-index search` vyvolá chybu. Tento příkaz se automaticky nevrátí k prohledávání bez indexu (příkazy AST mají volbu `--fallback scan`, textový příkaz ji ale nemá). Nejdříve index vytvořte.

## Pojmenované sady indexů

Pokud opakovaně vyhledáváte ve stejném zahřátém uspořádání, použijte místo opakovaného zadávání příznaků `--index-dir` manifest:

```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
    }
  ]
}
```

Uložte ho jako `.greph-index-set.json` a potom:

```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" .
```

## Co index ukládá

Textový index je úložiště trigramů a výskytů identifikátorů. Pro každý indexovaný soubor Greph extrahuje:

- **Sadu trigramů**: množinu tříbajtových sekvencí v obsahu souboru. Slouží jako hrubý předfiltr pro doslovné dotazy i dotazy s regulárním výrazem.
- **Výskyty identifikátorů**: množinu tokenů ve tvaru identifikátorů (`[A-Za-z_][A-Za-z0-9_]*`) v souboru. Urychluje dotazy na celá slova.
- **Metadata životního cyklu**: uložené limity a chování při kontrole zastaralých dat.
- **Metadata obsahu**: velikost a čas změny souboru pro rozhodování o obnovení a kontrolu zastaralých dat.

Po přijetí dotazu Greph extrahuje z hledaného vzoru stejné druhy údajů (trigramy z doslovných podřetězců, tokeny identifikátorů ze vzorů pro celá slova), protne seznamy výskytů a znovu načte pouze odpovídající soubory. Soubory, které po průniku zůstanou, prohledá stejný modul jako v nativním textovém režimu.

U některých zahřátých souhrnných dotazů může Greph odpovědět přímo z výskytů:

- celá slova `-l`
- celá slova `-L`
- celá slova `-q`

Díky tomu není nutné znovu otevírat kandidátní soubory, pokud už seznamy výskytů odpověď dokazují.

Jde o stejný princip jako u volitelného předběžného indexování v ripgrep a trigramového indexu v codesearch, ale je přizpůsoben podobě dat na disku, kterou zahřáté PHP zpracovává nejrychleji.

## Diagnostika plánovače

`--trace-plan` zaznamená rozhodnutí plánovače zahřátého textového vyhledávání do `stderr`, aniž by změnilo běžný proud výsledků.

Trasování obsahuje:

- profil životního cyklu
- počet vybraných souborů
- zdroj kandidátů (`full-scan`, `trigram-postings`, `word-postings` nebo `invert-scan`)
- počet hledaných výrazů ve výskytech
- počet kandidátních a ověřených souborů
- zda lze odpovědět přímo ze souhrnu
- zda lze použít mezipaměť dotazů a rozhodnutí o jejím naplnění

## Kdy jej použít

Režim s indexem je vhodný, když:

- Dotazujete se ve stejném repozitáři mnohokrát po sobě (úlohy CI, smyčky agentů, integrace do IDE).
- Repozitář je dost velký na to, aby nativní prohledávání představovalo úzké hrdlo.
- Vzory obsahují doslovné podřetězce nebo tokeny identifikátorů, které lze předfiltrovat pomocí indexu.

Nativní prohledávání je stále vhodnější, když:

- Prohledáváte malý repozitář, kde náklady na vytvoření indexu převyšují jeho přínos.
- Hledáte v cestě, kterou už nebudete znovu prohledávat.
- Potřebujete najít všechny shody v jediném jednorázovém dotazu a nechcete udržovat index.

Zveřejněné výsledky benchmarku v [README](https://github.com/inline0/greph#performance) ukazují, že zahřátý indexovaný režim předčí `rg` i `grep` při dotazech s regulárními výrazy a dotazech na počet, soubory se shodami a soubory bez shod v korpusu WordPress. U jednoduchých doslovných dotazů je rychlejší než `grep`, ale zůstává mírně pomalejší než `rg`.

## Programové použití

```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` přijímá stejné `TextSearchOptions` jako nativní textové vyhledávání a vrací stejný tvar `list<TextFileResult>`. Pro práci s více indexy nebo manifestem použijte `searchTextIndexedMany(...)` a `searchTextIndexedSet(...)`.
