跳转到正文

筛选器

筛选器用于控制快照中包含哪些数据。你可以排除缓存表、忽略易变的时间戳列,或跳过临时行。筛选器会在捕获快照时应用,并影响所有后续操作(差异比较、合并和应用)。

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

控制快照中包含哪些表。支持通过 PHP 的 fnmatch() 使用 glob 模式。

排除表

PHP
use Merql\Filter\TableFilter;

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

任何与排除模式匹配的表都会从快照中省略。* 通配符可匹配任意字符序列。

包含表

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

只捕获与包含模式匹配的表。其余表都会被排除。

工作原理

筛选器的 apply() 方法接收表名列表,并返回筛选后的子集:

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

如果同时存在包含和排除模式(通过单独的筛选器实例提供),则先评估排除模式。表必须不匹配任何排除模式,并且至少匹配一个包含模式,才会被保留。

ColumnFilter

在捕获期间从行数据中移除指定列。适用于每次写入都会变化、可能导致错误差异的易变列。

PHP
use Merql\Filter\ColumnFilter;

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

常见用例

  • updated_at / modified_at 时间戳,每次保存都会变化。
  • revision 自动递增计数器。
  • session_token 或其他临时字段。

工作原理

行键计算完成后,筛选器会从每一行中移除指定列:

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

行键(标识)根据未筛选的行计算。这意味着列筛选器不会影响标识列。只有用于指纹计算和差异比较的数据会经过筛选。

RowFilter

使用谓词闭包筛选单行。闭包接收表名和完整行数据,并返回 true 以包含该行,或返回 false 以排除该行。

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;
});

工作原理

捕获期间会为每一行调用 shouldInclude() 方法:

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

被排除的行不会计算指纹,不会存储在快照中,也不会出现在差异或合并结果中。

组合筛选器

向 capture() 传入多个筛选器。每种筛选器类型最多使用一个:

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

处理顺序:

  1. 首先运行 TableFilter,确定要捕获哪些表。
  2. 对每张表,从数据库读取行。
  3. 对每一行运行 RowFilter,排除不匹配的行。
  4. 根据未筛选的行计算 行键(身份列保持完整)。
  5. ColumnFilter 从行数据中移除忽略的列。
  6. 根据筛选后的行数据计算 指纹。

此顺序确保始终可以使用标识列计算键,即使这些列之后会被列筛选器移除。

筛选器与一致性

合并时,三个快照(base、ours、theirs)应使用相同的筛选器。如果使用不同的筛选器,某些行或列可能会看起来像是新增或删除,实际上只是筛选方式不同。

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);