Files
backend/docs/11-搜索功能差距分析.md
T
34047007@qq.com 62ca8fa6b8
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
chore: batch commit remaining changes
Includes search engine improvements, Alembic migrations,
new services (pubmed_daily_update, query_expansion),
frontend updates, and documentation sync.
2026-07-27 08:35:12 +08:00

184 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 搜索功能差距分析:与 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 清单(按严重性排列)
### 🚨 CRITICAL7 个)
| # | 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 结果 |
### 🟡 HIGH12 个)
| # | 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` |
### 🟠 MEDIUM9 个)
| # | 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 完全一致。**
这意味着:
1. **所有 PubMed 字段标签**必须有对应的搜索路径(数据已存储的立即接通,未存储的补充抽取)
2. **所有搜索行为**(MeSH 展开、ATM 映射、布尔运算、日期范围、精确短语等)必须与 PubMed 一致
3. **不允许任何字段标签退化到纯文本搜索**——`[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](12-搜索功能实施计划.md)。
数据统计详情见 [13-文献数据质量报告.md](13-文献数据质量报告.md)。