
# Indeksowane wyszukiwanie tekstu

Indeksowane wyszukiwanie tekstu to wersja [wyszukiwania tekstowego](https://greph.inline0.dev/pl/docs/modes/text) z rozgrzanym cache. Pierwsze wywołanie tworzy na dysku magazyn trójek i wystąpień identyfikatorów; kolejne wywołania korzystają z niego, aby pomijać pliki, które na pewno nie zawierają wzorca. Dzięki temu powtarzane zapytania w dużych repozytoriach mają znacznie mniejsze opóźnienia, bez wprowadzania procesu demona ani obserwatora.

Rozgrzany tryb wyszukiwania tekstu obejmuje teraz:

- wyszukiwanie w pojedynczym indeksie
- powtarzane `--index-dir` wyszukiwanie w wielu indeksach
- wyszukiwanie w zbiorze opartym na manifeście
- obsługę nieaktualnych danych uwzględniającą cykl życia
- diagnostykę planisty przez `--trace-plan`

Indeks jest dostępny pod `greph-index` CLI oraz metodami fasady `Greph::buildTextIndex()` / `Greph::searchTextIndexed()`.

## Tworzenie indeksu

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

Domyślnie indeks jest przechowywany w `<root>/.greph-index/`.

Po pomyślnym utworzeniu indeksu wyświetlane jest jednowierszowe podsumowanie:

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

Cztery liczniki obejmują liczbę dodanych, zaktualizowanych, usuniętych i niezmienionych plików. Przy świeżej kompilacji licznik dodanych plików jest równy ich liczbie, a wszystkie pozostałe wynoszą zero.

## Odświeżanie indeksu

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

`refresh` ponownie przechodzi po indeksowanym katalogu głównym, oblicza skróty wszystkich plików i przepisuje tylko wpisy, których metadane uległy zmianie. Wynik zawiera te same cztery liczniki, dzięki czemu dokładnie widać, co się zmieniło:

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

Uruchom `refresh` po edycji śledzonych plików, jeśli używasz `manual-refresh`. W przypadku indeksów `opportunistic-refresh` Greph może automatycznie odświeżyć indeks podczas wyszukiwania, jeśli zbiór zmienionych plików jest na tyle mały, że operacja pozostaje opłacalna.

## Profile cyklu życia

Greph nie wymaga demona. Profile cyklu życia określają zachowanie rozgrzanego indeksu po zmianie drzewa źródłowego:

- `static`: nigdy automatycznie nie sprawdzaj aktualności ani nie wprowadzaj zmian
- `manual-refresh`: pokazuj informacje o nieaktualnych danych w `stats`, ale nigdy nie odświeżaj indeksu podczas wyszukiwania
- `opportunistic-refresh`: odświeżaj indeks podczas wyszukiwania tylko wtedy, gdy zbiór zmienionych plików nie przekracza skonfigurowanych progów
- `strict-stale-check`: odrzucaj wyszukiwania w rozgrzanym indeksie z nieaktualnymi danymi zamiast odświeżać indeks

Użyj `greph-index stats --dry-refresh`, aby sprawdzić, co zrobiłoby wyszukiwanie w rozgrzanym indeksie przed jego uruchomieniem:

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

## Wyszukiwanie w indeksie

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

Zestaw flag jest taki sam jak w natywnym wyszukiwaniu tekstu. Pełną listę znajdziesz w [CLIdokumentacji](https://greph.inline0.dev/pl/docs/cli#greph-index).

Jeśli indeks nie istnieje we wskazanej lokalizacji, `greph-index search` zgłasza błąd. To polecenie nie przechodzi automatycznie na skanowanie bez indeksu (polecenia AST mają opcję `--fallback scan`, ale polecenie tekstowe jej nie ma). Najpierw zbuduj indeks.

## Nazwane zestawy indeksów

Jeśli wielokrotnie przeszukujesz ten sam rozgrzany układ, użyj manifestu zamiast powtarzać flagi `--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
    }
  ]
}
```

Zapisz go jako `.greph-index-set.json`, a następnie:

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

Indeks tekstowy to magazyn trójek i wystąpień identyfikatorów. Dla każdego indeksowanego pliku Greph wyodrębnia:

- **Zbiór trójek**: zbiór sekwencji trzech bajtów występujących w zawartości pliku. Służy jako zgrubny filtr wstępny dla zapytań dosłownych i regexowych.
- **Wystąpienia identyfikatorów**: zbiór tokenów przypominających identyfikatory (`[A-Za-z_][A-Za-z0-9_]*`) obecnych w pliku. Przyspieszają zapytania o całe słowa.
- **Metadane cyklu życia**: zapisane progi i sposób sprawdzania nieaktualnych danych.
- **Metadane zawartości**: rozmiar i czas modyfikacji, używane przy decyzjach o odświeżeniu i sprawdzaniu nieaktualności.

Po otrzymaniu zapytania Greph wyodrębnia te same typy danych ze wzorca (trójki z dosłownych podciągów, tokeny identyfikatorów ze wzorców całych słów), wyznacza część wspólną wystąpień i ponownie odczytuje tylko pasujące pliki. Pliki, które przejdą ten filtr, są przeszukiwane za pomocą tego samego silnika co w natywnym trybie tekstowym.

W przypadku niektórych zapytań podsumowujących na rozgrzanym indeksie Greph może od razu zwrócić wynik na podstawie wystąpień:

- `-l` całe słowo
- `-L` całe słowo
- `-q` całe słowo

Dzięki temu nie trzeba ponownie otwierać plików-kandydatów, jeśli wystąpienia już pozwalają ustalić odpowiedź.

To ten sam pomysł co opcjonalne indeksowanie wstępne w ripgrep i indeks trójek w codesearch, ale dostosowany specjalnie do postaci danych na dysku, które rozgrzany PHP może najszybciej przeszukiwać.

## Diagnostyka planisty

`--trace-plan` zapisuje decyzję planisty rozgrzanego trybu tekstowego do `stderr`, nie zmieniając zwykłego strumienia wyników.

Ślad diagnostyczny zawiera:

- profil cyklu życia
- liczbę wybranych plików
- źródło kandydatów (`full-scan`, `trigram-postings`, `word-postings` lub `invert-scan`)
- liczbę terminów w wystąpieniach
- liczbę plików-kandydatów i zweryfikowanych plików
- informację o kwalifikacji do bezpośredniego podsumowania
- informację o kwalifikacji do cache zapytań i decyzję o jego zapełnieniu

## Kiedy używać

Tryb indeksowany sprawdzi się, gdy:

- Wielokrotnie z rzędu przeszukujesz to samo repozytorium (zadania CI, pętle agenta, integracje z IDE).
- Repozytorium jest na tyle duże, że wąskim gardłem staje się natywne skanowanie.
- Wzorce zawierają dosłowne podciągi lub tokeny identyfikatorów, które indeks może wykorzystać jako filtr wstępny.

Natywne skanowanie sprawdzi się, gdy:

- Przeszukujesz małe repozytorium, w którym dominuje koszt budowy indeksu.
- Szukasz w ścieżce tylko raz i nie będziesz jej ponownie przeszukiwać.
- Potrzebujesz wszystkich dopasowań w pojedynczym, doraźnym wywołaniu i nie chcesz utrzymywać indeksu.

Opublikowane wyniki testów wydajnościowych w [README](https://github.com/inline0/greph#performance) pokazują, że rozgrzany tryb indeksowany przewyższa zarówno `rg`, jak i `grep` w zapytaniach regexowych oraz zapytaniach o liczbę dopasowań, pliki z dopasowaniami i pliki bez dopasowań w korpusie WordPress. W przypadku zwykłych zapytań dosłownych jest szybszy niż `grep`, ale pozostaje nieco wolniejszy od `rg`.

## Użycie programowe

```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` przyjmuje te same `TextSearchOptions` co natywne wyszukiwanie tekstu i zwraca dane o takim samym kształcie `list<TextFileResult>`. W przypadku przepływów pracy z wieloma indeksami lub manifestem użyj `searchTextIndexedMany(...)` i `searchTextIndexedSet(...)`.
