Indeksowane wyszukiwanie tekstu
Indeksowane wyszukiwanie tekstu to wersja wyszukiwania tekstowego 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-dirwyszukiwanie 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
# 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:
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
./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:
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 zmianmanual-refresh: pokazuj informacje o nieaktualnych danych wstats, ale nigdy nie odświeżaj indeksu podczas wyszukiwaniaopportunistic-refresh: odświeżaj indeks podczas wyszukiwania tylko wtedy, gdy zbiór zmienionych plików nie przekracza skonfigurowanych progówstrict-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:
./vendor/bin/greph-index stats . --dry-refresh
Wyszukiwanie w indeksie
# 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.
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:
{
"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:
./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ń:
-lcałe słowo-Lcałe słowo-qcał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-postingslubinvert-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 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
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(...).