
# Indeksoitu tekstihaku

Indeksoitu tekstihaku on välimuistin lämmittävä versio [tekstinhausta](https://greph.inline0.dev/fi/docs/modes/text). Ensimmäinen kutsu luo levylle tallennuspaikan trigrammeille ja tunnisteiden esiintymille; myöhemmät kutsut käyttävät sitä ohittaakseen tiedostot, joissa kuviota ei selvästikään voi olla. Näin toistuvien kyselyjen viive pienenee huomattavasti suurissa koodivarastoissa ilman taustapalvelua tai tiedostojen muutoksia tarkkailevaa prosessia.

Lämmitetty tekstihakutoiminto sisältää nyt:

- yhden indeksin haku
- toistuva `--index-dir` usean indeksin haku
- manifestiin perustuva joukkojen haku
- elinkaaren huomioiva vanhentuneiden tietojen käsittely
- suunnittelijan diagnostiikka komennolla `--trace-plan`

Indeksi on käytettävissä polussa `greph-index` CLI sekä facade-metodeissa `Greph::buildTextIndex()` / `Greph::searchTextIndexed()`.

## Indeksin rakentaminen

```bash
# 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
```

Indeksi tallennetaan oletusarvoisesti polkuun `<root>/.greph-index/`.

Onnistuneesta rakentamisesta tulostetaan yksirivinen yhteenveto:

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

Neljä laskuria ilmaisevat lisättyjen, päivitettyjen, poistettujen ja muuttumattomien tiedostojen määrän. Uudessa rakennuksessa lisättyjen laskurin arvo vastaa tiedostojen määrää, ja muiden laskureiden arvo on nolla.

## Indeksin päivittäminen

```bash
./vendor/bin/greph-index refresh .
```

`refresh` käy indeksoidun juuren uudelleen läpi, tiivistää jokaisen tiedoston ja kirjoittaa uudelleen vain ne tietueet, joiden metatiedot ovat muuttuneet. Tulosteessa käytetään samoja neljää laskuria, joten näet tarkasti, mikä muuttui:

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

Suorita `refresh` muokattuasi seurattuja tiedostoja, kun käytät `manual-refresh`. `opportunistic-refresh`-indekseissä Greph voi päivittää indeksin automaattisesti haun aikana, jos muuttuneiden tiedostojen joukko on riittävän pieni.

## Elinkaariprofiilit

Greph ei käytä taustaprosessia. Elinkaariprofiileilla määritetään, miten lämmitetty indeksi toimii lähdepuun muuttuessa:

- `static`: älä koskaan tarkista ajantasaisuutta tai muuta tietoja automaattisesti
- `manual-refresh`: tuo vanhentuneet tiedot näkyviin komennon `stats` tulosteessa, mutta älä koskaan päivitä haun aikana
- `opportunistic-refresh`: päivitä haun aikana vain, jos muuttuneiden tiedostojen määrä alittaa määritetyt raja-arvot
- `strict-stale-check`: hylkää vanhentuneet lämmitetyn indeksin haut päivittämisen sijaan

Tarkista komennolla `greph-index stats --dry-refresh`, mitä lämmitetty haku tekisi, ennen kuin suoritat sen:

```bash
./vendor/bin/greph-index stats . --dry-refresh
```

## Indeksin kyselyt

```bash
# 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" .
```

Valitsinjoukko on sama kuin alkuperäisessä tekstinhaussa. Katso [CLIviitteestä](https://greph.inline0.dev/fi/docs/cli#greph-index) täydellinen luettelo.

Jos indeksiä ei ole pyydetyssä sijainnissa, `greph-index search` antaa virheen. Tämä komento ei siirry automaattisesti indeksoimattomaan hakuun (AST-komennoissa on valinnainen `--fallback scan`, mutta tekstikomennossa ei). Rakenna indeksi ensin.

## Nimetyt indeksijoukot

Jos haet toistuvasti samasta lämmitetystä rakenteesta, käytä manifestia sen sijaan, että toistaisit `--index-dir`-valitsimia:

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

Tallenna se nimellä `.greph-index-set.json` ja suorita sitten:

```bash
./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" .
```

## Indeksin sisältö

Teksti-indeksi on trigrammien ja tunnisteiden esiintymien varasto. Jokaisesta indeksoidusta tiedostosta Greph poimii:

- **Trigrammijoukko**: tiedoston sisällössä esiintyvät kolmen tavun jonot. Käytetään karkeana esisuodattimena sekä literaali- että regex-kyselyille.
- **Tunnisteiden esiintymät**: tiedostossa esiintyvät tunnisteen muotoiset tokenit (`[A-Za-z_][A-Za-z0-9_]*`). Nopeuttavat kokonaisia sanoja koskevia kyselyjä.
- **Elinkaaren metatiedot**: tallennetut raja-arvot ja vanhentuneiden tietojen tarkistustapa.
- **Sisällön metatiedot**: koko ja muokkausaika päivityspäätöksiä ja vanhentuneiden tietojen tarkistusta varten.

Kun kysely saapuu, Greph poimii kuviosta vastaavat muodot (trigrammit literaaliosajonoista ja tunnistetokenit kokonaisia sanoja koskevista kuvioista), leikkaa esiintymäjoukot ja lukee uudelleen vain osumien tiedostot. Leikkauksen läpäisseet tiedostot haetaan samalla moottorilla kuin natiivissa tekstihakutilassa.

Joihinkin lämmitetyn haun yhteenvetokyselyihin Greph voi vastata suoraan esiintymätiedoista:

- kokonaisia sanoja koskeva `-l`
- kokonaisia sanoja koskeva `-L`
- kokonaisia sanoja koskeva `-q`

Näin ehdokastiedostoja ei tarvitse avata uudelleen, kun esiintymätiedot jo osoittavat vastauksen.

Tämä on sama idea kuin ripgrep-toiminnon valinnainen esindeksointi ja codesearchin trigrammi-indeksi, mutta rakennettu erityisesti levylle tallennettuja tietorakenteita varten, joita lämmitetty PHP pystyy hyödyntämään nopeimmin.

## Suunnittelijan diagnostiikka

`--trace-plan` tulostaa lämmitetyn tekstihakusuunnittelijan päätöksen kohteeseen `stderr` häiritsemättä tavallista tulosvirtaa.

Jäljitys sisältää:

- elinkaariprofiilin
- valittujen tiedostojen määrän
- ehdokkaiden lähteen (`full-scan`, `trigram-postings`, `word-postings` tai `invert-scan`)
- esiintymien hakutermien määrän
- ehdokastiedostojen ja varmennettujen tiedostojen määrät
- suoran yhteenvedon kelpoisuuden
- kyselyvälimuistin kelpoisuuden ja täyttämispäätöksen

## Milloin sitä kannattaa käyttää

Indeksoitu tila sopii, kun:

- Haet samasta tietovarastosta monta kertaa peräkkäin (CI-työt, agenttien silmukat, IDE-integraatiot).
- Tietovarasto on niin suuri, että natiivi läpikäynti muodostaa pullonkaulan.
- Kuvioissa on literaaliosajonoja tai tunnistetokeneita, joita indeksi voi käyttää esisuodatukseen.

Natiivi läpikäynti sopii edelleen, kun:

- Haet pienestä tietovarastosta, jossa rakentamiskustannus on suurin.
- Haet kertaluonteisesti polkua, jota et hae uudelleen.
- Tarvitset kaikki osumat yhdellä tilapäisellä suorituksella etkä halua ylläpitää indeksiä.

Julkaistut vertailutulokset [README](https://github.com/inline0/greph#performance) osoittavat lämmitetyn indeksoidun tilan olevan nopeampi kuin sekä `rg` että `grep` regex-kyselyissä sekä laskenta-, osumia sisältävien tiedostojen ja osumia sisältämättömien tiedostojen kyselyissä WordPress-aineistolla. Tavallisissa literaalikyselyissä se voittaa `grep`-toiminnon mutta jää hieman jälkeen `rg`-toiminnosta.

## Käyttö ohjelmallisesti

```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` hyväksyy samat `TextSearchOptions` kuin natiivi tekstihaku ja palauttaa saman `list<TextFileResult>`-rakenteen. Käytä usean indeksin tai manifestiin perustuvissa työnkuluissa komentoja `searchTextIndexedMany(...)` ja `searchTextIndexedSet(...)`.
