Files
backend/docs/14-高级搜索独立页面设计.md
T

278 lines
11 KiB
Markdown
Raw Normal View History

# 高级搜索独立页面设计(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 引擎、排序优化