fix: 第二轮PubMed搜索合规审计 — 8项修复 + 6项新测试
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s

F1: search_engine.py `_pubmed_conditions` 顶层 [MH:noexp] 丢失 _noexp 标志
    按 _noexp 分组 mesh_terms,分别调用 _expand_mesh_tag_ids(noexp=True/False)

F2: search_engine.py 组内 NOT 违反 De Morgan 律
    全组 is_not=True 时单 NOT 包裹组合条件而非 per-term not_()

F3: pubmed_query_parser.py `_parse_not_expr` 不支持重复 NOT
    改为递归调用,NOT NOT toggle is_not(三重 NOT 正确)

F4: SearchView.vue Custom Range datePreset 不发送日期参数
    增加 datePreset === 'custom' 分支发送 year_from/year_to

F5: HomeView.vue restoreFromUrl 恢复不全
    补全 sort/field/retracted/negative_result

F6: AdvancedSearchPanel.vue precision_mode 死控件移除(UI+emit+reset)
    types/index.ts precision_mode 死类型字段移除

F7: types/index.ts is_oa 标记为 unused, reserved

F8: search_engine.py `_expand_mesh_tag_ids` N+1 → 2 批量查询
    所有 entry_terms 和 name_en 分别合并为 OR 查询

测试: 新增 NOT NOT/triple NOT/NOT group/DM:noexp 顶层 共 7 项
This commit is contained in:
34047007@qq.com
2026-07-27 10:30:40 +08:00
parent 723c4fc5c9
commit e688241355
7 changed files with 106 additions and 35 deletions
@@ -12,7 +12,7 @@ const props = defineProps<{
}>()
const emit = defineEmits<{
search: [params: { query: string; field: string; date_from: string | null; date_to: string | null; journal_tiers: string[]; tag_ids: string[]; retracted: string; negative_result: string; precision_mode: string; sort: string }]
search: [params: { query: string; field: string; date_from: string | null; date_to: string | null; journal_tiers: string[]; tag_ids: string[]; retracted: string; negative_result: string; sort: string }]
}>()
const toast = useToast()
@@ -23,7 +23,6 @@ const selectedTiers = ref<string[]>([])
const selectedTags = ref<string[]>([])
const retracted = ref('')
const negativeResult = ref('')
const precisionMode = ref('majr')
const sort = ref('date')
const field = ref('all')
const fieldOptions = [
@@ -71,7 +70,6 @@ function handleSearch() {
tag_ids: selectedTags.value,
retracted: retracted.value,
negative_result: negativeResult.value,
precision_mode: precisionMode.value,
sort: sort.value,
})
}
@@ -85,7 +83,6 @@ function resetFilters() {
selectedTags.value = []
retracted.value = ''
negativeResult.value = ''
precisionMode.value = 'majr'
sort.value = 'date'
}
@@ -143,14 +140,6 @@ function resetFilters() {
<NRadio value="only" size="small">阴性结果</NRadio>
</NRadioGroup>
</div>
<div>
<label class="filter-label">🎯 精度模式</label>
<NRadioGroup v-model:value="precisionMode" name="precision">
<NRadio value="" size="small">全部</NRadio>
<NRadio value="majr" size="small">🎯 高精度 (Major Topic)</NRadio>
<NRadio value="mesh" size="small">📚 高召回 (MeSH)</NRadio>
</NRadioGroup>
</div>
<div>
<label class="filter-label">📊 排序</label>
<NSelect v-model:value="sort" :options="[
+1 -2
View File
@@ -339,13 +339,12 @@ export interface SearchRequestBody {
journal_tiers?: string[]
tag_ids?: string[]
pub_types?: string[]
is_oa?: boolean | null
is_oa?: boolean | null // unused, reserved for future OA filter UI
language?: string | null
languages?: string[]
nlm_subsets?: string[]
retracted?: string
negative_result?: string
precision_mode?: string
// PubMed 筛选器
has_abstract?: boolean
is_free_full_text?: boolean
+7 -2
View File
@@ -296,8 +296,13 @@ const { page, total, goToPage } = usePagination({
// 年份 / 日期(datePreset 与 year_* 互斥)
const yf = Number(yearFromStr.value)
const yt = Number(yearToStr.value)
if (!datePreset.value && yearFromStr.value && !isNaN(yf) && yf >= 1900 && yf <= 2100) body.year_from = yf
if (!datePreset.value && yearToStr.value && !isNaN(yt) && yt >= 1900 && yt <= 2100) body.year_to = yt
if (datePreset.value === 'custom') {
if (yearFromStr.value && !isNaN(yf) && yf >= 1900 && yf <= 2100) body.year_from = yf
if (yearToStr.value && !isNaN(yt) && yt >= 1900 && yt <= 2100) body.year_to = yt
} else {
if (!datePreset.value && yearFromStr.value && !isNaN(yf) && yf >= 1900 && yf <= 2100) body.year_from = yf
if (!datePreset.value && yearToStr.value && !isNaN(yt) && yt >= 1900 && yt <= 2100) body.year_to = yt
}
if (datePreset.value && datePreset.value !== 'custom') {
const now = new Date()
body.date_to = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())).toISOString().slice(0, 10)
+4
View File
@@ -361,6 +361,10 @@ function restoreFromUrl() {
searchParams.value.query = q
localQuery.value = q
}
if (route.query.sort) searchParams.value.sort = route.query.sort as string
if (route.query.field) searchParams.value.field = route.query.field as string
if (route.query.retracted) searchParams.value.retracted = route.query.retracted as string
if (route.query.negative) searchParams.value.negative_result = route.query.negative as string
}
// ── 同步状态到 URL ──