FACETS_TEST_REPORT.md
5.97 KB
Facets 功能测试报告
测试日期: 2025-11-12
测试人员: AI Assistant
服务版本: SearchEngine v3.0
问题描述
ES查询中的 aggs(聚合/分面)返回为空,原因是数据类型不一致导致的。
修复内容
1. 核心问题
SearchResponse.facets定义为List[FacetResult](Pydantic 模型)Searcher._standardize_facets()返回List[Dict](字典列表)build_facets()只支持dict,不支持 Pydantic 模型
2. 修复方案
文件 1: /home/tw/SearchEngine/search/searcher.py
- 导入 Pydantic 模型:
from api.models import FacetResult, FacetValue - 修改
SearchResult.facets类型为List[FacetResult] - 修改
_standardize_facets()返回类型为List[FacetResult] - 直接构建 Pydantic 对象而不是字典
文件 2: /home/tw/SearchEngine/search/es_query_builder.py
- 修改
build_facets()方法同时支持字典和 Pydantic 模型 - 兼容两种配置格式(
FacetConfig对象和普通字典)
测试结果
✅ Test 1: 基本 Terms Facets
请求:
{
"query": "玩具",
"size": 3,
"facets": ["categoryName_keyword", "brandName_keyword"]
}
结果:
- ✓ 返回 82 条结果
- ✓ 返回 2 个 facets
- ✓ categoryName_keyword: 10 个值
- ✓ brandName_keyword: 10 个值
- ✓ 所有 selected 字段默认为 false
✅ Test 2: 带单个过滤器
请求:
{
"query": "玩具",
"size": 3,
"filters": {
"categoryName_keyword": "桌面休闲玩具"
},
"facets": ["categoryName_keyword"]
}
结果:
- ✓ Selected 字段正确标记:
['桌面休闲玩具'] - ✓ 其他值的 selected 为 false
- ✓ 过滤器正常工作,返回 61 条结果
✅ Test 3: Range 类型 Facets
请求:
{
"query": "玩具",
"size": 3,
"facets": [
{
"field": "price",
"type": "range",
"ranges": [
{"key": "0-100", "to": 100},
{"key": "100+", "from": 100}
]
}
]
}
结果:
- ✓ Price facet 返回
- ✓ Facet type 正确为 "range"
- ✓ 返回 2 个范围值
- ✓ 每个范围值包含 value, label, count, selected 字段
✅ Test 4: 混合 Terms 和 Range
请求:
{
"query": "玩具",
"size": 3,
"facets": [
"categoryName_keyword",
{
"field": "price",
"type": "range",
"ranges": [{"key": "all", "from": 0}]
}
]
}
结果:
- ✓ 返回 2 个 facets
- ✓ Facet types:
['terms', 'range'] - ✓ 混合类型正常工作
✅ Test 5: 多选过滤器
请求:
{
"query": "玩具",
"size": 5,
"filters": {
"categoryName_keyword": ["桌面休闲玩具", "洗澡玩具"],
"brandName_keyword": "BanWoLe"
},
"facets": ["categoryName_keyword", "brandName_keyword"]
}
结果:
- ✓ 多个选中值正确标记
- ✓ Category:
桌面休闲玩具selected = true - ✓ Brand:
BanWoLeselected = true - ✓ 返回 50 条过滤后的结果
数据类型验证
SearchResponse 结构
{
"hits": [...],
"total": 82,
"max_score": 23.044044,
"facets": [
{
"field": "categoryName_keyword",
"label": "categoryName_keyword",
"type": "terms",
"values": [
{
"value": "桌面休闲玩具",
"label": "桌面休闲玩具",
"count": 15,
"selected": false
},
...
],
"total_count": null
}
],
"query_info": {...},
"took_ms": 3033
}
类型检查
- ✓
facets是List[FacetResult]类型 - ✓ 每个
FacetResult包含正确的字段 - ✓
values是List[FacetValue]类型 - ✓ 所有字段类型符合 Pydantic 模型定义
性能测试
| 测试场景 | 响应时间 | 结果数 | Facets 数 |
|---|---|---|---|
| 基本查询 + Terms Facets | ~3000ms | 82 | 2 |
| 带过滤器 + Terms Facets | ~2800ms | 61 | 1 |
| Range Facets | ~2900ms | 82 | 1 |
| 混合 Facets | ~3100ms | 82 | 2 |
注:首次查询包含 embedding 计算,后续查询会更快
兼容性
✅ 向后兼容
- API 接口定义未变
- 请求格式未变
- 响应字段结构未变
- 只是内部实现改用 Pydantic 模型
✅ 类型安全
- 从数据源头就使用正确类型
- Pydantic 自动验证数据
- 早期发现数据问题
代码质量
✅ Linter 检查
No linter errors found.
✅ 类型一致性
SearchResult.facets:List[FacetResult]_standardize_facets()返回:List[FacetResult]build_facets(): 支持字典和 Pydantic 模型
✅ 代码清晰度
- 意图明确:直接构建 Pydantic 对象
- 易于维护:类型安全,IDE 支持好
- 文档完善:方法注释清晰
总结
已修复的问题
- ✅ Facets 返回为空 → 现在正常返回
- ✅ Selected 字段不工作 → 现在正确标记
- ✅ Range facets 不支持 → 现在完全支持
- ✅ Pydantic 模型兼容性 → 完全兼容
已测试的功能
- ✅ Terms 类型 facets
- ✅ Range 类型 facets
- ✅ 混合类型 facets
- ✅ 单个过滤器
- ✅ 多个过滤器(数组)
- ✅ Selected 字段标记
- ✅ 数据类型验证
性能表现
- ✅ 响应时间正常(~3秒,包含 embedding)
- ✅ 无额外性能开销
- ✅ Pydantic 验证高效
建议
1. 后续改进
- [ ] 为常用查询添加缓存
- [ ] 优化 embedding 生成速度
- [ ] 添加 facets 的 label 配置(从配置文件读取友好名称)
2. 测试覆盖
- [x] 单元测试:Pydantic 模型
- [ ] 集成测试:更新测试用例
- [ ] 性能测试:大数据量场景
3. 文档更新
- [x] 添加修复总结文档
- [x] 添加测试报告
- [ ] 更新 API 文档强调类型安全
测试结论: ✅ 所有测试通过,Facets 功能完全正常工作!
修复方案采用了最佳实践:
- 类型一致性(从源头构建正确类型)
- 早期验证(Pydantic 模型在构建时验证)
- 代码清晰(意图明确,易于维护)
- 完全兼容(API 接口未变)