OncoLit: a multi-tenant oncology literature search, feed, and collaboration platform. Built with FastAPI + Vue 3 + PostgreSQL. Includes PubMed pipeline, drug approvals, AI summaries, and systematic review tools.
11 KiB
高级搜索独立页面设计(PubMed 风格)
文档日期:2026-07-24 状态:已规划,暂不实现(先完成普通搜索 Phase 0 Bug 修复) 设计目标:在
/app/search/advanced独立路由上实现 PubMed 风格的 Advanced Search
1. 问题
当前系统的"高级搜索"只是 SearchView 上的一个筛选面板(年份范围、期刊等级、标签),与 PubMed 的 Advanced Search(/pubmed/advanced/)完全不同。
PubMed Advanced Search 是两个核心功能的组合:
- 查询构建器(Query Builder) — 行式字段下拉 + AND/OR/NOT 组合
- 搜索历史(Search History) — #1/#2/#3 编号,可点击组合为
#1 AND #2
2. 目标界面
┌─────────────────────────────────────────────────────────┐
│ [Basic Search] [Advanced Search] ← Tab 切换 │
├─────────────────────────────────────────────────────────┤
│ 查询构建器(Query Builder) │
│ ┌───────┬────────────────────────────────────┬──────┐ │
│ │ 字段 │ 查询词 │ × │ │
│ ├───────┼────────────────────────────────────┼──────┤ │
│ │ All │ lung cancer │ × │ │
│ │ AND ▼ │ │ │ │
│ │ MeSH │ lung neoplasms │ × │ │
│ └───────┴────────────────────────────────────┴──────┘ │
│ [+ Add row] [Search] [Details ▼] │
│ ── PubMed 查询字符串 ──────────────────────────────── │
│ (lung cancer[All Fields]) AND (lung neoplasms[MeSH Terms])│
├─────────────────────────────────────────────────────────┤
│ 搜索历史(Search History) │
│ ┌──────┬─────────────────────────┬───────┬──────────┐ │
│ │ # │ Query │ Hits │ Time │ │
│ ├──────┼─────────────────────────┼───────┼──────────┤ │
│ │ #3 │ #1 AND #2 │ 42 │ 10:32 AM │ │
│ │ #2 │ lung neoplasms[MH] │ 128 │ 10:31 AM │ │
│ │ #1 │ lung cancer[TI] │ 215 │ 10:30 AM │ │
│ └──────┴─────────────────────────┴───────┴──────────┘ │
│ [Combine selected] [Clear history] │
├─────────────────────────────────────────────────────────┤
│ 搜索结果区域 │
│ (复用现有 LiteratureCard + Pagination) │
└─────────────────────────────────────────────────────────┘
3. 页面布局
┌──────────────┬──────────────────────────────────┐
│ 左栏 │ 右栏 │
│ 280px │ flex: 1 │
│ │ │
│ Search │ Query Builder (上区) │
│ History │ + 查询字符串显示 │
│ Panel │ │
│ │ ───────── 分割线 ──────── │
│ │ │
│ │ Search Results (下区) │
│ │ 复用 LiteratureCard │
│ │ + Pagination │
└──────────────┴──────────────────────────────────┘
4. 后端改动
4.1 新建模型 SearchHistory
| 字段 | 类型 | 说明 |
|---|---|---|
id |
UUID PK | |
user_id |
FK → users | |
query_text |
TEXT | 用户输入的原始查询字符串 |
query_json |
JSON | 结构化查询(各字段解析结果) |
result_count |
INTEGER | 结果数 |
created_at |
TIMESTAMPTZ | 搜索时间 |
文件:backend/app/models/search_history.py(新建)
导出:backend/app/models/__init__.py 添加 SearchHistory
迁移:alembic revision --autogenerate -m "add_search_history"
4.2 API 端点
挂载在 features router 下(backend/app/api/v1/features.py):
| 方法 | 路径 | 说明 |
|---|---|---|
POST |
/features/search/advanced |
已有。搜索结果后自动存入 history |
GET |
/features/search/history |
获取用户最近 20 条搜索历史 |
DELETE |
/features/search/history/{id} |
删除单条历史 |
GET |
/features/search/details |
查询翻译详情(ATM 展开后的结构) |
4.3 搜索自动入 History
修改 advanced_search():搜索成功后自动 INSERT 一条 SearchHistory 记录。
# 伪代码
result = await AdvancedSearchEngine.search(db, ...)
history = SearchHistory(
user_id=user.id,
query_text=req.query,
query_json=parsed_query, # 解析后的结构化字段
result_count=result["total"],
)
db.add(history)
await db.commit()
4.4 查询构建器 → 后端适配
前端行式构建器发送的结构:
{
"query": "(lung[TI]) AND (lung neoplasms[MH])",
"advanced_builder": {
"rows": [
{"field": "TI", "query": "lung", "operator": "AND"},
{"field": "MH", "query": "lung neoplasms", "operator": null}
]
}
}
AdvancedSearchRequest 新增 advanced_builder 可选字段。
4.5 查询详情端点
@router.get("/search/details")
async def search_details(query: str = Query(...)):
"""返回 ATM 展开后的查询结构(类似 PubMed 'Search details' 面板)"""
parsed = parse_pubmed_query(query)
return {
"parsed": parsed.model_dump(),
"translated": expanded_query, # ATM 引擎(Phase 1.7 后)
}
5. 前端改动
5.1 QueryBuilder.vue
位置:frontend/src/components/search/QueryBuilder.vue
功能:行式查询构建器
- 每行:
[NSelect 字段] + [NInput 查询词] + [NSelect AND/OR/NOT] + [删除按钮] - 第一行无运算符选择
- 底部
[+ Add row]按钮 - 字段下拉选项(12 个):
| 标签 | 值 |
|---|---|
| All Fields | ALL |
| Title | TI |
| Title/Abstract | TIAB |
| Author | AU |
| MeSH Terms | MH |
| MeSH Major Topic | MAJR |
| Journal | TA |
| Affiliation | AD |
| Language | LA |
| Publication Type | PT |
| Grant Number | GR |
| Chemical | NM |
- 自动生成 PubMed 语法字符串:
(lung[TI]) AND (lung neoplasms[MH]) [Search]→ 调用POST /features/search/advanced[Add to history]→ 后端自动存,无需手动
Props: 无
Emits: @search(query: string, field: string, ...params)
5.2 SearchHistoryPanel.vue
位置:frontend/src/components/search/SearchHistoryPanel.vue
功能:搜索历史表格
- 加载时调用
GET /features/search/history - 表格列:#、Query、Hits、Time
- 点击行 → 将
#1语法插入 Query Box - 行操作:删除
[Combine]→ 选中多条 → 生成#1 AND #2[Clear history]→ 调用批量删除
Props: 无
Emits: @useQuery(query: string)、@search(query: string)
5.3 AdvancedSearchView.vue
位置:frontend/src/views/app/AdvancedSearchView.vue
布局:
- 左栏 280px:SearchHistoryPanel
- 右栏:上区 QueryBuilder + 下区搜索结果(LiteratureCard + Pagination)
- 顶部
[Basic Search] / [Advanced Search]Tab 切换
依赖:
LiteratureCard.vue(现有,复用)Pagination(现有,复用)
5.4 路由更新
文件:frontend/src/router/index.ts
{ path: 'search/advanced', name: 'search-advanced',
component: () => import('../views/app/AdvancedSearchView.vue') }
5.5 侧边栏
在导航增加入口:搜索 → 高级搜索
文件:检查侧边栏组件(Sidebar.vue 或布局文件)
5.6 TypeScript 类型更新
文件:frontend/src/types/index.ts
export interface AdvancedBuilderRow {
field: string
query: string
operator: 'AND' | 'OR' | 'NOT' | null
}
export interface SearchHistoryItem {
id: string
query_text: string
result_count: number
created_at: string
}
// SearchRequestBody 补充
export interface SearchRequestBody {
// ...已有字段...
advanced_builder?: { rows: AdvancedBuilderRow[] }
}
6. 依赖关系
| 依赖 | 说明 |
|---|---|
| Phase 0(必须完成) | 搜索 Bug(OR 布尔、MH JOIN、recent_subq)不修复则高级搜索结果也不正确 |
| Phase 1.1(tree_number) | MeSH Terms 字段的子树展开依赖 |
| Phase 1.3-1.5(字段标签) | QueryBuilder 的 AD/LA/EDAT 字段依赖 |
| Phase 1.7(ATM 引擎) | 查询详情面板的 "Translated query" 依赖 |
| 无依赖 | QueryBuilder UI + SearchHistory 后端可立即实现(框架先搭好) |
7. 工作量估算
| 任务 | 文件 | 预估 |
|---|---|---|
| SearchHistory 模型 + 迁移 | models/search_history.py + 迁移脚本 + __init__.py |
30min |
| 后端 CRUD 端点 | features.py(GET/DELETE history) |
1h |
| 搜索自动入 history | features.py advanced_search() |
15min |
| 查询详情端点 | features.py |
30min |
| QueryBuilder.vue | components/search/QueryBuilder.vue |
2-3h |
| SearchHistoryPanel.vue | components/search/SearchHistoryPanel.vue |
2h |
| AdvancedSearchView.vue | views/app/AdvancedSearchView.vue |
2h |
| 路由 + 侧边栏 + 类型 | router, Sidebar, types | 30min |
| 合计 | ~9-10h |
8. 优先级排序
- 最优先:Phase 0 Bug 修复(12 项,代码已改待提交)
- 次优先:此高级搜索独立页面(9-10h)
- 后续:Phase 1-3 字段标签补齐、ATM 引擎、排序优化