Files
backend/docs/14-高级搜索独立页面设计.md
34047007@qq.com a6cd99a4ca
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
feat: initial commit - oncology literature search platform
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.
2026-07-27 07:59:18 +08:00

278 lines
11 KiB
Markdown
Raw Permalink 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
> 状态:**已规划,暂不实现**(先完成普通搜索 Phase 0 Bug 修复)
> 设计目标:在 `/app/search/advanced` 独立路由上实现 PubMed 风格的 Advanced Search
---
## 1. 问题
当前系统的"高级搜索"只是 SearchView 上的一个筛选面板(年份范围、期刊等级、标签),与 PubMed 的 Advanced Search`/pubmed/advanced/`)完全不同。
PubMed Advanced Search 是两个核心功能的组合:
1. **查询构建器(Query Builder** — 行式字段下拉 + AND/OR/NOT 组合
2. **搜索历史(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 记录。
```python
# 伪代码
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 查询构建器 → 后端适配
前端行式构建器发送的结构:
```json
{
"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 查询详情端点
```python
@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`
**布局**
- 左栏 280pxSearchHistoryPanel
- 右栏:上区 QueryBuilder + 下区搜索结果(LiteratureCard + Pagination
- 顶部 `[Basic Search] / [Advanced Search]` Tab 切换
**依赖**
- `LiteratureCard.vue`(现有,复用)
- `Pagination`(现有,复用)
### 5.4 路由更新
**文件**`frontend/src/router/index.ts`
```typescript
{ path: 'search/advanced', name: 'search-advanced',
component: () => import('../views/app/AdvancedSearchView.vue') }
```
### 5.5 侧边栏
在导航增加入口:`搜索``高级搜索`
**文件**:检查侧边栏组件(Sidebar.vue 或布局文件)
### 5.6 TypeScript 类型更新
**文件**`frontend/src/types/index.ts`
```typescript
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(必须完成)** | 搜索 BugOR 布尔、MH JOIN、recent_subq)不修复则高级搜索结果也不正确 |
| **Phase 1.1tree_number** | MeSH Terms 字段的子树展开依赖 |
| **Phase 1.3-1.5(字段标签)** | QueryBuilder 的 AD/LA/EDAT 字段依赖 |
| **Phase 1.7ATM 引擎)** | 查询详情面板的 "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. 优先级排序
1. **最优先**Phase 0 Bug 修复(12 项,代码已改待提交)
2. **次优先**:此高级搜索独立页面(9-10h
3. **后续**Phase 1-3 字段标签补齐、ATM 引擎、排序优化