인덱싱된 텍스트 검색
인덱싱된 텍스트 검색은 텍스트 검색의 웜 캐시 버전입니다. 첫 호출에서 트라이그램과 식별자 포스팅을 디스크에 저장하고, 이후 호출에서는 저장소를 사용해 패턴이 있을 수 없는 파일을 건너뜁니다. 그 결과 데몬이나 감시 프로세스를 추가하지 않고도 대규모 저장소에서 반복 쿼리의 지연 시간이 크게 줄어듭니다.
웜 텍스트 기능에는 이제 다음이 포함됩니다:
- 단일 인덱스 검색
- 반복
--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/에 저장됩니다.
빌드에 성공하면 한 줄 요약이 출력됩니다:
Built index for 2547 files in .greph-index (193428 trigrams, +2547 ~0 -0 =0)
네 가지 카운터는 추가, 업데이트, 삭제, 변경되지 않은 파일의 수입니다. 새로 빌드하면 추가 카운터는 파일 수와 같고 나머지는 모두 0입니다.
인덱스 새로 고침
./vendor/bin/greph-index refresh .
refresh은 인덱싱된 루트를 다시 순회하고 모든 파일을 해시한 다음 메타데이터가 변경된 항목만 다시 씁니다. 출력에는 동일한 네 가지 카운터가 사용되어 무엇이 바뀌었는지 정확히 확인할 수 있습니다:
Refreshed index for 2547 files in .greph-index (193428 trigrams, +3 ~7 -1 =2536)
manual-refresh을 사용하는 경우 추적 대상 파일을 편집한 후 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 플래그를 매번 지정하는 대신 매니페스트를 사용하세요:
{
"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이 추출하는 항목은 다음과 같습니다:
- 트라이그램 집합: 파일 콘텐츠에 있는 3바이트 시퀀스의 집합입니다. 리터럴 및 정규식 쿼리 모두에서 대략적인 사전 필터로 사용됩니다.
- 식별자 포스팅: 파일에 있는 식별자 형태의 토큰 (
[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에 게시된 벤치마크 결과에 따르면, WordPress 코퍼스를 대상으로 한 정규식 쿼리와 개수, 매치가 있는 파일, 매치가 없는 파일 쿼리에서 웜 인덱싱 모드가 rg 및 grep보다 앞섭니다. 일반 리터럴 쿼리에서는 grep보다 빠르지만 rg보다는 약간 느립니다.
프로그래밍 방식 사용
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(...)를 사용하세요.