Zum Inhalt springen

Indizierte Textsuche

Die indizierte Textsuche ist die Cache-aufgewärmte Variante der Textsuche. 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

Shell
# 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

Shell
./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:

Shell
./vendor/bin/greph-index stats . --dry-refresh

Index abfragen

Shell
# 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.

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:

Shell
./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 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(...).