fix: 第28轮搜索审计修复 — 否定范围pub_year/异常降级/YYYY-MM组内/NULL安全等8项
This commit is contained in:
@@ -2095,3 +2095,63 @@ if _PARTIAL_DATE_RE.match(end_val):
|
||||
- 解析器 36 测试全部通过
|
||||
- 5 个新增 R27 正确性检查通过
|
||||
- 全量套件中仅外部服务连接失败(httpx.ConnectError),与改动无关
|
||||
|
||||
## R28 (2026-07-29): 第三轮并行审计修复
|
||||
|
||||
### R28-1 (MEDIUM): 组作用域标记污染 `negated_date_ranges`
|
||||
|
||||
**文件**:[pubmed_query_parser.py:483-486](backend/app/services/pubmed_query_parser.py#L483)
|
||||
|
||||
**根因**:`__RANGE_*` 标记的 `negated_date_ranges` 聚合未过滤组内标记(`group_id >= 0`),导致组内 NOT 日期范围被错误地应用到顶层日期字段的 `not_(cond)`,产生 `NOT (pub_year >= X AND pub_year <= Y)` 而非正确的组内处理。
|
||||
|
||||
**修复**:过滤 `t.group_id < 0`(仅顶层标记),组内标记由 `_build_marker_condition` + group processing 路径独立处理。
|
||||
|
||||
### R28-2 (LOW): 标记 field 被组 field 覆盖
|
||||
|
||||
**文件**:[pubmed_query_parser.py:883](backend/app/services/pubmed_query_parser.py#L883)
|
||||
|
||||
**根因**:组 field 标签赋值循环未跳过 `__RANGE_*` 标记,导致标记的 `field` 属性从 `__RANGE_DP__` 被覆盖为 `dp`,破坏后续标记识别。
|
||||
|
||||
**修复**:跳过 `getattr(t, '_is_range_end', False)` 的标记项。
|
||||
|
||||
### R28-3 (CRITICAL): DP 否定范围使用 `pub_date` 而非 `pub_year`
|
||||
|
||||
**文件**:[search_engine.py:1407-1436](backend/app/services/search_engine.py#L1407)
|
||||
|
||||
**根因**:`_neg_single_dates` DP 分支对所有否定范围使用 `pub_date` 比较,但当范围是完整年份(如 `2024:2024`)且用户在 `pub_year` 列上有不同数据质量时,`pub_date` 可能产生不准确的结果。
|
||||
|
||||
**修复**:检测 `_neg_from` 以 `-01-01` 结尾且 `_neg_to` 以 `-12-31` 结尾时,切换到 `pub_year` 比较,同时保持 NULL 安全(`or_(not_(cond), pub_year.is_(None))`)。
|
||||
|
||||
### R28-4 (CRITICAL): `_pubmed_conditions` 异常导致未清理查询
|
||||
|
||||
**文件**:[search_engine.py:254,300,306,331](backend/app/services/search_engine.py#L254)
|
||||
|
||||
**根因**:`_pubmed_conditions()` 内未捕获的异常传播到 `search()` 外导致 500 错误,且解析失败后的文本降级检查 `not any([...])` 未包含此路径。
|
||||
|
||||
**修复**:添加 `_pubmed_failed` 标志,`except` 块中设为 `True` 并强制文本降级和查询清理。
|
||||
|
||||
### R28-5 (HIGH): YYYY-MM 在组内路径中未处理
|
||||
|
||||
**文件**:[search_engine.py:1645-1665](backend/app/services/search_engine.py#L1645)
|
||||
|
||||
**根因**:`_single_term_condition()` 处理日期字段时,仅在 `len(_term_text) == 4`(年份)和完整 ISO 日期(YYYY-MM-DD)之间处理。`YYYY-MM` 格式(如 `2024-06`)既不匹配 4 位数字也不匹配完整日期,静默降级。R27 修复仅在顶层路径生效,组内 `_single_term_condition` 路径遗漏。
|
||||
|
||||
**修复**:插入 `_PARTIAL_DATE_RE` + `_expand_partial_date` 处理,将 `YYYY-MM` 展开为 `YYYY-MM-01`~`YYYY-MM-30/31` 的日期范围条件。
|
||||
|
||||
### R28-6 (MEDIUM): NULL 安全 NOT 缺失
|
||||
|
||||
**文件**:[search_engine.py:1267-1273,1343-1365,1444-1446,1502-1505](backend/app/services/search_engine.py)
|
||||
|
||||
**根因**:多处 `not_(date_condition)` 未包装 NULL 安全,导致 `pub_date`/`pub_year` 等日期列为 NULL 的行被错误排除:
|
||||
- 顶层 DP `not_(cond)` 路径(line 1446)
|
||||
- 非 DP 字段 `negated_date_ranges` 路径(line 1505)
|
||||
- 组标记 NOT 路径(非否定组中的 `is_not` 标记,line 1271)
|
||||
- 否定组包装 `not_(combined)`(line 1347,通过 `_neg_group_date_fields` 跟踪)
|
||||
|
||||
**修复**:对所有路径使用 `or_(not_(cond), col.is_(None))`。组标记路径按 `_tag` 查找对应列(DP 同时加上 `pub_year.is_(None)` + `pub_date.is_(None)`)。
|
||||
|
||||
## 验证
|
||||
|
||||
- ✅ **1007 tests passed, 0 failed**
|
||||
- 全部搜索测试通过(parser 36 + search engine 集成测试)
|
||||
- 无回归
|
||||
|
||||
Reference in New Issue
Block a user