Includes search engine improvements, Alembic migrations, new services (pubmed_daily_update, query_expansion), frontend updates, and documentation sync.
11 KiB
11 KiB
搜索功能差距分析:与 PubMed 对比
审计日期:2026-07-24 数据基准:本地 PG 数据库 1,662 篇文献(seed_data: 110, pubmed_api: 1,552) 分析方法:9 Agent 审计 + 真实数据库字段覆盖率查询
1. 现状概览
当前搜索系统架构:
- 查询解析:
backend/app/services/pubmed_query_parser.py— 递归下降解析器,支持 11 个字段标签,AND/OR/NOT 布尔 - 搜索引擎:
backend/app/services/search_engine.py— PostgreSQL tsvector GIN 索引 + ILIKE 回退 - 前端入口:
SearchView.vue(主搜索页)+AdvancedSearchPanel.vue(高级筛选面板) - API 端点:
POST /features/search/advanced(主要)+GET /literature/search(死代码)
2. 数据充分性评估
2.1 充分字段(可直接支撑搜索)
| 字段 | 覆盖率 | 用途 |
|---|---|---|
pmid |
1,662/1,662 (100%) | [PMID] 精确搜索 |
title |
1,652/1,662 (99.4%) | [TI] 全文搜索 |
abstract |
1,550/1,662 (93.3%) | [AB] 全文搜索 |
doi |
1,548/1,662 (93.1%) | [DOI] 精确搜索 |
authors (JSON) |
1,659/1,662 (99.8%) | [AU] [AD] 搜索 |
pub_types (JSON) |
1,557/1,662 (93.7%) | [PT] 筛选 |
journal |
1,662/1,662 (100%) | [TA] 全称搜索 |
publication_status |
1,552/1,662 (93.4%) | 状态筛选 |
pub_year |
1,662/1,662 (100%) | [DP] 年份搜索 |
pub_date |
1,148/1,662 (69.1%) | [DP] 日期搜索 |
language |
1,644/1,662 (98.9%) | [LA] 搜索 |
search_tsv |
1,662/1,662 (100%) | tsvector GIN 全文搜索 |
mesh_headings (JSON) |
845/1,662 (50.8%) | MeSH 展示(搜索由 literature_tags 完成) |
literature_tags |
997 条关联 | [MH]/[MAJR] 搜索 |
grants (JSON) |
323/1,662 (19.4%) | [GR] 搜索 |
chemical_list (JSON) |
508/1,662 (30.6%) | [NM] 搜索 |
study_design (JSON) |
1,552/1,662 (93.4%) | 临床研究设计筛选 |
2.2 严重不足字段
| 字段 | 覆盖率 | 影响 | 优先级 |
|---|---|---|---|
journal_iso |
0/1,662 (0%) | [TA] 缩写期刊搜索完全不可用 |
P0 |
tree_numbers |
0 行数据 | _expand_mesh_tag_ids INNER JOIN → 所有 [MH]/[MAJR] 查询返回 0 结果 |
P0 |
keywords (JSON) |
0/1,662 (0%) | [OT] 其他关键词搜索不可用 |
P1 |
pmc_id |
9/1,662 (0.5%) | XPath bug,[PMC] 搜索和 PMC OA 全文抓取不可用 |
P1 |
gene_symbols (JSON) |
0% | 基因符号搜索不可用 | P2 |
is_negative_result |
仅 pubmed_api 来源有 | 构造函数遗漏 seed_data | P1 |
retraction_details |
0% | 全库无撤稿记录 | P3 |
full_text_sections |
0.4% | PMC OA 全文解析进度 | P3 |
2.3 核心数据指标
总计: 1,662 篇
年份分布: 2025=58, 2026=1,604 (96.5% ∈ 2026)
标签: 99 个 (全部 manual, 28 个未关联任何文献)
文献-标签: 997 条 (946 major, 51 non-major)
每篇标签数: P25=1, P50=1, P75=2, P90=5, P99=6
被引次数: median=0, avg=0.6, max=491
撤稿: 0 篇
语言分布: eng=1,496, en=110, chi=35, jpn=7, rus=4
citation_status: MEDLINE=502, Publisher=254, In-Process=64, PubMed-not-MEDLINE=59
publication_status: ppublish=544, epublish=530, aheadofprint=478
3. 与 PubMed 搜索功能差距矩阵
| 搜索功能 | PubMed | 当前系统 | 差距 |
|---|---|---|---|
| 纯文本搜索 (title/abstract) | tsvector + 权重 | ✅ tsvector + GIN | 无 setweight |
[TI] 标题字段 |
精确 + 词干 | ✅ | — |
[AB] 摘要字段 |
精确 + 词干 | ✅ | — |
[AU] 作者字段 |
精确 + 自动截词 | ✅ (ILIKE) | exact 参数被忽略 |
[TA] 期刊字段 |
全称 + 缩写 | ❌ journal_iso 0% | 未实现 |
[MH] MeSH 字段 |
tree_number 展开 + 子树 | ❌ INNER JOIN 0 结果 | Bug |
[MAJR] 主要 MeSH |
同上 + major | ❌ 同上 | Bug |
[MH:NoExp] 不展开 |
精确 MeSH | ❌ 解析器不支持 | 未实现 |
[PT] 文献类型 |
精确匹配 | ✅ JSONB contains | — |
[DP] 出版日期 |
范围 + 格式灵活 | ✅ 年份/日期 | 单值年份 bug |
[LA] 语言 |
2 字母代码 | ❌ 解析器无映射 | 未实现 |
[AD] 机构 |
地址文本 | ❌ 解析器无映射 | 未实现 |
[EDAT] 入库日期 |
精确日期 | ❌ 字段不存在 | 未实现 |
[OT] 其他关键词 |
关键词文本 | ❌ keywords 0% | 未实现 |
[GR] 基金号 |
基金信息 | ❌ 解析器无映射 | 未实现 |
[NM] 化学物质 |
化学物质名 | ❌ 解析器无映射 | 未实现 |
[TW] 文本词 |
全字段文本 | ❌ 解析器无映射 | 未实现 |
[ALL] 全字段 |
等价于无标签 | ❌ 被静默丢弃 | Bug |
[PMC] PMCID |
精确匹配 | ❌ XPath bug 数据不足 | Bug |
通配符 * |
单/多字符 | ❌ plainto_tsquery 不支持 | 未实现 |
精确短语 "..." |
phraseto_tsquery | ❌ ILIKE 回退 | 未实现 |
| AND/OR/NOT 布尔 | 从左到右优先级 | ⚠️ AND>OR 优先级 | Bug |
:NoExp 后缀 |
MeSH 不展开 | ❌ 解析器不支持 | 未实现 |
[Title/Abstract] 长标签 |
标题+摘要 | ❌ 仅 TIAB | 未实现 |
| ATM 自动术语映射 | MeSH + Journal + Author 翻译 | ❌ 完全缺失 | P0 |
| Entry Terms 入口词 | ~200k 同义词 | ❌ 完全缺失 | P1 |
| 搜索历史 #1 AND #2 | 会话级历史链 | ❌ 完全缺失 | P2 |
| 搜索建议/自动补全 | MeSH + Author + Journal | ❌ 完全缺失 | P2 |
| 搜索结果排序 | 相关度 + 日期 + 被引 | ⚠️ best_match 权重公式错误 | Bug |
| ts_rank 权重 | title(A) > abstract(B) | ❌ 全部相同 | 未实现 |
| Faceted 筛选 | 文献类型 + 日期 + 语言 | ❌ 完全缺失 | P2 |
| Spell correction | "Did you mean" | ❌ 完全缺失 | P3 |
| 搜索高亮 | 匹配词高亮 | ❌ 完全缺失 | P3 |
4. 关键 Bug 清单(按严重性排列)
🚨 CRITICAL(7 个)
| # | Bug | 位置 | 描述 |
|---|---|---|---|
| 1 | recent_subq 破坏历史搜索 |
search_engine.py:314-319 |
sort=date 时预取最近 2000 篇 ID(无条件),早期文献搜索 0 结果 |
| 2 | OR 布尔被 AND 替代 | search_engine.py:387-414 |
所有 field_map 条件 and_() 合并,result.boolean_operator 未被消费 |
| 3 | 多 [MH] 词用 OR 而非 AND | search_engine.py:516-519 |
所有 mesh term 同一 or_() 子查询 |
| 4 | Date Sort Optimization 无条件 | search_engine.py:314 |
无 80% 匹配率门槛,固定 2000 篇限制 |
| 5 | PMC/trial_reg XPath 上下文错误 | pubmed_api.py:440,447 |
在 MedlineCitation/Article 下搜 ArticleIdList(实际在 PubmedData 下) |
| 6 | 5 字段构造函数遗漏 | pubmed_api.py:914-944 |
pubmed_revised/citation_status/date_completed/article_date/suppl_mesh_list |
| 7 | _expand_mesh_tag_ids INNER JOIN |
search_engine.py:518 |
global_tag_tree_numbers 0 行 → 全部 [MH] 查询 0 结果 |
🟡 HIGH(12 个)
| # | Bug | 位置 | 描述 |
|---|---|---|---|
| 8 | ArticleTitle.text |
pubmed_api.py:409 |
跳过嵌套 <i> 等标签 |
| 9 | [TA] 不搜 journal_iso |
search_engine.py:476 |
缩写期刊名零结果 |
| 10 | _field_condition("all") exact 不搜 abstract |
search_engine.py:482-486 |
精确短语只在 title 内搜 |
| 11 | _field_condition exact 被 author/journal 忽略 |
search_engine.py:473-478 |
exact 参数传递中断 |
| 12 | 中文 → English tsquery 零结果 | search_engine.py:123-149 |
中文不经过词干分析 |
| 13 | best_match 权重被淹没 |
search_engine.py:556-558 |
ts_rank*0.3 vs recency+5, ln(cited)*2 |
| 14 | tsvector 无 setweight | 迁移文件 | title/abstract/author/affiliation 权重相等 |
| 15 | 多 affiliation 仅取第一个 | pubmed_api.py:427 |
find 而非 findall |
| 16 | CommentsCorrections 仅撤稿 | pubmed_api.py:484-497 |
仅 RetractedBy/RetractionOf,缺 ~18 个 RefType |
| 17 | Entry Terms 无法导入 | scripts/import_mesh_tags.py |
mtrees.bin 无 entry terms,需 asc 解析器 |
| 18 | PMC_ID 格式不一致 | pubmed_api.py:264,442 |
Europe PMC 带 "PMC" 前缀,XML 路径纯数字 |
| 19 | Europe PMC 7 字段 None | pubmed_api.py:170-299 |
pubmed_revised/citation_status/date_completed/trial_reg/retraction_details/grants/article_date |
🟠 MEDIUM(9 个)
| # | Bug | 位置 | 描述 |
|---|---|---|---|
| 20 | 搜索端点无 try/except | features.py:68 |
异常 → 500 |
| 21 | DISMISS_THRESHOLD 从未检查 | literature.py:23 |
已读/忽略过滤失效 |
| 22 | SynonymExpander 零引用 | backend/app/services/synonym_expander.py |
312 行死代码 |
| 23 | import re 在函数内 |
search_engine.py:113 |
每次调用重新 import |
| 24 | 前端 field:all 硬编码 | SearchView.vue:83 |
AdvancedSearchPanel 形同虚设 |
| 25 | [ALL] 标签静默丢弃 |
pubmed_query_parser.py:40,69 |
不在 FIELD 集合 |
| 26 | [Title/Abstract] [MeSH] 不被识别 |
pubmed_query_parser.py:40,69 |
只识别短标签 |
| 27 | exact 被 MeSH/Author 丢弃 | search_engine.py:474,516 |
ILIKE 模糊匹配 |
| 28 | 查询构建代码重复 | search_engine.py:225-250,273-288 |
两段完全相同 |
5. 结论
硬性要求:搜索功能必须与 PubMed 完全一致。
这意味着:
- 所有 PubMed 字段标签必须有对应的搜索路径(数据已存储的立即接通,未存储的补充抽取)
- 所有搜索行为(MeSH 展开、ATM 映射、布尔运算、日期范围、精确短语等)必须与 PubMed 一致
- 不允许任何字段标签退化到纯文本搜索——
[IP]、[VI]、[PG]、[LA]、[GR]、[NM]、[SH]、[EDAT]、[CRDT]、[MHDA]、[LR]、[DCOM]、[LID]、[AUID]、[COIS]、[SI]等,凡数据已存储的必须接通,缺失数据的补充抽取
当前阻塞或缺失项:
| 问题 | 严重性 | 说明 |
|---|---|---|
| MeSH 搜索 — tree_numbers 空表 + INNER JOIN | 阻塞 | 所有 [MH]/[MAJR] 查询返回 0 结果 |
期刊缩写搜索 — journal_iso 覆盖率 0% |
阻塞 | [TA] 缩写搜索不可用 |
| ATM 自动术语映射 — Entry Terms 缺失 | 缺失 | 普通用户输入"lung cancer"不会触发 MeSH 展开 |
精确短语 — phraseto_tsquery 未使用 |
缺失 | "lung cancer" 精确搜索低效 |
| 字段标签未接通(15+ 个) | 缺失 | 数据已存储但解析器未路由到搜索 |
作者 ORCID 数据 — [AUID] |
缺失 | PubMed XML 中有 <Identifier> 但未抽取 |
搜索排名 — best_match 权重公式错误 |
Bug | ts_rank 几乎不影响排序 |
详细实施步骤见 12-搜索功能实施计划.md。 数据统计详情见 13-文献数据质量报告.md。