Commit 4a677843805747d44b86f3e8394a9244a2ea4c19
1 parent
670c701f
文档完善
Showing
17 changed files
with
248 additions
and
2229 deletions
Show diff stats
API_DOCUMENTATION.md deleted
| @@ -1,938 +0,0 @@ | @@ -1,938 +0,0 @@ | ||
| 1 | -# 搜索引擎 API 接口文档 | ||
| 2 | - | ||
| 3 | -## 概述 | ||
| 4 | - | ||
| 5 | -本文档描述了电商搜索 SaaS 系统的 RESTful API 接口。系统提供强大的搜索功能,包括: | ||
| 6 | - | ||
| 7 | -- **多语言搜索**:支持中文、英文、俄文等多语言查询和自动翻译 | ||
| 8 | -- **语义搜索**:基于 BGE-M3 文本向量和 CN-CLIP 图片向量的语义检索 | ||
| 9 | -- **布尔表达式**:支持 AND、OR、RANK、ANDNOT 操作符 | ||
| 10 | -- **灵活过滤**:精确匹配过滤器和数值范围过滤器 | ||
| 11 | -- **分面搜索**:动态生成过滤选项,提供分组统计 | ||
| 12 | -- **自定义排序**:支持按任意字段排序 | ||
| 13 | -- **个性化排序**:可配置的相关性排序表达式 | ||
| 14 | - | ||
| 15 | -## 基础信息 | ||
| 16 | - | ||
| 17 | -- **Base URL**: `http://your-domain:6002` (http://120.76.41.98:6002) | ||
| 18 | -- **协议**: HTTP/HTTPS | ||
| 19 | -- **数据格式**: JSON | ||
| 20 | -- **字符编码**: UTF-8 | ||
| 21 | - | ||
| 22 | -## 搜索接口 | ||
| 23 | - | ||
| 24 | -### 1. 文本搜索 | ||
| 25 | - | ||
| 26 | -**端点**: `POST /search/` | ||
| 27 | - | ||
| 28 | -**描述**: 执行文本搜索查询,支持多语言、布尔表达式、过滤器和分面搜索。 | ||
| 29 | - | ||
| 30 | -#### 请求参数 | ||
| 31 | - | ||
| 32 | -```json | ||
| 33 | -{ | ||
| 34 | - "query": "string (required)", | ||
| 35 | - "size": 10, | ||
| 36 | - "from": 0, | ||
| 37 | - "filters": {}, | ||
| 38 | - "range_filters": {}, | ||
| 39 | - "facets": [], | ||
| 40 | - "sort_by": "string", | ||
| 41 | - "sort_order": "desc", | ||
| 42 | - "min_score": 0.0, | ||
| 43 | - "debug": false, | ||
| 44 | - "user_id": "string", | ||
| 45 | - "session_id": "string" | ||
| 46 | -} | ||
| 47 | -``` | ||
| 48 | - | ||
| 49 | -#### 参数说明 | ||
| 50 | - | ||
| 51 | -| 参数 | 类型 | 必填 | 默认值 | 描述 | | ||
| 52 | -|------|------|------|--------|------| | ||
| 53 | -| `query` | string | Y | - | 搜索查询字符串,支持布尔表达式(AND, OR, RANK, ANDNOT) | | ||
| 54 | -| `size` | integer | N | 10 | 返回结果数量(1-100) | | ||
| 55 | -| `from` | integer | N | 0 | 分页偏移量 | | ||
| 56 | -| `filters` | object | N | null | 精确匹配过滤器(见下文) | | ||
| 57 | -| `range_filters` | object | N | null | 数值范围过滤器(见下文) | | ||
| 58 | -| `facets` | array | N | null | 分面配置(见下文) | | ||
| 59 | -| `sort_by` | string | N | null | 排序字段名 | | ||
| 60 | -| `sort_order` | string | N | "desc" | 排序方向:`asc` 或 `desc` | | ||
| 61 | -| `min_score` | float | N | null | 最小相关性分数阈值 | | ||
| 62 | -| `debug` | boolean | N | false | 是否返回调试信息 | | ||
| 63 | -| `user_id` | string | N | null | 用户ID(用于个性化,预留) | | ||
| 64 | -| `session_id` | string | N | null | 会话ID(用于分析,预留) | | ||
| 65 | - | ||
| 66 | -#### 过滤器详解 | ||
| 67 | - | ||
| 68 | -##### 精确匹配过滤器 (filters) | ||
| 69 | - | ||
| 70 | -用于精确匹配或多值匹配(OR 逻辑)。 | ||
| 71 | - | ||
| 72 | -**格式**: | ||
| 73 | -```json | ||
| 74 | -{ | ||
| 75 | - "filters": { | ||
| 76 | - "categoryName_keyword": "玩具", // 单值:精确匹配 | ||
| 77 | - "brandName_keyword": ["乐高", "孩之宝"], // 数组:匹配任意值(OR) | ||
| 78 | - "in_stock": true // 布尔值 | ||
| 79 | - } | ||
| 80 | -} | ||
| 81 | -``` | ||
| 82 | - | ||
| 83 | -**支持的值类型**: | ||
| 84 | -- 字符串:精确匹配 | ||
| 85 | -- 整数:精确匹配 | ||
| 86 | -- 布尔值:精确匹配 | ||
| 87 | -- 数组:匹配任意值(OR 逻辑) | ||
| 88 | - | ||
| 89 | -##### 范围过滤器 (range_filters) | ||
| 90 | - | ||
| 91 | -用于数值字段的范围过滤。 | ||
| 92 | - | ||
| 93 | -**格式**: | ||
| 94 | -```json | ||
| 95 | -{ | ||
| 96 | - "range_filters": { | ||
| 97 | - "price": { | ||
| 98 | - "gte": 50, // 大于等于 | ||
| 99 | - "lte": 200 // 小于等于 | ||
| 100 | - }, | ||
| 101 | - "days_since_last_update": { | ||
| 102 | - "lte": 30 // 最近30天更新 | ||
| 103 | - } | ||
| 104 | - } | ||
| 105 | -} | ||
| 106 | -``` | ||
| 107 | - | ||
| 108 | -**支持的操作符**: | ||
| 109 | -- `gte`: 大于等于 (>=) | ||
| 110 | -- `gt`: 大于 (>) | ||
| 111 | -- `lte`: 小于等于 (<=) | ||
| 112 | -- `lt`: 小于 (<) | ||
| 113 | - | ||
| 114 | -**注意**: 至少需要指定一个操作符。 | ||
| 115 | - | ||
| 116 | -##### 分面配置 (facets) | ||
| 117 | - | ||
| 118 | -用于生成分面统计(分组聚合)。 | ||
| 119 | - | ||
| 120 | -**简单模式**(字符串数组): | ||
| 121 | -```json | ||
| 122 | -{ | ||
| 123 | - "facets": ["categoryName_keyword", "brandName_keyword"] | ||
| 124 | -} | ||
| 125 | -``` | ||
| 126 | - | ||
| 127 | -**高级模式**(配置对象数组): | ||
| 128 | -```json | ||
| 129 | -{ | ||
| 130 | - "facets": [ | ||
| 131 | - { | ||
| 132 | - "field": "categoryName_keyword", | ||
| 133 | - "size": 15, | ||
| 134 | - "type": "terms" | ||
| 135 | - }, | ||
| 136 | - { | ||
| 137 | - "field": "price", | ||
| 138 | - "type": "range", | ||
| 139 | - "ranges": [ | ||
| 140 | - {"key": "0-50", "to": 50}, | ||
| 141 | - {"key": "50-100", "from": 50, "to": 100}, | ||
| 142 | - {"key": "100-200", "from": 100, "to": 200}, | ||
| 143 | - {"key": "200+", "from": 200} | ||
| 144 | - ] | ||
| 145 | - } | ||
| 146 | - ] | ||
| 147 | -} | ||
| 148 | -``` | ||
| 149 | - | ||
| 150 | -**分面配置参数**: | ||
| 151 | -- `field`: 字段名(必填) | ||
| 152 | -- `size`: 返回的分组数量(默认:10,范围:1-100) | ||
| 153 | -- `type`: 分面类型,`terms`(分组统计)或 `range`(范围统计) | ||
| 154 | -- `ranges`: 范围定义(仅当 type='range' 时需要) | ||
| 155 | - | ||
| 156 | -#### 响应格式 | ||
| 157 | - | ||
| 158 | -```json | ||
| 159 | -{ | ||
| 160 | - "hits": [ | ||
| 161 | - { | ||
| 162 | - "_id": "12345", | ||
| 163 | - "_score": 8.5, | ||
| 164 | - "_custom_score": 12.3, | ||
| 165 | - "_source": { | ||
| 166 | - "name": "芭比时尚娃娃", | ||
| 167 | - "price": 89.99, | ||
| 168 | - "categoryName": "玩具", | ||
| 169 | - "brandName": "美泰", | ||
| 170 | - "imageUrl": "https://example.com/image.jpg" | ||
| 171 | - } | ||
| 172 | - } | ||
| 173 | - ], | ||
| 174 | - "total": 118, | ||
| 175 | - "max_score": 8.5, | ||
| 176 | - "took_ms": 45, | ||
| 177 | - "facets": [ | ||
| 178 | - { | ||
| 179 | - "field": "categoryName_keyword", | ||
| 180 | - "label": "商品类目", | ||
| 181 | - "type": "terms", | ||
| 182 | - "values": [ | ||
| 183 | - { | ||
| 184 | - "value": "玩具", | ||
| 185 | - "label": "玩具", | ||
| 186 | - "count": 85, | ||
| 187 | - "selected": false | ||
| 188 | - }, | ||
| 189 | - { | ||
| 190 | - "value": "益智玩具", | ||
| 191 | - "label": "益智玩具", | ||
| 192 | - "count": 33, | ||
| 193 | - "selected": false | ||
| 194 | - } | ||
| 195 | - ] | ||
| 196 | - } | ||
| 197 | - ], | ||
| 198 | - "query_info": { | ||
| 199 | - "original_query": "芭比娃娃", | ||
| 200 | - "detected_language": "zh", | ||
| 201 | - "translations": { | ||
| 202 | - "en": "barbie doll" | ||
| 203 | - } | ||
| 204 | - }, | ||
| 205 | - "related_queries": null, | ||
| 206 | - "performance_info": { | ||
| 207 | - "total_duration": 45.2, | ||
| 208 | - "stage_durations": { | ||
| 209 | - "query_parsing": 5.3, | ||
| 210 | - "elasticsearch_search": 35.1, | ||
| 211 | - "result_processing": 4.8 | ||
| 212 | - } | ||
| 213 | - }, | ||
| 214 | - "debug_info": null | ||
| 215 | -} | ||
| 216 | -``` | ||
| 217 | - | ||
| 218 | -#### 响应字段说明 | ||
| 219 | - | ||
| 220 | -| 字段 | 类型 | 描述 | | ||
| 221 | -|------|------|------| | ||
| 222 | -| `hits` | array | 搜索结果列表 | | ||
| 223 | -| `hits[]._id` | string | 文档ID | | ||
| 224 | -| `hits[]._score` | float | 相关性分数 | | ||
| 225 | -| `hits[]._custom_score` | float | 自定义排序分数(如启用) | | ||
| 226 | -| `hits[]._source` | object | 文档内容 | | ||
| 227 | -| `total` | integer | 匹配的总文档数 | | ||
| 228 | -| `max_score` | float | 最高相关性分数 | | ||
| 229 | -| `took_ms` | integer | 搜索耗时(毫秒) | | ||
| 230 | -| `facets` | array | 分面统计结果(标准化格式) | | ||
| 231 | -| `facets[].field` | string | 字段名 | | ||
| 232 | -| `facets[].label` | string | 显示标签 | | ||
| 233 | -| `facets[].type` | string | 分面类型:`terms` 或 `range` | | ||
| 234 | -| `facets[].values` | array | 分面值列表 | | ||
| 235 | -| `facets[].values[].value` | any | 分面值 | | ||
| 236 | -| `facets[].values[].label` | string | 显示标签 | | ||
| 237 | -| `facets[].values[].count` | integer | 文档数量 | | ||
| 238 | -| `facets[].values[].selected` | boolean | 是否已选中 | | ||
| 239 | -| `query_info` | object | 查询处理信息 | | ||
| 240 | -| `related_queries` | array | 相关搜索(预留) | | ||
| 241 | -| `performance_info` | object | 性能信息 | | ||
| 242 | -| `debug_info` | object | 调试信息(仅当 debug=true) | | ||
| 243 | - | ||
| 244 | -#### 请求示例 | ||
| 245 | - | ||
| 246 | -**示例 1: 简单搜索** | ||
| 247 | - | ||
| 248 | -```bash | ||
| 249 | -curl -X POST "http://localhost:6002/search/" \ | ||
| 250 | - -H "Content-Type: application/json" \ | ||
| 251 | - -d '{ | ||
| 252 | - "query": "芭比娃娃", | ||
| 253 | - "size": 20 | ||
| 254 | - }' | ||
| 255 | -``` | ||
| 256 | - | ||
| 257 | -**示例 2: 带过滤器的搜索** | ||
| 258 | - | ||
| 259 | -```bash | ||
| 260 | -curl -X POST "http://localhost:6002/search/" \ | ||
| 261 | - -H "Content-Type: application/json" \ | ||
| 262 | - -d '{ | ||
| 263 | - "query": "玩具", | ||
| 264 | - "size": 20, | ||
| 265 | - "filters": { | ||
| 266 | - "categoryName_keyword": ["玩具", "益智玩具"], | ||
| 267 | - "in_stock": true | ||
| 268 | - }, | ||
| 269 | - "range_filters": { | ||
| 270 | - "price": { | ||
| 271 | - "gte": 50, | ||
| 272 | - "lte": 200 | ||
| 273 | - } | ||
| 274 | - } | ||
| 275 | - }' | ||
| 276 | -``` | ||
| 277 | - | ||
| 278 | -**示例 3: 带分面搜索(简单模式)** | ||
| 279 | - | ||
| 280 | -```bash | ||
| 281 | -curl -X POST "http://localhost:6002/search/" \ | ||
| 282 | - -H "Content-Type: application/json" \ | ||
| 283 | - -d '{ | ||
| 284 | - "query": "玩具", | ||
| 285 | - "size": 20, | ||
| 286 | - "facets": ["categoryName_keyword", "brandName_keyword"] | ||
| 287 | - }' | ||
| 288 | -``` | ||
| 289 | - | ||
| 290 | -**示例 4: 带分面搜索(高级模式)** | ||
| 291 | - | ||
| 292 | -```bash | ||
| 293 | -curl -X POST "http://localhost:6002/search/" \ | ||
| 294 | - -H "Content-Type: application/json" \ | ||
| 295 | - -d '{ | ||
| 296 | - "query": "玩具", | ||
| 297 | - "size": 20, | ||
| 298 | - "facets": [ | ||
| 299 | - { | ||
| 300 | - "field": "categoryName_keyword", | ||
| 301 | - "size": 15, | ||
| 302 | - "type": "terms" | ||
| 303 | - }, | ||
| 304 | - { | ||
| 305 | - "field": "brandName_keyword", | ||
| 306 | - "size": 15, | ||
| 307 | - "type": "terms" | ||
| 308 | - }, | ||
| 309 | - { | ||
| 310 | - "field": "price", | ||
| 311 | - "type": "range", | ||
| 312 | - "ranges": [ | ||
| 313 | - {"key": "0-50", "to": 50}, | ||
| 314 | - {"key": "50-100", "from": 50, "to": 100}, | ||
| 315 | - {"key": "100-200", "from": 100, "to": 200}, | ||
| 316 | - {"key": "200+", "from": 200} | ||
| 317 | - ] | ||
| 318 | - } | ||
| 319 | - ] | ||
| 320 | - }' | ||
| 321 | -``` | ||
| 322 | - | ||
| 323 | -**示例 5: 复杂搜索(布尔表达式+过滤+排序)** | ||
| 324 | - | ||
| 325 | -```bash | ||
| 326 | -curl -X POST "http://localhost:6002/search/" \ | ||
| 327 | - -H "Content-Type: application/json" \ | ||
| 328 | - -d '{ | ||
| 329 | - "query": "玩具 AND (乐高 OR 芭比)", | ||
| 330 | - "size": 20, | ||
| 331 | - "filters": { | ||
| 332 | - "categoryName_keyword": "玩具" | ||
| 333 | - }, | ||
| 334 | - "range_filters": { | ||
| 335 | - "price": { | ||
| 336 | - "gte": 50, | ||
| 337 | - "lte": 200 | ||
| 338 | - }, | ||
| 339 | - "days_since_last_update": { | ||
| 340 | - "lte": 30 | ||
| 341 | - } | ||
| 342 | - }, | ||
| 343 | - "facets": [ | ||
| 344 | - {"field": "brandName_keyword", "size": 15}, | ||
| 345 | - {"field": "supplierName_keyword", "size": 10} | ||
| 346 | - ], | ||
| 347 | - "sort_by": "min_price", | ||
| 348 | - "sort_order": "asc", | ||
| 349 | - "debug": false | ||
| 350 | - }' | ||
| 351 | -``` | ||
| 352 | - | ||
| 353 | ---- | ||
| 354 | - | ||
| 355 | -### 2. 图片搜索 | ||
| 356 | - | ||
| 357 | -**端点**: `POST /search/image` | ||
| 358 | - | ||
| 359 | -**描述**: 基于图片相似度进行搜索,使用图片向量进行语义匹配。 | ||
| 360 | - | ||
| 361 | -#### 请求参数 | ||
| 362 | - | ||
| 363 | -```json | ||
| 364 | -{ | ||
| 365 | - "image_url": "string (required)", | ||
| 366 | - "size": 10, | ||
| 367 | - "filters": {}, | ||
| 368 | - "range_filters": {} | ||
| 369 | -} | ||
| 370 | -``` | ||
| 371 | - | ||
| 372 | -#### 参数说明 | ||
| 373 | - | ||
| 374 | -| 参数 | 类型 | 必填 | 默认值 | 描述 | | ||
| 375 | -|------|------|------|--------|------| | ||
| 376 | -| `image_url` | string | ✅ | - | 查询图片的 URL | | ||
| 377 | -| `size` | integer | ❌ | 10 | 返回结果数量(1-100) | | ||
| 378 | -| `filters` | object | ❌ | null | 精确匹配过滤器 | | ||
| 379 | -| `range_filters` | object | ❌ | null | 数值范围过滤器 | | ||
| 380 | - | ||
| 381 | -#### 响应格式 | ||
| 382 | - | ||
| 383 | -与文本搜索相同,但 `query_info` 包含图片信息: | ||
| 384 | - | ||
| 385 | -```json | ||
| 386 | -{ | ||
| 387 | - "hits": [...], | ||
| 388 | - "total": 50, | ||
| 389 | - "max_score": 0.95, | ||
| 390 | - "took_ms": 120, | ||
| 391 | - "query_info": { | ||
| 392 | - "image_url": "https://example.com/image.jpg", | ||
| 393 | - "search_type": "image_similarity" | ||
| 394 | - } | ||
| 395 | -} | ||
| 396 | -``` | ||
| 397 | - | ||
| 398 | -#### 请求示例 | ||
| 399 | - | ||
| 400 | -```bash | ||
| 401 | -curl -X POST "http://localhost:6002/search/image" \ | ||
| 402 | - -H "Content-Type: application/json" \ | ||
| 403 | - -d '{ | ||
| 404 | - "image_url": "https://example.com/barbie.jpg", | ||
| 405 | - "size": 20, | ||
| 406 | - "filters": { | ||
| 407 | - "categoryName_keyword": "玩具" | ||
| 408 | - }, | ||
| 409 | - "range_filters": { | ||
| 410 | - "price": { | ||
| 411 | - "lte": 100 | ||
| 412 | - } | ||
| 413 | - } | ||
| 414 | - }' | ||
| 415 | -``` | ||
| 416 | - | ||
| 417 | ---- | ||
| 418 | - | ||
| 419 | -### 3. 搜索建议(框架) | ||
| 420 | - | ||
| 421 | -**端点**: `GET /search/suggestions` | ||
| 422 | - | ||
| 423 | -**描述**: 获取搜索建议(自动补全)。 | ||
| 424 | - | ||
| 425 | -**注意**: 此功能暂未实现,仅返回框架响应。 | ||
| 426 | - | ||
| 427 | -#### 查询参数 | ||
| 428 | - | ||
| 429 | -| 参数 | 类型 | 必填 | 默认值 | 描述 | | ||
| 430 | -|------|------|------|--------|------| | ||
| 431 | -| `q` | string | ✅ | - | 搜索查询字符串(最少1个字符) | | ||
| 432 | -| `size` | integer | ❌ | 5 | 建议数量(1-20) | | ||
| 433 | -| `types` | string | ❌ | "query" | 建议类型(逗号分隔):query, product, category, brand | | ||
| 434 | - | ||
| 435 | -#### 响应格式 | ||
| 436 | - | ||
| 437 | -```json | ||
| 438 | -{ | ||
| 439 | - "query": "芭", | ||
| 440 | - "suggestions": [ | ||
| 441 | - { | ||
| 442 | - "text": "芭比娃娃", | ||
| 443 | - "type": "query", | ||
| 444 | - "highlight": "<em>芭</em>比娃娃", | ||
| 445 | - "popularity": 850 | ||
| 446 | - } | ||
| 447 | - ], | ||
| 448 | - "took_ms": 5 | ||
| 449 | -} | ||
| 450 | -``` | ||
| 451 | - | ||
| 452 | -#### 请求示例 | ||
| 453 | - | ||
| 454 | -```bash | ||
| 455 | -curl "http://localhost:6002/search/suggestions?q=芭&size=5&types=query,product" | ||
| 456 | -``` | ||
| 457 | - | ||
| 458 | ---- | ||
| 459 | - | ||
| 460 | -### 4. 即时搜索(框架) | ||
| 461 | - | ||
| 462 | -**端点**: `GET /search/instant` | ||
| 463 | - | ||
| 464 | -**描述**: 即时搜索,边输入边搜索。 | ||
| 465 | - | ||
| 466 | -**注意**: 此功能暂未实现,调用标准搜索接口。 | ||
| 467 | - | ||
| 468 | -#### 查询参数 | ||
| 469 | - | ||
| 470 | -| 参数 | 类型 | 必填 | 默认值 | 描述 | | ||
| 471 | -|------|------|------|--------|------| | ||
| 472 | -| `q` | string | ✅ | - | 搜索查询(最少2个字符) | | ||
| 473 | -| `size` | integer | ❌ | 5 | 结果数量(1-20) | | ||
| 474 | - | ||
| 475 | -#### 请求示例 | ||
| 476 | - | ||
| 477 | -```bash | ||
| 478 | -curl "http://localhost:6002/search/instant?q=玩具&size=5" | ||
| 479 | -``` | ||
| 480 | - | ||
| 481 | ---- | ||
| 482 | - | ||
| 483 | -### 5. 获取单个文档 | ||
| 484 | - | ||
| 485 | -**端点**: `GET /search/{doc_id}` | ||
| 486 | - | ||
| 487 | -**描述**: 根据文档ID获取单个文档详情。 | ||
| 488 | - | ||
| 489 | -#### 路径参数 | ||
| 490 | - | ||
| 491 | -| 参数 | 类型 | 描述 | | ||
| 492 | -|------|------|------| | ||
| 493 | -| `doc_id` | string | 文档ID | | ||
| 494 | - | ||
| 495 | -#### 响应格式 | ||
| 496 | - | ||
| 497 | -```json | ||
| 498 | -{ | ||
| 499 | - "id": "12345", | ||
| 500 | - "source": { | ||
| 501 | - "name": "芭比时尚娃娃", | ||
| 502 | - "price": 89.99, | ||
| 503 | - "categoryName": "玩具" | ||
| 504 | - } | ||
| 505 | -} | ||
| 506 | -``` | ||
| 507 | - | ||
| 508 | -#### 请求示例 | ||
| 509 | - | ||
| 510 | -```bash | ||
| 511 | -curl "http://localhost:6002/search/12345" | ||
| 512 | -``` | ||
| 513 | - | ||
| 514 | ---- | ||
| 515 | - | ||
| 516 | -## 管理接口 | ||
| 517 | - | ||
| 518 | -### 1. 健康检查 | ||
| 519 | - | ||
| 520 | -**端点**: `GET /admin/health` | ||
| 521 | - | ||
| 522 | -**描述**: 检查服务健康状态。 | ||
| 523 | - | ||
| 524 | -#### 响应格式 | ||
| 525 | - | ||
| 526 | -```json | ||
| 527 | -{ | ||
| 528 | - "status": "healthy", | ||
| 529 | - "elasticsearch": "connected", | ||
| 530 | - "tenant_id": "tenant1" | ||
| 531 | -} | ||
| 532 | -``` | ||
| 533 | - | ||
| 534 | ---- | ||
| 535 | - | ||
| 536 | -### 2. 获取配置 | ||
| 537 | - | ||
| 538 | -**端点**: `GET /admin/config` | ||
| 539 | - | ||
| 540 | -**描述**: 获取当前客户配置(脱敏)。 | ||
| 541 | - | ||
| 542 | -#### 响应格式 | ||
| 543 | - | ||
| 544 | -```json | ||
| 545 | -{ | ||
| 546 | - "tenant_id": "tenant1", | ||
| 547 | - "tenant_name": "Tenant1 Test Instance", | ||
| 548 | - "es_index_name": "search_tenant1", | ||
| 549 | - "num_fields": 20, | ||
| 550 | - "num_indexes": 4, | ||
| 551 | - "supported_languages": ["zh", "en", "ru"], | ||
| 552 | - "ranking_expression": "bm25() + 0.2*text_embedding_relevance()", | ||
| 553 | - "spu_enabled": false | ||
| 554 | -} | ||
| 555 | -``` | ||
| 556 | - | ||
| 557 | ---- | ||
| 558 | - | ||
| 559 | -### 3. 索引统计 | ||
| 560 | - | ||
| 561 | -**端点**: `GET /admin/stats` | ||
| 562 | - | ||
| 563 | -**描述**: 获取索引统计信息。 | ||
| 564 | - | ||
| 565 | -#### 响应格式 | ||
| 566 | - | ||
| 567 | -```json | ||
| 568 | -{ | ||
| 569 | - "index_name": "search_tenant1", | ||
| 570 | - "document_count": 10000, | ||
| 571 | - "size_mb": 523.45 | ||
| 572 | -} | ||
| 573 | -``` | ||
| 574 | - | ||
| 575 | ---- | ||
| 576 | - | ||
| 577 | -### 4. 查询改写规则 | ||
| 578 | - | ||
| 579 | -**端点**: `GET /admin/rewrite-rules` | ||
| 580 | - | ||
| 581 | -**描述**: 获取当前的查询改写规则。 | ||
| 582 | - | ||
| 583 | -#### 响应格式 | ||
| 584 | - | ||
| 585 | -```json | ||
| 586 | -{ | ||
| 587 | - "rules": { | ||
| 588 | - "乐高": "brand:乐高 OR name:乐高", | ||
| 589 | - "玩具": "category:玩具" | ||
| 590 | - }, | ||
| 591 | - "count": 2 | ||
| 592 | -} | ||
| 593 | -``` | ||
| 594 | - | ||
| 595 | -**端点**: `POST /admin/rewrite-rules` | ||
| 596 | - | ||
| 597 | -**描述**: 更新查询改写规则。 | ||
| 598 | - | ||
| 599 | -#### 请求格式 | ||
| 600 | - | ||
| 601 | -```json | ||
| 602 | -{ | ||
| 603 | - "乐高": "brand:乐高 OR name:乐高", | ||
| 604 | - "芭比": "brand:芭比 OR name:芭比" | ||
| 605 | -} | ||
| 606 | -``` | ||
| 607 | - | ||
| 608 | ---- | ||
| 609 | - | ||
| 610 | -## 使用示例 | ||
| 611 | - | ||
| 612 | -### Python 示例 | ||
| 613 | - | ||
| 614 | -```python | ||
| 615 | -import requests | ||
| 616 | - | ||
| 617 | -API_URL = "http://localhost:6002/search/" | ||
| 618 | - | ||
| 619 | -# 简单搜索 | ||
| 620 | -response = requests.post(API_URL, json={ | ||
| 621 | - "query": "芭比娃娃", | ||
| 622 | - "size": 20 | ||
| 623 | -}) | ||
| 624 | -data = response.json() | ||
| 625 | -print(f"找到 {data['total']} 个结果") | ||
| 626 | - | ||
| 627 | -# 带过滤器和分面的搜索 | ||
| 628 | -response = requests.post(API_URL, json={ | ||
| 629 | - "query": "玩具", | ||
| 630 | - "size": 20, | ||
| 631 | - "filters": { | ||
| 632 | - "categoryName_keyword": ["玩具", "益智玩具"] | ||
| 633 | - }, | ||
| 634 | - "range_filters": { | ||
| 635 | - "price": {"gte": 50, "lte": 200} | ||
| 636 | - }, | ||
| 637 | - "facets": [ | ||
| 638 | - {"field": "brandName_keyword", "size": 15}, | ||
| 639 | - {"field": "categoryName_keyword", "size": 15} | ||
| 640 | - ], | ||
| 641 | - "sort_by": "min_price", | ||
| 642 | - "sort_order": "asc" | ||
| 643 | -}) | ||
| 644 | -result = response.json() | ||
| 645 | - | ||
| 646 | -# 处理分面结果 | ||
| 647 | -for facet in result.get('facets', []): | ||
| 648 | - print(f"\n{facet['label']}:") | ||
| 649 | - for value in facet['values']: | ||
| 650 | - print(f" - {value['label']}: {value['count']}") | ||
| 651 | -``` | ||
| 652 | - | ||
| 653 | -### JavaScript 示例 | ||
| 654 | - | ||
| 655 | -```javascript | ||
| 656 | -// 搜索函数 | ||
| 657 | -async function searchProducts(query, filters, rangeFilters, facets) { | ||
| 658 | - const response = await fetch('http://localhost:6002/search/', { | ||
| 659 | - method: 'POST', | ||
| 660 | - headers: { | ||
| 661 | - 'Content-Type': 'application/json' | ||
| 662 | - }, | ||
| 663 | - body: JSON.stringify({ | ||
| 664 | - query: query, | ||
| 665 | - size: 20, | ||
| 666 | - filters: filters, | ||
| 667 | - range_filters: rangeFilters, | ||
| 668 | - facets: facets | ||
| 669 | - }) | ||
| 670 | - }); | ||
| 671 | - | ||
| 672 | - const data = await response.json(); | ||
| 673 | - return data; | ||
| 674 | -} | ||
| 675 | - | ||
| 676 | -// 使用示例 | ||
| 677 | -const result = await searchProducts( | ||
| 678 | - "玩具", | ||
| 679 | - { categoryName_keyword: ["玩具"] }, | ||
| 680 | - { price: { gte: 50, lte: 200 } }, | ||
| 681 | - [ | ||
| 682 | - { field: "brandName_keyword", size: 15 }, | ||
| 683 | - { field: "categoryName_keyword", size: 15 } | ||
| 684 | - ] | ||
| 685 | -); | ||
| 686 | - | ||
| 687 | -// 显示分面结果 | ||
| 688 | -result.facets.forEach(facet => { | ||
| 689 | - console.log(`${facet.label}:`); | ||
| 690 | - facet.values.forEach(value => { | ||
| 691 | - console.log(` - ${value.label}: ${value.count}`); | ||
| 692 | - }); | ||
| 693 | -}); | ||
| 694 | - | ||
| 695 | -// 显示搜索结果 | ||
| 696 | -result.hits.forEach(hit => { | ||
| 697 | - const product = hit._source; | ||
| 698 | - console.log(`${product.name} - ¥${product.price}`); | ||
| 699 | -}); | ||
| 700 | -``` | ||
| 701 | - | ||
| 702 | -### cURL 示例 | ||
| 703 | - | ||
| 704 | -```bash | ||
| 705 | -# 简单搜索 | ||
| 706 | -curl -X POST "http://localhost:6002/search/" \ | ||
| 707 | - -H "Content-Type: application/json" \ | ||
| 708 | - -d '{"query": "芭比娃娃", "size": 20}' | ||
| 709 | - | ||
| 710 | -# 带过滤和排序 | ||
| 711 | -curl -X POST "http://localhost:6002/search/" \ | ||
| 712 | - -H "Content-Type: application/json" \ | ||
| 713 | - -d '{ | ||
| 714 | - "query": "玩具", | ||
| 715 | - "size": 20, | ||
| 716 | - "filters": {"categoryName_keyword": "玩具"}, | ||
| 717 | - "range_filters": {"price": {"gte": 50, "lte": 200}}, | ||
| 718 | - "sort_by": "min_price", | ||
| 719 | - "sort_order": "asc" | ||
| 720 | - }' | ||
| 721 | - | ||
| 722 | -# 带分面搜索 | ||
| 723 | -curl -X POST "http://localhost:6002/search/" \ | ||
| 724 | - -H "Content-Type: application/json" \ | ||
| 725 | - -d '{ | ||
| 726 | - "query": "玩具", | ||
| 727 | - "size": 20, | ||
| 728 | - "facets": [ | ||
| 729 | - {"field": "categoryName_keyword", "size": 15}, | ||
| 730 | - {"field": "brandName_keyword", "size": 15} | ||
| 731 | - ] | ||
| 732 | - }' | ||
| 733 | -``` | ||
| 734 | - | ||
| 735 | ---- | ||
| 736 | - | ||
| 737 | -## 布尔表达式语法 | ||
| 738 | - | ||
| 739 | -### 支持的操作符 | ||
| 740 | - | ||
| 741 | -| 操作符 | 描述 | 示例 | | ||
| 742 | -|--------|------|------| | ||
| 743 | -| `AND` | 所有词必须匹配 | `玩具 AND 乐高` | | ||
| 744 | -| `OR` | 任意词匹配 | `芭比 OR 娃娃` | | ||
| 745 | -| `ANDNOT` | 排除特定词 | `玩具 ANDNOT 电动` | | ||
| 746 | -| `RANK` | 排序加权(不强制匹配) | `玩具 RANK 乐高` | | ||
| 747 | -| `()` | 分组 | `玩具 AND (乐高 OR 芭比)` | | ||
| 748 | - | ||
| 749 | -### 操作符优先级 | ||
| 750 | - | ||
| 751 | -从高到低: | ||
| 752 | -1. `()` - 括号 | ||
| 753 | -2. `ANDNOT` - 排除 | ||
| 754 | -3. `AND` - 与 | ||
| 755 | -4. `OR` - 或 | ||
| 756 | -5. `RANK` - 排序 | ||
| 757 | - | ||
| 758 | -### 查询示例 | ||
| 759 | - | ||
| 760 | -``` | ||
| 761 | -# 简单查询 | ||
| 762 | -"芭比娃娃" | ||
| 763 | - | ||
| 764 | -# AND 查询 | ||
| 765 | -"玩具 AND 乐高" | ||
| 766 | - | ||
| 767 | -# OR 查询 | ||
| 768 | -"芭比 OR 娃娃" | ||
| 769 | - | ||
| 770 | -# 排除查询 | ||
| 771 | -"玩具 ANDNOT 电动" | ||
| 772 | - | ||
| 773 | -# 复杂查询 | ||
| 774 | -"玩具 AND (乐高 OR 芭比) ANDNOT 电动" | ||
| 775 | - | ||
| 776 | -# 域查询 | ||
| 777 | -"brand:乐高" | ||
| 778 | -"category:玩具" | ||
| 779 | -"title:芭比娃娃" | ||
| 780 | -``` | ||
| 781 | - | ||
| 782 | ---- | ||
| 783 | - | ||
| 784 | -## 数据模型 | ||
| 785 | - | ||
| 786 | -### 商品字段 | ||
| 787 | - | ||
| 788 | -常见的商品字段包括: | ||
| 789 | - | ||
| 790 | -| 字段名 | 类型 | 描述 | | ||
| 791 | -|--------|------|------| | ||
| 792 | -| `skuId` | long | SKU ID(主键) | | ||
| 793 | -| `name` | text | 商品名称(中文) | | ||
| 794 | -| `enSpuName` | text | 商品名称(英文) | | ||
| 795 | -| `ruSkuName` | text | 商品名称(俄文) | | ||
| 796 | -| `categoryName` | text | 类目名称 | | ||
| 797 | -| `categoryName_keyword` | keyword | 类目名称(精确匹配) | | ||
| 798 | -| `brandName` | text | 品牌名称 | | ||
| 799 | -| `brandName_keyword` | keyword | 品牌名称(精确匹配) | | ||
| 800 | -| `supplierName` | text | 供应商名称 | | ||
| 801 | -| `supplierName_keyword` | keyword | 供应商名称(精确匹配) | | ||
| 802 | -| `price` | double | 价格 | | ||
| 803 | -| `imageUrl` | keyword | 商品图片URL | | ||
| 804 | -| `create_time` | date | 创建时间 | | ||
| 805 | -| `days_since_last_update` | int | 距上次更新天数 | | ||
| 806 | - | ||
| 807 | -**注意**: 不同客户可能有不同的字段配置。 | ||
| 808 | - | ||
| 809 | ---- | ||
| 810 | - | ||
| 811 | - | ||
| 812 | -## 常见问题 | ||
| 813 | - | ||
| 814 | -### Q1: 如何判断一个字段应该用哪种过滤器? | ||
| 815 | - | ||
| 816 | -**A**: | ||
| 817 | -- **精确匹配过滤器** (`filters`): 用于 KEYWORD 类型字段(如类目、品牌、标签等) | ||
| 818 | -- **范围过滤器** (`range_filters`): 用于数值类型字段(如价格、库存、时间等) | ||
| 819 | - | ||
| 820 | -### Q2: 可以同时使用多个过滤器吗? | ||
| 821 | - | ||
| 822 | -**A**: 可以。多个过滤器之间是 AND 关系(必须同时满足)。 | ||
| 823 | - | ||
| 824 | -```json | ||
| 825 | -{ | ||
| 826 | - "filters": { | ||
| 827 | - "categoryName_keyword": "玩具", | ||
| 828 | - "brandName_keyword": "乐高" | ||
| 829 | - }, | ||
| 830 | - "range_filters": { | ||
| 831 | - "price": {"gte": 50, "lte": 200} | ||
| 832 | - } | ||
| 833 | -} | ||
| 834 | -``` | ||
| 835 | -结果:类目是"玩具" **并且** 品牌是"乐高" **并且** 价格在50-200之间。 | ||
| 836 | - | ||
| 837 | -### Q3: 如何实现"价格小于50或大于200"的过滤? | ||
| 838 | - | ||
| 839 | -**A**: 当前版本不支持单字段多个不连续范围。建议分两次查询或使用布尔查询。 | ||
| 840 | - | ||
| 841 | -### Q4: 分面搜索返回的 selected 字段是什么意思? | ||
| 842 | - | ||
| 843 | -**A**: `selected` 表示该分面值是否在当前的过滤器中。前端可以用它来高亮已选中的过滤项。 | ||
| 844 | - | ||
| 845 | -### Q5: 如何使用自定义排序? | ||
| 846 | - | ||
| 847 | -**A**: 使用 `sort_by` 和 `sort_order` 参数: | ||
| 848 | - | ||
| 849 | -```json | ||
| 850 | -{ | ||
| 851 | - "query": "玩具", | ||
| 852 | - "sort_by": "price", | ||
| 853 | - "sort_order": "asc" // 价格从低到高 | ||
| 854 | -} | ||
| 855 | -``` | ||
| 856 | - | ||
| 857 | -常用排序字段: | ||
| 858 | -- `price`: 价格 | ||
| 859 | -- `create_time`: 创建时间 | ||
| 860 | -- `days_since_last_update`: 更新时间 | ||
| 861 | - | ||
| 862 | -### Q6: 如何启用调试模式? | ||
| 863 | - | ||
| 864 | -**A**: 设置 `debug: true`,响应中会包含 `debug_info` 字段,包含: | ||
| 865 | -- 查询分析过程 | ||
| 866 | -- ES 查询 DSL | ||
| 867 | -- ES 响应详情 | ||
| 868 | -- 各阶段耗时 | ||
| 869 | - | ||
| 870 | ---- | ||
| 871 | - | ||
| 872 | -## 版本历史 | ||
| 873 | - | ||
| 874 | -### v3.0 (2024-11-12) | ||
| 875 | - | ||
| 876 | -**重大更新**: | ||
| 877 | -- ✅ 移除硬编码的 `price_ranges` 逻辑 | ||
| 878 | -- ✅ 新增 `range_filters` 参数,支持任意数值字段的范围过滤 | ||
| 879 | -- ✅ 新增 `facets` 参数,替代 `aggregations`,提供简化接口 | ||
| 880 | -- ✅ 标准化分面搜索响应格式 | ||
| 881 | -- ✅ 新增 `/search/suggestions` 端点(框架) | ||
| 882 | -- ✅ 新增 `/search/instant` 端点(框架) | ||
| 883 | -- ❌ **移除** `aggregations` 参数(不向后兼容) | ||
| 884 | - | ||
| 885 | -**迁移指南**: | ||
| 886 | - | ||
| 887 | -旧接口: | ||
| 888 | -```json | ||
| 889 | -{ | ||
| 890 | - "filters": { | ||
| 891 | - "price_ranges": ["0-50", "50-100"] | ||
| 892 | - }, | ||
| 893 | - "aggregations": { | ||
| 894 | - "category_stats": {"terms": {"field": "categoryName_keyword", "size": 15}} | ||
| 895 | - } | ||
| 896 | -} | ||
| 897 | -``` | ||
| 898 | - | ||
| 899 | -新接口: | ||
| 900 | -```json | ||
| 901 | -{ | ||
| 902 | - "range_filters": { | ||
| 903 | - "price": {"gte": 50, "lte": 100} | ||
| 904 | - }, | ||
| 905 | - "facets": [ | ||
| 906 | - {"field": "categoryName_keyword", "size": 15} | ||
| 907 | - ] | ||
| 908 | -} | ||
| 909 | -``` | ||
| 910 | - | ||
| 911 | ---- | ||
| 912 | - | ||
| 913 | -## 附录 | ||
| 914 | - | ||
| 915 | -### A. 支持的分析器 | ||
| 916 | - | ||
| 917 | -| 分析器 | 语言 | 描述 | | ||
| 918 | -|--------|------|------| | ||
| 919 | -| `chinese_ecommerce` | 中文 | Ansj 中文分词器(电商优化) | | ||
| 920 | -| `english` | 英文 | 标准英文分析器 | | ||
| 921 | -| `russian` | 俄文 | 俄文分析器 | | ||
| 922 | -| `arabic` | 阿拉伯文 | 阿拉伯文分析器 | | ||
| 923 | -| `spanish` | 西班牙文 | 西班牙文分析器 | | ||
| 924 | -| `japanese` | 日文 | 日文分析器 | | ||
| 925 | - | ||
| 926 | -### B. 字段类型 | ||
| 927 | - | ||
| 928 | -| 类型 | ES 映射 | 用途 | | ||
| 929 | -|------|---------|------| | ||
| 930 | -| `TEXT` | text | 全文检索 | | ||
| 931 | -| `KEYWORD` | keyword | 精确匹配、聚合、排序 | | ||
| 932 | -| `LONG` | long | 整数 | | ||
| 933 | -| `DOUBLE` | double | 浮点数 | | ||
| 934 | -| `DATE` | date | 日期时间 | | ||
| 935 | -| `BOOLEAN` | boolean | 布尔值 | | ||
| 936 | -| `TEXT_EMBEDDING` | dense_vector | 文本向量(1024维) | | ||
| 937 | -| `IMAGE_EMBEDDING` | dense_vector | 图片向量(1024维) | | ||
| 938 | - |
BEST_PRACTICES_REFACTORING.md deleted
| @@ -1,274 +0,0 @@ | @@ -1,274 +0,0 @@ | ||
| 1 | -# 最佳实践重构总结 | ||
| 2 | - | ||
| 3 | -**重构日期**: 2025-11-12 | ||
| 4 | -**重构人员**: AI Assistant | ||
| 5 | -**重构原则**: 代码简洁、类型安全、单一职责 | ||
| 6 | - | ||
| 7 | ---- | ||
| 8 | - | ||
| 9 | -## 重构目标 | ||
| 10 | - | ||
| 11 | -1. **移除兼容代码**:不再支持多种数据格式,统一使用最佳实践 | ||
| 12 | -2. **修复前端422错误**:支持日期时间字符串的 range filter | ||
| 13 | -3. **保持代码简洁**:单一数据流,清晰的类型定义 | ||
| 14 | - | ||
| 15 | ---- | ||
| 16 | - | ||
| 17 | -## 修改文件 | ||
| 18 | - | ||
| 19 | -### 1. `/home/tw/SearchEngine/api/models.py` | ||
| 20 | - | ||
| 21 | -#### 修改:RangeFilter 模型 | ||
| 22 | - | ||
| 23 | -**之前**:只支持数值 (float) | ||
| 24 | -```python | ||
| 25 | -gte: Optional[float] = Field(None, description="大于等于 (>=)") | ||
| 26 | -``` | ||
| 27 | - | ||
| 28 | -**之后**:支持数值和日期时间字符串 | ||
| 29 | -```python | ||
| 30 | -gte: Optional[Union[float, str]] = Field(None, description="大于等于 (>=)。数值或ISO日期时间字符串") | ||
| 31 | -``` | ||
| 32 | - | ||
| 33 | -**理由**: | ||
| 34 | -- 价格字段需要数值过滤:`{"gte": 50, "lte": 200}` | ||
| 35 | -- 时间字段需要字符串过滤:`{"gte": "2023-01-01T00:00:00Z"}` | ||
| 36 | -- 使用 `Union[float, str]` 同时支持两种类型 | ||
| 37 | - | ||
| 38 | ---- | ||
| 39 | - | ||
| 40 | -### 2. `/home/tw/SearchEngine/search/es_query_builder.py` | ||
| 41 | - | ||
| 42 | -#### 修改:build_facets 方法 | ||
| 43 | - | ||
| 44 | -**之前**:兼容字典和 Pydantic 模型 | ||
| 45 | -```python | ||
| 46 | -else: | ||
| 47 | - if isinstance(config, dict): | ||
| 48 | - field = config['field'] | ||
| 49 | - facet_type = config.get('type', 'terms') | ||
| 50 | - ... | ||
| 51 | - else: | ||
| 52 | - # Pydantic模型 | ||
| 53 | - field = config.field | ||
| 54 | - facet_type = config.type | ||
| 55 | - ... | ||
| 56 | -``` | ||
| 57 | - | ||
| 58 | -**之后**:只支持标准格式(str 或 FacetConfig) | ||
| 59 | -```python | ||
| 60 | -# 简单模式:只有字段名(字符串) | ||
| 61 | -if isinstance(config, str): | ||
| 62 | - field = config | ||
| 63 | - ... | ||
| 64 | - | ||
| 65 | -# 高级模式:FacetConfig 对象 | ||
| 66 | -else: | ||
| 67 | - field = config.field | ||
| 68 | - facet_type = config.type | ||
| 69 | - ... | ||
| 70 | -``` | ||
| 71 | - | ||
| 72 | -**理由**: | ||
| 73 | -- API 层已经定义标准格式:`List[Union[str, FacetConfig]]` | ||
| 74 | -- 移除字典支持,保持单一数据流 | ||
| 75 | -- 代码更简洁,意图更清晰 | ||
| 76 | - | ||
| 77 | ---- | ||
| 78 | - | ||
| 79 | -### 3. `/home/tw/SearchEngine/search/searcher.py` | ||
| 80 | - | ||
| 81 | -#### 修改:_standardize_facets 方法 | ||
| 82 | - | ||
| 83 | -**之前**:兼容字典和 Pydantic 模型 | ||
| 84 | -```python | ||
| 85 | -else: | ||
| 86 | - field = config.get('field') if isinstance(config, dict) else config.field | ||
| 87 | - facet_type = config.get('type', 'terms') if isinstance(config, dict) else getattr(config, 'type', 'terms') | ||
| 88 | -``` | ||
| 89 | - | ||
| 90 | -**之后**:只支持标准格式 | ||
| 91 | -```python | ||
| 92 | -else: | ||
| 93 | - # FacetConfig 对象 | ||
| 94 | - field = config.field | ||
| 95 | - facet_type = config.type | ||
| 96 | -``` | ||
| 97 | - | ||
| 98 | -**理由**: | ||
| 99 | -- 与 build_facets 保持一致 | ||
| 100 | -- 移除冗余的类型检查 | ||
| 101 | -- 代码更清晰易读 | ||
| 102 | - | ||
| 103 | ---- | ||
| 104 | - | ||
| 105 | -## 数据流设计 | ||
| 106 | - | ||
| 107 | -### 标准数据流(最佳实践) | ||
| 108 | - | ||
| 109 | -``` | ||
| 110 | -API Request | ||
| 111 | - ↓ | ||
| 112 | -Pydantic 验证 (SearchRequest) | ||
| 113 | - ↓ facets: List[Union[str, FacetConfig]] | ||
| 114 | -Searcher.search() | ||
| 115 | - ↓ | ||
| 116 | -QueryBuilder.build_facets() | ||
| 117 | - ↓ 只接受 str 或 FacetConfig | ||
| 118 | -ES Query (aggs) | ||
| 119 | - ↓ | ||
| 120 | -ES Response (aggregations) | ||
| 121 | - ↓ | ||
| 122 | -Searcher._standardize_facets() | ||
| 123 | - ↓ 只处理 str 或 FacetConfig | ||
| 124 | -List[FacetResult] (Pydantic 模型) | ||
| 125 | - ↓ | ||
| 126 | -SearchResponse | ||
| 127 | - ↓ | ||
| 128 | -API Response (JSON) | ||
| 129 | -``` | ||
| 130 | - | ||
| 131 | -### 类型定义 | ||
| 132 | - | ||
| 133 | -```python | ||
| 134 | -# API 层 | ||
| 135 | -facets: Optional[List[Union[str, FacetConfig]]] = None | ||
| 136 | - | ||
| 137 | -# Query Builder 层 | ||
| 138 | -def build_facets(facet_configs: Optional[List[Union[str, 'FacetConfig']]]) | ||
| 139 | - | ||
| 140 | -# Searcher 层 | ||
| 141 | -def _standardize_facets(...) -> Optional[List[FacetResult]] | ||
| 142 | - | ||
| 143 | -# Response 层 | ||
| 144 | -facets: Optional[List[FacetResult]] = None | ||
| 145 | -``` | ||
| 146 | - | ||
| 147 | ---- | ||
| 148 | - | ||
| 149 | -## 测试结果 | ||
| 150 | - | ||
| 151 | -### ✅ 所有测试通过 | ||
| 152 | - | ||
| 153 | -| 测试场景 | 状态 | 说明 | | ||
| 154 | -|---------|------|------| | ||
| 155 | -| 字符串 facets | ✓ | `["categoryName_keyword"]` | | ||
| 156 | -| FacetConfig 对象 | ✓ | `{"field": "price", "type": "range", ...}` | | ||
| 157 | -| 数值 range filter | ✓ | `{"gte": 50, "lte": 200}` | | ||
| 158 | -| 日期时间 range filter | ✓ | `{"gte": "2023-01-01T00:00:00Z"}` | | ||
| 159 | -| 混合使用 | ✓ | filters + range_filters + facets | | ||
| 160 | - | ||
| 161 | -### ✅ 前端 422 错误已修复 | ||
| 162 | - | ||
| 163 | -**问题**:前端筛选 listing time 时返回 422 Unprocessable Entity | ||
| 164 | - | ||
| 165 | -**原因**:`RangeFilter` 只接受 `float`,不接受日期时间字符串 | ||
| 166 | - | ||
| 167 | -**解决**:修改 `RangeFilter` 支持 `Union[float, str]` | ||
| 168 | - | ||
| 169 | -**验证**: | ||
| 170 | -```bash | ||
| 171 | -curl -X POST /search/ \ | ||
| 172 | - -d '{"query": "玩具", "range_filters": {"create_time": {"gte": "2023-01-01T00:00:00Z"}}}' | ||
| 173 | -# ✓ 200 OK | ||
| 174 | -``` | ||
| 175 | - | ||
| 176 | ---- | ||
| 177 | - | ||
| 178 | -## 代码质量 | ||
| 179 | - | ||
| 180 | -### ✅ 无 Linter 错误 | ||
| 181 | -```bash | ||
| 182 | -No linter errors found. | ||
| 183 | -``` | ||
| 184 | - | ||
| 185 | -### ✅ 类型安全 | ||
| 186 | -- 所有类型明确定义 | ||
| 187 | -- Pydantic 自动验证 | ||
| 188 | -- IDE 类型提示完整 | ||
| 189 | - | ||
| 190 | -### ✅ 代码简洁 | ||
| 191 | -- 移除所有兼容代码 | ||
| 192 | -- 单一数据格式 | ||
| 193 | -- 清晰的控制流 | ||
| 194 | - | ||
| 195 | ---- | ||
| 196 | - | ||
| 197 | -## 最佳实践原则 | ||
| 198 | - | ||
| 199 | -### 1. **单一职责** | ||
| 200 | -每个方法只处理一种标准格式,不兼容多种输入 | ||
| 201 | - | ||
| 202 | -### 2. **类型明确** | ||
| 203 | -使用 Pydantic 模型而不是字典,类型在编译时就确定 | ||
| 204 | - | ||
| 205 | -### 3. **数据流清晰** | ||
| 206 | -API → Pydantic → 业务逻辑 → Pydantic → Response | ||
| 207 | - | ||
| 208 | -### 4. **早期验证** | ||
| 209 | -在 API 层就验证数据,不在内部做多重兼容检查 | ||
| 210 | - | ||
| 211 | -### 5. **代码可维护** | ||
| 212 | -- 删除冗余代码 | ||
| 213 | -- 保持一致性 | ||
| 214 | -- 易于理解和修改 | ||
| 215 | - | ||
| 216 | ---- | ||
| 217 | - | ||
| 218 | -## 对比总结 | ||
| 219 | - | ||
| 220 | -| 方面 | 重构前 | 重构后 | | ||
| 221 | -|------|-------|--------| | ||
| 222 | -| **数据格式** | 字典 + Pydantic(兼容) | 只有 Pydantic | | ||
| 223 | -| **类型检查** | 运行时多重检查 | 编译时类型明确 | | ||
| 224 | -| **代码行数** | 更多(兼容代码) | 更少(单一逻辑) | | ||
| 225 | -| **可维护性** | 复杂(多种路径) | 简单(单一路径) | | ||
| 226 | -| **错误处理** | 隐式容错 | 明确验证 | | ||
| 227 | -| **RangeFilter** | 只支持数值 | 支持数值+字符串 | | ||
| 228 | -| **前端兼容** | 422 错误 | 完全兼容 | | ||
| 229 | - | ||
| 230 | ---- | ||
| 231 | - | ||
| 232 | -## 后续建议 | ||
| 233 | - | ||
| 234 | -### 1. 代码审查 | ||
| 235 | -- [x] 移除所有字典兼容代码 | ||
| 236 | -- [x] 统一使用 Pydantic 模型 | ||
| 237 | -- [x] 修复前端 422 错误 | ||
| 238 | - | ||
| 239 | -### 2. 测试覆盖 | ||
| 240 | -- [x] 字符串 facets | ||
| 241 | -- [x] FacetConfig 对象 | ||
| 242 | -- [x] 数值 range filter | ||
| 243 | -- [x] 日期时间 range filter | ||
| 244 | -- [x] 混合场景 | ||
| 245 | - | ||
| 246 | -### 3. 文档更新 | ||
| 247 | -- [x] 最佳实践文档 | ||
| 248 | -- [x] 数据流设计 | ||
| 249 | -- [ ] API 文档更新 | ||
| 250 | - | ||
| 251 | -### 4. 性能优化 | ||
| 252 | -- [ ] 添加请求缓存 | ||
| 253 | -- [ ] 优化 Pydantic 验证 | ||
| 254 | -- [ ] 监控性能指标 | ||
| 255 | - | ||
| 256 | ---- | ||
| 257 | - | ||
| 258 | -## 结论 | ||
| 259 | - | ||
| 260 | -本次重构成功实现了以下目标: | ||
| 261 | - | ||
| 262 | -✅ **代码简洁**:移除所有兼容代码,保持单一数据流 | ||
| 263 | -✅ **类型安全**:统一使用 Pydantic 模型,编译时类型检查 | ||
| 264 | -✅ **功能完整**:修复前端 422 错误,支持所有筛选场景 | ||
| 265 | -✅ **可维护性**:代码清晰,易于理解和修改 | ||
| 266 | - | ||
| 267 | -**核心原则**:*不做兼容多种方式的代码,定义一种最佳实践,所有模块都适配这种新方式* | ||
| 268 | - | ||
| 269 | ---- | ||
| 270 | - | ||
| 271 | -**版本**: v3.1 | ||
| 272 | -**状态**: ✅ 完成并通过测试 | ||
| 273 | -**下次更新**: 根据业务需求扩展 | ||
| 274 | - |
CHANGES.md deleted
| @@ -1,611 +0,0 @@ | @@ -1,611 +0,0 @@ | ||
| 1 | -# 变更日志 (CHANGELOG) | ||
| 2 | - | ||
| 3 | ---- | ||
| 4 | - | ||
| 5 | -## v3.0 - API 接口重构 (2024-11-12) | ||
| 6 | - | ||
| 7 | -### 重大更新 | ||
| 8 | - | ||
| 9 | -本版本对搜索 API 进行了全面重构,移除硬编码逻辑,实现了灵活通用的 SaaS 接口设计。 | ||
| 10 | - | ||
| 11 | -#### ❌ 破坏性变更(不向后兼容) | ||
| 12 | - | ||
| 13 | -1. **移除硬编码的 price_ranges 参数** | ||
| 14 | - - 旧方式:`filters: {"price_ranges": ["0-50", "50-100"]}` | ||
| 15 | - - 新方式:`range_filters: {"price": {"gte": 50, "lte": 100}}` | ||
| 16 | - | ||
| 17 | -2. **移除 aggregations 参数** | ||
| 18 | - - 旧方式:`aggregations: {"category_stats": {"terms": {...}}}`(ES DSL) | ||
| 19 | - - 新方式:`facets: [{"field": "categoryName_keyword", "size": 15}]`(简化配置) | ||
| 20 | - | ||
| 21 | -3. **响应格式变更** | ||
| 22 | - - 旧方式:`response.aggregations`(ES 原始格式) | ||
| 23 | - - 新方式:`response.facets`(标准化格式) | ||
| 24 | - | ||
| 25 | -#### ✅ 新增功能 | ||
| 26 | - | ||
| 27 | -1. **结构化过滤参数** | ||
| 28 | - - 新增 `range_filters` 参数:支持任意数值字段的范围过滤 | ||
| 29 | - - 支持 `gte`, `gt`, `lte`, `lt` 操作符 | ||
| 30 | - - 示例: | ||
| 31 | - ```json | ||
| 32 | - { | ||
| 33 | - "range_filters": { | ||
| 34 | - "price": {"gte": 50, "lte": 200}, | ||
| 35 | - "days_since_last_update": {"lte": 30} | ||
| 36 | - } | ||
| 37 | - } | ||
| 38 | - ``` | ||
| 39 | - | ||
| 40 | -2. **简化的分面配置** | ||
| 41 | - - 新增 `facets` 参数:替代复杂的 ES DSL | ||
| 42 | - - 支持简单模式(字符串数组)和高级模式(配置对象) | ||
| 43 | - - 示例: | ||
| 44 | - ```json | ||
| 45 | - { | ||
| 46 | - "facets": [ | ||
| 47 | - "categoryName_keyword", // 简单模式 | ||
| 48 | - {"field": "brandName_keyword", "size": 15} // 高级模式 | ||
| 49 | - ] | ||
| 50 | - } | ||
| 51 | - ``` | ||
| 52 | - | ||
| 53 | -3. **标准化分面响应** | ||
| 54 | - - 统一的分面结果格式 | ||
| 55 | - - 包含 `field`, `label`, `type`, `values` | ||
| 56 | - - `values` 包含 `value`, `label`, `count`, `selected` | ||
| 57 | - - 示例: | ||
| 58 | - ```json | ||
| 59 | - { | ||
| 60 | - "facets": [ | ||
| 61 | - { | ||
| 62 | - "field": "categoryName_keyword", | ||
| 63 | - "label": "商品类目", | ||
| 64 | - "type": "terms", | ||
| 65 | - "values": [ | ||
| 66 | - {"value": "玩具", "label": "玩具", "count": 85, "selected": false} | ||
| 67 | - ] | ||
| 68 | - } | ||
| 69 | - ] | ||
| 70 | - } | ||
| 71 | - ``` | ||
| 72 | - | ||
| 73 | -4. **新增搜索建议端点**(框架) | ||
| 74 | - - `GET /search/suggestions`: 自动补全 | ||
| 75 | - - `GET /search/instant`: 即时搜索 | ||
| 76 | - - 注:暂未实现,仅返回框架响应 | ||
| 77 | - | ||
| 78 | -#### 🔧 代码改进 | ||
| 79 | - | ||
| 80 | -1. **后端模型层** (`api/models.py`) | ||
| 81 | - - 新增 `RangeFilter` 模型 | ||
| 82 | - - 新增 `FacetConfig` 模型 | ||
| 83 | - - 新增 `FacetValue` 和 `FacetResult` 模型 | ||
| 84 | - - 更新 `SearchRequest` 和 `SearchResponse` | ||
| 85 | - | ||
| 86 | -2. **查询构建器** (`search/es_query_builder.py`) | ||
| 87 | - - **完全移除** 硬编码的 `price_ranges` 逻辑(第 205-233 行) | ||
| 88 | - - 重构 `_build_filters` 方法,支持 `range_filters` | ||
| 89 | - - **删除** `add_dynamic_aggregations` 方法 | ||
| 90 | - - 新增 `build_facets` 方法 | ||
| 91 | - | ||
| 92 | -3. **搜索执行层** (`search/searcher.py`) | ||
| 93 | - - 更新 `search()` 方法签名 | ||
| 94 | - - 新增 `_standardize_facets()` 方法 | ||
| 95 | - - 新增 `_get_field_label()` 方法 | ||
| 96 | - - 更新 `SearchResult` 类 | ||
| 97 | - | ||
| 98 | -4. **API 路由层** (`api/routes/search.py`) | ||
| 99 | - - 更新所有搜索端点 | ||
| 100 | - - 新增 `/search/suggestions` 端点 | ||
| 101 | - - 新增 `/search/instant` 端点 | ||
| 102 | - | ||
| 103 | -5. **前端代码** (`frontend/static/js/app.js`) | ||
| 104 | - - 更新状态管理,添加 `rangeFilters` | ||
| 105 | - - 完全重写 `displayAggregations` 为 `displayFacets` | ||
| 106 | - - 更新过滤器处理逻辑 | ||
| 107 | - - 删除所有硬编码的 `price_ranges` | ||
| 108 | - | ||
| 109 | -#### 📚 文档更新 | ||
| 110 | - | ||
| 111 | -- 新增 `API_DOCUMENTATION.md`:完整的 API 接口文档 | ||
| 112 | -- 更新 `README.md`:添加 v3.0 新功能说明 | ||
| 113 | -- 更新 `USER_GUIDE.md`:更新 API 使用示例 | ||
| 114 | - | ||
| 115 | -#### 🎯 改进要点 | ||
| 116 | - | ||
| 117 | -**从特定实现到通用 SaaS**: | ||
| 118 | -- ❌ 移除:硬编码的价格范围值 | ||
| 119 | -- ❌ 移除:暴露 ES DSL 的聚合接口 | ||
| 120 | -- ❌ 移除:不统一的响应格式 | ||
| 121 | -- ✅ 新增:通用的范围过滤器 | ||
| 122 | -- ✅ 新增:简化的分面配置 | ||
| 123 | -- ✅ 新增:标准化的响应格式 | ||
| 124 | - | ||
| 125 | -#### 📋 迁移指南 | ||
| 126 | - | ||
| 127 | -**旧接口** → **新接口** | ||
| 128 | - | ||
| 129 | -1. **过滤器迁移**: | ||
| 130 | - ```json | ||
| 131 | - // 旧 | ||
| 132 | - {"filters": {"price_ranges": ["50-100"]}} | ||
| 133 | - | ||
| 134 | - // 新 | ||
| 135 | - {"range_filters": {"price": {"gte": 50, "lte": 100}}} | ||
| 136 | - ``` | ||
| 137 | - | ||
| 138 | -2. **聚合迁移**: | ||
| 139 | - ```json | ||
| 140 | - // 旧 | ||
| 141 | - { | ||
| 142 | - "aggregations": { | ||
| 143 | - "category_stats": { | ||
| 144 | - "terms": {"field": "categoryName_keyword", "size": 15} | ||
| 145 | - } | ||
| 146 | - } | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | - // 新 | ||
| 150 | - { | ||
| 151 | - "facets": [ | ||
| 152 | - {"field": "categoryName_keyword", "size": 15} | ||
| 153 | - ] | ||
| 154 | - } | ||
| 155 | - ``` | ||
| 156 | - | ||
| 157 | -3. **响应解析迁移**: | ||
| 158 | - ```javascript | ||
| 159 | - // 旧 | ||
| 160 | - data.aggregations.category_stats.buckets.forEach(bucket => { | ||
| 161 | - console.log(bucket.key, bucket.doc_count); | ||
| 162 | - }); | ||
| 163 | - | ||
| 164 | - // 新 | ||
| 165 | - data.facets.forEach(facet => { | ||
| 166 | - facet.values.forEach(value => { | ||
| 167 | - console.log(value.value, value.count); | ||
| 168 | - }); | ||
| 169 | - }); | ||
| 170 | - ``` | ||
| 171 | - | ||
| 172 | ---- | ||
| 173 | - | ||
| 174 | -## v2.x - 前端优化更新 (2025-11-11) | ||
| 175 | - | ||
| 176 | -## 概述 | ||
| 177 | -基于提供的电商搜索引擎参考图片,对前端界面进行了全面重新设计和优化,采用更现代、简洁的布局风格。 | ||
| 178 | - | ||
| 179 | ---- | ||
| 180 | - | ||
| 181 | -## 修改的文件 | ||
| 182 | - | ||
| 183 | -### 1. `/home/tw/SearchEngine/frontend/index.html` ✅ 完全重写 | ||
| 184 | -**更改内容:** | ||
| 185 | -- 去除旧的搜索示例和复杂布局 | ||
| 186 | -- 添加简洁的顶部标题栏(Product + 商品数量 + Fold按钮) | ||
| 187 | -- 重新设计搜索栏(更简洁) | ||
| 188 | -- 添加水平筛选标签区域(Categories, Brand, Supplier) | ||
| 189 | -- 添加排序工具栏(带上下箭头的排序按钮) | ||
| 190 | -- 改用网格布局展示商品 | ||
| 191 | -- 添加分页组件 | ||
| 192 | -- 将查询信息改为可折叠的Debug区域 | ||
| 193 | - | ||
| 194 | -**关键改进:** | ||
| 195 | -```html | ||
| 196 | -<!-- 新增顶部标题栏 --> | ||
| 197 | -<header class="top-header"> | ||
| 198 | - <div class="header-left"> | ||
| 199 | - <span class="logo">Product</span> | ||
| 200 | - <span class="product-count">0 products found</span> | ||
| 201 | - </div> | ||
| 202 | - <div class="header-right"> | ||
| 203 | - <button class="fold-btn">Fold</button> | ||
| 204 | - </div> | ||
| 205 | -</header> | ||
| 206 | - | ||
| 207 | -<!-- 新增水平筛选标签 --> | ||
| 208 | -<div class="filter-section"> | ||
| 209 | - <div class="filter-row"> | ||
| 210 | - <div class="filter-label">Categories:</div> | ||
| 211 | - <div class="filter-tags" id="categoryTags"></div> | ||
| 212 | - </div> | ||
| 213 | - <!-- 品牌、供应商等 --> | ||
| 214 | -</div> | ||
| 215 | - | ||
| 216 | -<!-- 新增排序栏(带箭头) --> | ||
| 217 | -<div class="sort-section"> | ||
| 218 | - <button class="sort-btn"> | ||
| 219 | - By Price | ||
| 220 | - <span class="sort-arrows"> | ||
| 221 | - <span class="arrow-up">▲</span> | ||
| 222 | - <span class="arrow-down">▼</span> | ||
| 223 | - </span> | ||
| 224 | - </button> | ||
| 225 | -</div> | ||
| 226 | - | ||
| 227 | -<!-- 商品网格 --> | ||
| 228 | -<div class="product-grid"></div> | ||
| 229 | - | ||
| 230 | -<!-- 分页 --> | ||
| 231 | -<div class="pagination"></div> | ||
| 232 | -``` | ||
| 233 | - | ||
| 234 | ---- | ||
| 235 | - | ||
| 236 | -### 2. `/home/tw/SearchEngine/frontend/static/css/style.css` ✅ 完全重写 | ||
| 237 | -**更改内容:** | ||
| 238 | -- 去除紫色渐变背景,改为白色简洁背景 | ||
| 239 | -- 重新设计所有组件样式 | ||
| 240 | -- 添加顶部标题栏样式 | ||
| 241 | -- 添加水平筛选标签样式(带hover和active状态) | ||
| 242 | -- 添加排序按钮样式(带箭头) | ||
| 243 | -- 重新设计商品卡片样式(网格布局) | ||
| 244 | -- 添加分页样式 | ||
| 245 | -- 优化响应式设计 | ||
| 246 | - | ||
| 247 | -**关键样式:** | ||
| 248 | -```css | ||
| 249 | -/* 白色背景 */ | ||
| 250 | -body { | ||
| 251 | - background: #f5f5f5; | ||
| 252 | -} | ||
| 253 | - | ||
| 254 | -/* 筛选标签 */ | ||
| 255 | -.filter-tag { | ||
| 256 | - padding: 6px 15px; | ||
| 257 | - background: #f8f8f8; | ||
| 258 | - border: 1px solid #ddd; | ||
| 259 | - cursor: pointer; | ||
| 260 | -} | ||
| 261 | - | ||
| 262 | -.filter-tag.active { | ||
| 263 | - background: #e74c3c; | ||
| 264 | - color: white; | ||
| 265 | -} | ||
| 266 | - | ||
| 267 | -/* 排序箭头 */ | ||
| 268 | -.sort-arrows { | ||
| 269 | - display: inline-flex; | ||
| 270 | - flex-direction: column; | ||
| 271 | - font-size: 10px; | ||
| 272 | -} | ||
| 273 | - | ||
| 274 | -/* 商品网格 */ | ||
| 275 | -.product-grid { | ||
| 276 | - display: grid; | ||
| 277 | - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); | ||
| 278 | - gap: 20px; | ||
| 279 | -} | ||
| 280 | - | ||
| 281 | -/* 商品卡片 */ | ||
| 282 | -.product-card { | ||
| 283 | - background: white; | ||
| 284 | - border: 1px solid #e0e0e0; | ||
| 285 | - border-radius: 8px; | ||
| 286 | - transition: all 0.3s; | ||
| 287 | -} | ||
| 288 | - | ||
| 289 | -.product-card:hover { | ||
| 290 | - box-shadow: 0 4px 12px rgba(0,0,0,0.1); | ||
| 291 | - transform: translateY(-2px); | ||
| 292 | -} | ||
| 293 | -``` | ||
| 294 | - | ||
| 295 | -**代码量对比:** | ||
| 296 | -- 旧版:433行 | ||
| 297 | -- 新版:450行 | ||
| 298 | -- 变化:+17行(增加了更多功能和响应式样式) | ||
| 299 | - | ||
| 300 | ---- | ||
| 301 | - | ||
| 302 | -### 3. `/home/tw/SearchEngine/frontend/static/js/app.js` ✅ 完全重构 | ||
| 303 | -**更改内容:** | ||
| 304 | -- 添加状态管理对象(统一管理所有状态) | ||
| 305 | -- 重写搜索函数(支持分页) | ||
| 306 | -- 重写结果展示函数(商品网格布局) | ||
| 307 | -- 重写筛选聚合函数(水平标签展示) | ||
| 308 | -- 添加排序函数(支持字段+方向) | ||
| 309 | -- 添加分页函数(完整分页导航) | ||
| 310 | -- 优化代码结构(更模块化) | ||
| 311 | - | ||
| 312 | -**关键功能:** | ||
| 313 | -```javascript | ||
| 314 | -// 状态管理 | ||
| 315 | -let state = { | ||
| 316 | - query: '', | ||
| 317 | - currentPage: 1, | ||
| 318 | - pageSize: 20, | ||
| 319 | - totalResults: 0, | ||
| 320 | - filters: {}, | ||
| 321 | - sortBy: '', | ||
| 322 | - sortOrder: 'desc', | ||
| 323 | - aggregations: null | ||
| 324 | -}; | ||
| 325 | - | ||
| 326 | -// 排序函数(支持上下箭头) | ||
| 327 | -function sortByField(field, order) { | ||
| 328 | - state.sortBy = field; | ||
| 329 | - state.sortOrder = order; | ||
| 330 | - performSearch(state.currentPage); | ||
| 331 | -} | ||
| 332 | - | ||
| 333 | -// 分页函数 | ||
| 334 | -function goToPage(page) { | ||
| 335 | - performSearch(page); | ||
| 336 | - window.scrollTo({ top: 0, behavior: 'smooth' }); | ||
| 337 | -} | ||
| 338 | - | ||
| 339 | -// 商品网格展示 | ||
| 340 | -function displayResults(data) { | ||
| 341 | - // 生成商品卡片HTML | ||
| 342 | - data.hits.forEach((hit) => { | ||
| 343 | - html += ` | ||
| 344 | - <div class="product-card"> | ||
| 345 | - <div class="product-image-wrapper">...</div> | ||
| 346 | - <div class="product-price">...</div> | ||
| 347 | - <div class="product-moq">...</div> | ||
| 348 | - <div class="product-title">...</div> | ||
| 349 | - </div> | ||
| 350 | - `; | ||
| 351 | - }); | ||
| 352 | -} | ||
| 353 | - | ||
| 354 | -// 水平筛选标签 | ||
| 355 | -function displayAggregations(aggregations) { | ||
| 356 | - // 显示为可点击的标签 | ||
| 357 | - html += ` | ||
| 358 | - <span class="filter-tag ${isActive ? 'active' : ''}" | ||
| 359 | - onclick="toggleFilter(...)"> | ||
| 360 | - ${key} (${count}) | ||
| 361 | - </span> | ||
| 362 | - `; | ||
| 363 | -} | ||
| 364 | -``` | ||
| 365 | - | ||
| 366 | -**代码量对比:** | ||
| 367 | -- 旧版:516行 | ||
| 368 | -- 新版:465行 | ||
| 369 | -- 变化:-51行(代码更简洁,功能更强) | ||
| 370 | - | ||
| 371 | ---- | ||
| 372 | - | ||
| 373 | -### 4. `/home/tw/SearchEngine/api/app.py` ✅ 添加静态文件服务 | ||
| 374 | -**更改内容:** | ||
| 375 | -- 导入 `FileResponse` 和 `StaticFiles` | ||
| 376 | -- 添加前端HTML服务路由 | ||
| 377 | -- 挂载静态文件目录(CSS, JS) | ||
| 378 | -- 将原有的 `/` 路由改为 `/api` | ||
| 379 | - | ||
| 380 | -**关键代码:** | ||
| 381 | -```python | ||
| 382 | -from fastapi.responses import FileResponse | ||
| 383 | -from fastapi.staticfiles import StaticFiles | ||
| 384 | - | ||
| 385 | -# 在文件末尾添加 | ||
| 386 | -frontend_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "frontend") | ||
| 387 | -if os.path.exists(frontend_path): | ||
| 388 | - # 服务前端HTML | ||
| 389 | - @app.get("/") | ||
| 390 | - async def serve_frontend(): | ||
| 391 | - index_path = os.path.join(frontend_path, "index.html") | ||
| 392 | - if os.path.exists(index_path): | ||
| 393 | - return FileResponse(index_path) | ||
| 394 | - | ||
| 395 | - # 挂载静态文件 | ||
| 396 | - app.mount("/static", StaticFiles(directory=os.path.join(frontend_path, "static")), name="static") | ||
| 397 | -``` | ||
| 398 | - | ||
| 399 | ---- | ||
| 400 | - | ||
| 401 | -## 新增的文件 | ||
| 402 | - | ||
| 403 | -### 5. `/home/tw/SearchEngine/frontend/README.md` ✅ 新建 | ||
| 404 | -前端详细文档,包含: | ||
| 405 | -- 优化说明 | ||
| 406 | -- 功能介绍 | ||
| 407 | -- 使用方法 | ||
| 408 | -- 技术特点 | ||
| 409 | -- 浏览器兼容性 | ||
| 410 | -- 未来改进计划 | ||
| 411 | - | ||
| 412 | -### 6. `/home/tw/SearchEngine/FRONTEND_GUIDE.md` ✅ 新建 | ||
| 413 | -快速上手指南,包含: | ||
| 414 | -- 优化总结 | ||
| 415 | -- 启动方法 | ||
| 416 | -- 测试步骤 | ||
| 417 | -- 常见问题 | ||
| 418 | -- API接口说明 | ||
| 419 | -- 性能指标 | ||
| 420 | - | ||
| 421 | -### 7. `/home/tw/SearchEngine/scripts/test_frontend.sh` ✅ 新建 | ||
| 422 | -自动化测试脚本,测试: | ||
| 423 | -- 健康检查 | ||
| 424 | -- 前端HTML | ||
| 425 | -- CSS文件 | ||
| 426 | -- JavaScript文件 | ||
| 427 | -- 搜索API | ||
| 428 | - | ||
| 429 | -### 8. `/home/tw/SearchEngine/CHANGES.md` ✅ 新建 | ||
| 430 | -本文件,记录所有更改。 | ||
| 431 | - | ||
| 432 | ---- | ||
| 433 | - | ||
| 434 | -## 功能对比表 | ||
| 435 | - | ||
| 436 | -| 功能 | 旧版前端 | 新版前端 | 状态 | | ||
| 437 | -|------|---------|---------|------| | ||
| 438 | -| 背景颜色 | 紫色渐变 | 白色简洁 | ✅ 优化 | | ||
| 439 | -| 顶部标题栏 | 大标题+副标题 | Product + 商品数 | ✅ 优化 | | ||
| 440 | -| 搜索框 | 带多个选项 | 简洁搜索框 | ✅ 优化 | | ||
| 441 | -| 筛选方式 | 左侧垂直面板 | 顶部水平标签 | ✅ 优化 | | ||
| 442 | -| 筛选交互 | 复选框 | 可点击标签 | ✅ 优化 | | ||
| 443 | -| 排序方式 | 下拉选择 | 按钮+箭头 | ✅ 优化 | | ||
| 444 | -| 商品展示 | 列表布局 | 网格布局 | ✅ 优化 | | ||
| 445 | -| 商品卡片 | 横向卡片 | 垂直卡片 | ✅ 优化 | | ||
| 446 | -| 分页功能 | ❌ 无 | ✅ 完整分页 | ✅ 新增 | | ||
| 447 | -| 响应式设计 | 基础支持 | 完整响应式 | ✅ 优化 | | ||
| 448 | -| 代码结构 | 混乱 | 模块化 | ✅ 优化 | | ||
| 449 | -| 状态管理 | 分散 | 统一管理 | ✅ 优化 | | ||
| 450 | - | ||
| 451 | ---- | ||
| 452 | - | ||
| 453 | -## 技术改进 | ||
| 454 | - | ||
| 455 | -### 前端架构 | ||
| 456 | -- ✅ **状态管理**:统一的state对象 | ||
| 457 | -- ✅ **模块化**:功能清晰分离 | ||
| 458 | -- ✅ **代码简化**:去除冗余代码 | ||
| 459 | -- ✅ **性能优化**:减少DOM操作 | ||
| 460 | - | ||
| 461 | -### UI/UX设计 | ||
| 462 | -- ✅ **视觉一致性**:统一的设计语言 | ||
| 463 | -- ✅ **交互直观**:标签式筛选,箭头排序 | ||
| 464 | -- ✅ **响应迅速**:即时反馈 | ||
| 465 | -- ✅ **移动友好**:完整的响应式支持 | ||
| 466 | - | ||
| 467 | -### 代码质量 | ||
| 468 | -- ✅ **可维护性**:清晰的结构 | ||
| 469 | -- ✅ **可扩展性**:易于添加新功能 | ||
| 470 | -- ✅ **可读性**:注释完整 | ||
| 471 | -- ✅ **无linter错误**:代码规范 | ||
| 472 | - | ||
| 473 | ---- | ||
| 474 | - | ||
| 475 | -## 测试步骤 | ||
| 476 | - | ||
| 477 | -### 1. 启动服务 | ||
| 478 | -```bash | ||
| 479 | -cd /home/tw/SearchEngine | ||
| 480 | -bash scripts/start_backend.sh | ||
| 481 | -``` | ||
| 482 | - | ||
| 483 | -### 2. 运行测试 | ||
| 484 | -```bash | ||
| 485 | -bash scripts/test_frontend.sh | ||
| 486 | -``` | ||
| 487 | - | ||
| 488 | -### 3. 手动测试 | ||
| 489 | -访问:`http://120.76.41.98:6002/` | ||
| 490 | - | ||
| 491 | -测试项目: | ||
| 492 | -- [ ] 页面正常加载 | ||
| 493 | -- [ ] 搜索功能正常 | ||
| 494 | -- [ ] 筛选标签可点击 | ||
| 495 | -- [ ] 排序箭头可用 | ||
| 496 | -- [ ] 商品网格展示正常 | ||
| 497 | -- [ ] 分页功能正常 | ||
| 498 | -- [ ] 响应式布局正常 | ||
| 499 | - | ||
| 500 | ---- | ||
| 501 | - | ||
| 502 | -## 兼容性 | ||
| 503 | - | ||
| 504 | -### 浏览器 | ||
| 505 | -- ✅ Chrome 90+ | ||
| 506 | -- ✅ Firefox 88+ | ||
| 507 | -- ✅ Safari 14+ | ||
| 508 | -- ✅ Edge 90+ | ||
| 509 | -- ✅ 移动浏览器 | ||
| 510 | - | ||
| 511 | -### 屏幕尺寸 | ||
| 512 | -- ✅ 桌面(1920x1080) | ||
| 513 | -- ✅ 笔记本(1366x768) | ||
| 514 | -- ✅ 平板(768x1024) | ||
| 515 | -- ✅ 手机(375x667) | ||
| 516 | - | ||
| 517 | ---- | ||
| 518 | - | ||
| 519 | -## 性能指标 | ||
| 520 | - | ||
| 521 | -| 指标 | 旧版 | 新版 | 改进 | | ||
| 522 | -|------|------|------|------| | ||
| 523 | -| 首屏加载 | ~1.5s | ~0.8s | ⬇️ 47% | | ||
| 524 | -| JavaScript大小 | 15KB | 13KB | ⬇️ 13% | | ||
| 525 | -| CSS大小 | 12KB | 11KB | ⬇️ 8% | | ||
| 526 | -| DOM节点数 | ~350 | ~200 | ⬇️ 43% | | ||
| 527 | -| 重绘次数 | 高 | 低 | ⬆️ 优化 | | ||
| 528 | - | ||
| 529 | ---- | ||
| 530 | - | ||
| 531 | -## 最佳实践应用 | ||
| 532 | - | ||
| 533 | -### HTML | ||
| 534 | -- ✅ 语义化标签 | ||
| 535 | -- ✅ 无障碍支持(ARIA) | ||
| 536 | -- ✅ SEO友好 | ||
| 537 | - | ||
| 538 | -### CSS | ||
| 539 | -- ✅ CSS Grid布局 | ||
| 540 | -- ✅ Flexbox布局 | ||
| 541 | -- ✅ CSS变量 | ||
| 542 | -- ✅ 媒体查询(响应式) | ||
| 543 | - | ||
| 544 | -### JavaScript | ||
| 545 | -- ✅ ES6+语法 | ||
| 546 | -- ✅ 事件委托 | ||
| 547 | -- ✅ 防抖/节流(如需要) | ||
| 548 | -- ✅ 错误处理 | ||
| 549 | - | ||
| 550 | ---- | ||
| 551 | - | ||
| 552 | -## 下一步优化建议 | ||
| 553 | - | ||
| 554 | -### 短期(1-2周) | ||
| 555 | -- [ ] 添加加载骨架屏 | ||
| 556 | -- [ ] 优化图片懒加载 | ||
| 557 | -- [ ] 添加搜索建议(自动完成) | ||
| 558 | - | ||
| 559 | -### 中期(1个月) | ||
| 560 | -- [ ] 添加用户偏好设置 | ||
| 561 | -- [ ] 支持多主题切换 | ||
| 562 | -- [ ] 添加商品收藏功能 | ||
| 563 | - | ||
| 564 | -### 长期(3个月) | ||
| 565 | -- [ ] PWA支持(离线访问) | ||
| 566 | -- [ ] 国际化(多语言) | ||
| 567 | -- [ ] 性能监控 | ||
| 568 | - | ||
| 569 | ---- | ||
| 570 | - | ||
| 571 | -## 回滚方案 | ||
| 572 | - | ||
| 573 | -如需回滚到旧版: | ||
| 574 | - | ||
| 575 | -```bash | ||
| 576 | -cd /home/tw/SearchEngine | ||
| 577 | -git checkout HEAD~1 frontend/ | ||
| 578 | -# 或从备份恢复 | ||
| 579 | -``` | ||
| 580 | - | ||
| 581 | ---- | ||
| 582 | - | ||
| 583 | -## 总结 | ||
| 584 | - | ||
| 585 | -### 完成情况 | ||
| 586 | -- ✅ HTML重构:100% | ||
| 587 | -- ✅ CSS重写:100% | ||
| 588 | -- ✅ JavaScript重构:100% | ||
| 589 | -- ✅ 后端适配:100% | ||
| 590 | -- ✅ 文档编写:100% | ||
| 591 | -- ✅ 测试脚本:100% | ||
| 592 | - | ||
| 593 | -### 核心成果 | ||
| 594 | -1. **更好的用户体验**:简洁、直观的界面 | ||
| 595 | -2. **更强的功能**:完整的筛选、排序、分页 | ||
| 596 | -3. **更好的代码**:模块化、可维护 | ||
| 597 | -4. **更好的性能**:更快的加载和响应 | ||
| 598 | - | ||
| 599 | -### 达成目标 | ||
| 600 | -✅ 完全符合参考图片的布局风格 | ||
| 601 | -✅ 实现了所有要求的功能 | ||
| 602 | -✅ 遵循了最佳实践 | ||
| 603 | -✅ 代码质量高,易于维护 | ||
| 604 | -✅ 响应式设计,支持多端 | ||
| 605 | - | ||
| 606 | ---- | ||
| 607 | - | ||
| 608 | -**优化完成时间**:2025-11-11 | ||
| 609 | -**总耗时**:约2小时 | ||
| 610 | -**状态**:✅ 生产就绪 | ||
| 611 | - |
README.md
| @@ -15,12 +15,12 @@ | @@ -15,12 +15,12 @@ | ||
| 15 | 15 | ||
| 16 | | 步骤 | 去哪里看 | 摘要 | | 16 | | 步骤 | 去哪里看 | 摘要 | |
| 17 | |------|---------|------| | 17 | |------|---------|------| |
| 18 | -| 1. 准备环境 | `环境相关.md` / `USAGE_GUIDE.md` | Conda/依赖、Elasticsearch、MySQL、必需的变量 | | ||
| 19 | -| 2. 构造测试数据 | `TEST_DATA_GUIDE.md` | Tenant1 Mock、Tenant2 CSV、`mock_data.sh` / `ingest.sh` | | ||
| 20 | -| 3. 启动与验证 | `USAGE_GUIDE.md` | `run.sh` 一键启动、分步脚本、日志与健康检查 | | ||
| 21 | -| 4. 理解架构 | `设计文档.md` | 数据流、配置系统、查询/搜索/索引模块 | | ||
| 22 | -| 5. 接入搜索 API | `API_DOCUMENTATION.md` / `API_INTEGRATION_GUIDE.md` | REST 端点、参数、响应、最佳实践 | | ||
| 23 | -| 6. 查字段定义 | `INDEX_FIELDS_DOCUMENTATION.md` | `search_products` 映射、字段来源、类型与用途 | | 18 | +| 1. 准备环境 | `环境配置说明.md` / `Usage-Guide.md` | Conda/依赖、Elasticsearch、MySQL、必需变量 | |
| 19 | +| 2. 构造测试数据 | `测试数据指南.md` | Tenant1 Mock、Tenant2 CSV、`mock_data.sh` / `ingest.sh` | | ||
| 20 | +| 3. 启动与验证 | `Usage-Guide.md` | `run.sh` 一键启动、分步脚本、日志与健康检查 | | ||
| 21 | +| 4. 理解架构 | `系统设计文档.md` | 数据流、配置系统、查询/搜索/索引模块 | | ||
| 22 | +| 5. 接入搜索 API | `搜索API对接指南.md` / `搜索API速查表.md` | REST 端点、参数、响应、最佳实践 | | ||
| 23 | +| 6. 查字段定义 | `索引字段说明.md` | `search_products` 映射、字段来源、类型与用途 | | ||
| 24 | 24 | ||
| 25 | > README 仅保留最常用命令的“索引”。细节以主题文档为准。 | 25 | > README 仅保留最常用命令的“索引”。细节以主题文档为准。 |
| 26 | 26 | ||
| @@ -52,19 +52,21 @@ curl -X POST http://localhost:6002/search/ \ | @@ -52,19 +52,21 @@ curl -X POST http://localhost:6002/search/ \ | ||
| 52 | 52 | ||
| 53 | | 文档 | 内容提要 | 适用场景 | | 53 | | 文档 | 内容提要 | 适用场景 | |
| 54 | |------|----------|----------| | 54 | |------|----------|----------| |
| 55 | -| `环境相关.md` | 系统要求、Conda/依赖、外部服务账号、常用端口 | 首次部署、环境核对 | | ||
| 56 | -| `USAGE_GUIDE.md` | 环境准备、服务启动、配置、日志、验证手册 | 日常运维、调试 | | ||
| 57 | -| `TEST_DATA_GUIDE.md` | 两个租户的模拟/CSV数据构造 & MySQL→ES流程 | 数据准备、联调 | | ||
| 58 | -| `设计文档.md` | 架构、配置系统、索引/查询/排序模块细节 | 研发/扩展功能 | | ||
| 59 | -| `INDEX_FIELDS_DOCUMENTATION.md` | `search_products` 字段、类型、来源、嵌套结构 | 新增字段、数据对齐 | | ||
| 60 | -| `API_DOCUMENTATION.md` | REST API(搜索/图片/管理)详解、示例、响应格式 | API 使用、测试 | | ||
| 61 | -| `API_INTEGRATION_GUIDE.md` | 客户对接指引、最佳实践、错误处理、语言说明 | 第三方集成、SDK 开发 | | ||
| 62 | -| `API_QUICK_REFERENCE.md` | 常用请求体速查表 | 支持团队快速查阅 | | ||
| 63 | -| `环境相关.md` + `.env` 模板 | 运行依赖账号、端口、密钥对照表 | 交付 & 运维 | | 55 | +| `环境配置说明.md` | 系统要求、Conda/依赖、外部服务账号、常用端口 | 首次部署、环境核对 | |
| 56 | +| `Usage-Guide.md` | 环境准备、服务启动、配置、日志、验证手册 | 日常运维、调试 | | ||
| 57 | +| `基础配置指南.md` | 租户字段、索引域、排序表达式配置流程 | 新租户开通、配置变更 | | ||
| 58 | +| `测试数据指南.md` | 两个租户的模拟/CSV 数据构造 & MySQL→ES 流程 | 数据准备、联调 | | ||
| 59 | +| `测试Pipeline说明.md` | 测试流水线、CI 脚本、上下文说明 | 自动化测试、追踪流水线 | | ||
| 60 | +| `系统设计文档.md` | 架构、配置系统、索引/查询/排序模块细节 | 研发/扩展功能 | | ||
| 61 | +| `索引字段说明.md` | `search_products` 字段、类型、来源、嵌套结构 | 新增字段、数据对齐 | | ||
| 62 | +| `搜索API对接指南.md` | REST API(文本/图片/管理)详解、示例、响应格式 | API 使用、测试 | | ||
| 63 | +| `搜索API速查表.md` | 常用请求体、过滤器、分面速查表 | 支持团队快速查阅 | | ||
| 64 | +| `Search-API-Examples.md` | Python/JS/cURL 端到端示例 | 客户工程、SDK 参考 | | ||
| 65 | +| `环境配置说明.md` + `.env` 模板 | 运行依赖账号、端口、密钥对照表 | 交付 & 运维 | | ||
| 64 | 66 | ||
| 65 | 更多补充材料: | 67 | 更多补充材料: |
| 66 | 68 | ||
| 67 | -- `TEST_DATA_GUIDE.md`:包含完整工作流脚本示例 | 69 | +- `测试数据指南.md`:包含完整工作流脚本示例 |
| 68 | - `商品数据源入ES配置规范.md`:数据源映射约定 | 70 | - `商品数据源入ES配置规范.md`:数据源映射约定 |
| 69 | - `MULTILANG_FEATURE.md`:多语言处理细节 | 71 | - `MULTILANG_FEATURE.md`:多语言处理细节 |
| 70 | 72 | ||
| @@ -73,12 +75,12 @@ curl -X POST http://localhost:6002/search/ \ | @@ -73,12 +75,12 @@ curl -X POST http://localhost:6002/search/ \ | ||
| 73 | - **数据构建 → MySQL → Elasticsearch** | 75 | - **数据构建 → MySQL → Elasticsearch** |
| 74 | - `scripts/mock_data.sh`:Tenant1 Mock + Tenant2 CSV 一条龙 | 76 | - `scripts/mock_data.sh`:Tenant1 Mock + Tenant2 CSV 一条龙 |
| 75 | - `scripts/ingest.sh <tenant_id> [recreate]`:驱动 `indexer/` 模块写入 `search_products` | 77 | - `scripts/ingest.sh <tenant_id> [recreate]`:驱动 `indexer/` 模块写入 `search_products` |
| 76 | - - 详解:`TEST_DATA_GUIDE.md` | 78 | + - 详解:`测试数据指南.md` |
| 77 | 79 | ||
| 78 | - **搜索服务 & API** | 80 | - **搜索服务 & API** |
| 79 | - `api/`(FastAPI)承载 REST API,`search/` + `query/` 负责查询解析与下发 | 81 | - `api/`(FastAPI)承载 REST API,`search/` + `query/` 负责查询解析与下发 |
| 80 | - - API、分页、过滤、Facet、KNN 等:`API_DOCUMENTATION.md` | ||
| 81 | - - 对接案例与错误码:`API_INTEGRATION_GUIDE.md` | 82 | + - API、分页、过滤、Facet、KNN 等:`搜索API对接指南.md` |
| 83 | + - 对接案例、示例与错误码:`搜索API对接指南.md`、`Search-API-Examples.md` | ||
| 82 | 84 | ||
| 83 | - **配置驱动能力** | 85 | - **配置驱动能力** |
| 84 | - `config/schema/{tenant_id}/config.yaml`:字段定义、索引域、排序表达式、SPU 聚合 | 86 | - `config/schema/{tenant_id}/config.yaml`:字段定义、索引域、排序表达式、SPU 聚合 |
| @@ -96,16 +98,3 @@ scripts/ 数据/服务脚本(mock_data, ingest, run 等) | @@ -96,16 +98,3 @@ scripts/ 数据/服务脚本(mock_data, ingest, run 等) | ||
| 96 | frontend/ 简易调试页面 | 98 | frontend/ 简易调试页面 |
| 97 | docs/ 运营及中文资料 | 99 | docs/ 运营及中文资料 |
| 98 | ``` | 100 | ``` |
| 99 | - | ||
| 100 | -## 常用参考 | ||
| 101 | - | ||
| 102 | -- **运行/排障**:`USAGE_GUIDE.md`、`环境相关.md` | ||
| 103 | -- **功能设计**:`设计文档.md` | ||
| 104 | -- **字段/数据对齐**:`INDEX_FIELDS_DOCUMENTATION.md` | ||
| 105 | -- **API 对接**:`API_DOCUMENTATION.md`、`API_INTEGRATION_GUIDE.md` | ||
| 106 | -- **测试数据**:`TEST_DATA_GUIDE.md` | ||
| 107 | - | ||
| 108 | -## 许可证 | ||
| 109 | - | ||
| 110 | -专有软件 - 保留所有权利 | ||
| 111 | - |
docs/RequestContext_README.md deleted
| @@ -1,374 +0,0 @@ | @@ -1,374 +0,0 @@ | ||
| 1 | -# RequestContext 使用指南 | ||
| 2 | - | ||
| 3 | -## 概述 | ||
| 4 | - | ||
| 5 | -`RequestContext` 是一个请求粒度的上下文管理器,用于跟踪和管理搜索请求的整个生命周期。它提供了统一的数据存储、性能监控和日志记录功能。 | ||
| 6 | - | ||
| 7 | -## 核心功能 | ||
| 8 | - | ||
| 9 | -### 1. 查询分析结果存储 | ||
| 10 | -- 原始查询、规范化查询、重写查询 | ||
| 11 | -- 检测语言和翻译结果 | ||
| 12 | -- 查询向量(embedding) | ||
| 13 | -- 布尔查询AST | ||
| 14 | - | ||
| 15 | -### 2. 各检索阶段中间结果 | ||
| 16 | -- 解析后的查询对象 | ||
| 17 | -- 布尔查询语法树 | ||
| 18 | -- ES查询DSL | ||
| 19 | -- ES响应数据 | ||
| 20 | -- 处理后的搜索结果 | ||
| 21 | - | ||
| 22 | -### 3. 性能监控 | ||
| 23 | -- 自动计时各阶段耗时 | ||
| 24 | -- 计算各阶段耗时占比 | ||
| 25 | -- 识别性能瓶颈 | ||
| 26 | -- 详细的性能摘要日志 | ||
| 27 | - | ||
| 28 | -### 4. 错误处理和警告 | ||
| 29 | -- 统一的错误信息存储 | ||
| 30 | -- 警告信息收集 | ||
| 31 | -- 完整的上下文错误跟踪 | ||
| 32 | - | ||
| 33 | -## 支持的搜索阶段 | ||
| 34 | - | ||
| 35 | -```python | ||
| 36 | -class RequestContextStage(Enum): | ||
| 37 | - TOTAL = "total_search" # 总搜索时间 | ||
| 38 | - QUERY_PARSING = "query_parsing" # 查询解析 | ||
| 39 | - BOOLEAN_PARSING = "boolean_parsing" # 布尔查询解析 | ||
| 40 | - QUERY_BUILDING = "query_building" # ES查询构建 | ||
| 41 | - ELASTICSEARCH_SEARCH = "elasticsearch_search" # ES搜索 | ||
| 42 | - RESULT_PROCESSING = "result_processing" # 结果处理 | ||
| 43 | - RERANKING = "reranking" # 重排序 | ||
| 44 | -``` | ||
| 45 | - | ||
| 46 | -## 基本使用方法 | ||
| 47 | - | ||
| 48 | -### 1. 创建RequestContext | ||
| 49 | - | ||
| 50 | -```python | ||
| 51 | -from context import create_request_context, RequestContext | ||
| 52 | - | ||
| 53 | -# 方式1: 使用工厂函数 | ||
| 54 | -context = create_request_context(reqid="req-001", uid="user-123") | ||
| 55 | - | ||
| 56 | -# 方式2: 直接创建 | ||
| 57 | -context = RequestContext(reqid="req-001", uid="user-123") | ||
| 58 | - | ||
| 59 | -# 方式3: 作为上下文管理器使用 | ||
| 60 | -with create_request_context("req-002", "user-456") as context: | ||
| 61 | - # 搜索逻辑 | ||
| 62 | - pass # 自动记录性能摘要 | ||
| 63 | -``` | ||
| 64 | - | ||
| 65 | -### 2. 阶段计时 | ||
| 66 | - | ||
| 67 | -```python | ||
| 68 | -from context import RequestContextStage | ||
| 69 | - | ||
| 70 | -# 开始计时 | ||
| 71 | -context.start_stage(RequestContextStage.QUERY_PARSING) | ||
| 72 | - | ||
| 73 | -# 执行查询解析逻辑 | ||
| 74 | -# parsed_query = query_parser.parse(query, context=context) | ||
| 75 | - | ||
| 76 | -# 结束计时 | ||
| 77 | -duration = context.end_stage(RequestContextStage.QUERY_PARSING) | ||
| 78 | -print(f"查询解析耗时: {duration:.2f}ms") | ||
| 79 | -``` | ||
| 80 | - | ||
| 81 | -### 3. 存储查询分析结果 | ||
| 82 | - | ||
| 83 | -```python | ||
| 84 | -context.store_query_analysis( | ||
| 85 | - original_query="红色连衣裙", | ||
| 86 | - normalized_query="红色 连衣裙", | ||
| 87 | - rewritten_query="红色 女 连衣裙", | ||
| 88 | - detected_language="zh", | ||
| 89 | - translations={"en": "red dress"}, | ||
| 90 | - query_vector=[0.1, 0.2, 0.3, ...], # 如果有向量 | ||
| 91 | - is_simple_query=True | ||
| 92 | -) | ||
| 93 | -``` | ||
| 94 | - | ||
| 95 | -### 4. 存储中间结果 | ||
| 96 | - | ||
| 97 | -```python | ||
| 98 | -# 存储解析后的查询对象 | ||
| 99 | -context.store_intermediate_result('parsed_query', parsed_query) | ||
| 100 | - | ||
| 101 | -# 存储ES查询DSL | ||
| 102 | -context.store_intermediate_result('es_query', es_query_dict) | ||
| 103 | - | ||
| 104 | -# 存储ES响应 | ||
| 105 | -context.store_intermediate_result('es_response', es_response) | ||
| 106 | - | ||
| 107 | -# 存储处理后的结果 | ||
| 108 | -context.store_intermediate_result('processed_hits', hits) | ||
| 109 | -``` | ||
| 110 | - | ||
| 111 | -### 5. 错误处理和警告 | ||
| 112 | - | ||
| 113 | -```python | ||
| 114 | -try: | ||
| 115 | - # 可能出错的操作 | ||
| 116 | - risky_operation() | ||
| 117 | -except Exception as e: | ||
| 118 | - context.set_error(e) | ||
| 119 | - | ||
| 120 | -# 添加警告信息 | ||
| 121 | -context.add_warning("查询结果较少,建议放宽搜索条件") | ||
| 122 | - | ||
| 123 | -# 检查是否有错误 | ||
| 124 | -if context.has_error(): | ||
| 125 | - print(f"搜索出错: {context.metadata['error_info']}") | ||
| 126 | -``` | ||
| 127 | - | ||
| 128 | -## 在Searcher中使用 | ||
| 129 | - | ||
| 130 | -### 1. 自动创建Context(向后兼容) | ||
| 131 | - | ||
| 132 | -```python | ||
| 133 | -searcher = Searcher(config, es_client) | ||
| 134 | - | ||
| 135 | -# Searcher会自动创建RequestContext | ||
| 136 | -result = searcher.search( | ||
| 137 | - query="无线蓝牙耳机", | ||
| 138 | - size=10, | ||
| 139 | - enable_embedding=True | ||
| 140 | -) | ||
| 141 | - | ||
| 142 | -# 结果中包含context信息 | ||
| 143 | -print(result.context.get_summary()) | ||
| 144 | -``` | ||
| 145 | - | ||
| 146 | -### 2. 手动创建和传递Context | ||
| 147 | - | ||
| 148 | -```python | ||
| 149 | -# 创建自己的context | ||
| 150 | -context = create_request_context("my-req-001", "user-789") | ||
| 151 | - | ||
| 152 | -# 传递给searcher | ||
| 153 | -result = searcher.search( | ||
| 154 | - query="运动鞋", | ||
| 155 | - context=context # 传递自定义context | ||
| 156 | -) | ||
| 157 | - | ||
| 158 | -# 使用context进行详细分析 | ||
| 159 | -summary = context.get_summary() | ||
| 160 | -print(f"总耗时: {summary['performance']['total_duration_ms']:.1f}ms") | ||
| 161 | -``` | ||
| 162 | - | ||
| 163 | -## 性能分析 | ||
| 164 | - | ||
| 165 | -### 1. 获取性能摘要 | ||
| 166 | - | ||
| 167 | -```python | ||
| 168 | -summary = context.get_summary() | ||
| 169 | - | ||
| 170 | -# 基本信息 | ||
| 171 | -print(f"请求ID: {summary['request_info']['reqid']}") | ||
| 172 | -print(f"总耗时: {summary['performance']['total_duration_ms']:.1f}ms") | ||
| 173 | - | ||
| 174 | -# 各阶段耗时 | ||
| 175 | -for stage, duration in summary['performance']['stage_timings_ms'].items(): | ||
| 176 | - percentage = summary['performance']['stage_percentages'].get(stage, 0) | ||
| 177 | - print(f"{stage}: {duration:.1f}ms ({percentage:.1f}%)") | ||
| 178 | - | ||
| 179 | -# 查询分析信息 | ||
| 180 | -query_info = summary['query_analysis'] | ||
| 181 | -print(f"原查询: {query_info['original_query']}") | ||
| 182 | -print(f"重写查询: {query_info['rewritten_query']}") | ||
| 183 | -print(f"检测语言: {query_info['detected_language']}") | ||
| 184 | -``` | ||
| 185 | - | ||
| 186 | -### 2. 识别性能瓶颈 | ||
| 187 | - | ||
| 188 | -```python | ||
| 189 | -summary = context.get_summary() | ||
| 190 | - | ||
| 191 | -# 找出耗时超过20%的阶段 | ||
| 192 | -bottlenecks = [] | ||
| 193 | -for stage, percentage in summary['performance']['stage_percentages'].items(): | ||
| 194 | - if percentage > 20: | ||
| 195 | - bottlenecks.append((stage, percentage)) | ||
| 196 | - | ||
| 197 | -if bottlenecks: | ||
| 198 | - print("性能瓶颈:") | ||
| 199 | - for stage, percentage in bottlenecks: | ||
| 200 | - print(f" - {stage}: {percentage:.1f}%") | ||
| 201 | -``` | ||
| 202 | - | ||
| 203 | -### 3. 自动性能日志 | ||
| 204 | - | ||
| 205 | -RequestContext会在以下时机自动记录详细的性能摘要日志: | ||
| 206 | - | ||
| 207 | -- 上下文管理器退出时 (`with context:`) | ||
| 208 | -- 手动调用 `context.log_performance_summary()` | ||
| 209 | -- Searcher.search() 完成时 | ||
| 210 | - | ||
| 211 | -日志格式示例: | ||
| 212 | -``` | ||
| 213 | -[2024-01-01 10:30:45] [INFO] [request_context] 搜索请求性能摘要 | reqid: req-001 | 总耗时: 272.6ms | 阶段耗时: | - query_parsing: 35.3ms (13.0%) | - elasticsearch_search: 146.0ms (53.6%) | - result_processing: 18.6ms (6.8%) | 查询: '红色连衣裙' -> '红色 女 连衣裙' (zh) | 结果: 156 hits ES查询: 2456 chars | ||
| 214 | -``` | ||
| 215 | - | ||
| 216 | -## 线程安全 | ||
| 217 | - | ||
| 218 | -RequestContext是线程安全的,支持并发请求处理。每个请求使用独立的context实例,互不干扰。 | ||
| 219 | - | ||
| 220 | -```python | ||
| 221 | -import threading | ||
| 222 | -from context import create_request_context | ||
| 223 | - | ||
| 224 | -def worker(request_id, query): | ||
| 225 | - context = create_request_context(request_id) | ||
| 226 | - # 搜索逻辑 | ||
| 227 | - # context自动跟踪此线程的请求 | ||
| 228 | - pass | ||
| 229 | - | ||
| 230 | -# 多线程并发处理 | ||
| 231 | -threads = [] | ||
| 232 | -for i in range(5): | ||
| 233 | - t = threading.Thread(target=worker, args=(f"req-{i}", f"query-{i}")) | ||
| 234 | - threads.append(t) | ||
| 235 | - t.start() | ||
| 236 | - | ||
| 237 | -for t in threads: | ||
| 238 | - t.join() | ||
| 239 | -``` | ||
| 240 | - | ||
| 241 | -## 调试支持 | ||
| 242 | - | ||
| 243 | -### 1. 检查中间结果 | ||
| 244 | - | ||
| 245 | -```python | ||
| 246 | -# 获取查询解析结果 | ||
| 247 | -parsed_query = context.get_intermediate_result('parsed_query') | ||
| 248 | - | ||
| 249 | -# 获取ES查询DSL | ||
| 250 | -es_query = context.get_intermediate_result('es_query') | ||
| 251 | - | ||
| 252 | -# 获取ES响应 | ||
| 253 | -es_response = context.get_intermediate_result('es_response') | ||
| 254 | - | ||
| 255 | -# 获取原始搜索结果 | ||
| 256 | -raw_hits = context.get_intermediate_result('raw_hits') | ||
| 257 | - | ||
| 258 | -# 获取最终处理后的结果 | ||
| 259 | -processed_hits = context.get_intermediate_result('processed_hits') | ||
| 260 | -``` | ||
| 261 | - | ||
| 262 | -### 2. 错误诊断 | ||
| 263 | - | ||
| 264 | -```python | ||
| 265 | -if context.has_error(): | ||
| 266 | - error_info = context.metadata['error_info'] | ||
| 267 | - print(f"错误类型: {error_info['type']}") | ||
| 268 | - print(f"错误消息: {error_info['message']}") | ||
| 269 | - | ||
| 270 | - # 检查是否有警告 | ||
| 271 | - if context.metadata['warnings']: | ||
| 272 | - print("警告信息:") | ||
| 273 | - for warning in context.metadata['warnings']: | ||
| 274 | - print(f" - {warning}") | ||
| 275 | -``` | ||
| 276 | - | ||
| 277 | -## 最佳实践 | ||
| 278 | - | ||
| 279 | -### 1. 统一使用Context | ||
| 280 | - | ||
| 281 | -```python | ||
| 282 | -# 推荐:在整个搜索流程中传递同一个context | ||
| 283 | -result = searcher.search(query, context=context) | ||
| 284 | - | ||
| 285 | -# 不推荐:在各个环节创建不同的context | ||
| 286 | -``` | ||
| 287 | - | ||
| 288 | -### 2. 合理设置阶段边界 | ||
| 289 | - | ||
| 290 | -```python | ||
| 291 | -# 只在有意义的大阶段之间计时 | ||
| 292 | -context.start_stage(RequestContextStage.QUERY_PARSING) | ||
| 293 | -# 整个查询解析逻辑 | ||
| 294 | -context.end_stage(RequestContextStage.QUERY_PARSING) | ||
| 295 | - | ||
| 296 | -# 避免在细粒度操作间频繁计时 | ||
| 297 | -``` | ||
| 298 | - | ||
| 299 | -### 3. 及时存储关键数据 | ||
| 300 | - | ||
| 301 | -```python | ||
| 302 | -# 在每个阶段完成后及时存储结果 | ||
| 303 | -context.store_intermediate_result('parsed_query', parsed_query) | ||
| 304 | -context.store_intermediate_result('es_query', es_query) | ||
| 305 | - | ||
| 306 | -# 便于后续调试和分析 | ||
| 307 | -``` | ||
| 308 | - | ||
| 309 | -### 4. 适当使用警告 | ||
| 310 | - | ||
| 311 | -```python | ||
| 312 | -# 使用警告记录非致命问题 | ||
| 313 | -if total_hits < 10: | ||
| 314 | - context.add_warning("搜索结果较少,建议放宽搜索条件") | ||
| 315 | - | ||
| 316 | -if query_time > 5.0: | ||
| 317 | - context.add_warning(f"查询耗时较长: {query_time:.1f}秒") | ||
| 318 | -``` | ||
| 319 | - | ||
| 320 | -## 集成示例 | ||
| 321 | - | ||
| 322 | -### API接口集成 | ||
| 323 | - | ||
| 324 | -```python | ||
| 325 | -from flask import Flask, request, jsonify | ||
| 326 | -from context import create_request_context | ||
| 327 | - | ||
| 328 | -app = Flask(__name__) | ||
| 329 | - | ||
| 330 | -@app.route('/search') | ||
| 331 | -def api_search(): | ||
| 332 | - # 从请求中获取参数 | ||
| 333 | - query = request.args.get('q', '') | ||
| 334 | - uid = request.args.get('uid', 'anonymous') | ||
| 335 | - | ||
| 336 | - # 创建context | ||
| 337 | - context = create_request_context(uid=uid) | ||
| 338 | - | ||
| 339 | - try: | ||
| 340 | - # 执行搜索 | ||
| 341 | - result = searcher.search(query, context=context) | ||
| 342 | - | ||
| 343 | - # 返回结果(包含性能信息) | ||
| 344 | - response = { | ||
| 345 | - 'results': result.to_dict(), | ||
| 346 | - 'performance': context.get_summary()['performance'] | ||
| 347 | - } | ||
| 348 | - | ||
| 349 | - return jsonify(response) | ||
| 350 | - | ||
| 351 | - except Exception as e: | ||
| 352 | - context.set_error(e) | ||
| 353 | - context.log_performance_summary() | ||
| 354 | - | ||
| 355 | - return jsonify({ | ||
| 356 | - 'error': str(e), | ||
| 357 | - 'request_id': context.reqid | ||
| 358 | - }), 500 | ||
| 359 | -``` | ||
| 360 | - | ||
| 361 | -## 总结 | ||
| 362 | - | ||
| 363 | -RequestContext提供了一个强大而灵活的框架,用于管理搜索请求的整个生命周期。通过统一的上下文管理、自动性能监控和详细的日志记录,它显著提升了搜索系统的可观测性和调试能力。 | ||
| 364 | - | ||
| 365 | -主要优势: | ||
| 366 | - | ||
| 367 | -1. **统一管理**: 所有请求相关数据集中存储 | ||
| 368 | -2. **自动监控**: 无需手动计时,自动跟踪性能 | ||
| 369 | -3. **详细日志**: 完整的请求生命周期记录 | ||
| 370 | -4. **向后兼容**: 现有代码无需修改即可受益 | ||
| 371 | -5. **线程安全**: 支持高并发场景 | ||
| 372 | -6. **易于调试**: 丰富的中间结果和错误信息 | ||
| 373 | - | ||
| 374 | -通过合理使用RequestContext,可以构建更加可靠、高性能和易维护的搜索系统。 | ||
| 375 | \ No newline at end of file | 0 | \ No newline at end of file |
API_EXAMPLES.md renamed to docs/Search-API-Examples.md
USAGE_GUIDE.md renamed to docs/Usage-Guide.md
商品数据源入ES配置规范.md renamed to docs/reference/商品数据源入ES配置规范.md
阿里opensearch电商行业.md renamed to docs/reference/阿里opensearch电商行业.md
docs/BASE_CONFIG_GUIDE.md renamed to docs/基础配置指南.md
API_INTEGRATION_GUIDE.md renamed to docs/搜索API对接指南.md
| @@ -761,6 +761,209 @@ result.results.forEach(product => { | @@ -761,6 +761,209 @@ result.results.forEach(product => { | ||
| 761 | 761 | ||
| 762 | --- | 762 | --- |
| 763 | 763 | ||
| 764 | +## 其他接口 | ||
| 765 | + | ||
| 766 | +### 搜索建议(框架) | ||
| 767 | + | ||
| 768 | +- **端点**: `GET /search/suggestions` | ||
| 769 | +- **描述**: 返回搜索建议(自动补全/热词)。当前为框架实现,接口和响应格式已经固定,可平滑扩展。 | ||
| 770 | + | ||
| 771 | +#### 查询参数 | ||
| 772 | + | ||
| 773 | +| 参数 | 类型 | 必填 | 默认值 | 描述 | | ||
| 774 | +|------|------|------|--------|------| | ||
| 775 | +| `q` | string | ✅ | - | 查询字符串(至少 1 个字符) | | ||
| 776 | +| `size` | integer | ❌ | 5 | 返回建议数量(1-20) | | ||
| 777 | +| `types` | string | ❌ | `query` | 建议类型(逗号分隔):`query`, `product`, `category`, `brand` | | ||
| 778 | + | ||
| 779 | +#### 响应示例 | ||
| 780 | + | ||
| 781 | +```json | ||
| 782 | +{ | ||
| 783 | + "query": "芭", | ||
| 784 | + "suggestions": [ | ||
| 785 | + { | ||
| 786 | + "text": "芭比娃娃", | ||
| 787 | + "type": "query", | ||
| 788 | + "highlight": "<em>芭</em>比娃娃", | ||
| 789 | + "popularity": 850 | ||
| 790 | + } | ||
| 791 | + ], | ||
| 792 | + "took_ms": 5 | ||
| 793 | +} | ||
| 794 | +``` | ||
| 795 | + | ||
| 796 | +#### 请求示例 | ||
| 797 | + | ||
| 798 | +```bash | ||
| 799 | +curl "http://localhost:6002/search/suggestions?q=芭&size=5&types=query,product" | ||
| 800 | +``` | ||
| 801 | + | ||
| 802 | +--- | ||
| 803 | + | ||
| 804 | +### 即时搜索(框架) | ||
| 805 | + | ||
| 806 | +- **端点**: `GET /search/instant` | ||
| 807 | +- **描述**: 边输入边搜索,采用轻量参数响应当前输入。底层复用标准搜索能力。 | ||
| 808 | + | ||
| 809 | +#### 查询参数 | ||
| 810 | + | ||
| 811 | +| 参数 | 类型 | 必填 | 默认值 | 描述 | | ||
| 812 | +|------|------|------|--------|------| | ||
| 813 | +| `q` | string | ✅ | - | 搜索查询(至少 2 个字符) | | ||
| 814 | +| `size` | integer | ❌ | 5 | 返回结果数量(1-20) | | ||
| 815 | + | ||
| 816 | +#### 请求示例 | ||
| 817 | + | ||
| 818 | +```bash | ||
| 819 | +curl "http://localhost:6002/search/instant?q=玩具&size=5" | ||
| 820 | +``` | ||
| 821 | + | ||
| 822 | +--- | ||
| 823 | + | ||
| 824 | +### 获取单个文档 | ||
| 825 | + | ||
| 826 | +- **端点**: `GET /search/{doc_id}` | ||
| 827 | +- **描述**: 根据文档 ID 获取单个商品详情,用于点击结果后的详情页或排查问题。 | ||
| 828 | + | ||
| 829 | +#### 路径参数 | ||
| 830 | + | ||
| 831 | +| 参数 | 类型 | 描述 | | ||
| 832 | +|------|------|------| | ||
| 833 | +| `doc_id` | string | 商品或文档 ID | | ||
| 834 | + | ||
| 835 | +#### 响应示例 | ||
| 836 | + | ||
| 837 | +```json | ||
| 838 | +{ | ||
| 839 | + "id": "12345", | ||
| 840 | + "source": { | ||
| 841 | + "title": "芭比时尚娃娃", | ||
| 842 | + "min_price": 89.99, | ||
| 843 | + "category_keyword": "玩具" | ||
| 844 | + } | ||
| 845 | +} | ||
| 846 | +``` | ||
| 847 | + | ||
| 848 | +#### 请求示例 | ||
| 849 | + | ||
| 850 | +```bash | ||
| 851 | +curl "http://localhost:6002/search/12345" | ||
| 852 | +``` | ||
| 853 | + | ||
| 854 | +--- | ||
| 855 | + | ||
| 856 | +## 管理接口 | ||
| 857 | + | ||
| 858 | +### 健康检查 | ||
| 859 | + | ||
| 860 | +- **端点**: `GET /admin/health` | ||
| 861 | +- **描述**: 检查服务与依赖(如 Elasticsearch)状态。 | ||
| 862 | + | ||
| 863 | +```json | ||
| 864 | +{ | ||
| 865 | + "status": "healthy", | ||
| 866 | + "elasticsearch": "connected", | ||
| 867 | + "tenant_id": "tenant1" | ||
| 868 | +} | ||
| 869 | +``` | ||
| 870 | + | ||
| 871 | +--- | ||
| 872 | + | ||
| 873 | +### 获取配置 | ||
| 874 | + | ||
| 875 | +- **端点**: `GET /admin/config` | ||
| 876 | +- **描述**: 返回当前租户的脱敏配置,便于核对索引及排序表达式。 | ||
| 877 | + | ||
| 878 | +```json | ||
| 879 | +{ | ||
| 880 | + "tenant_id": "tenant1", | ||
| 881 | + "tenant_name": "Tenant1 Test Instance", | ||
| 882 | + "es_index_name": "search_tenant1", | ||
| 883 | + "num_fields": 20, | ||
| 884 | + "num_indexes": 4, | ||
| 885 | + "supported_languages": ["zh", "en", "ru"], | ||
| 886 | + "ranking_expression": "bm25() + 0.2*text_embedding_relevance()", | ||
| 887 | + "spu_enabled": false | ||
| 888 | +} | ||
| 889 | +``` | ||
| 890 | + | ||
| 891 | +--- | ||
| 892 | + | ||
| 893 | +### 索引统计 | ||
| 894 | + | ||
| 895 | +- **端点**: `GET /admin/stats` | ||
| 896 | +- **描述**: 获取索引文档数量与磁盘大小,方便监控。 | ||
| 897 | + | ||
| 898 | +```json | ||
| 899 | +{ | ||
| 900 | + "index_name": "search_tenant1", | ||
| 901 | + "document_count": 10000, | ||
| 902 | + "size_mb": 523.45 | ||
| 903 | +} | ||
| 904 | +``` | ||
| 905 | + | ||
| 906 | +--- | ||
| 907 | + | ||
| 908 | +## 数据模型 | ||
| 909 | + | ||
| 910 | +### 商品字段 | ||
| 911 | + | ||
| 912 | +| 字段名 | 类型 | 描述 | | ||
| 913 | +|--------|------|------| | ||
| 914 | +| `product_id` | keyword | 商品 ID(SPU) | | ||
| 915 | +| `sku_id` | keyword/long | SKU ID(主键) | | ||
| 916 | +| `title` | text | 商品名称(中文) | | ||
| 917 | +| `en_title` | text | 商品名称(英文) | | ||
| 918 | +| `ru_title` | text | 商品名称(俄文) | | ||
| 919 | +| `category_keyword` | keyword | 类目(精确匹配) | | ||
| 920 | +| `vendor_keyword` | keyword | 品牌/供应商(精确匹配) | | ||
| 921 | +| `product_type_keyword` | keyword | 商品类型 | | ||
| 922 | +| `tags_keyword` | keyword | 标签 | | ||
| 923 | +| `min_price` | double | 最低价格 | | ||
| 924 | +| `max_price` | double | 最高价格 | | ||
| 925 | +| `compare_at_price` | double | 原价 | | ||
| 926 | +| `create_time` | date | 创建时间 | | ||
| 927 | +| `update_time` | date | 更新时间 | | ||
| 928 | +| `in_stock` | boolean | 是否有库存 | | ||
| 929 | +| `text_embedding` | dense_vector | 文本向量(1024 维) | | ||
| 930 | +| `image_embedding` | dense_vector | 图片向量(1024 维) | | ||
| 931 | + | ||
| 932 | +> 不同租户可自定义字段名称,但最佳实践是对可过滤字段建立 `*_keyword` 版本,对可排序字段显式建 keyword/数值映射。 | ||
| 933 | + | ||
| 934 | +--- | ||
| 935 | + | ||
| 936 | +## 常见问题(FAQ) | ||
| 937 | + | ||
| 938 | +**Q1: 如何判断一个字段应该用哪种过滤器?** | ||
| 939 | +`filters` 针对 keyword/布尔/整数字段做精确匹配;`range_filters` 针对数值或日期字段做区间查询。 | ||
| 940 | + | ||
| 941 | +**Q2: 可以同时使用多个过滤器吗?** | ||
| 942 | +可以,所有过滤条件为 AND 关系。例如: | ||
| 943 | + | ||
| 944 | +```json | ||
| 945 | +{ | ||
| 946 | + "filters": { | ||
| 947 | + "category_keyword": "玩具", | ||
| 948 | + "vendor_keyword": "乐高" | ||
| 949 | + }, | ||
| 950 | + "range_filters": { | ||
| 951 | + "min_price": {"gte": 50, "lte": 200} | ||
| 952 | + } | ||
| 953 | +} | ||
| 954 | +``` | ||
| 955 | + | ||
| 956 | +**Q4: 分面结果里的 `selected` 字段含义是什么?** | ||
| 957 | +指示该分面值是否已在当前过滤条件中,前端可据此高亮。 | ||
| 958 | + | ||
| 959 | +**Q5: 如何自定义排序?** | ||
| 960 | +设置 `sort_by` 和 `sort_order`,常用字段包括 `min_price`, `max_price`, `title`, `create_time`, `update_time`, `relevance_score`。 | ||
| 961 | + | ||
| 962 | +**Q6: 如何启用调试模式?** | ||
| 963 | +添加 `debug: true`,即可在响应中看到 `debug_info`(ES DSL、阶段耗时、打分细节)。 | ||
| 964 | + | ||
| 965 | +--- | ||
| 966 | + | ||
| 764 | ## 附录 | 967 | ## 附录 |
| 765 | 968 | ||
| 766 | ### 常用字段列表 | 969 | ### 常用字段列表 |
| @@ -789,3 +992,27 @@ result.results.forEach(product => { | @@ -789,3 +992,27 @@ result.results.forEach(product => { | ||
| 789 | - `update_time`: 更新时间 | 992 | - `update_time`: 更新时间 |
| 790 | - `relevance_score`: 相关性分数(默认) | 993 | - `relevance_score`: 相关性分数(默认) |
| 791 | 994 | ||
| 995 | +### 支持的分析器 | ||
| 996 | + | ||
| 997 | +| 分析器 | 语言 | 描述 | | ||
| 998 | +|--------|------|------| | ||
| 999 | +| `chinese_ecommerce` | 中文 | 基于 Ansj 的电商优化中文分析器 | | ||
| 1000 | +| `english` | 英文 | 标准英文分析器 | | ||
| 1001 | +| `russian` | 俄文 | 俄文分析器 | | ||
| 1002 | +| `arabic` | 阿拉伯文 | 阿拉伯文分析器 | | ||
| 1003 | +| `spanish` | 西班牙文 | 西班牙文分析器 | | ||
| 1004 | +| `japanese` | 日文 | 日文分析器 | | ||
| 1005 | + | ||
| 1006 | +### 字段类型速查 | ||
| 1007 | + | ||
| 1008 | +| 类型 | ES Mapping | 用途 | | ||
| 1009 | +|------|------------|------| | ||
| 1010 | +| `TEXT` | `text` | 全文检索 | | ||
| 1011 | +| `KEYWORD` | `keyword` | 精确匹配、聚合、排序 | | ||
| 1012 | +| `LONG` | `long` | 整数 | | ||
| 1013 | +| `DOUBLE` | `double` | 浮点数 | | ||
| 1014 | +| `DATE` | `date` | 日期时间 | | ||
| 1015 | +| `BOOLEAN` | `boolean` | 布尔值 | | ||
| 1016 | +| `TEXT_EMBEDDING` | `dense_vector` | 文本语义向量 | | ||
| 1017 | +| `IMAGE_EMBEDDING` | `dense_vector` | 图片语义向量 | | ||
| 1018 | + |
API_QUICK_REFERENCE.md renamed to docs/搜索API速查表.md
docs/TestingPipeline_README.md renamed to docs/测试Pipeline说明.md
TEST_DATA_GUIDE.md renamed to docs/测试数据指南.md
环境相关.md renamed to docs/环境配置说明.md
设计文档.md renamed to docs/系统设计文档.md
INDEX_FIELDS_DOCUMENTATION.md renamed to docs/索引字段说明.md