be52af70
tangwang
first commit
|
1
2
3
4
|
"""
Request and response models for the API.
"""
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
5
6
7
8
9
|
from pydantic import BaseModel, Field, field_validator
from typing import List, Dict, Any, Optional, Union, Literal
class RangeFilter(BaseModel):
|
ff5325fa
tangwang
修复:直接在 Searcher 层...
|
10
11
12
13
14
|
"""范围过滤器(支持数值和日期时间字符串)"""
gte: Optional[Union[float, str]] = Field(None, description="大于等于 (>=)。数值或ISO日期时间字符串")
gt: Optional[Union[float, str]] = Field(None, description="大于 (>)。数值或ISO日期时间字符串")
lte: Optional[Union[float, str]] = Field(None, description="小于等于 (<=)。数值或ISO日期时间字符串")
lt: Optional[Union[float, str]] = Field(None, description="小于 (<)。数值或ISO日期时间字符串")
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
15
16
17
18
19
20
21
22
23
24
25
|
def model_post_init(self, __context):
"""确保至少指定一个边界值"""
if not any([self.gte, self.gt, self.lte, self.lt]):
raise ValueError('至少需要指定一个范围边界(gte, gt, lte, lt)')
class Config:
json_schema_extra = {
"examples": [
{"gte": 50, "lte": 200},
{"gt": 100},
|
ff5325fa
tangwang
修复:直接在 Searcher 层...
|
26
27
|
{"lt": 50},
{"gte": "2023-01-01T00:00:00Z"}
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
28
29
30
31
32
33
34
35
36
|
]
}
class FacetConfig(BaseModel):
"""分面配置(简化版)"""
field: str = Field(..., description="分面字段名")
size: int = Field(10, ge=1, le=100, description="返回的分面值数量")
type: Literal["terms", "range"] = Field("terms", description="分面类型")
|
9a9b9ec5
tangwang
1. facet disjunctive
|
37
|
disjunctive: bool = Field(
|
7ac1534b
tangwang
disjunctive 修改默认值...
|
38
|
False,
|
c581becd
tangwang
feat: 实现 Multi-Se...
|
39
40
|
description="是否支持多选(disjunctive faceting)。启用后,选中该分面的过滤器时,仍会显示其他可选项"
)
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
41
42
43
44
45
46
47
48
49
|
ranges: Optional[List[Dict[str, Any]]] = Field(
None,
description="范围分面的范围定义(仅当 type='range' 时需要)"
)
class Config:
json_schema_extra = {
"examples": [
{
|
cadc77b6
tangwang
索引字段名、变量名、API数据结构...
|
50
|
"field": "category.keyword",
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
"size": 15,
"type": "terms"
},
{
"field": "price",
"size": 4,
"type": "range",
"ranges": [
{"key": "0-50", "to": 50},
{"key": "50-100", "from": 50, "to": 100},
{"key": "100-200", "from": 100, "to": 200},
{"key": "200+", "from": 200}
]
}
]
}
|
be52af70
tangwang
first commit
|
67
68
69
|
class SearchRequest(BaseModel):
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
70
71
72
73
|
"""搜索请求模型(重构版)"""
# 基础搜索参数
query: str = Field(..., description="搜索查询字符串,支持布尔表达式(AND, OR, RANK, ANDNOT)")
|
e7ad2b4a
tangwang
测试页面分页配置
|
74
|
size: int = Field(10, ge=1, le=1000, description="返回结果数量")
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
75
|
from_: int = Field(0, ge=0, alias="from", description="分页偏移量")
|
577ec972
tangwang
返回给前端的字段、格式适配。主要包...
|
76
77
78
79
|
language: Literal["zh", "en"] = Field(
"zh",
description="响应语言:'zh'(中文)或 'en'(英文),用于选择 title/description/vendor 等多语言字段"
)
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
80
81
|
# 过滤器 - 精确匹配和多值匹配
|
f7d3cf70
tangwang
更新文档
|
82
|
filters: Optional[Dict[str, Union[str, int, bool, List[Union[str, int]], Dict[str, Any], List[Dict[str, Any]]]]] = Field(
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
83
|
None,
|
985d7fe3
tangwang
为 filters 中所有字段加上...
|
84
|
description="精确匹配过滤器。单值表示精确匹配,数组表示 OR 匹配(匹配任意一个值)。字段名加 _all 后缀表示 AND(如 tags_all: ['A','B'] 表示同时包含 A 和 B)。支持 specifications 嵌套过滤:{\"specifications\": {\"name\": \"color\", \"value\": \"green\"}} 或 [{\"name\": \"color\", \"value\": \"green\"}, ...];specifications_all 表示列表内所有规格条件都要满足。",
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
85
86
87
|
json_schema_extra={
"examples": [
{
|
f7d3cf70
tangwang
更新文档
|
88
|
"category_name": ["手机", "电子产品"],
|
d7d48f52
tangwang
改动(mapping + 灌入结构)
|
89
|
"vendor.zh.keyword": "奇乐",
|
f7d3cf70
tangwang
更新文档
|
90
91
92
93
94
95
96
|
"specifications": {"name": "颜色", "value": "白色"}
},
{
"specifications": [
{"name": "颜色", "value": "白色"},
{"name": "尺寸", "value": "256GB"}
]
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
}
]
}
)
# 范围过滤器 - 数值范围
range_filters: Optional[Dict[str, RangeFilter]] = Field(
None,
description="数值范围过滤器。支持 gte, gt, lte, lt 操作符",
json_schema_extra={
"examples": [
{
"price": {"gte": 50, "lte": 200},
"days_since_last_update": {"lte": 30}
}
]
}
)
# 排序
|
13320ac6
tangwang
分面接口修改:
|
117
118
|
sort_by: Optional[str] = Field(None, description="排序字段名。支持:'price'(价格,自动根据sort_order选择min_price或max_price)、'sales'(销量)、'create_time'(创建时间)、'update_time'(更新时间)")
sort_order: Optional[str] = Field("desc", description="排序方向: 'asc'(升序)或 'desc'(降序)。注意:price+asc=价格从低到高,price+desc=价格从高到低")
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
119
|
|
13320ac6
tangwang
分面接口修改:
|
120
121
|
# 分面搜索
facets: Optional[List[FacetConfig]] = Field(
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
122
|
None,
|
13320ac6
tangwang
分面接口修改:
|
123
|
description="分面配置对象列表。支持 specifications 分面:field=\"specifications\"(所有规格名称)或 field=\"specifications.color\"(指定规格名称)",
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
124
125
|
json_schema_extra={
"examples": [
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
126
|
[
|
13320ac6
tangwang
分面接口修改:
|
127
128
129
130
131
132
133
|
{"field": "category1_name", "size": 15, "type": "terms"},
{"field": "category2_name", "size": 10, "type": "terms"},
{"field": "specifications.color", "size": 20, "type": "terms"},
{"field": "specifications.size", "size": 15, "type": "terms"}
],
[
{"field": "category1_name", "size": 15, "type": "terms"},
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
134
|
{
|
f7d3cf70
tangwang
更新文档
|
135
|
"field": "min_price",
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
136
137
138
|
"type": "range",
"ranges": [
{"key": "0-50", "to": 50},
|
13320ac6
tangwang
分面接口修改:
|
139
140
141
|
{"key": "50-100", "from": 50, "to": 100},
{"key": "100-200", "from": 100, "to": 200},
{"key": "200+", "from": 200}
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
142
|
]
|
f7d3cf70
tangwang
更新文档
|
143
|
},
|
13320ac6
tangwang
分面接口修改:
|
144
|
{"field": "specifications", "size": 10, "type": "terms"}
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
145
146
147
148
149
150
151
152
153
|
]
]
}
)
# 高级选项
min_score: Optional[float] = Field(None, ge=0, description="最小相关性分数阈值")
highlight: bool = Field(False, description="是否高亮搜索关键词(暂不实现)")
debug: bool = Field(False, description="是否返回调试信息")
|
ff32d894
tangwang
rerank
|
154
|
enable_rerank: bool = Field(
|
506c39b7
tangwang
feat(search): 统一重...
|
155
|
False,
|
ff32d894
tangwang
rerank
|
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
description="是否开启重排(调用外部重排服务对 ES 结果进行二次排序)"
)
rerank_query_template: Optional[str] = Field(
None,
description=(
"重排 query 模板(可选)。支持 {query} 占位符。"
"不传则使用服务端配置的 rerank_query_template。"
),
)
rerank_doc_template: Optional[str] = Field(
None,
description=(
"重排 doc 模板(可选)。支持 {title} {brief} {vendor} {description} {category_path} 占位符。"
"不传则使用服务端配置的 rerank_doc_template。"
),
|
506c39b7
tangwang
feat(search): 统一重...
|
171
|
)
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
172
|
|
ca91352a
tangwang
更新文档
|
173
|
# SKU筛选参数
|
a3a5d41b
tangwang
(sku_filter_dimen...
|
174
|
sku_filter_dimension: Optional[List[str]] = Field(
|
001b4889
tangwang
1. docs
|
175
|
["option1"],
|
a3a5d41b
tangwang
(sku_filter_dimen...
|
176
177
178
179
180
181
|
description=(
"子SKU筛选维度(店铺配置),为字符串列表。"
"指定后,每个SPU下的SKU将按这些维度的组合进行分组,每个维度组合只保留一个SKU返回。"
"例如:['color'] 表示按颜色分组,每种颜色选一款;['color', 'size'] 表示按颜色+尺码组合分组。"
"支持的值:'option1'、'option2'、'option3' 或选项名称(如 'color'、'size',将通过 option1_name/2_name/3_name 匹配)。"
)
|
ca91352a
tangwang
更新文档
|
182
183
|
)
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
184
185
186
|
# 个性化参数(预留)
user_id: Optional[str] = Field(None, description="用户ID,用于个性化搜索和推荐")
session_id: Optional[str] = Field(None, description="会话ID,用于搜索分析")
|
be52af70
tangwang
first commit
|
187
188
189
|
class ImageSearchRequest(BaseModel):
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
"""图片搜索请求模型"""
image_url: str = Field(..., description="查询图片的 URL")
size: int = Field(10, ge=1, le=100, description="返回结果数量")
filters: Optional[Dict[str, Union[str, int, bool, List[Union[str, int]]]]] = None
range_filters: Optional[Dict[str, RangeFilter]] = None
class SearchSuggestRequest(BaseModel):
"""搜索建议请求模型(框架,暂不实现)"""
query: str = Field(..., min_length=1, description="搜索查询字符串")
size: int = Field(5, ge=1, le=20, description="返回建议数量")
types: List[Literal["query", "product", "category", "brand"]] = Field(
["query"],
description="建议类型:query(查询建议), product(商品建议), category(类目建议), brand(品牌建议)"
)
|
ded6f29e
tangwang
补充suggestion模块
|
205
206
207
|
language: Optional[str] = Field(None, description="请求语言(如 zh/en/ar/ru)")
with_results: bool = Field(True, description="是否返回每条 suggestion 的直达商品结果")
result_size: int = Field(3, ge=1, le=10, description="每条 suggestion 返回商品数量")
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
class FacetValue(BaseModel):
"""分面值"""
value: Union[str, int, float] = Field(..., description="分面值")
label: Optional[str] = Field(None, description="显示标签(如果与 value 不同)")
count: int = Field(..., description="匹配的文档数量")
selected: bool = Field(False, description="是否已选中(当前过滤器中)")
class FacetResult(BaseModel):
"""分面结果(标准化格式)"""
field: str = Field(..., description="字段名")
label: str = Field(..., description="分面显示名称")
type: Literal["terms", "range"] = Field(..., description="分面类型")
values: List[FacetValue] = Field(..., description="分面值列表")
total_count: Optional[int] = Field(None, description="该字段的总文档数")
|
be52af70
tangwang
first commit
|
225
226
|
|
cadc77b6
tangwang
索引字段名、变量名、API数据结构...
|
227
228
229
|
class SkuResult(BaseModel):
"""SKU 结果"""
sku_id: str = Field(..., description="SKU ID")
|
577ec972
tangwang
返回给前端的字段、格式适配。主要包...
|
230
|
# 与 ES nested skus 结构对齐
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
231
232
|
price: Optional[float] = Field(None, description="价格")
compare_at_price: Optional[float] = Field(None, description="原价")
|
577ec972
tangwang
返回给前端的字段、格式适配。主要包...
|
233
|
sku_code: Optional[str] = Field(None, description="SKU编码")
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
234
|
stock: int = Field(0, description="库存数量")
|
577ec972
tangwang
返回给前端的字段、格式适配。主要包...
|
235
236
237
238
239
240
|
weight: Optional[float] = Field(None, description="重量")
weight_unit: Optional[str] = Field(None, description="重量单位")
option1_value: Optional[str] = Field(None, description="选项1取值(如颜色)")
option2_value: Optional[str] = Field(None, description="选项2取值(如尺码)")
option3_value: Optional[str] = Field(None, description="选项3取值")
image_src: Optional[str] = Field(None, description="SKU图片地址")
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
241
242
|
|
cadc77b6
tangwang
索引字段名、变量名、API数据结构...
|
243
244
245
|
class SpuResult(BaseModel):
"""SPU 搜索结果"""
spu_id: str = Field(..., description="SPU ID")
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
246
|
title: Optional[str] = Field(None, description="商品标题")
|
577ec972
tangwang
返回给前端的字段、格式适配。主要包...
|
247
|
brief: Optional[str] = Field(None, description="商品短描述")
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
248
249
250
|
handle: Optional[str] = Field(None, description="商品handle")
description: Optional[str] = Field(None, description="商品描述")
vendor: Optional[str] = Field(None, description="供应商/品牌")
|
577ec972
tangwang
返回给前端的字段、格式适配。主要包...
|
251
252
253
254
255
256
257
258
|
category: Optional[str] = Field(None, description="类目(兼容字段,等同于category_name)")
category_path: Optional[str] = Field(None, description="类目路径(多级,用于面包屑)")
category_name: Optional[str] = Field(None, description="类目名称(展示用)")
category_id: Optional[str] = Field(None, description="类目ID")
category_level: Optional[int] = Field(None, description="类目层级")
category1_name: Optional[str] = Field(None, description="一级类目名称")
category2_name: Optional[str] = Field(None, description="二级类目名称")
category3_name: Optional[str] = Field(None, description="三级类目名称")
|
bf89b597
tangwang
feat(search): ada...
|
259
|
tags: Optional[List[str]] = Field(None, description="标签列表")
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
260
261
262
263
264
|
price: Optional[float] = Field(None, description="价格(min_price)")
compare_at_price: Optional[float] = Field(None, description="原价")
currency: str = Field("USD", description="货币单位")
image_url: Optional[str] = Field(None, description="主图URL")
in_stock: bool = Field(True, description="是否有库存")
|
577ec972
tangwang
返回给前端的字段、格式适配。主要包...
|
265
266
267
268
269
270
271
272
273
274
275
276
|
# SKU 扁平化信息
sku_prices: Optional[List[float]] = Field(None, description="所有SKU价格列表")
sku_weights: Optional[List[int]] = Field(None, description="所有SKU重量列表")
sku_weight_units: Optional[List[str]] = Field(None, description="所有SKU重量单位列表")
total_inventory: Optional[int] = Field(None, description="总库存")
option1_name: Optional[str] = Field(None, description="选项1名称(如颜色)")
option2_name: Optional[str] = Field(None, description="选项2名称(如尺码)")
option3_name: Optional[str] = Field(None, description="选项3名称")
specifications: Optional[List[Dict[str, Any]]] = Field(
None,
description="规格列表(与 ES specifications 字段对应)"
)
|
cadc77b6
tangwang
索引字段名、变量名、API数据结构...
|
277
|
skus: List[SkuResult] = Field(default_factory=list, description="SKU列表")
|
f0577ce4
tangwang
fix last up
|
278
|
relevance_score: float = Field(..., ge=0.0, description="相关性分数(ES原始分数)")
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
279
280
|
|
be52af70
tangwang
first commit
|
281
|
class SearchResponse(BaseModel):
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
282
|
"""搜索响应模型(外部友好格式)"""
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
283
284
|
# 核心结果
|
cadc77b6
tangwang
索引字段名、变量名、API数据结构...
|
285
|
results: List[SpuResult] = Field(..., description="搜索结果列表")
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
|
total: int = Field(..., description="匹配的总文档数")
max_score: float = Field(..., description="最高相关性分数")
# 分面搜索结果(标准化格式)
facets: Optional[List[FacetResult]] = Field(
None,
description="分面统计结果(标准化格式)"
)
# 查询信息
query_info: Dict[str, Any] = Field(
default_factory=dict,
description="查询处理信息(原始查询、改写、语言检测、翻译等)"
)
|
1f6d15fa
tangwang
重构:SPU级别索引、统一索引架构...
|
301
302
303
|
# 推荐与建议
suggestions: List[str] = Field(default_factory=list, description="搜索建议")
related_searches: List[str] = Field(default_factory=list, description="相关搜索")
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
304
305
306
307
308
309
310
311
312
313
314
315
|
# 性能指标
took_ms: int = Field(..., description="搜索总耗时(毫秒)")
performance_info: Optional[Dict[str, Any]] = Field(None, description="详细性能信息")
# 调试信息
debug_info: Optional[Dict[str, Any]] = Field(None, description="调试信息(仅当 debug=True)")
class SearchSuggestResponse(BaseModel):
"""搜索建议响应模型(框架,暂不实现)"""
query: str = Field(..., description="原始查询")
|
ded6f29e
tangwang
补充suggestion模块
|
316
317
|
language: Optional[str] = Field(None, description="请求语言")
resolved_language: Optional[str] = Field(None, description="服务端解析后的语言")
|
6aa246be
tangwang
问题:Pydantic 应该能自动...
|
318
319
|
suggestions: List[Dict[str, Any]] = Field(..., description="建议列表")
took_ms: int = Field(..., description="耗时(毫秒)")
|
be52af70
tangwang
first commit
|
320
321
322
323
324
325
326
327
328
329
330
331
|
class DocumentResponse(BaseModel):
"""Single document response model."""
id: str = Field(..., description="Document ID")
source: Dict[str, Any] = Field(..., description="Document source")
class HealthResponse(BaseModel):
"""Health check response model."""
status: str = Field(..., description="Service status")
elasticsearch: str = Field(..., description="Elasticsearch status")
|
be52af70
tangwang
first commit
|
332
333
334
335
336
337
|
class ErrorResponse(BaseModel):
"""Error response model."""
error: str = Field(..., description="Error message")
detail: Optional[str] = Field(None, description="Detailed error information")
|