Recherche textuelle indexée
La recherche textuelle indexée est la version avec cache préchauffé de la recherche textuelle. Le premier appel crée un stockage sur disque de trigrammes et d'occurrences d'identifiants ; les appels suivants s'en servent pour ignorer les fichiers qui ne peuvent manifestement pas contenir le motif. Les requêtes répétées sur de grands dépôts ont ainsi une latence bien plus faible, sans processus démon ni observateur.
La recherche textuelle préchauffée comprend désormais :
- la recherche dans un seul index
- la recherche répétée
--index-dirdans plusieurs index - la recherche dans un ensemble défini par manifeste
- la gestion des index obsolètes tenant compte de leur cycle de vie
- les diagnostics du planificateur via
--trace-plan
L'index est accessible via greph-index CLI et les méthodes de façade Greph::buildTextIndex() / Greph::searchTextIndexed().
Création de l’index
# 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
Par défaut, l'index est stocké dans <root>/.greph-index/.
Une création réussie affiche un résumé sur une seule ligne :
Built index for 2547 files in .greph-index (193428 trigrams, +2547 ~0 -0 =0)
Les quatre compteurs indiquent le nombre de fichiers ajoutés, mis à jour, supprimés et inchangés. Lors d'une nouvelle création, le compteur des ajouts correspond au nombre de fichiers et tous les autres sont à zéro.
Actualisation de l’index
./vendor/bin/greph-index refresh .
refresh parcourt de nouveau la racine indexée, calcule le hachage de chaque fichier et ne réécrit que les entrées dont les métadonnées ont changé. Le résultat utilise les mêmes quatre compteurs pour vous montrer précisément les changements :
Refreshed index for 2547 files in .greph-index (193428 trigrams, +3 ~7 -1 =2536)
Exécutez refresh après avoir modifié des fichiers suivis si vous utilisez manual-refresh. Pour les index opportunistic-refresh, Greph peut actualiser automatiquement l'index pendant la recherche si l'ensemble des changements reste assez limité.
Profils de cycle de vie
Greph fonctionne sans démon. Les profils de cycle de vie déterminent le comportement d'un index préchauffé lorsque l'arborescence source change :
static: ne vérifie jamais l'actualité et ne modifie jamais l'index automatiquementmanual-refresh: signale les informations obsolètes dansstats, sans jamais actualiser l'index pendant la rechercheopportunistic-refresh: actualise l'index pendant la recherche uniquement si l'ensemble des changements respecte les seuils configurésstrict-stale-check: rejette les recherches préchauffées obsolètes au lieu d'actualiser l'index
Utilisez greph-index stats --dry-refresh pour examiner ce que ferait la recherche préchauffée avant de la lancer :
./vendor/bin/greph-index stats . --dry-refresh
Interrogation de l’index
# 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" .
Les options sont les mêmes que pour la recherche textuelle native. Consultez la référence CLI pour la liste complète.
Si l'index n'existe pas à l'emplacement demandé, greph-index search renvoie une erreur. Cette commande ne lance pas automatiquement une recherche sans index (les commandes AST proposent une option --fallback scan à activer, contrairement à la commande textuelle). Créez d'abord l'index.
Ensembles d'index nommés
Si vous recherchez régulièrement dans la même configuration préchauffée, utilisez un manifeste plutôt que de répéter les options --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
}
]
}
Enregistrez-le sous .greph-index-set.json, puis :
./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" .
Contenu de l’index
L'index textuel stocke des trigrammes et des occurrences d'identifiants. Pour chaque fichier indexé, Greph extrait :
- Ensemble de trigrammes : les séquences de trois octets présentes dans le contenu du fichier. Elles servent de préfiltre général pour les requêtes littérales et regex.
- Occurrences d'identifiants : les jetons ayant la forme d'identifiants (
[A-Za-z_][A-Za-z0-9_]*) présents dans le fichier. Elles accélèrent les recherches de mots entiers. - Métadonnées de cycle de vie : seuils stockés et comportement de vérification des données obsolètes.
- Métadonnées de contenu : taille et date de modification pour décider des actualisations et détecter les données obsolètes.
Lorsqu'une requête arrive, Greph extrait les mêmes formes du motif (trigrammes des sous-chaînes littérales et jetons d'identifiant des motifs de mots entiers), recoupe les occurrences et relit uniquement les fichiers correspondants. Les fichiers retenus sont recherchés avec le même moteur qu'en mode textuel natif.
Pour certaines requêtes récapitulatives préchauffées, Greph peut répondre directement à partir des occurrences :
-lde mot entier-Lde mot entier-qde mot entier
Cela évite de rouvrir les fichiers candidats lorsque les occurrences suffisent déjà à établir le résultat.
Le principe est le même que le préindexage facultatif de ripgrep et l'index de trigrammes de codesearch, mais il est conçu spécialement pour les formes sur disque que la recherche préchauffée PHP peut traiter le plus rapidement.
Diagnostics du planificateur
--trace-plan envoie la décision du planificateur de recherche textuelle préchauffée vers stderr, sans modifier le flux normal des résultats.
La trace comprend :
- le profil de cycle de vie
- le nombre de fichiers sélectionnés
- la source des candidats (
full-scan,trigram-postings,word-postingsouinvert-scan) - le nombre de termes des occurrences
- le nombre de fichiers candidats et vérifiés
- l'admissibilité du récapitulatif direct
- l'admissibilité du cache de requêtes et la décision de le remplir
Quand l’utiliser
Le mode indexé est adapté lorsque :
- Vous interrogez plusieurs fois de suite le même dépôt (tâches CI, boucles d'agents, intégrations IDE).
- Le dépôt est assez volumineux pour que l'analyse native soit le goulot d'étranglement.
- Les motifs contiennent des sous-chaînes littérales ou des jetons d'identifiant que l'index peut filtrer en amont.
L'analyse native reste adaptée lorsque :
- Vous interrogez un petit dépôt pour lequel le coût de création de l'index est prépondérant.
- Vous effectuez une recherche ponctuelle dans un chemin que vous ne consulterez plus.
- Vous avez besoin de toutes les correspondances en une seule recherche ponctuelle et ne souhaitez pas gérer d'index.
Les chiffres publiés dans le benchmark README montrent que le mode indexé préchauffé devance rg et grep pour les requêtes regex ainsi que pour les requêtes de comptage, avec fichiers correspondants ou sans correspondance, sur le corpus WordPress. Pour les recherches littérales simples, il devance grep, mais reste légèrement derrière rg.
Utilisation par programmation
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 accepte les mêmes TextSearchOptions que la recherche textuelle native et renvoie la même structure list<TextFileResult>. Pour les flux multi-index ou reposant sur un manifeste, utilisez searchTextIndexedMany(...) et searchTextIndexedSet(...).