Pular para o conteúdo

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-dir com 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

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

Por padrão, o índice é armazenado em <root>/.greph-index/.

Uma criação bem-sucedida exibe um resumo de uma linha:

Text
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

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

Text
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 automaticamente
  • manual-refresh: mostra informações desatualizadas em stats, mas nunca atualiza durante a pesquisa
  • opportunistic-refresh: atualiza durante a pesquisa somente quando o conjunto alterado fica abaixo dos limites configurados
  • strict-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:

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

Consultar o índice

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" .

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:

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
    }
  ]
}

Salve como .greph-index-set.json e, em seguida:

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" .

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:

  • -l de palavra inteira
  • -L de palavra inteira
  • -q de 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-postings ou invert-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

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