getMessage() . "\n"); } $con = Database::get('main'); $targets = [ 'driver' => [ 'phone_bidx' => ['scope' => 'driver.phone', 'columns' => ['phone']], 'email_bidx' => ['scope' => 'driver.email', 'columns' => ['email']], 'name_bidx' => ['scope' => 'driver.name', 'columns' => ['first_name', 'last_name']], ], 'passengers' => [ 'phone_bidx' => ['scope' => 'passengers.phone', 'columns' => ['phone']], 'email_bidx' => ['scope' => 'passengers.email', 'columns' => ['email']], 'name_bidx' => ['scope' => 'passengers.name', 'columns' => ['first_name', 'last_name']], ], 'adminUser' => [ 'phone_bidx' => ['scope' => 'adminUser.phone', 'columns' => ['phone']], 'email_bidx' => ['scope' => 'adminUser.email', 'columns' => ['email']], ], ]; echo $dryRun ? "── DRY RUN — nothing will be written ──\n" : "── Backfilling blind indexes ──\n"; foreach ($targets as $table => $fields) { if ($only && $only !== $table) continue; echo "\n[$table]\n"; $sourceColumns = []; foreach ($fields as $spec) { foreach ($spec['columns'] as $c) $sourceColumns[$c] = true; } $select = 'id, ' . implode(', ', array_keys($sourceColumns)); $where = $force ? '' : ' WHERE ' . implode(' OR ', array_map( fn($f) => "`$f` IS NULL", array_keys($fields) )); try { $rows = $con->query("SELECT $select FROM `$table`$where")->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { echo " ✘ skipped: " . $e->getMessage() . "\n"; continue; } $total = count($rows); echo " rows to process: $total\n"; if ($total === 0) continue; $updated = 0; $failed = 0; foreach (array_chunk($rows, $batch) as $chunk) { if (!$dryRun) $con->beginTransaction(); foreach ($chunk as $row) { $set = []; $params = [':id' => $row['id']]; foreach ($fields as $column => $spec) { // فك التشفير لقراءة القيمة الأصلية — القيمة المخزَّنة لا تتغير. $parts = []; foreach ($spec['columns'] as $src) { $plain = $encryptionHelper->decryptData($row[$src] ?? null); if ($plain === false) { $failed++; $parts = []; break; } if ($plain !== '') $parts[] = $plain; } if (!$parts) continue; $value = implode(' ', $parts); $index = $blind->index($spec['scope'], $value); if ($index === null) continue; $set[] = "`$column` = :$column"; $params[":$column"] = $index; } if (!$set) continue; if ($dryRun) { $updated++; continue; } $stmt = $con->prepare("UPDATE `$table` SET " . implode(', ', $set) . " WHERE id = :id"); $stmt->execute($params); $updated++; } if (!$dryRun) $con->commit(); usleep(50_000); // نفس متعمّد حتى لا تُحتكر القاعدة أثناء الخدمة echo " … $updated/$total\n"; } echo " ✔ indexed: $updated" . ($failed ? " (undecryptable values skipped: $failed)" : '') . "\n"; } echo "\nDone." . ($dryRun ? " (dry run — re-run without --dry-run to apply)" : '') . "\n";