fix: 生产高级搜索 500 — 预算超时降级 + pub_date 排序索引 + affiliations_text trgm 反范式列
CI / backend (push) Waiting to run
CI / frontend (push) Waiting to run

- count/主查询独立短预算,超时降级不再 500:count 超时返回 _COUNT_CAP(10 万+)
  而非 0(修复"找到 0 条结果"、分页被隐藏),主查询超时降级空结果
- 年份筛选改用 pub_date 范围,主查询 ORDER BY pub_date DESC NULLS LAST, id DESC
  走新索引 ix_gl_pub_date_sort(Index Scan,替代 Parallel Seq Scan + Sort,89.5 万行 40s→<0.2s)
- affiliations_text 反规范化列 + gin_trgm 索引 + 触发器维护,替换 7 处 jsonb lateral
  EXISTS(病理查询慢 ~40x);存量由 scripts/backfill_affiliations.py 分批后台回填
- ATM 门控 _has_structured_terms:结构化查询(含字段标签)的 plain 词不做 MeSH 展开,
  修复 "lung neoplasms[MAJR]+年份" count 5s + main 6s 双超时(11.72s→2.02s)
- query_expansion/mesh IN(subquery) 去显式 DISTINCT(百万级 id 集合物化排序)
- alembic env 改 connect()(transaction_per_migration 每迁移独立事务,支持迁移内分批提交)
This commit is contained in:
34047007@qq.com
2026-08-10 21:48:42 +08:00
parent ab64d2aa0a
commit a4d27da8ea
9 changed files with 516 additions and 89 deletions
+12 -1
View File
@@ -179,6 +179,8 @@ CREATE TABLE global_literature (
is_negative_result BOOLEAN NOT NULL DEFAULT FALSE, -- 是否为阴性结果(AI 判定,非 PubMed 原始字段)
rct_detection JSONB, -- RCT 自动检测结果 {is_rct, confidence, source}AI,非 PubMed 原始字段)
search_tsv TSVECTOR, -- 全文检索向量(内部 PG tsvector,非 PubMed 字段)
author_names_text TEXT, -- 作者姓名字符串(从 authors.family 拼接,由触发器维护,供 trgm 索引 ILIKE
affiliations_text TEXT, -- 机构字符串(从 authors.affiliation 拼接,由触发器维护,供 trgm 索引 ILIKE
negative_result_details JSONB, -- 阴性结果详情(AI 抽取,非 PubMed 原始字段)
ai_summary JSONB, -- AI 摘要(内部,非 PubMed 字段)
chemical_list JSONB NOT NULL DEFAULT '[]', -- 化学物质列表 【PubMed: ChemicalList/Chemical】
@@ -235,12 +237,21 @@ CREATE INDEX ix_gl_is_preprint_true ON global_literature(is_preprint) WHERE is_p
-- ═══ GIN 反范式标签数组索引(标签筛选免 JOIN global_literature_tags ═══
CREATE INDEX ix_gl_tag_ids_gin ON global_literature USING gin(tag_ids);
CREATE INDEX ix_global_literature_journal_iso_trgm ON global_literature USING gin(journal_iso gin_trgm_ops); -- PubMed [TA] ILIKE 兜底
-- ═══ 反范式文本列 trgm 索引(ILIKE 走索引,避免 jsonb lateral EXISTS ═══
CREATE INDEX ix_gl_author_names_trgm ON global_literature USING gin(author_names_text gin_trgm_ops); -- 作者 [AU] ILIKE
CREATE INDEX ix_gl_affiliations_trgm ON global_literature USING gin(affiliations_text gin_trgm_ops); -- 机构 [AD] ILIKE
-- ═══ 排序索引(pub_date DESC NULLS LAST, id DESC,高级搜索按时间排序) ═══
CREATE INDEX ix_gl_pub_date_sort ON global_literature(pub_date DESC NULLS LAST, id DESC);
```
> 预估值:约 600 万条(PubMed 中肿瘤相关的历史累积),单表不需要分区
**全文搜索:**
> `search_tsv``TSVECTOR` 类型,GIN 索引 `ix_gl_search_tsv`)由 PG 触发器 `trg_global_literature_tsv` 自动维护。使用 `setweight()` 区分字段权重:**title=A 权重、abstract=B 权重、authors.family=A 权重**。所有搜索(普通搜索 `field="all"` 和高级搜索字段选择中的作者/机构)均走 tsvector `@@` `plainto_tsquery()`ILIKE 仅作为兜底(NULL tsvector 记录)。触发器在 INSERT 或 UPDATE title/abstract/authors 时触发重建。千万级无压力。
> `search_tsv``TSVECTOR` 类型,GIN 索引 `ix_gl_search_tsv`)由 PG 触发器 `trg_global_literature_tsv` 自动维护。使用 `setweight()` 区分字段权重:**title=A 权重、abstract=B 权重、author_names_text=A 权重**。所有搜索(普通搜索 `field="all"` 和高级搜索字段选择中的作者/机构)均走 tsvector `@@` `plainto_tsquery()`ILIKE 仅作为兜底(NULL tsvector 记录)。触发器在 INSERT 或 UPDATE title/abstract/authors/chemical_list/gene_symbols/mesh_headings/keywords 时触发重建。千万级无压力。
>
> **反规范化文本列:** `author_names_text`authors.family 拼接)和 `affiliations_text`authors.affiliation 拼接)由同一触发器维护,配套 `gin_trgm_ops` 索引让作者 [AU]/机构 [AD] 的 ILIKE 搜索走索引——替代逐行 `jsonb_array_elements(authors)` 的 lateral EXISTS(病理查询下慢 ~40x)。`affiliations_text` 迁移 `3355d1f9dbba` 只建列/索引/触发器,存量回填由 `scripts/backfill_affiliations.py` 分批后台执行(每批 2 万行、批间提交,幂等 `WHERE affiliations_text IS NULL`);新插入/更新的行由触发器直接维护。
>
> > **GIN 索引重建说明:** 迁移 `f80dca5baa02`add_pico_column_to_review_literatures)的 `upgrade()` 误将 `ix_gl_search_tsv` 删除后未重建,导致所有 tsvector 搜索走全表扫描。迁移 `0314f4d28728`rebuild_search_tsv_gin_index)已修复,紧跟在 `e341edea85e2` 之后。