# API v3.0 迁移指南 本文档帮助你从旧版 API 迁移到 v3.0。 --- ## 重要变更概述 v3.0 是一个**不向后兼容**的版本,主要变更包括: 1. ❌ **移除** 硬编码的 `price_ranges` 参数 2. ❌ **移除** `aggregations` 参数(ES DSL) 3. ✅ **新增** `range_filters` 参数 4. ✅ **新增** `facets` 参数(简化接口) 5. ✅ **新增** 标准化的分面响应格式 --- ## 迁移步骤 ### 第一步:更新过滤器参数 #### 旧代码(v2.x) ```json { "query": "玩具", "filters": { "price_ranges": ["0-50", "50-100"] } } ``` #### 新代码(v3.0) ```json { "query": "玩具", "range_filters": { "price": { "gte": 50, "lte": 100 } } } ``` #### 迁移对照表 | 旧格式 | 新格式 | |--------|--------| | `"price_ranges": ["0-50"]` | `"price": {"lt": 50}` | | `"price_ranges": ["50-100"]` | `"price": {"gte": 50, "lt": 100}` | | `"price_ranges": ["100-200"]` | `"price": {"gte": 100, "lt": 200}` | | `"price_ranges": ["200+"]` | `"price": {"gte": 200}` | ### 第二步:更新聚合参数 #### 旧代码(v2.x) ```json { "query": "玩具", "aggregations": { "category_stats": { "terms": { "field": "categoryName_keyword", "size": 15 } }, "brand_stats": { "terms": { "field": "brandName_keyword", "size": 15 } } } } ``` #### 新代码(v3.0)- 简单模式 ```json { "query": "玩具", "facets": ["categoryName_keyword", "brandName_keyword"] } ``` #### 新代码(v3.0)- 高级模式 ```json { "query": "玩具", "facets": [ { "field": "categoryName_keyword", "size": 15, "type": "terms" }, { "field": "brandName_keyword", "size": 15, "type": "terms" } ] } ``` ### 第三步:更新响应解析 #### 旧代码(v2.x) ```javascript // JavaScript const data = await response.json(); // 解析聚合结果(ES 原始格式) if (data.aggregations && data.aggregations.category_stats) { data.aggregations.category_stats.buckets.forEach(bucket => { console.log(bucket.key, bucket.doc_count); }); } ``` ```python # Python data = response.json() # 解析聚合结果(ES 原始格式) if 'aggregations' in data and 'category_stats' in data['aggregations']: for bucket in data['aggregations']['category_stats']['buckets']: print(bucket['key'], bucket['doc_count']) ``` #### 新代码(v3.0) ```javascript // JavaScript const data = await response.json(); // 解析分面结果(标准化格式) if (data.facets) { data.facets.forEach(facet => { console.log(`${facet.label} (${facet.type}):`); facet.values.forEach(value => { console.log(` ${value.label}: ${value.count}`); }); }); } ``` ```python # Python data = response.json() # 解析分面结果(标准化格式) if 'facets' in data: for facet in data['facets']: print(f"{facet['label']} ({facet['type']}):") for value in facet['values']: print(f" {value['label']}: {value['count']}") ``` --- ## 完整迁移示例 ### 示例 1:带价格过滤的搜索 #### 旧代码 ```python import requests response = requests.post('http://localhost:6002/search/', json={ "query": "玩具", "size": 20, "filters": { "categoryName_keyword": "玩具", "price_ranges": ["50-100", "100-200"] }, "aggregations": { "brand_stats": { "terms": { "field": "brandName_keyword", "size": 15 } } } }) data = response.json() # 解析聚合 for bucket in data['aggregations']['brand_stats']['buckets']: print(f"{bucket['key']}: {bucket['doc_count']}") ``` #### 新代码 ```python import requests response = requests.post('http://localhost:6002/search/', json={ "query": "玩具", "size": 20, "filters": { "categoryName_keyword": "玩具" }, "range_filters": { "price": { "gte": 50, "lte": 200 } }, "facets": [ {"field": "brandName_keyword", "size": 15} ] }) data = response.json() # 解析分面 for facet in data['facets']: if facet['field'] == 'brandName_keyword': for value in facet['values']: print(f"{value['label']}: {value['count']}") ``` ### 示例 2:前端 JavaScript 迁移 #### 旧代码 ```javascript // 构建请求 const requestBody = { query: "玩具", filters: { price_ranges: ["50-100"] }, aggregations: { category_stats: { terms: { field: "categoryName_keyword", size: 15 } } } }; // 发送请求 const response = await fetch('/search/', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(requestBody) }); const data = await response.json(); // 显示聚合结果 const buckets = data.aggregations.category_stats.buckets; buckets.forEach(bucket => { console.log(`${bucket.key}: ${bucket.doc_count}`); }); ``` #### 新代码 ```javascript // 构建请求 const requestBody = { query: "玩具", range_filters: { price: { gte: 50, lte: 100 } }, facets: [ {field: "categoryName_keyword", size: 15} ] }; // 发送请求 const response = await fetch('/search/', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(requestBody) }); const data = await response.json(); // 显示分面结果 data.facets.forEach(facet => { console.log(`${facet.label}:`); facet.values.forEach(value => { console.log(` ${value.label}: ${value.count}`); }); }); ``` --- ## 字段映射对照 ### 请求字段 | v2.x | v3.0 | 说明 | |------|------|------| | `filters.price_ranges` | `range_filters.price` | 价格范围过滤 | | `aggregations` | `facets` | 分面配置 | | - | `range_filters.*` | 任意数值字段范围过滤 | ### 响应字段 | v2.x | v3.0 | 说明 | |------|------|------| | `aggregations` | `facets` | 分面结果 | | `aggregations.*.buckets[].key` | `facets[].values[].value` | 分面值 | | `aggregations.*.buckets[].doc_count` | `facets[].values[].count` | 文档数量 | | - | `facets[].values[].label` | 显示标签 | | - | `facets[].values[].selected` | 是否选中 | | - | `facets[].label` | 分面名称 | | - | `facets[].type` | 分面类型 | --- ## 常见问题 ### Q1: 我的代码使用了 `price_ranges`,如何迁移? **A**: 使用 `range_filters` 替代: ```python # 旧 filters = {"price_ranges": ["50-100"]} # 新 range_filters = {"price": {"gte": 50, "lte": 100}} ``` ### Q2: 我使用了 ES 聚合语法,如何迁移? **A**: 使用简化的 `facets` 配置: ```python # 旧 aggregations = { "my_agg": { "terms": { "field": "categoryName_keyword", "size": 15 } } } # 新(简单模式) facets = ["categoryName_keyword"] # 新(高级模式) facets = [ {"field": "categoryName_keyword", "size": 15} ] ``` ### Q3: 响应中的 `aggregations` 字段去哪了? **A**: 已改名为 `facets`,并且格式标准化了: ```python # 旧 for bucket in data['aggregations']['category_stats']['buckets']: print(bucket['key'], bucket['doc_count']) # 新 for facet in data['facets']: for value in facet['values']: print(value['value'], value['count']) ``` ### Q4: 如何实现多个不连续的价格范围? **A**: v3.0 不支持单字段多个不连续范围。请使用布尔查询或分多次查询: ```python # 方案1:分两次查询 result1 = search(query="玩具", range_filters={"price": {"lt": 50}}) result2 = search(query="玩具", range_filters={"price": {"gte": 200}}) # 方案2:使用范围分面统计(不过滤,只统计) facets = [{ "field": "price", "type": "range", "ranges": [ {"key": "低价", "to": 50}, {"key": "高价", "from": 200} ] }] ``` ### Q5: 我需要继续使用旧接口,怎么办? **A**: v3.0 不提供向后兼容。建议: 1. 尽快迁移到新接口 2. 如果暂时无法迁移,请继续使用 v2.x 版本 3. 参考本文档快速完成迁移(通常只需要 1-2 小时) --- ## 迁移检查清单 完成以下检查项,确保迁移完整: ### 后端代码 - [ ] 移除所有 `price_ranges` 参数的使用 - [ ] 将范围过滤改为 `range_filters` - [ ] 将 `aggregations` 改为 `facets` - [ ] 更新响应解析,使用 `facets` 而不是 `aggregations` ### 前端代码 - [ ] 更新搜索请求体,使用新参数 - [ ] 更新状态管理,添加 `rangeFilters` - [ ] 重写分面结果显示逻辑 - [ ] 移除所有 ES DSL 聚合代码 ### 测试 - [ ] 测试简单搜索功能 - [ ] 测试范围过滤功能 - [ ] 测试分面搜索功能 - [ ] 测试组合查询功能 - [ ] 测试排序功能 ### 文档 - [ ] 更新 API 调用文档 - [ ] 更新代码注释 - [ ] 通知团队成员新的 API 变更 --- ## 获取帮助 - **API 文档**: `API_DOCUMENTATION.md` - **使用示例**: `API_EXAMPLES.md` - **测试脚本**: `test_new_api.py` - **变更日志**: `CHANGES.md` --- **迁移难度**: ⭐⭐ (简单到中等) **预计时间**: 1-2 小时 **优势**: 更灵活、更通用、更易用的 API --- **版本**: 3.0 **日期**: 2024-11-12