
# Indizierte Textsuche

Die indizierte Textsuche ist die Cache-aufgewärmte Variante der [Textsuche](https://greph.inline0.dev/de/docs/modes/text). Beim ersten Aufruf wird ein Datenspeicher auf der Festplatte mit Trigrammen und Bezeichner-Vorkommen erstellt. Bei späteren Aufrufen überspringt dieser Dateien, die das Muster offensichtlich nicht enthalten können. So sinkt die Latenz bei wiederholten Abfragen in großen Repositories deutlich, ohne dass ein Daemon oder Watcher-Prozess nötig ist.

Die aufgewärmte Textsuche umfasst jetzt:

- Suche mit einem Index
- Wiederholte `--index-dir` Suche mit mehreren Indizes
- Suche in manifestbasierten Mengen
- Lebenszyklusbewusste Behandlung veralteter Daten
- Planner-Diagnosen über `--trace-plan`

Der Index ist unter `greph-index` CLI sowie über die Fassadenmethoden `Greph::buildTextIndex()` / `Greph::searchTextIndexed()` verfügbar.

## Index erstellen

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

Der Index wird standardmäßig unter `<root>/.greph-index/` gespeichert.

Nach erfolgreicher Erstellung wird eine einzeilige Zusammenfassung ausgegeben:

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

Die vier Zähler geben die Anzahl hinzugefügter, aktualisierter, gelöschter und unveränderter Dateien an. Bei einem neuen Index entspricht der Zähler für hinzugefügte Dateien der Dateianzahl; alle anderen Zähler stehen auf null.

## Index aktualisieren

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

`refresh` durchläuft das indizierte Stammverzeichnis erneut, berechnet für jede Datei einen Hash und schreibt nur Einträge neu, deren Metadaten sich geändert haben. Die Ausgabe verwendet dieselben vier Zähler, damit du genau siehst, was sich geändert hat:

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

Führe `refresh` nach dem Bearbeiten erfasster Dateien aus, wenn du `manual-refresh` verwendest. Bei `opportunistic-refresh`-Indizes kann Greph den Index während der Suche automatisch aktualisieren, sofern sich die Änderungen noch effizient genug verarbeiten lassen.

## Lebenszyklusprofile

Greph kommt ohne Daemon aus. Lebenszyklusprofile legen fest, wie sich ein aufgewärmter Index bei Änderungen am Quellbaum verhält:

- `static`: Aktualität nie automatisch prüfen oder ändern
- `manual-refresh`: Veraltete Informationen in `stats` anzeigen, aber den Index während der Suche nie aktualisieren
- `opportunistic-refresh`: Den Index während der Suche nur aktualisieren, wenn die geänderte Dateimenge unter den festgelegten Grenzwerten liegt
- `strict-stale-check`: Veraltete Suchen im aufgewärmten Index ablehnen, statt ihn zu aktualisieren

Verwende `greph-index stats --dry-refresh`, um vor der Suche zu prüfen, was die aufgewärmte Suche tun würde:

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

## Index abfragen

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

Es gelten dieselben Flags wie bei der nativen Textsuche. Die vollständige Liste findest du in der [Referenz zu CLI](https://greph.inline0.dev/de/docs/cli#greph-index).

Existiert der Index nicht am angegebenen Speicherort, löst `greph-index search` einen Fehler aus. Dieser Befehl wechselt nicht automatisch zu einer Suche ohne Index (bei den AST-Befehlen gibt es dafür die optionale Option `--fallback scan`, beim Textbefehl jedoch nicht). Erstelle zuerst den Index.

## Benannte Indexmengen

Wenn du dasselbe aufgewärmte Layout wiederholt durchsuchst, verwende ein Manifest, statt die Flags `--index-dir` immer wieder anzugeben:

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

Speichere es unter `.greph-index-set.json` und führe dann Folgendes aus:

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

## Gespeicherte Indexdaten

Der Textindex speichert Trigramme und Bezeichner-Vorkommen. Für jede indizierte Datei extrahiert Greph:

- **Trigrammmenge**: Die Menge der Drei-Byte-Sequenzen im Dateiinhalt. Dient als grober Vorfilter für Literale und Regex-Abfragen.
- **Bezeichner-Vorkommen**: Die Menge bezeichnerähnlicher Tokens (`[A-Za-z_][A-Za-z0-9_]*`) in der Datei. Beschleunigt die Suche nach ganzen Wörtern.
- **Lebenszyklusmetadaten**: Gespeicherte Grenzwerte und Verhalten bei der Prüfung auf veraltete Daten.
- **Inhaltsmetadaten**: Größe und mtime für Aktualisierungsentscheidungen und die Prüfung auf veraltete Daten.

Bei einer Abfrage extrahiert Greph dieselben Formen aus dem Muster (Trigramme aus literalen Teilzeichenfolgen, Bezeichner-Tokens aus Mustern für ganze Wörter), schneidet die Vorkommenmengen miteinander und liest nur die passenden Dateien erneut ein. Die Dateien, die diese Schnittmenge überstehen, werden mit derselben Engine wie im nativen Textmodus durchsucht.

Bei manchen aufgewärmten Zusammenfassungsabfragen kann Greph direkt anhand der Vorkommen antworten:

- Ganzwort-`-l`
- Ganzwort-`-L`
- Ganzwort-`-q`

So müssen Kandidatendateien gar nicht erneut geöffnet werden, wenn die Vorkommen die Antwort bereits belegen.

Das entspricht dem optionalen Vorindizieren von ripgrep und dem Trigrammindex von codesearch, ist jedoch speziell auf die Formen auf der Festplatte ausgelegt, die die aufgewärmte Suche PHP am schnellsten abfragen kann.

## Planner-Diagnosen

`--trace-plan` gibt die Entscheidung des aufgewärmten Text-Planners an `stderr` aus, ohne den normalen Ergebnisstrom zu verändern.

Die Ablaufverfolgung enthält:

- Lebenszyklusprofil
- Anzahl ausgewählter Dateien
- Quelle der Kandidaten (`full-scan`, `trigram-postings`, `word-postings` oder `invert-scan`)
- Anzahl der Vorkommen-Begriffe
- Anzahl der Kandidaten und geprüften Dateien
- Eignung für direkte Zusammenfassungen
- Eignung und Befüllungsentscheidung des Abfrage-Caches

## Wann du ihn verwenden solltest

Der indizierte Modus ist das richtige Werkzeug, wenn:

- Du dasselbe Repository viele Male hintereinander durchsuchst (CI-Jobs, Agentenschleifen, IDE-Integrationen).
- Das Repository groß genug ist, dass das native Durchsuchen zum Engpass wird.
- Die Muster literale Teilzeichenfolgen oder Bezeichner-Tokens enthalten, die der Index vorfiltern kann.

Das native Durchsuchen ist weiterhin das richtige Werkzeug, wenn:

- Du ein kleines Repository durchsuchst, bei dem die Kosten für den Indexaufbau überwiegen.
- Du einen einmaligen Pfad durchsuchst, den du nicht erneut abfragen wirst.
- Du alle Treffer in einem einzigen Ad-hoc-Aufruf brauchst und keinen Index pflegen möchtest.

Die veröffentlichten Benchmark-Zahlen in der [README](https://github.com/inline0/greph#performance) zeigen, dass der aufgewärmte indizierte Modus bei Regex-Abfragen sowie bei Abfragen nach Anzahl, Dateien mit Treffern und Dateien ohne Treffer im Korpus WordPress sowohl `rg` als auch `grep` übertrifft. Bei einfachen Literalsuchen ist er schneller als `grep`, bleibt aber knapp hinter `rg` zurück.

## Programmatische Verwendung

```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` akzeptiert dieselben `TextSearchOptions` wie die native Textsuche und liefert dieselbe `list<TextFileResult>`-Struktur zurück. Für Abläufe mit mehreren Indizes oder Manifesten verwendest du `searchTextIndexedMany(...)` und `searchTextIndexedSet(...)`.
