Pesquisa de texto indexada
A pesquisa de texto indexada é a versão com cache aquecido da pesquisa de texto. A primeira chamada cria um armazenamento em disco de trigramas e ocorrências de identificadores; as chamadas seguintes o usam para ignorar arquivos que claramente não podem conter o padrão. O resultado é uma latência muito menor em consultas repetidas em repositórios grandes, sem introduzir um processo de daemon ou monitoramento.
A superfície de pesquisa de texto aquecida agora inclui:
- pesquisa com índice único
- pesquisa repetida
--index-dircom vários índices - pesquisa de conjuntos baseados em manifestos
- tratamento de dados obsoletos ciente do ciclo de vida
- diagnósticos do planejador via
--trace-plan
O índice fica disponível em greph-index CLI e nos métodos de fachada Greph::buildTextIndex() / Greph::searchTextIndexed().
Criar o índice
# 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
Por padrão, o índice é armazenado em <root>/.greph-index/.
Uma criação bem-sucedida exibe um resumo de uma linha:
Built index for 2547 files in .greph-index (193428 trigrams, +2547 ~0 -0 =0)
Os quatro contadores indicam o número de arquivos adicionados, atualizados, excluídos e inalterados. Em uma criação nova, o contador de adicionados é igual à quantidade de arquivos e os demais ficam em zero.
Atualizar o índice
./vendor/bin/greph-index refresh .
refresh percorre novamente a raiz indexada, calcula o hash de cada arquivo e regrava somente as entradas cujos metadados mudaram. A saída usa os mesmos quatro contadores para mostrar exatamente o que mudou:
Refreshed index for 2547 files in .greph-index (193428 trigrams, +3 ~7 -1 =2536)
Execute refresh após editar arquivos rastreados ao usar manual-refresh. Para índices opportunistic-refresh, Greph pode atualizar o índice automaticamente durante a pesquisa se o conjunto alterado ainda for pequeno o bastante.
Perfis do ciclo de vida
Greph dispensa daemons. Os perfis do ciclo de vida controlam o comportamento de um índice aquecido quando a árvore de origem muda:
static: nunca verifica a atualidade nem altera dados automaticamentemanual-refresh: mostra informações desatualizadas emstats, mas nunca atualiza durante a pesquisaopportunistic-refresh: atualiza durante a pesquisa somente quando o conjunto alterado fica abaixo dos limites configuradosstrict-stale-check: rejeita pesquisas aquecidas desatualizadas em vez de atualizá-las
Use greph-index stats --dry-refresh para inspecionar o que a pesquisa aquecida faria antes de executá-la:
./vendor/bin/greph-index stats . --dry-refresh
Consultar o índice
# 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" .
O conjunto de sinalizadores é o mesmo da pesquisa de texto nativa. Consulte a CLIreferência para ver a lista completa.
Se o índice não existir no local solicitado, greph-index search gera um erro. Este comando não recorre automaticamente a uma varredura sem índice (os comandos AST têm uma opção --fallback scan, mas o comando de texto não). Crie o índice primeiro.
Conjuntos de índices nomeados
Se você pesquisa repetidamente o mesmo layout aquecido, use um manifesto em vez de repetir sinalizadores --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
}
]
}
Salve como .greph-index-set.json e, em seguida:
./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" .
O que o índice armazena
O índice de texto é um armazenamento de trigramas e ocorrências de identificadores. Para cada arquivo indexado, Greph extrai:
- Conjunto de trigramas: o conjunto de sequências de três bytes presentes no conteúdo do arquivo. Usado como pré-filtro aproximado em consultas literais e de regex.
- Ocorrências de identificadores: o conjunto de tokens com formato de identificador (
[A-Za-z_][A-Za-z0-9_]*) presentes no arquivo. Usado para acelerar consultas por palavra inteira. - Metadados do ciclo de vida: limites armazenados e comportamento de verificação de dados desatualizados.
- Metadados do conteúdo: tamanho e mtime para decisões de atualização e inspeção de dados desatualizados.
Quando uma consulta chega, Greph extrai as mesmas formas do padrão (trigramas de trechos literais, tokens de identificador de padrões de palavra inteira), cruza as ocorrências e relê somente os arquivos correspondentes. Os arquivos que passam pelo cruzamento são pesquisados com o mesmo mecanismo do modo de texto nativo.
Em algumas consultas de resumo aquecidas, Greph pode responder diretamente usando as ocorrências:
-lde palavra inteira-Lde palavra inteira-qde palavra inteira
Isso evita reabrir arquivos candidatos quando as ocorrências já comprovam a resposta.
A ideia é a mesma do pré-indexamento opcional de ripgrep e do índice de trigramas de codesearch, mas foi desenvolvida especificamente para as formas em disco que a pesquisa aquecida PHP consegue acessar mais rapidamente.
Diagnósticos do planejador
--trace-plan envia a decisão do planejador de texto aquecido para stderr sem alterar o fluxo normal de resultados.
O rastreamento inclui:
- perfil do ciclo de vida
- quantidade de arquivos selecionados
- origem dos candidatos (
full-scan,trigram-postings,word-postingsouinvert-scan) - quantidade de termos nas ocorrências
- quantidade de arquivos candidatos e verificados
- elegibilidade para resumo direto
- elegibilidade para cache de consultas e decisão de preenchimento
Quando usar
O modo indexado é a ferramenta certa quando:
- Você consulta o mesmo repositório várias vezes seguidas (tarefas de CI, ciclos de agentes, integrações com IDEs).
- O repositório é grande o bastante para que a varredura nativa seja o gargalo.
- Os padrões incluem trechos literais ou tokens de identificador que o índice pode usar como pré-filtro.
A varredura nativa ainda é a ferramenta certa quando:
- Você consulta um repositório pequeno, onde o custo de criação predomina.
- Você pesquisa um caminho pontual que não consultará novamente.
- Você precisa de todas as correspondências em uma única chamada improvisada e não quer manter um índice.
Os números publicados de benchmark em README mostram o modo indexado aquecido à frente de rg e grep tanto nas consultas de regex quanto nas consultas de contagem, arquivos com correspondências e arquivos sem correspondências, no corpus WordPress. Em consultas literais simples, ele supera grep, mas fica um pouco atrás de rg.
Uso programático
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 aceita os mesmos TextSearchOptions da pesquisa de texto nativa e retorna a mesma estrutura de list<TextFileResult>. Para fluxos com vários índices ou baseados em manifestos, use searchTextIndexedMany(...) e searchTextIndexedSet(...).