
# Szűrők

A szűrők határozzák meg, milyen adatok kerüljenek a pillanatfelvételbe. Kizárhat gyorsítótár-táblákat, figyelmen kívül hagyhat változó időbélyeg-oszlopokat, vagy kihagyhat átmeneti sorokat. A szűrők a pillanatfelvétel készítésekor lépnek működésbe, és minden további műveletre hatással vannak (diff, merge, apply).

```php
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Snapshot\Snapshotter;

$snapshotter = new Snapshotter($connection);

$snapshot = $snapshotter->capture('baseline', filters: [
    TableFilter::exclude(['sessions', 'cache_*']),
    ColumnFilter::ignore(['updated_at', 'modified_date']),
]);
```

## TableFilter

Meghatározza, mely táblák kerüljenek a pillanatfelvételbe. Globmintákat támogat a PHP `fnmatch()` segítségével.

### Táblák kizárása

```php
use Merql\Filter\TableFilter;

$filter = TableFilter::exclude(['sessions', 'cache_*', 'wp_*_log']);
```

A kizárási mintára illeszkedő táblák kimaradnak a pillanatfelvételből. A `*` helyettesítő karakter tetszőleges karaktersorozatot jelöl.

### Táblák felvétele

```php
$filter = TableFilter::include(['posts', 'comments', 'users']);
```

Csak a felvételi mintára illeszkedő táblák kerülnek rögzítésre. Minden más tábla kimarad.

### Működés

A szűrő `apply()` metódusa táblanevek listáját kapja, és visszaadja a szűrt részhalmazt:

```php
$allTables = ['posts', 'comments', 'sessions', 'cache_pages', 'users'];

$filter = TableFilter::exclude(['sessions', 'cache_*']);
$filtered = $filter->apply($allTables);
// ['posts', 'comments', 'users']

$filter = TableFilter::include(['posts', 'users']);
$filtered = $filter->apply($allTables);
// ['posts', 'users']
```

Ha felvételi és kizárási mintákat is megad (külön szűrőpéldányokon keresztül), először a kizárás történik meg. Egy tábla csak akkor marad meg, ha egyetlen kizárási mintára sem illeszkedik, és legalább egy felvételi mintára illeszkedik.

## ColumnFilter

Rögzítéskor meghatározott oszlopokat távolít el a sorok adataiból. Hasznos az íráskor mindig változó oszlopokhoz, amelyek téves diffeket okoznának.

```php
use Merql\Filter\ColumnFilter;

$filter = ColumnFilter::ignore(['updated_at', 'modified_date', 'last_login']);
```

### Gyakori felhasználási esetek

- `updated_at` / `modified_at` időbélyegek, amelyek minden mentéskor megváltoznak.
- `revision` számlálók, amelyek automatikusan növekednek.
- `session_token` vagy más átmeneti mezők.

### Működés

A szűrő eltávolítja a megnevezett oszlopokat minden sorból, miután kiszámította a sor kulcsát:

```php
$filter = ColumnFilter::ignore(['updated_at']);

$row = ['id' => 1, 'title' => 'Hello', 'updated_at' => '2026-01-15 10:30:00'];
$filtered = $filter->applyToRow($row);
// ['id' => 1, 'title' => 'Hello']
```

A sor kulcsát (azonosítóját) a szűretlen sorból számítja ki. Ez azt jelenti, hogy az oszlopok szűrése az azonosítóoszlopokat soha nem érinti. Csak az ujjlenyomat-képzéshez és a diffek összehasonlításához használt adatok kerülnek szűrésre.

## RowFilter

Egy predikátum-closure segítségével szűri az egyes sorokat. A closure megkapja a tábla nevét és a teljes soradatot, majd `true` értéket ad vissza a sor felvételéhez, vagy `false` értéket a kizárásához.

```php
use Merql\Filter\RowFilter;

// Exclude draft posts.
$filter = RowFilter::create(function (string $table, array $row): bool {
    if ($table === 'posts' && ($row['status'] ?? '') === 'draft') {
        return false;
    }
    return true;
});

// Exclude rows with NULL email.
$filter = RowFilter::create(function (string $table, array $row): bool {
    if ($table === 'users') {
        return $row['email'] !== null;
    }
    return true;
});
```

### Működés

A `shouldInclude()` metódus rögzítéskor minden sorra meghívódik:

```php
$include = $filter->shouldInclude('posts', ['id' => 1, 'status' => 'draft']);
// false
```

A kizárt sorokhoz nem készül ujjlenyomat, nem kerülnek a pillanatfelvételbe, és nem jelennek meg a diffekben vagy a merge-ekben.

## Szűrők kombinálása

Adjon át több szűrőt a `capture()` számára. Szűrőtípusonként legfeljebb egy használható:

```php
$snapshot = $snapshotter->capture('baseline', filters: [
    TableFilter::exclude(['cache_*']),
    ColumnFilter::ignore(['updated_at']),
    RowFilter::create(fn($table, $row) => ($row['active'] ?? '1') === '1'),
]);
```

Feldolgozási sorrend:

1. Először a **TableFilter** fut le, meghatározva, mely táblákat kell rögzíteni.
2. Az adatbázisból beolvassa az egyes táblák sorait.
3. A **RowFilter** minden sorra lefut, és kizárja a nem megfelelő sorokat.
4. A **row key** a szűretlen sorból készül (az azonosítóoszlopok érintetlenek maradnak).
5. A **ColumnFilter** eltávolítja a figyelmen kívül hagyott oszlopokat a sor adataiból.
6. Az **fingerprint** a szűrt soradatokból készül.

Ez a sorrend biztosítja, hogy az azonosítóoszlopok mindig rendelkezésre álljanak a kulcs kiszámításához, még akkor is, ha a szűrő később eltávolítja őket.

## Szűrők és konzisztencia

A merge mindhárom pillanatfelvételéhez ugyanazokat a szűrőket kell használni (base, ours, theirs). Ha eltérő szűrőket használ, úgy tűnhet, mintha sorok vagy oszlopok kerültek volna be, illetve törlődtek volna, holott csak eltérő szűrés miatt nem láthatók.

```php
// Use the same filter set for all snapshots.
$filters = [
    TableFilter::exclude(['sessions']),
    ColumnFilter::ignore(['updated_at']),
];

$base   = $snapshotter->capture('base', filters: $filters);
$ours   = $snapshotter->capture('ours', filters: $filters);
$theirs = $snapshotter->capture('theirs', filters: $filters);
```
