
# Indexierte AST-Suche

Die indexierte AST-Suche ist die Variante der [AST-Suche](https://greph.inline0.dev/de/docs/modes/ast) mit aufgewärmtem Cache. Beim ersten Aufruf werden Fakten auf Knotenebene (Aufrufnamen, Instanziierungen, Klassendeklarationen, Methodendeklarationen) extrahiert und in einem Speicher auf der Festplatte abgelegt. Bei späteren Aufrufen wird damit das Durchsuchen von Dateien übersprungen, die offensichtlich keinen strukturellen Treffer enthalten können. Anschließend prüft der Matcher die Kandidaten erneut anhand des Quellcodes.

Du erreichst den Faktenindex über den Unterbefehl `greph-index ast-index` CLI und die Fassadenmethoden `Greph::buildAstIndex()` / `Greph::searchAstIndexed()`. Er wird auch bei aufgewärmten Suchen über mehrere Indizes und manifestbasierten Gruppen verwendet.

## Index erstellen

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

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

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

# Build a mutable overlay that can refresh cheaply on demand
./vendor/bin/greph-index ast-index build . \
  --lifecycle opportunistic-refresh \
  --auto-refresh-max-files 32 \
  --auto-refresh-max-bytes 1048576
```

Standardmäßig wird der Faktenindex unter `<root>/.greph-ast-index/` gespeichert.

Nach erfolgreicher Erstellung wird eine einzeilige Zusammenfassung ausgegeben:

```text
Built AST index for 2547 files in .greph-ast-index (84931 fact rows, +2547 ~0 -0 =0)
```

Die vier Zähler geben an, wie viele Dateien seit dem letzten Erstellen des Indexes hinzugefügt, aktualisiert, gelöscht oder nicht verändert wurden.

## Index aktualisieren

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

`refresh` durchsucht den indexierten Stammordner erneut, parst geänderte Dateien neu und schreibt nur die veränderten Faktzeilen um. Die Ausgabe verwendet dieselben vier Zähler wie der Textindex.

Lebenszyklusprofile funktionieren genauso wie bei aufgewärmten Textindizes:

- `static`: Aktualität nie prüfen oder automatisch ändern
- `manual-refresh`: veralteten Zustand in `stats` melden, aber bei der Suche nie aktualisieren
- `opportunistic-refresh`: bei der Suche nur aktualisieren, wenn die Zahl geänderter Dateien gering genug ist
- `strict-stale-check`: veraltete Suchen ablehnen, anstatt den Index zu aktualisieren

## Index abfragen

```bash
./vendor/bin/greph-index ast-index search 'new $CLASS()' src
./vendor/bin/greph-index ast-index search '$obj->$method($$$ARGS)' src
./vendor/bin/greph-index ast-index search --json 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-index search -l 'array($$$ITEMS)' src
./vendor/bin/greph-index ast-index search --trace-plan 'new $CLASS()' src
```

Die Metavariablen-Syntax entspricht der nativen AST-Suche. Die vollständige Grammatik findest du unter [Modi / AST-Suche](https://greph.inline0.dev/de/docs/modes/ast).

### Optionen

| Flag | Bedeutung |
|---|---|
| `--lang NAME` | AST-Sprache. Standard: `php` |
| `-j N`, `--jobs N` | Anzahl paralleler Worker |
| `-l`, `--files-with-matches` | Nur passende Dateien auflisten |
| `--glob GLOB` | Nur Dateien einschließen, deren Pfad auf GLOB passt |
| `--type NAME` / `--type-not NAME` | Dateitypfilter |
| `--json` | JSON-Ausgabe erzeugen |
| `--no-ignore` | `.gitignore` und `.grephignore` ignorieren |
| `--hidden` | Versteckte Dateien einschließen |
| `--strict-parse` | Bei Parse-Fehlern abbrechen, statt sie zu überspringen |
| `--fallback MODE` | Verhalten bei fehlendem Index: `fail` (Standard) oder `scan` |
| `--index-dir DIR` | Ein anderes Indexverzeichnis verwenden |
| `--trace-plan` | Planner-Diagnosen der aufgewärmten AST-Suche an `stderr` ausgeben |
| `--show-index-origin` | Bei Suchen über mehrere Indizes oder Gruppen die Ausgabe mit der Bezeichnung des passenden Indexes versehen |

### Rückfall bei fehlendem Index

Standardmäßig meldet `ast-index search` einen Fehler, wenn am angegebenen Speicherort kein Index vorhanden ist. Übergib `--fallback scan`, um stattdessen eine native AST-Suche auszuführen. Das ist sinnvoll für Tools, die nach Möglichkeit die indexierte Suche verwenden, aber auch ohne zuvor erstellten Index Ergebnisse liefern sollen.

```bash
./vendor/bin/greph-index ast-index search --fallback scan 'new $CLASS()' src
```

### Suche über mehrere Indizes

Du kannst mit einem einzigen Befehl mehrere aufgewärmte AST-Indizes durchsuchen, indem du `--index-dir` wiederholst. Das ist nützlich für Setups wie WordPress-Kern plus Plugin oder Theme.

```bash
./vendor/bin/greph-index ast-index search \
  --index-dir ../wordpress/.greph-ast-index \
  --index-dir ./.greph-ast-index \
  --show-index-origin \
  'new $CLASS()' .
```

Wenn `--show-index-origin` aktiviert ist, wird der Name des passenden aufgewärmten Indexeintrags vorangestellt. So siehst du, aus welchem Stammverzeichnis der Treffer stammt.

### Manifestbasierte Indexgruppen

Für stabile Setups mit aufgewärmten Indizes kannst du ein Manifest definieren und `greph-index set` die gruppierten Indizes durchsuchen lassen:

```bash
./vendor/bin/greph-index set build
./vendor/bin/greph-index set stats --mode ast-index --dry-refresh
./vendor/bin/greph-index set search --mode ast-index --show-index-origin 'new $CLASS()' .
```

Die Unterbefehle für Gruppen verwenden dieselbe Lebenszyklussteuerung, dieselben Prüfungen auf veraltete Indizes und dieselben Planner-Diagnosen wie die direkte Verwendung von `ast-index`.

### Planner-Diagnosen

`--trace-plan` zeigt, wie Greph die Abfrage vor der Ausführung des vollständigen Matchers eingegrenzt hat. Bei `ast-index` umfasst das üblicherweise:

- die grobe Faktensignatur, die aus dem Muster extrahiert wurde
- die Anzahl der durchsuchten aufgewärmten Indizes
- die Anzahl der Dateien, die der Faktenfilter ausgeschlossen hat
- die Anzahl der Kandidatendateien, die für die genaue strukturelle Prüfung erneut geparst wurden

## Gespeicherte Indexdaten

Für jede indexierte Datei zeichnet der AST-Faktenextraktor für jeden „interessanten“ Knoten eine Zeile auf. Die verschiedenen Faktarten decken die Konstrukte ab, auf die AST-Muster am häufigsten abzielen:

- Funktions- und Methodenaufrufe (nach aufgerufenem Namen)
- Klasseninstanziierungen (nach Klassenname)
- Deklarationen von Klassen, Interfaces, Traits und Enums (nach Name)
- Funktionsdeklarationen (nach Name)
- Methodendeklarationen (nach Name und Sichtbarkeit)
- Use-Anweisungen

Bei einer Abfrage wandelt `Greph\Ast\AstFactQuery` das Suchmuster in eine grobe Faktensignatur um (zum Beispiel „muss ein `new` einer beliebigen Klasse enthalten“) und gleicht diese mit dem Faktenspeicher ab. Die übrig gebliebenen Dateien werden dann erneut geparst und mit dem vollständigen Muster verglichen.

Der Faktenindex ist also ein *Vorfilter* und kein vollständiger Speicher für Treffer. Beim Abgleich wird der Quellcode weiterhin geparst. Die strukturelle Genauigkeit entspricht daher der nativen AST-Suche; lediglich die Menge der Kandidaten ist kleiner.

## Wann du ihn verwenden solltest

Der Modus „Indexierte AST-Suche“ eignet sich, wenn:

- du wiederholt dieselben strukturellen Abfragen für dieselbe Codebasis ausführst.
- die Muster auf erkennbare Strukturen abzielen (Aufrufname, Klassenname, Deklarationsart), die der Faktenextraktor speichert.
- du schneller Ergebnisse erhalten möchtest, als wenn jede Datei erneut geparst würde.

Bei Mustern, deren Unterscheidungsmerkmal nicht im Faktensatz enthalten ist (zum Beispiel komplexen verschachtelten Ausdrücken ohne erkennbaren Kopf), eignet sich der [AST-Cache-Modus](https://greph.inline0.dev/de/docs/indexed/cache) meist besser. Er überspringt den Parse-Schritt vollständig, ohne sich auf eine grobe Signatur zu stützen.

## Programmatische Verwendung

```php
use Greph\Greph;
use Greph\Ast\AstSearchOptions;
use Greph\Index\IndexLifecycleProfile;

Greph::buildAstIndex('.', lifecycle: IndexLifecycleProfile::OpportunisticRefresh);

$matches = Greph::searchAstIndexed(
    'new $CLASS()',
    'src',
    new AstSearchOptions(jobs: 4, tracePlan: true),
);
```

`searchAstIndexed` akzeptiert dieselben `AstSearchOptions` wie die native AST-Suche und gibt dieselbe `list<AstMatch>`-Struktur zurück. Für Abläufe über mehrere Indizes oder manifestbasierte Gruppen kannst du `searchAstIndexedMany(...)` und `searchAstIndexedSet(...)` verwenden.
