IndexTerm.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Models\DhammaTerm;
  4. use App\Services\OpenSearchService;
  5. use App\Services\TermIndexService;
  6. use Illuminate\Console\Command;
  7. use Illuminate\Support\Facades\Cache;
  8. use Illuminate\Support\Facades\Log;
  9. class IndexTerm extends Command
  10. {
  11. protected $signature = 'opensearch:index-term
  12. {--test}
  13. {--word= : 指定单个词条进行索引,省略则索引全部}
  14. {--fresh : 清除缓存断点,从头开始}';
  15. protected $description = 'Index Term data into OpenSearch(可重入:中断后重跑自动跳过已索引的词条)';
  16. // 缓存键:记录最后成功索引的游标位置,48h 过期
  17. private const CACHE_KEY = 'index-term:cursor';
  18. private bool $isTest = false;
  19. public function __construct(
  20. protected OpenSearchService $openSearchService,
  21. protected TermIndexService $termIndexService,
  22. ) {
  23. parent::__construct();
  24. }
  25. public function handle(): int
  26. {
  27. $word = $this->option('word');
  28. if ($this->option('test')) {
  29. $this->isTest = true;
  30. $this->info('test mode');
  31. }
  32. if ($this->option('fresh')) {
  33. Cache::forget(self::CACHE_KEY);
  34. $this->info('Cleared cached cursor.');
  35. }
  36. try {
  37. [$connected, $message] = $this->openSearchService->testConnection();
  38. if (! $connected) {
  39. $this->error($message);
  40. Log::error($message);
  41. return 1;
  42. }
  43. // 按自增 id 排序,保证游标稳定(updated_at 可能在运行中被修改)
  44. $terms = DhammaTerm::select(['id', 'guid', 'word'])->orderBy('id');
  45. if ($word) {
  46. $terms = $terms->where('word', $word);
  47. }
  48. // 从缓存恢复断点:跳过上次已处理的记录
  49. $lastId = Cache::get(self::CACHE_KEY);
  50. if ($lastId && ! $word) {
  51. $terms = $terms->where('id', '>', $lastId);
  52. $this->info("Resuming after id={$lastId}");
  53. }
  54. $total = $terms->count();
  55. $this->info("terms to index: {$total}");
  56. $curr = 0;
  57. foreach ($terms->cursor() as $term) {
  58. $curr++;
  59. if ($curr % 10 === 0) {
  60. $percent = (int) ($curr * 100 / $total);
  61. $this->info("[{$percent}%]-{$curr}/{$total} {$term->word}");
  62. // 每 10 条保存一次断点
  63. Cache::put(self::CACHE_KEY, $term->id, now()->addHours(48));
  64. }
  65. if ($this->isTest) {
  66. $document = $this->termIndexService->buildDocument($term->guid);
  67. $this->info($document['title']['text']['pali']);
  68. } else {
  69. $this->termIndexService->index($term->guid);
  70. }
  71. }
  72. // 全部完成,清除断点缓存
  73. Cache::forget(self::CACHE_KEY);
  74. $this->info("index-term finished. total: {$curr}");
  75. return 0;
  76. } catch (\Exception $e) {
  77. $this->error('Failed to index Term data: '.$e->getMessage());
  78. Log::error('Failed to index Term data', ['error' => $e]);
  79. return 1;
  80. }
  81. }
  82. }