build_markers.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. <?php
  2. /**
  3. * 把 wbw_templates 里的 .ctl. 锚点解析成工作表 rp_markers。
  4. *
  5. * 导出器和检测器都要按标记做集合运算,逐次在 PHP 里重解析 33 万行既慢又容易两边
  6. * 规则跑偏,所以物化一次、共用一份。改了 MarkerParser 就要重跑这个脚本。
  7. *
  8. * 用法:php scripts/related/build_markers.php [--table=rp_markers]
  9. */
  10. require __DIR__.'/lib/boot.php';
  11. require __DIR__.'/lib/MarkerParser.php';
  12. use Illuminate\Support\Facades\DB;
  13. [$options] = rp_args($argv);
  14. $table = $options['table'] ?? 'rp_markers';
  15. if (! preg_match('/^rp_[a-z0-9_]+$/', $table)) {
  16. throw new InvalidArgumentException("工作表名必须以 rp_ 开头:$table");
  17. }
  18. $log = new RpLogger('build_markers', rp_report_dir());
  19. $log->info("目标工作表:$table");
  20. DB::statement("drop table if exists $table");
  21. DB::statement("
  22. create table $table (
  23. book integer not null,
  24. paragraph integer not null,
  25. wid integer not null,
  26. word varchar(1024) not null,
  27. kind varchar(16) not null,
  28. book_name varchar(64),
  29. cs_from integer,
  30. cs_to integer,
  31. cs_count integer not null default 0,
  32. numbers varchar(64),
  33. is_abbrev boolean not null default false,
  34. repaired text,
  35. error text
  36. )
  37. ");
  38. $total = DB::table('wbw_templates')->where('type', '.ctl.')->where('style', '#a#')->count();
  39. $log->info("待解析锚点:$total 行");
  40. $buffer = [];
  41. $written = 0;
  42. $flush = function () use (&$buffer, &$written, $table, $log) {
  43. if ($buffer === []) {
  44. return;
  45. }
  46. DB::table($table)->insert($buffer);
  47. $written += count($buffer);
  48. $buffer = [];
  49. if ($written % 50000 === 0) {
  50. $log->info(" 已写入 $written 行");
  51. }
  52. };
  53. DB::table('wbw_templates')
  54. ->where('type', '.ctl.')
  55. ->where('style', '#a#')
  56. ->select('book', 'paragraph', 'wid', 'word')
  57. ->orderBy('book')->orderBy('paragraph')->orderBy('wid')
  58. ->chunk(20000, function ($chunk) use (&$buffer, $flush, $log) {
  59. foreach ($chunk as $row) {
  60. $parsed = MarkerParser::parse($row->word);
  61. $log->count('kind.'.$parsed['kind']);
  62. // 缩写区间:右端位数少于左端,正是旧生成器翻车的那一类,单独打标以便统计。
  63. $isAbbrev = false;
  64. if ($parsed['kind'] === MarkerParser::KIND_INTERVAL
  65. && preg_match('/^para([0-9]+)-([0-9]+)/', $row->word, $m)
  66. && strlen($m[2]) < strlen($m[1])) {
  67. $isAbbrev = true;
  68. $log->count('interval.abbrev');
  69. }
  70. if ($parsed['error']) {
  71. $log->unresolved('marker_parse_failed', [
  72. 'book' => $row->book, 'paragraph' => $row->paragraph,
  73. 'word' => $row->word, 'detail' => $parsed['error'],
  74. ]);
  75. }
  76. if ($parsed['repaired']) {
  77. $log->warn("修补标记 book={$row->book} para={$row->paragraph}: {$parsed['repaired']}");
  78. $log->unresolved('marker_repaired', [
  79. 'book' => $row->book, 'paragraph' => $row->paragraph,
  80. 'word' => $row->word, 'detail' => $parsed['repaired'],
  81. ]);
  82. }
  83. $buffer[] = [
  84. 'book' => $row->book,
  85. 'paragraph' => $row->paragraph,
  86. 'wid' => $row->wid,
  87. 'word' => $row->word,
  88. 'kind' => $parsed['kind'],
  89. 'book_name' => $parsed['book_name'],
  90. 'cs_from' => $parsed['cs'] === [] ? null : $parsed['cs'][0],
  91. 'cs_to' => $parsed['cs'] === [] ? null : end($parsed['cs']),
  92. 'cs_count' => count($parsed['cs']),
  93. 'numbers' => $parsed['numbers'] ? implode('.', $parsed['numbers']) : null,
  94. 'is_abbrev' => $isAbbrev,
  95. 'repaired' => $parsed['repaired'],
  96. 'error' => $parsed['error'],
  97. ];
  98. if (count($buffer) >= 5000) {
  99. $flush();
  100. }
  101. }
  102. });
  103. $flush();
  104. $log->info("写入完成:$written 行");
  105. if ($written !== $total) {
  106. $log->error("行数不符:源 $total vs 写入 $written");
  107. }
  108. $log->info('建索引中…');
  109. DB::statement("create index on $table (book, paragraph)");
  110. DB::statement("create index on $table (kind)");
  111. DB::statement("create index on $table (book_name)");
  112. DB::statement("create index on $table (is_abbrev) where is_abbrev");
  113. $log->finish();