Commit 13320ac6b0d8b3ca7a554e3d9923b019faaf7fb9
1 parent
e7ad2b4a
分面接口修改:
{
"facets": [
{
"field": "category1_name",
"size": 15,
"type": "terms"
},
"specifications.color",
"specifications.size"
]
}
{
"facets": [
{"field": "category1_name", "size": 15, "type": "terms"},
{"field": "specifications.color", "size": 10, "type": "terms"},
{"field": "specifications.size", "size": 10, "type": "terms"}
]
}
之前是上面的接口形式,主要是考虑 属性的分面, 因为 款式都是有限的 不需要设定 "size": 10, "type": "terms" 这些参数。
但是从接口设计层面,最好按下面这样,这样的话 specifications.color 和 category1_name 的组装格式 完全一样。前端不需要感知 属性分面 和 类别等其他字段分面的差异。
Showing
10 changed files
with
273 additions
and
175 deletions
Show diff stats
| @@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
| 1 | +#!/bin/bash | ||
| 2 | +source /home/tw/miniconda3/etc/profile.d/conda.sh | ||
| 3 | +conda activate searchengine | ||
| 4 | + | ||
| 5 | +# 如果需要加载 .env 中的环境变量 | ||
| 6 | +if [ -f .env ]; then | ||
| 7 | + set -a # 自动导出所有变量 | ||
| 8 | + source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/\r$//') | ||
| 9 | + set +a # 关闭自动导出 | ||
| 10 | +fi | ||
| 11 | + | ||
| 12 | +echo "Environment activated: searchengine" |
api/models.py
| @@ -110,31 +110,34 @@ class SearchRequest(BaseModel): | @@ -110,31 +110,34 @@ class SearchRequest(BaseModel): | ||
| 110 | ) | 110 | ) |
| 111 | 111 | ||
| 112 | # 排序 | 112 | # 排序 |
| 113 | - sort_by: Optional[str] = Field(None, description="排序字段名(如 'min_price', 'max_price', 'title')") | ||
| 114 | - sort_order: Optional[str] = Field("desc", description="排序方向: 'asc'(升序)或 'desc'(降序)") | 113 | + sort_by: Optional[str] = Field(None, description="排序字段名。支持:'price'(价格,自动根据sort_order选择min_price或max_price)、'sales'(销量)、'create_time'(创建时间)、'update_time'(更新时间)") |
| 114 | + sort_order: Optional[str] = Field("desc", description="排序方向: 'asc'(升序)或 'desc'(降序)。注意:price+asc=价格从低到高,price+desc=价格从高到低") | ||
| 115 | 115 | ||
| 116 | - # 分面搜索 - 简化接口 | ||
| 117 | - facets: Optional[List[Union[str, FacetConfig]]] = Field( | 116 | + # 分面搜索 |
| 117 | + facets: Optional[List[FacetConfig]] = Field( | ||
| 118 | None, | 118 | None, |
| 119 | - description="分面配置。可以是字段名列表(使用默认配置)或详细的分面配置对象。支持 specifications 分面:\"specifications\"(所有name)或 \"specifications.color\"(指定name)", | 119 | + description="分面配置对象列表。支持 specifications 分面:field=\"specifications\"(所有规格名称)或 field=\"specifications.color\"(指定规格名称)", |
| 120 | json_schema_extra={ | 120 | json_schema_extra={ |
| 121 | "examples": [ | 121 | "examples": [ |
| 122 | - # 简单模式:只指定字段名,使用默认配置 | ||
| 123 | - ["category1_name", "category2_name", "specifications"], | ||
| 124 | - # 指定specifications的某个name | ||
| 125 | - ["specifications.颜色", "specifications.尺寸"], | ||
| 126 | - # 高级模式:详细配置 | ||
| 127 | [ | 122 | [ |
| 128 | - {"field": "category1_name", "size": 15}, | 123 | + {"field": "category1_name", "size": 15, "type": "terms"}, |
| 124 | + {"field": "category2_name", "size": 10, "type": "terms"}, | ||
| 125 | + {"field": "specifications.color", "size": 20, "type": "terms"}, | ||
| 126 | + {"field": "specifications.size", "size": 15, "type": "terms"} | ||
| 127 | + ], | ||
| 128 | + [ | ||
| 129 | + {"field": "category1_name", "size": 15, "type": "terms"}, | ||
| 129 | { | 130 | { |
| 130 | "field": "min_price", | 131 | "field": "min_price", |
| 131 | "type": "range", | 132 | "type": "range", |
| 132 | "ranges": [ | 133 | "ranges": [ |
| 133 | {"key": "0-50", "to": 50}, | 134 | {"key": "0-50", "to": 50}, |
| 134 | - {"key": "50-100", "from": 50, "to": 100} | 135 | + {"key": "50-100", "from": 50, "to": 100}, |
| 136 | + {"key": "100-200", "from": 100, "to": 200}, | ||
| 137 | + {"key": "200+", "from": 200} | ||
| 135 | ] | 138 | ] |
| 136 | }, | 139 | }, |
| 137 | - "specifications" # 所有specifications name的分面 | 140 | + {"field": "specifications", "size": 10, "type": "terms"} |
| 138 | ] | 141 | ] |
| 139 | ] | 142 | ] |
| 140 | } | 143 | } |
docs/搜索API对接指南.md
| @@ -50,7 +50,7 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -50,7 +50,7 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 50 | "gte": "2020-01-01T00:00:00Z" | 50 | "gte": "2020-01-01T00:00:00Z" |
| 51 | } | 51 | } |
| 52 | }, | 52 | }, |
| 53 | - "sort_by": "min_price", | 53 | + "sort_by": "price", |
| 54 | "sort_order": "asc" | 54 | "sort_order": "asc" |
| 55 | }' | 55 | }' |
| 56 | ``` | 56 | ``` |
| @@ -63,7 +63,11 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -63,7 +63,11 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 63 | -H "X-Tenant-ID: 162" \ | 63 | -H "X-Tenant-ID: 162" \ |
| 64 | -d '{ | 64 | -d '{ |
| 65 | "query": "芭比娃娃", | 65 | "query": "芭比娃娃", |
| 66 | - "facets": ["category1_name", "specifications.color", "specifications.size", "specifications.material"], | 66 | + "facets": [ |
| 67 | + {"field": "category1_name", "size": 10, "type": "terms"}, | ||
| 68 | + {"field": "specifications.color", "size": 10, "type": "terms"}, | ||
| 69 | + {"field": "specifications.size", "size": 10, "type": "terms"} | ||
| 70 | + ], | ||
| 67 | "min_score": 0.2 | 71 | "min_score": 0.2 |
| 68 | }' | 72 | }' |
| 69 | ``` | 73 | ``` |
| @@ -124,8 +128,8 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -124,8 +128,8 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 124 | | `filters` | object | N | null | 精确匹配过滤器(见下文) | | 128 | | `filters` | object | N | null | 精确匹配过滤器(见下文) | |
| 125 | | `range_filters` | object | N | null | 数值范围过滤器(见下文) | | 129 | | `range_filters` | object | N | null | 数值范围过滤器(见下文) | |
| 126 | | `facets` | array | N | null | 分面配置(见下文) | | 130 | | `facets` | array | N | null | 分面配置(见下文) | |
| 127 | -| `sort_by` | string | N | null | 排序字段名(如 `min_price`, `max_price`) | | ||
| 128 | -| `sort_order` | string | N | "desc" | 排序方向:`asc`(升序)或 `desc`(降序) | | 131 | +| `sort_by` | string | N | null | 排序字段名。支持:`price`(价格)、`sales`(销量)、`create_time`(创建时间)、`update_time`(更新时间)。默认按相关性排序 | |
| 132 | +| `sort_order` | string | N | "desc" | 排序方向:`asc`(升序)或 `desc`(降序)。注意:`price`+`asc`=价格从低到高,`price`+`desc`=价格从高到低(后端自动映射为min_price或max_price) | | ||
| 129 | | `min_score` | float | N | null | 最小相关性分数阈值 | | 133 | | `min_score` | float | N | null | 最小相关性分数阈值 | |
| 130 | | `sku_filter_dimension` | array[string] | N | null | 子SKU筛选维度列表(店铺配置)。指定后,每个SPU下的SKU将按这些维度的组合进行分组,每个组合只返回第一个SKU。支持的值:`option1`、`option2`、`option3` 或选项名称(如 `color`、`size`)。详见下文说明 | | 134 | | `sku_filter_dimension` | array[string] | N | null | 子SKU筛选维度列表(店铺配置)。指定后,每个SPU下的SKU将按这些维度的组合进行分组,每个组合只返回第一个SKU。支持的值:`option1`、`option2`、`option3` 或选项名称(如 `color`、`size`)。详见下文说明 | |
| 131 | | `debug` | boolean | N | false | 是否返回调试信息 | | 135 | | `debug` | boolean | N | false | 是否返回调试信息 | |
| @@ -263,14 +267,8 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -263,14 +267,8 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 263 | 267 | ||
| 264 | 用于生成分面统计(分组聚合),常用于构建筛选器UI。 | 268 | 用于生成分面统计(分组聚合),常用于构建筛选器UI。 |
| 265 | 269 | ||
| 266 | -**简单模式**(字符串数组): | ||
| 267 | -```json | ||
| 268 | -{ | ||
| 269 | - "facets": ["category1_name", "category2_name", "category3_name", "specifications"] | ||
| 270 | -} | ||
| 271 | -``` | 270 | +**配置格式**(对象数组): |
| 272 | 271 | ||
| 273 | -**高级模式**(配置对象数组): | ||
| 274 | ```json | 272 | ```json |
| 275 | { | 273 | { |
| 276 | "facets": [ | 274 | "facets": [ |
| @@ -280,6 +278,16 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -280,6 +278,16 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 280 | "type": "terms" | 278 | "type": "terms" |
| 281 | }, | 279 | }, |
| 282 | { | 280 | { |
| 281 | + "field": "category2_name", | ||
| 282 | + "size": 10, | ||
| 283 | + "type": "terms" | ||
| 284 | + }, | ||
| 285 | + { | ||
| 286 | + "field": "specifications.color", | ||
| 287 | + "size": 20, | ||
| 288 | + "type": "terms" | ||
| 289 | + }, | ||
| 290 | + { | ||
| 283 | "field": "min_price", | 291 | "field": "min_price", |
| 284 | "type": "range", | 292 | "type": "range", |
| 285 | "ranges": [ | 293 | "ranges": [ |
| @@ -288,8 +296,7 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -288,8 +296,7 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 288 | {"key": "100-200", "from": 100, "to": 200}, | 296 | {"key": "100-200", "from": 100, "to": 200}, |
| 289 | {"key": "200+", "from": 200} | 297 | {"key": "200+", "from": 200} |
| 290 | ] | 298 | ] |
| 291 | - }, | ||
| 292 | - "specifications" // 规格分面(特殊处理:嵌套聚合,按name分组,然后按value聚合) | 299 | + } |
| 293 | ] | 300 | ] |
| 294 | } | 301 | } |
| 295 | ``` | 302 | ``` |
| @@ -298,18 +305,35 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -298,18 +305,35 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 298 | 305 | ||
| 299 | `specifications` 是嵌套字段,支持两种分面模式: | 306 | `specifications` 是嵌套字段,支持两种分面模式: |
| 300 | 307 | ||
| 301 | -**模式1:所有规格名称的分面** (`"specifications"`): | 308 | +**模式1:所有规格名称的分面**: |
| 302 | ```json | 309 | ```json |
| 303 | { | 310 | { |
| 304 | - "facets": ["specifications"] | 311 | + "facets": [ |
| 312 | + { | ||
| 313 | + "field": "specifications", | ||
| 314 | + "size": 10, | ||
| 315 | + "type": "terms" | ||
| 316 | + } | ||
| 317 | + ] | ||
| 305 | } | 318 | } |
| 306 | ``` | 319 | ``` |
| 307 | 返回所有规格名称(name)及其对应的值(value)列表。每个 name 会生成一个独立的分面结果。 | 320 | 返回所有规格名称(name)及其对应的值(value)列表。每个 name 会生成一个独立的分面结果。 |
| 308 | 321 | ||
| 309 | -**模式2:指定规格名称的分面** (`"specifications.color"`): | 322 | +**模式2:指定规格名称的分面**: |
| 310 | ```json | 323 | ```json |
| 311 | { | 324 | { |
| 312 | - "facets": ["specifications.color", "specifications.size", "specifications.material"] | 325 | + "facets": [ |
| 326 | + { | ||
| 327 | + "field": "specifications.color", | ||
| 328 | + "size": 20, | ||
| 329 | + "type": "terms" | ||
| 330 | + }, | ||
| 331 | + { | ||
| 332 | + "field": "specifications.size", | ||
| 333 | + "size": 15, | ||
| 334 | + "type": "terms" | ||
| 335 | + } | ||
| 336 | + ] | ||
| 313 | } | 337 | } |
| 314 | ``` | 338 | ``` |
| 315 | 只返回指定规格名称的值列表。格式:`specifications.{name}`,其中 `{name}` 是规格名称(如"color"、"size"、"material")。 | 339 | 只返回指定规格名称的值列表。格式:`specifications.{name}`,其中 `{name}` 是规格名称(如"color"、"size"、"material")。 |
| @@ -564,6 +588,7 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -564,6 +588,7 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 564 | | `sku_weights` | array[integer] | 所有SKU重量列表 | | 588 | | `sku_weights` | array[integer] | 所有SKU重量列表 | |
| 565 | | `sku_weight_units` | array[string] | 所有SKU重量单位列表 | | 589 | | `sku_weight_units` | array[string] | 所有SKU重量单位列表 | |
| 566 | | `total_inventory` | integer | 总库存 | | 590 | | `total_inventory` | integer | 总库存 | |
| 591 | +| `sales` | integer | 销量(展示销量) | | ||
| 567 | | `option1_name` | string | 选项1名称(如"color") | | 592 | | `option1_name` | string | 选项1名称(如"color") | |
| 568 | | `option2_name` | string | 选项2名称(如"size") | | 593 | | `option2_name` | string | 选项2名称(如"size") | |
| 569 | | `option3_name` | string | 选项3名称 | | 594 | | `option3_name` | string | 选项3名称 | |
| @@ -605,11 +630,45 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -605,11 +630,45 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 605 | "query": "玩具", | 630 | "query": "玩具", |
| 606 | "size": 20, | 631 | "size": 20, |
| 607 | "from": 0, | 632 | "from": 0, |
| 608 | - "sort_by": "min_price", | 633 | + "sort_by": "price", |
| 609 | "sort_order": "asc" | 634 | "sort_order": "asc" |
| 610 | } | 635 | } |
| 611 | ``` | 636 | ``` |
| 612 | 637 | ||
| 638 | +**需求**: 搜索"玩具",按价格从高到低排序 | ||
| 639 | + | ||
| 640 | +```json | ||
| 641 | +{ | ||
| 642 | + "query": "玩具", | ||
| 643 | + "size": 20, | ||
| 644 | + "from": 0, | ||
| 645 | + "sort_by": "price", | ||
| 646 | + "sort_order": "desc" | ||
| 647 | +} | ||
| 648 | +``` | ||
| 649 | + | ||
| 650 | +**需求**: 搜索"玩具",按销量从高到低排序 | ||
| 651 | + | ||
| 652 | +```json | ||
| 653 | +{ | ||
| 654 | + "query": "玩具", | ||
| 655 | + "size": 20, | ||
| 656 | + "from": 0, | ||
| 657 | + "sort_by": "sales", | ||
| 658 | + "sort_order": "desc" | ||
| 659 | +} | ||
| 660 | +``` | ||
| 661 | + | ||
| 662 | +**需求**: 搜索"玩具",按默认(相关性)排序 | ||
| 663 | + | ||
| 664 | +```json | ||
| 665 | +{ | ||
| 666 | + "query": "玩具", | ||
| 667 | + "size": 20, | ||
| 668 | + "from": 0 | ||
| 669 | +} | ||
| 670 | +``` | ||
| 671 | + | ||
| 613 | ### 场景2:SKU筛选(按维度过滤) | 672 | ### 场景2:SKU筛选(按维度过滤) |
| 614 | 673 | ||
| 615 | **需求**: 搜索"芭比娃娃",每个SPU下按颜色筛选,每种颜色只显示一个SKU | 674 | **需求**: 搜索"芭比娃娃",每个SPU下按颜色筛选,每种颜色只显示一个SKU |
| @@ -650,7 +709,7 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -650,7 +709,7 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 650 | 709 | ||
| 651 | ### 场景4:带分面的商品搜索 | 710 | ### 场景4:带分面的商品搜索 |
| 652 | 711 | ||
| 653 | -**需求**: 搜索"玩具",获取类目和品牌的分面统计,用于构建筛选器 | 712 | +**需求**: 搜索"玩具",获取类目和规格的分面统计,用于构建筛选器 |
| 654 | 713 | ||
| 655 | ```json | 714 | ```json |
| 656 | { | 715 | { |
| @@ -658,9 +717,9 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -658,9 +717,9 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 658 | "size": 20, | 717 | "size": 20, |
| 659 | "language": "zh", | 718 | "language": "zh", |
| 660 | "facets": [ | 719 | "facets": [ |
| 661 | - "category1_name", | ||
| 662 | - "category2_name", | ||
| 663 | - "specifications" | 720 | + {"field": "category1_name", "size": 15, "type": "terms"}, |
| 721 | + {"field": "category2_name", "size": 10, "type": "terms"}, | ||
| 722 | + {"field": "specifications", "size": 10, "type": "terms"} | ||
| 664 | ] | 723 | ] |
| 665 | } | 724 | } |
| 666 | ``` | 725 | ``` |
| @@ -686,7 +745,8 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -686,7 +745,8 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 686 | "facets": [ | 745 | "facets": [ |
| 687 | { | 746 | { |
| 688 | "field": "category1_name", | 747 | "field": "category1_name", |
| 689 | - "size": 15 | 748 | + "size": 15, |
| 749 | + "type": "terms" | ||
| 690 | }, | 750 | }, |
| 691 | { | 751 | { |
| 692 | "field": "min_price", | 752 | "field": "min_price", |
| @@ -698,7 +758,11 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -698,7 +758,11 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 698 | {"key": "200+", "from": 200} | 758 | {"key": "200+", "from": 200} |
| 699 | ] | 759 | ] |
| 700 | }, | 760 | }, |
| 701 | - "specifications" | 761 | + { |
| 762 | + "field": "specifications", | ||
| 763 | + "size": 10, | ||
| 764 | + "type": "terms" | ||
| 765 | + } | ||
| 702 | ], | 766 | ], |
| 703 | "sort_by": "min_price", | 767 | "sort_by": "min_price", |
| 704 | "sort_order": "asc" | 768 | "sort_order": "asc" |
| @@ -768,18 +832,23 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -768,18 +832,23 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 768 | "query": "手机", | 832 | "query": "手机", |
| 769 | "size": 20, | 833 | "size": 20, |
| 770 | "language": "zh", | 834 | "language": "zh", |
| 771 | - "facets": ["specifications"] | 835 | + "facets": [ |
| 836 | + {"field": "specifications", "size": 10, "type": "terms"} | ||
| 837 | + ] | ||
| 772 | } | 838 | } |
| 773 | ``` | 839 | ``` |
| 774 | 840 | ||
| 775 | -**需求**: 只获取"color"规格的分面统计 | 841 | +**需求**: 只获取"color"和"size"规格的分面统计 |
| 776 | 842 | ||
| 777 | ```json | 843 | ```json |
| 778 | { | 844 | { |
| 779 | "query": "手机", | 845 | "query": "手机", |
| 780 | "size": 20, | 846 | "size": 20, |
| 781 | "language": "zh", | 847 | "language": "zh", |
| 782 | - "facets": ["specifications.color", "specifications.size"] | 848 | + "facets": [ |
| 849 | + {"field": "specifications.color", "size": 20, "type": "terms"}, | ||
| 850 | + {"field": "specifications.size", "size": 15, "type": "terms"} | ||
| 851 | + ] | ||
| 783 | } | 852 | } |
| 784 | ``` | 853 | ``` |
| 785 | 854 | ||
| @@ -800,10 +869,10 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | @@ -800,10 +869,10 @@ curl -X POST "http://120.76.41.98:6002/search/" \ | ||
| 800 | } | 869 | } |
| 801 | }, | 870 | }, |
| 802 | "facets": [ | 871 | "facets": [ |
| 803 | - "category1_name", | ||
| 804 | - "category2_name", | ||
| 805 | - "specifications.color", | ||
| 806 | - "specifications.size" | 872 | + {"field": "category1_name", "size": 15, "type": "terms"}, |
| 873 | + {"field": "category2_name", "size": 10, "type": "terms"}, | ||
| 874 | + {"field": "specifications.color", "size": 20, "type": "terms"}, | ||
| 875 | + {"field": "specifications.size", "size": 15, "type": "terms"} | ||
| 807 | ] | 876 | ] |
| 808 | } | 877 | } |
| 809 | ``` | 878 | ``` |
| @@ -1006,6 +1075,7 @@ curl "http://localhost:6002/search/12345" | @@ -1006,6 +1075,7 @@ curl "http://localhost:6002/search/12345" | ||
| 1006 | | `sku_weights` | long | SKU重量列表(数组) | | 1075 | | `sku_weights` | long | SKU重量列表(数组) | |
| 1007 | | `sku_weight_units` | keyword | SKU重量单位列表(数组) | | 1076 | | `sku_weight_units` | keyword | SKU重量单位列表(数组) | |
| 1008 | | `total_inventory` | long | 总库存 | | 1077 | | `total_inventory` | long | 总库存 | |
| 1078 | +| `sales` | long | 销量(展示销量) | | ||
| 1009 | | `skus` | nested | SKU详细信息(嵌套对象数组) | | 1079 | | `skus` | nested | SKU详细信息(嵌套对象数组) | |
| 1010 | | `create_time`, `update_time` | date | 创建/更新时间 | | 1080 | | `create_time`, `update_time` | date | 创建/更新时间 | |
| 1011 | | `title_embedding` | dense_vector | 标题向量(1024维,仅用于搜索) | | 1081 | | `title_embedding` | dense_vector | 标题向量(1024维,仅用于搜索) | |
| @@ -1038,11 +1108,15 @@ curl "http://localhost:6002/search/12345" | @@ -1038,11 +1108,15 @@ curl "http://localhost:6002/search/12345" | ||
| 1038 | 1108 | ||
| 1039 | #### 排序字段 | 1109 | #### 排序字段 |
| 1040 | 1110 | ||
| 1041 | -- `min_price`: 最低价格 | ||
| 1042 | -- `max_price`: 最高价格 | 1111 | +- `price`: 价格(后端自动根据sort_order映射:asc→min_price,desc→max_price) |
| 1112 | +- `sales`: 销量 | ||
| 1043 | - `create_time`: 创建时间 | 1113 | - `create_time`: 创建时间 |
| 1044 | - `update_time`: 更新时间 | 1114 | - `update_time`: 更新时间 |
| 1045 | -- `relevance_score`: 相关性分数(默认) | 1115 | +- `relevance_score`: 相关性分数(默认,不指定sort_by时使用) |
| 1116 | + | ||
| 1117 | +**注意**: 前端只需传 `price`,后端会自动处理: | ||
| 1118 | +- `sort_by: "price"` + `sort_order: "asc"` → 按 `min_price` 升序(价格从低到高) | ||
| 1119 | +- `sort_by: "price"` + `sort_order: "desc"` → 按 `max_price` 降序(价格从高到低) | ||
| 1046 | 1120 | ||
| 1047 | ### 支持的分析器 | 1121 | ### 支持的分析器 |
| 1048 | 1122 |
docs/系统设计文档.md
| @@ -699,45 +699,44 @@ Elasticsearch | @@ -699,45 +699,44 @@ Elasticsearch | ||
| 699 | 699 | ||
| 700 | #### 8.3.2 Facets 配置数据流 | 700 | #### 8.3.2 Facets 配置数据流 |
| 701 | 701 | ||
| 702 | -**输入格式**:`List[Union[str, FacetConfig]]` | 702 | +**输入格式**:`List[FacetConfig]` |
| 703 | 703 | ||
| 704 | -- **简单模式**:字符串列表(字段名),使用默认配置 | ||
| 705 | - ```json | ||
| 706 | - ["category1_name", "category2_name", "specifications"] | ||
| 707 | - ``` | ||
| 708 | - | ||
| 709 | -- **Specifications分面**: | ||
| 710 | - - 所有规格名称:`"specifications"` - 返回所有name及其value列表 | ||
| 711 | - - 指定规格名称:`"specifications.color"` - 只返回指定name的value列表 | 704 | +**配置对象列表**:所有分面配置必须使用 FacetConfig 对象 |
| 705 | +```json | ||
| 706 | +[ | ||
| 707 | + { | ||
| 708 | + "field": "category1_name", | ||
| 709 | + "size": 15, | ||
| 710 | + "type": "terms" | ||
| 711 | + }, | ||
| 712 | + { | ||
| 713 | + "field": "specifications.color", | ||
| 714 | + "size": 20, | ||
| 715 | + "type": "terms" | ||
| 716 | + }, | ||
| 717 | + { | ||
| 718 | + "field": "min_price", | ||
| 719 | + "type": "range", | ||
| 720 | + "ranges": [ | ||
| 721 | + {"key": "0-50", "to": 50}, | ||
| 722 | + {"key": "50-100", "from": 50, "to": 100} | ||
| 723 | + ] | ||
| 724 | + } | ||
| 725 | +] | ||
| 726 | +``` | ||
| 712 | 727 | ||
| 713 | -- **高级模式**:FacetConfig 对象列表,支持自定义配置 | ||
| 714 | - ```json | ||
| 715 | - [ | ||
| 716 | - { | ||
| 717 | - "field": "category1_name", | ||
| 718 | - "size": 15, | ||
| 719 | - "type": "terms" | ||
| 720 | - }, | ||
| 721 | - { | ||
| 722 | - "field": "min_price", | ||
| 723 | - "type": "range", | ||
| 724 | - "ranges": [ | ||
| 725 | - {"key": "0-50", "to": 50}, | ||
| 726 | - {"key": "50-100", "from": 50, "to": 100} | ||
| 727 | - ] | ||
| 728 | - }, | ||
| 729 | - "specifications.color" // 指定规格名称的分面 | ||
| 730 | - ] | ||
| 731 | - ``` | 728 | +**Specifications 分面支持**: |
| 729 | +- 所有规格名称:`field: "specifications"` - 返回所有 name 及其 value 列表 | ||
| 730 | +- 指定规格名称:`field: "specifications.color"` - 只返回指定 name 的 value 列表 | ||
| 732 | 731 | ||
| 733 | **数据流**: | 732 | **数据流**: |
| 734 | -1. API 层:接收 `List[Union[str, FacetConfig]]` | ||
| 735 | -2. Searcher 层:透传,不做转换 | ||
| 736 | -3. ES Query Builder:只接受 `str` 或 `FacetConfig`,自动处理两种格式 | 733 | +1. API 层:接收 `List[FacetConfig]`,Pydantic 验证参数 |
| 734 | +2. Searcher 层:透传 FacetConfig 对象列表 | ||
| 735 | +3. ES Query Builder:解析 FacetConfig 对象 | ||
| 737 | - 检测 `"specifications"` 或 `"specifications.{name}"` 格式 | 736 | - 检测 `"specifications"` 或 `"specifications.{name}"` 格式 |
| 738 | - - 构建对应的嵌套聚合查询 | ||
| 739 | -4. 输出:转换为 ES 聚合查询(包括specifications嵌套聚合) | ||
| 740 | -5. Result Formatter:格式化ES聚合结果,处理specifications嵌套结构 | 737 | + - 构建对应的嵌套聚合查询或普通聚合查询 |
| 738 | +4. 输出:转换为 ES 聚合查询(包括 specifications 嵌套聚合) | ||
| 739 | +5. Result Formatter:格式化 ES 聚合结果,处理 specifications 嵌套结构 | ||
| 741 | 740 | ||
| 742 | #### 8.3.3 Range Filters 数据流 | 741 | #### 8.3.3 Range Filters 数据流 |
| 743 | 742 |
docs/索引字段说明v2.md
| @@ -284,11 +284,12 @@ | @@ -284,11 +284,12 @@ | ||
| 284 | | `sku_weights` | long | 所有 SKU 重量列表(数组) | 从所有 SKU 重量汇总 | | 284 | | `sku_weights` | long | 所有 SKU 重量列表(数组) | 从所有 SKU 重量汇总 | |
| 285 | | `sku_weight_units` | keyword | 所有 SKU 重量单位列表(数组) | 从所有 SKU 重量单位汇总 | | 285 | | `sku_weight_units` | keyword | 所有 SKU 重量单位列表(数组) | 从所有 SKU 重量单位汇总 | |
| 286 | 286 | ||
| 287 | -### 9. 库存字段 | 287 | +### 9. 库存与销量字段 |
| 288 | 288 | ||
| 289 | | 字段名 | ES类型 | 说明 | 数据来源 | | 289 | | 字段名 | ES类型 | 说明 | 数据来源 | |
| 290 | |--------|--------|------|----------| | 290 | |--------|--------|------|----------| |
| 291 | | `total_inventory` | long | 总库存(所有 SKU 库存之和) | 从所有 SKU 库存汇总 | | 291 | | `total_inventory` | long | 总库存(所有 SKU 库存之和) | 从所有 SKU 库存汇总 | |
| 292 | +| `sales` | long | 销量(展示销量) | MySQL: `shoplazza_product_spu.fake_sales` | | ||
| 292 | 293 | ||
| 293 | ### 10. SKU 嵌套字段 | 294 | ### 10. SKU 嵌套字段 |
| 294 | 295 | ||
| @@ -363,6 +364,16 @@ | @@ -363,6 +364,16 @@ | ||
| 363 | - `vendor_zh.keyword`, `vendor_en.keyword` | 364 | - `vendor_zh.keyword`, `vendor_en.keyword` |
| 364 | - `specifications` (嵌套查询) | 365 | - `specifications` (嵌套查询) |
| 365 | - `min_price`, `max_price` (范围过滤) | 366 | - `min_price`, `max_price` (范围过滤) |
| 367 | +- `sales` (范围过滤) | ||
| 368 | +- `total_inventory` (范围过滤) | ||
| 369 | + | ||
| 370 | +### 排序字段 | ||
| 371 | + | ||
| 372 | +- `price`: 价格(前端传入,后端自动映射:asc→min_price,desc→max_price) | ||
| 373 | +- `sales`: 销量 | ||
| 374 | +- `create_time`: 创建时间 | ||
| 375 | +- `update_time`: 更新时间 | ||
| 376 | +- `relevance_score`: 相关性分数(默认) | ||
| 366 | 377 | ||
| 367 | ### 分面字段(聚合统计) | 378 | ### 分面字段(聚合统计) |
| 368 | 379 |
frontend/index.html
| @@ -94,11 +94,18 @@ | @@ -94,11 +94,18 @@ | ||
| 94 | <span class="arrow-down" data-field="create_time" data-order="asc" onclick="sortByField('create_time', 'asc')">▼</span> | 94 | <span class="arrow-down" data-field="create_time" data-order="asc" onclick="sortByField('create_time', 'asc')">▼</span> |
| 95 | </span> | 95 | </span> |
| 96 | </button> | 96 | </button> |
| 97 | - <button class="sort-btn" data-sort="min_price"> | 97 | + <button class="sort-btn" data-sort="price"> |
| 98 | By Price | 98 | By Price |
| 99 | <span class="sort-arrows"> | 99 | <span class="sort-arrows"> |
| 100 | - <span class="arrow-up" data-field="min_price" data-order="asc" onclick="sortByField('min_price', 'asc')">▲</span> | ||
| 101 | - <span class="arrow-down" data-field="min_price" data-order="desc" onclick="sortByField('min_price', 'desc')">▼</span> | 100 | + <span class="arrow-up" data-field="price" data-order="asc" onclick="sortByField('price', 'asc')">▲</span> |
| 101 | + <span class="arrow-down" data-field="price" data-order="desc" onclick="sortByField('price', 'desc')">▼</span> | ||
| 102 | + </span> | ||
| 103 | + </button> | ||
| 104 | + <button class="sort-btn" data-sort="sales"> | ||
| 105 | + By Sales | ||
| 106 | + <span class="sort-arrows"> | ||
| 107 | + <span class="arrow-up" data-field="sales" data-order="desc" onclick="sortByField('sales', 'desc')">▲</span> | ||
| 108 | + <span class="arrow-down" data-field="sales" data-order="asc" onclick="sortByField('sales', 'asc')">▼</span> | ||
| 102 | </span> | 109 | </span> |
| 103 | </button> | 110 | </button> |
| 104 | 111 | ||
| @@ -135,6 +142,6 @@ | @@ -135,6 +142,6 @@ | ||
| 135 | <p>SearchEngine © 2025 | API: <span id="apiUrl">Loading...</span></p> | 142 | <p>SearchEngine © 2025 | API: <span id="apiUrl">Loading...</span></p> |
| 136 | </footer> | 143 | </footer> |
| 137 | 144 | ||
| 138 | - <script src="/static/js/app.js?v=3.4"></script> | 145 | + <script src="/static/js/app.js?v=3.7"></script> |
| 139 | </body> | 146 | </body> |
| 140 | </html> | 147 | </html> |
frontend/static/js/app.js
| @@ -86,10 +86,10 @@ async function performSearch(page = 1) { | @@ -86,10 +86,10 @@ async function performSearch(page = 1) { | ||
| 86 | 86 | ||
| 87 | // Define facets (一级分类 + 三个属性分面) | 87 | // Define facets (一级分类 + 三个属性分面) |
| 88 | const facets = [ | 88 | const facets = [ |
| 89 | - "category1_name", // 一级分类 | ||
| 90 | - "specifications.color", // 颜色属性 | ||
| 91 | - "specifications.size", // 尺寸属性 | ||
| 92 | - "specifications.material" // 材质属性 | 89 | + { field: "category1_name", size: 15, type: "terms" }, // 一级分类 |
| 90 | + { field: "specifications.color", size: 20, type: "terms" }, // 颜色属性 | ||
| 91 | + { field: "specifications.size", size: 15, type: "terms" }, // 尺寸属性 | ||
| 92 | + { field: "specifications.material", size: 10, type: "terms" } // 材质属性 | ||
| 93 | ]; | 93 | ]; |
| 94 | 94 | ||
| 95 | // Show loading | 95 | // Show loading |
indexer/spu_transformer.py
| @@ -98,6 +98,7 @@ class SPUTransformer: | @@ -98,6 +98,7 @@ class SPUTransformer: | ||
| 98 | image_src, image_width, image_height, image_path, image_alt, | 98 | image_src, image_width, image_height, image_path, image_alt, |
| 99 | tags, note, category, category_id, category_google_id, | 99 | tags, note, category, category_id, category_google_id, |
| 100 | category_level, category_path, | 100 | category_level, category_path, |
| 101 | + fake_sales, display_fake_sales, | ||
| 101 | tenant_id, creator, create_time, updater, update_time, deleted | 102 | tenant_id, creator, create_time, updater, update_time, deleted |
| 102 | FROM shoplazza_product_spu | 103 | FROM shoplazza_product_spu |
| 103 | WHERE tenant_id = :tenant_id AND deleted = 0 | 104 | WHERE tenant_id = :tenant_id AND deleted = 0 |
| @@ -447,6 +448,15 @@ class SPUTransformer: | @@ -447,6 +448,15 @@ class SPUTransformer: | ||
| 447 | image_src = f"//{image_src}" if image_src.startswith('//') else image_src | 448 | image_src = f"//{image_src}" if image_src.startswith('//') else image_src |
| 448 | doc['image_url'] = image_src | 449 | doc['image_url'] = image_src |
| 449 | 450 | ||
| 451 | + # Sales (fake_sales) | ||
| 452 | + if pd.notna(spu_row.get('fake_sales')): | ||
| 453 | + try: | ||
| 454 | + doc['sales'] = int(spu_row['fake_sales']) | ||
| 455 | + except (ValueError, TypeError): | ||
| 456 | + doc['sales'] = 0 | ||
| 457 | + else: | ||
| 458 | + doc['sales'] = 0 | ||
| 459 | + | ||
| 450 | # Process SKUs and build specifications | 460 | # Process SKUs and build specifications |
| 451 | skus_list = [] | 461 | skus_list = [] |
| 452 | prices = [] | 462 | prices = [] |
search/es_query_builder.py
| @@ -519,7 +519,7 @@ class ESQueryBuilder: | @@ -519,7 +519,7 @@ class ESQueryBuilder: | ||
| 519 | 519 | ||
| 520 | Args: | 520 | Args: |
| 521 | es_query: Existing ES query | 521 | es_query: Existing ES query |
| 522 | - sort_by: Field name for sorting | 522 | + sort_by: Field name for sorting (支持 'price' 自动映射) |
| 523 | sort_order: Sort order: 'asc' or 'desc' | 523 | sort_order: Sort order: 'asc' or 'desc' |
| 524 | 524 | ||
| 525 | Returns: | 525 | Returns: |
| @@ -531,6 +531,13 @@ class ESQueryBuilder: | @@ -531,6 +531,13 @@ class ESQueryBuilder: | ||
| 531 | if not sort_order: | 531 | if not sort_order: |
| 532 | sort_order = "desc" | 532 | sort_order = "desc" |
| 533 | 533 | ||
| 534 | + # Auto-map 'price' to 'min_price' or 'max_price' based on sort_order | ||
| 535 | + if sort_by == "price": | ||
| 536 | + if sort_order.lower() == "asc": | ||
| 537 | + sort_by = "min_price" # 价格从低到高 | ||
| 538 | + else: | ||
| 539 | + sort_by = "max_price" # 价格从高到低 | ||
| 540 | + | ||
| 534 | if "sort" not in es_query: | 541 | if "sort" not in es_query: |
| 535 | es_query["sort"] = [] | 542 | es_query["sort"] = [] |
| 536 | 543 | ||
| @@ -546,20 +553,18 @@ class ESQueryBuilder: | @@ -546,20 +553,18 @@ class ESQueryBuilder: | ||
| 546 | 553 | ||
| 547 | def build_facets( | 554 | def build_facets( |
| 548 | self, | 555 | self, |
| 549 | - facet_configs: Optional[List[Union[str, 'FacetConfig']]] = None | 556 | + facet_configs: Optional[List['FacetConfig']] = None |
| 550 | ) -> Dict[str, Any]: | 557 | ) -> Dict[str, Any]: |
| 551 | """ | 558 | """ |
| 552 | 构建分面聚合。 | 559 | 构建分面聚合。 |
| 553 | 560 | ||
| 554 | - 支持: | ||
| 555 | - 1. 分类分面:category1_name, category2_name, category3_name, category_name | ||
| 556 | - 2. specifications分面:嵌套聚合,按name聚合,然后按value聚合 | ||
| 557 | - | ||
| 558 | Args: | 561 | Args: |
| 559 | - facet_configs: 分面配置列表(标准格式): | ||
| 560 | - - str: 字段名,使用默认 terms 配置 | ||
| 561 | - - FacetConfig: 详细的分面配置对象 | ||
| 562 | - - 特殊值 "specifications": 构建specifications嵌套分面 | 562 | + facet_configs: 分面配置对象列表 |
| 563 | + | ||
| 564 | + 支持的字段类型: | ||
| 565 | + - 普通字段: 如 "category1_name"(terms 或 range 类型) | ||
| 566 | + - specifications: "specifications"(返回所有规格名称及其值) | ||
| 567 | + - specifications.{name}: 如 "specifications.color"(返回指定规格名称的值) | ||
| 563 | 568 | ||
| 564 | Returns: | 569 | Returns: |
| 565 | ES aggregations 字典 | 570 | ES aggregations 字典 |
| @@ -570,99 +575,76 @@ class ESQueryBuilder: | @@ -570,99 +575,76 @@ class ESQueryBuilder: | ||
| 570 | aggs = {} | 575 | aggs = {} |
| 571 | 576 | ||
| 572 | for config in facet_configs: | 577 | for config in facet_configs: |
| 573 | - # 特殊处理:specifications嵌套分面 | ||
| 574 | - if isinstance(config, str): | ||
| 575 | - # 格式1: "specifications" - 返回所有name的分面 | ||
| 576 | - if config == "specifications": | ||
| 577 | - aggs["specifications_facet"] = { | ||
| 578 | - "nested": { | ||
| 579 | - "path": "specifications" | ||
| 580 | - }, | ||
| 581 | - "aggs": { | ||
| 582 | - "by_name": { | ||
| 583 | - "terms": { | ||
| 584 | - "field": "specifications.name", | ||
| 585 | - "size": 20, | ||
| 586 | - "order": {"_count": "desc"} | ||
| 587 | - }, | ||
| 588 | - "aggs": { | ||
| 589 | - "value_counts": { | ||
| 590 | - "terms": { | ||
| 591 | - "field": "specifications.value", | ||
| 592 | - "size": 10, | ||
| 593 | - "order": {"_count": "desc"} | ||
| 594 | - } | 578 | + field = config.field |
| 579 | + size = config.size | ||
| 580 | + facet_type = config.type | ||
| 581 | + | ||
| 582 | + # 处理 specifications(所有规格名称) | ||
| 583 | + if field == "specifications": | ||
| 584 | + aggs["specifications_facet"] = { | ||
| 585 | + "nested": {"path": "specifications"}, | ||
| 586 | + "aggs": { | ||
| 587 | + "by_name": { | ||
| 588 | + "terms": { | ||
| 589 | + "field": "specifications.name", | ||
| 590 | + "size": 20, | ||
| 591 | + "order": {"_count": "desc"} | ||
| 592 | + }, | ||
| 593 | + "aggs": { | ||
| 594 | + "value_counts": { | ||
| 595 | + "terms": { | ||
| 596 | + "field": "specifications.value", | ||
| 597 | + "size": size, | ||
| 598 | + "order": {"_count": "desc"} | ||
| 595 | } | 599 | } |
| 596 | } | 600 | } |
| 597 | } | 601 | } |
| 598 | } | 602 | } |
| 599 | } | 603 | } |
| 600 | - continue | ||
| 601 | - | ||
| 602 | - # 格式2: "specifications.color" 或 "specifications.颜色" - 只返回指定name的value列表 | ||
| 603 | - if config.startswith("specifications."): | ||
| 604 | - name = config[len("specifications."):] | ||
| 605 | - agg_name = f"specifications_{name}_facet" | ||
| 606 | - aggs[agg_name] = { | ||
| 607 | - "nested": { | ||
| 608 | - "path": "specifications" | ||
| 609 | - }, | ||
| 610 | - "aggs": { | ||
| 611 | - "filter_by_name": { | ||
| 612 | - "filter": { | ||
| 613 | - "term": {"specifications.name": name} | ||
| 614 | - }, | ||
| 615 | - "aggs": { | ||
| 616 | - "value_counts": { | ||
| 617 | - "terms": { | ||
| 618 | - "field": "specifications.value", | ||
| 619 | - "size": 10, | ||
| 620 | - "order": {"_count": "desc"} | ||
| 621 | - } | 604 | + } |
| 605 | + continue | ||
| 606 | + | ||
| 607 | + # 处理 specifications.{name}(指定规格名称) | ||
| 608 | + if field.startswith("specifications."): | ||
| 609 | + name = field[len("specifications."):] | ||
| 610 | + agg_name = f"specifications_{name}_facet" | ||
| 611 | + aggs[agg_name] = { | ||
| 612 | + "nested": {"path": "specifications"}, | ||
| 613 | + "aggs": { | ||
| 614 | + "filter_by_name": { | ||
| 615 | + "filter": {"term": {"specifications.name": name}}, | ||
| 616 | + "aggs": { | ||
| 617 | + "value_counts": { | ||
| 618 | + "terms": { | ||
| 619 | + "field": "specifications.value", | ||
| 620 | + "size": size, | ||
| 621 | + "order": {"_count": "desc"} | ||
| 622 | } | 622 | } |
| 623 | } | 623 | } |
| 624 | } | 624 | } |
| 625 | } | 625 | } |
| 626 | } | 626 | } |
| 627 | - continue | 627 | + } |
| 628 | + continue | ||
| 629 | + | ||
| 630 | + # 处理普通字段 | ||
| 631 | + agg_name = f"{field}_facet" | ||
| 628 | 632 | ||
| 629 | - # 简单模式:只有字段名(字符串,非specifications) | ||
| 630 | - if isinstance(config, str): | ||
| 631 | - field = config | ||
| 632 | - agg_name = f"{field}_facet" | 633 | + if facet_type == 'terms': |
| 633 | aggs[agg_name] = { | 634 | aggs[agg_name] = { |
| 634 | "terms": { | 635 | "terms": { |
| 635 | "field": field, | 636 | "field": field, |
| 636 | - "size": 10, | 637 | + "size": size, |
| 637 | "order": {"_count": "desc"} | 638 | "order": {"_count": "desc"} |
| 638 | } | 639 | } |
| 639 | } | 640 | } |
| 640 | - continue | ||
| 641 | - | ||
| 642 | - # 高级模式:FacetConfig 对象 | ||
| 643 | - else: | ||
| 644 | - # 此时 config 应该是 FacetConfig 对象 | ||
| 645 | - field = config.field | ||
| 646 | - facet_type = config.type | ||
| 647 | - size = config.size | ||
| 648 | - agg_name = f"{field}_facet" | ||
| 649 | - | ||
| 650 | - if facet_type == 'terms': | 641 | + elif facet_type == 'range': |
| 642 | + if config.ranges: | ||
| 651 | aggs[agg_name] = { | 643 | aggs[agg_name] = { |
| 652 | - "terms": { | 644 | + "range": { |
| 653 | "field": field, | 645 | "field": field, |
| 654 | - "size": size, | ||
| 655 | - "order": {"_count": "desc"} | 646 | + "ranges": config.ranges |
| 656 | } | 647 | } |
| 657 | } | 648 | } |
| 658 | - | ||
| 659 | - elif facet_type == 'range': | ||
| 660 | - if config.ranges: | ||
| 661 | - aggs[agg_name] = { | ||
| 662 | - "range": { | ||
| 663 | - "field": field, | ||
| 664 | - "ranges": config.ranges | ||
| 665 | - } | ||
| 666 | - } | ||
| 667 | 649 | ||
| 668 | return aggs | 650 | return aggs |
search/searcher.py
| @@ -17,7 +17,7 @@ from .rerank_engine import RerankEngine | @@ -17,7 +17,7 @@ from .rerank_engine import RerankEngine | ||
| 17 | from config import SearchConfig | 17 | from config import SearchConfig |
| 18 | from config.utils import get_match_fields_for_index | 18 | from config.utils import get_match_fields_for_index |
| 19 | from context.request_context import RequestContext, RequestContextStage, create_request_context | 19 | from context.request_context import RequestContext, RequestContextStage, create_request_context |
| 20 | -from api.models import FacetResult, FacetValue | 20 | +from api.models import FacetResult, FacetValue, FacetConfig |
| 21 | from api.result_formatter import ResultFormatter | 21 | from api.result_formatter import ResultFormatter |
| 22 | 22 | ||
| 23 | logger = logging.getLogger(__name__) | 23 | logger = logging.getLogger(__name__) |
| @@ -123,7 +123,7 @@ class Searcher: | @@ -123,7 +123,7 @@ class Searcher: | ||
| 123 | from_: int = 0, | 123 | from_: int = 0, |
| 124 | filters: Optional[Dict[str, Any]] = None, | 124 | filters: Optional[Dict[str, Any]] = None, |
| 125 | range_filters: Optional[Dict[str, Any]] = None, | 125 | range_filters: Optional[Dict[str, Any]] = None, |
| 126 | - facets: Optional[List[Any]] = None, | 126 | + facets: Optional[List[FacetConfig]] = None, |
| 127 | min_score: Optional[float] = None, | 127 | min_score: Optional[float] = None, |
| 128 | context: Optional[RequestContext] = None, | 128 | context: Optional[RequestContext] = None, |
| 129 | sort_by: Optional[str] = None, | 129 | sort_by: Optional[str] = None, |