Към съдържанието

Индексирано търсене в текст

Индексираното търсене в текст е вариантът с предварително загрят кеш на търсенето в текст. При първото извикване се изгражда дисково хранилище на триграми и публикации на идентификатори; следващите извиквания използват хранилището, за да пропускат файлове, които очевидно не могат да съдържат шаблона. Така латентността при повтарящи се заявки към големи хранилища става много по-ниска, без да се добавя демон или процес наблюдател.

Обхватът на загрятото търсене в текст вече включва:

  • търсене в един индекс
  • повтарящо се търсене в множество индекси --index-dir
  • търсене в набор, зададен чрез манифест
  • обработка на остарели данни с отчитане на жизнения цикъл
  • диагностика на планировчика чрез --trace-plan

Индексът е достъпен чрез greph-index CLI и фасадните методи Greph::buildTextIndex() / Greph::searchTextIndexed().

Създаване на индекса

Обвивка
# 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

По подразбиране индексът се съхранява в <root>/.greph-index/.

При успешно създаване се извежда едноредово обобщение:

Text
Built index for 2547 files in .greph-index (193428 trigrams, +2547 ~0 -0 =0)

Четирите брояча са броят на добавените, актуализираните, изтритите и непроменените файлове. При ново изграждане броят на добавените съвпада с броя на файловете, а всички останали стойности са нула.

Обновяване на индекса

Обвивка
./vendor/bin/greph-index refresh .

refresh обхожда отново индексирания корен, хешира всеки файл и пренаписва само записите, чиито метаданни са се променили. Резултатът използва същите четири брояча, за да виждате точно какво се е променило:

Text
Refreshed index for 2547 files in .greph-index (193428 trigrams, +3 ~7 -1 =2536)

Изпълнете refresh след редактиране на проследявани файлове, когато използвате manual-refresh. За индекси opportunistic-refresh, Greph може автоматично да обнови индекса по време на търсене, ако наборът от промени все още е достатъчно малък.

Профили на жизнения цикъл

Greph не използва демон. Профилите на жизнения цикъл определят как се държи загрятият индекс при промяна на дървото с изходен код:

  • static: никога не проверява актуалността и не променя автоматично
  • manual-refresh: показва информация за остарели данни в stats, но никога не обновява по време на търсене
  • opportunistic-refresh: обновява по време на търсене само когато наборът от промени е под зададените прагове
  • strict-stale-check: отхвърля търсения със загрят индекс, ако данните са остарели, вместо да ги обновява

Използвайте greph-index stats --dry-refresh, за да проверите какво би направило загрятото търсене, преди да го изпълните:

Обвивка
./vendor/bin/greph-index stats . --dry-refresh

Заявки към индекса

Обвивка
# 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" .

Наборът от флагове е същият като при нативното търсене в текст. Вижте справочника за CLI за пълния списък.

Ако индексът не съществува на посоченото място, greph-index search извежда грешка. Тази команда няма автоматично резервно преминаване към сканиране без индекс (AST командите имат опция --fallback scan, но текстовата команда няма). Първо изградете индекса.

Именувани набори от индекси

Ако многократно търсите в едно и също загрято оформление, използвайте манифест, вместо да повтаряте флаговете --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
    }
  ]
}

Запазете го като .greph-index-set.json, след което:

Обвивка
./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" .

Какво съхранява индексът

Текстовият индекс е хранилище на триграми и публикации на идентификатори. За всеки индексиран файл Greph извлича:

  • Набор от триграми: наборът от последователности от три байта, които се срещат във файла. Използва се като груб предварителен филтър както за буквални заявки, така и за заявки с регулярни изрази.
  • Публикации на идентификатори: наборът от токени с форма на идентификатор ([A-Za-z_][A-Za-z0-9_]*), които се срещат във файла. Използва се за ускоряване на търсенията по цели думи.
  • Метаданни за жизнения цикъл: съхранени прагове и поведение при проверка за остарели данни.
  • Метаданни за съдържанието: размер и време на последна промяна за решения при обновяване и проверка за остарели данни.

При постъпване на заявка Greph извлича същите видове данни от шаблона (триграми от буквалните поднизове, токени на идентификатори от шаблони за цели думи), пресича публикациите и препрочита само съвпадащите файлове. Файловете, които остават след пресичането, се търсят със същия механизъм като в нативен текстов режим.

За някои обобщаващи заявки със загрят индекс Greph може да отговори директно от публикациите:

  • -l за цяла дума
  • -L за цяла дума
  • -q за цяла дума

Така изобщо не се налага повторно отваряне на файловете кандидати, когато публикациите вече доказват отговора.

Това е същата идея като незадължителното предварително индексиране на ripgrep и индекса на триграми на codesearch, но е изградено специално около формите на данните на диска, които загретият PHP може да обработва най-бързо.

Диагностика на планировчика

--trace-plan извежда решението на планировчика за загрято търсене в текст към stderr, като оставя обичайния поток от резултати непроменен.

Проследяването включва:

  • профил на жизнения цикъл
  • брой избрани файлове
  • източник на кандидатите (full-scan, trigram-postings, word-postings или invert-scan)
  • брой термини в публикациите
  • брой файлове кандидати и проверени файлове
  • допустимост за директно обобщение
  • допустимост за кеширане на заявката и решение дали да се попълни кешът

Кога да го използвате

Индексираният режим е подходящият инструмент, когато:

  • Заявявате едно и също хранилище многократно (CI задачи, цикли на агенти, IDE интеграции).
  • Хранилището е достатъчно голямо, за да се превърне нативното сканиране в тясно място.
  • Шаблоните включват буквални поднизове или токени на идентификатори, които индексът може предварително да филтрира.

Нативното сканиране все още е подходящият инструмент, когато:

  • Търсите в малко хранилище, където разходът за изграждане на индекса е по-голям.
  • Търсите еднократно в път, който няма да заявявате отново.
  • Нуждаете се от всички съвпадения при еднократно импровизирано извикване и не искате да поддържате индекс.

Публикуваните бенчмарк резултати в README показват, че загретият индексиран режим превъзхожда както rg, така и grep при заявки с регулярни изрази и при заявки за брой, файлове със съвпадения и файлове без съвпадения в корпуса WordPress. При обикновени буквални заявки той е по-бърз от grep, но остава малко по-бавен от rg.

Програмно използване

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 приема същия TextSearchOptions като нативното търсене в текст и връща същата форма на list<TextFileResult>. За потоци с множество индекси или с манифест използвайте searchTextIndexedMany(...) и searchTextIndexedSet(...).