Commit 1c5366f5e46d0d8ed4bd7de0c9cbbfbb9fbbc884
1 parent
9d0214bb
query分析性能优化
Showing
12 changed files
with
3179 additions
and
291 deletions
Show diff stats
| ... | ... | @@ -0,0 +1,43 @@ |
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | +工程(金伟)配合修改: | |
| 7 | + | |
| 8 | + | |
| 9 | +一、tags字段改支持多语言: | |
| 10 | +spu表tags字段,跟title走一样的翻译逻辑,填入原始语言、zh、en。 | |
| 11 | + | |
| 12 | +检查以下字段,都跟title一样走翻译逻辑 | |
| 13 | +title | |
| 14 | +keywords | |
| 15 | +tags | |
| 16 | +brief | |
| 17 | +description | |
| 18 | +vendor | |
| 19 | +category_path | |
| 20 | +category_name_text | |
| 21 | + | |
| 22 | + | |
| 23 | +二、/indexer/enrich-content接口的修改 | |
| 24 | +1. 请求参数,把language去掉,因为我返回的内容直接对应索引结构,不用你做处理了,因此不需要指定语言,降低耦合。 | |
| 25 | +2. 返回 enriched_attributes enriched_tags qanchors三个字段,按原始内容填入。 | |
| 26 | +3. enriched_tags是本次新增的,注意区别于tags字段。tags字段来源于mysql spu表,enriched_tags是本接口返回的。 | |
| 27 | + | |
| 28 | + | |
| 29 | +三、specifications的value,需要翻译,也是需要填中英文: | |
| 30 | +{ | |
| 31 | + "specifications": [ | |
| 32 | + { | |
| 33 | + "sku_id": "sku-red-s", | |
| 34 | + "name": "color", | |
| 35 | + "value_keyword": "красный", | |
| 36 | + "value_text": { | |
| 37 | + "zh": "红色", | |
| 38 | + "en": "red" | |
| 39 | + } | |
| 40 | + } | |
| 41 | + ] | |
| 42 | +} | |
| 43 | + | ... | ... |
| ... | ... | @@ -0,0 +1,264 @@ |
| 1 | + | |
| 2 | +总体的目的是: | |
| 3 | +1)要对原始query进行翻译(通常是en/zh的一种或者两种) | |
| 4 | +2)对原始query要有关键词提取(关键词提取可能依赖分词;中英文分词要不一样,各自寻求最优性能的方法。zh的可以保持不变,en的可以优化) | |
| 5 | +3)其他的一些任务可能依赖分词 | |
| 6 | +4)获取text embedding/clip embedding | |
| 7 | + | |
| 8 | + | |
| 9 | +英文关键词提取:走spacy进行关键词提取(即主干分析)。提取出query中的核心词,用于搜索时候的term求交,其余的词不参与求交、只用于权重计算。 | |
| 10 | + | |
| 11 | + | |
| 12 | +实现: | |
| 13 | + | |
| 14 | +# Query 模块说明 | |
| 15 | + | |
| 16 | +本目录实现搜索请求侧的**查询理解与解析**:在不做 Elasticsearch 语言计划拼装的前提下,产出可供检索层、重排层与调试界面消费的**结构化事实**(规范化文本、检测语言、可选翻译、文本与 CLIP 向量、分词与关键词、可选的样式意图与标题排除配置等)。下面按**当前实现**说明策略与数据流,便于与 `search/`、`context/`、`frontend/` 对照阅读。 | |
| 17 | + | |
| 18 | +--- | |
| 19 | + | |
| 20 | +## 包内文件与职责 | |
| 21 | + | |
| 22 | +| 文件 | 作用 | | |
| 23 | +|------|------| | |
| 24 | +| `query_parser.py` | 入口 `QueryParser`:编排规范化、改写、语言检测、异步翻译与向量、分词、关键词、意图与排除检测;定义 `ParsedQuery`。 | | |
| 25 | +| `tokenization.py` | 轻量分词、文本规范化、`TokenizedText` 与按请求复用的 `QueryTextAnalysisCache`(模型分词与语言提示、粗细分词策略)。 | | |
| 26 | +| `keyword_extractor.py` | `KeywordExtractor`:中文走 HanLP 分词 + 词性名词串;英文走 spaCy 核心词;`collect_keywords_queries` 汇总 `base` 与各翻译语种。 | | |
| 27 | +| `english_keyword_extractor.py` | `EnglishKeywordExtractor`:`en_core_web_sm` + 依存/名词块规则,产出短字符串供检索侧关键词子句使用。 | | |
| 28 | +| `language_detector.py` | 脚本优先 + Lingua 的通用语言检测(与 `QueryParser` 的英文 ASCII 快路径配合使用)。 | | |
| 29 | +| `query_rewriter.py` | 基于配置词典的查询改写与规范化。 | | |
| 30 | +| `style_intent.py` | 从配置加载样式意图词表,对查询变体做候选匹配,产出 `StyleIntentProfile`。 | | |
| 31 | +| `product_title_exclusion.py` | 从配置加载标题排除规则,对多路查询文本做触发词匹配,产出 `ProductTitleExclusionProfile`。 | | |
| 32 | + | |
| 33 | +公开符号见 `query/__init__.py`(`QueryParser`、`ParsedQuery`、`KEYWORDS_QUERY_BASE_KEY` 等)。 | |
| 34 | + | |
| 35 | +--- | |
| 36 | + | |
| 37 | +## 解析产物:`ParsedQuery` | |
| 38 | + | |
| 39 | +`ParsedQuery` 是单次 `parse()` 的权威结果容器,字段含义与下游约定如下。 | |
| 40 | + | |
| 41 | +- **`original_query` / `query_normalized` / `rewritten_query`**:分别为原始输入、规范化后、词典改写后的主查询文本;后续翻译、向量、默认分词与 `base` 关键词均以**改写后的 `rewritten_query`(在代码变量中常名为 `query_text`)**为基准。 | |
| 42 | +- **`detected_language`**:解析时认定的源语言代码;若检测为 `unknown` 或空,则回退到 `SearchConfig.query_config.default_language`。 | |
| 43 | +- **`translations`**:键为**目标语言代码**(如 `zh`、`en`),值为翻译服务返回的字符串;仅包含本次请求实际需要的目标语种(见下文翻译目标推导)。 | |
| 44 | +- **`query_vector` / `image_query_vector`**:分别为 BGE 类文本向量与 CLIP 文本向量(维度由各自编码服务决定);未生成或未在超时内完成则为 `None`。 | |
| 45 | +- **`query_tokens`**:对**改写后主查询**做分词后的字符串列表,供例如 KNN 参数按 token 数分支等逻辑使用;分词路径由 `QueryTextAnalysisCache` 决定(纯拉丁英文可走轻量分词,含汉字则走 HanLP)。 | |
| 46 | +- **`keywords_queries`**:与「主查询 + 各翻译变体」平行的**关键词子查询**映射:键 `base`(常量 `KEYWORDS_QUERY_BASE_KEY`)对应源语言侧关键词串,其它键与 `translations` 的语种键一致。空串或无法提取的条目**不会写入**字典。 | |
| 47 | +- **`style_intent_profile` / `product_title_exclusion_profile`**:可选的理解结果;是否生效完全由 `config.yaml` 中 `query_config` 的对应开关与词表/规则决定。 | |
| 48 | +- **`_text_analysis_cache`**:单次解析内的分词与语言提示缓存,**不参与序列化**,仅供同一次 `parse` 内各检测器复用,避免对同一文本重复调用 HanLP。 | |
| 49 | + | |
| 50 | +与重排相关的文本选择由独立函数 `rerank_query_text()` 完成:检测为 `zh` 或 `en` 时始终用原始查询;其它语言优先英译再中译,见 `query_parser.py` 中实现。 | |
| 51 | + | |
| 52 | +--- | |
| 53 | + | |
| 54 | +## `QueryParser.parse()` 的执行顺序与策略 | |
| 55 | + | |
| 56 | +解析主流程在 `QueryParser.parse()` 中实现。整体目标是:在**共享等待预算**下并行完成翻译与向量请求,同时尽量减少主线程上重复、昂贵的分词与 NLP 调用,并把结果写入可选的 `context`(请求上下文)供日志与 `debug_info` 使用。 | |
| 57 | + | |
| 58 | +### 1. 规范化与改写 | |
| 59 | + | |
| 60 | +- 使用 `QueryNormalizer` 得到 `query_normalized` 并可选写入 `context.store_intermediate_result('query_normalized', ...)`。 | |
| 61 | +- 若配置了改写词典,则用 `QueryRewriter` 可能更新 `query_text`;改写成功时记录 `rewritten_query` 与告警。 | |
| 62 | + | |
| 63 | +### 2. 语言检测:通用路径与英文 ASCII 快路径 | |
| 64 | + | |
| 65 | +- **快路径**:当「活跃语种集合」仅为 `en` 与 `zh` 的子集时(活跃集合取 `target_languages` 归一化结果,若为空则回退到 `query_config.supported_languages`),且当前查询为**纯 ASCII、含字母、不含汉字**,则**直接判定为 `en`**,不再调用 `LanguageDetector`(避免 Lingua 等开销)。逻辑见 `_detect_query_language()` 与 `_is_ascii_latin_query()`。 | |
| 66 | + | |
| 67 | +```303:317:query/query_parser.py | |
| 68 | + def _detect_query_language( | |
| 69 | + self, | |
| 70 | + query_text: str, | |
| 71 | + *, | |
| 72 | + target_languages: Optional[List[str]] = None, | |
| 73 | + ) -> str: | |
| 74 | + normalized_targets = self._normalize_language_codes(target_languages) | |
| 75 | + supported_languages = self._normalize_language_codes( | |
| 76 | + getattr(self.config.query_config, "supported_languages", None) | |
| 77 | + ) | |
| 78 | + active_languages = normalized_targets or supported_languages | |
| 79 | + if active_languages and set(active_languages).issubset({"en", "zh"}): | |
| 80 | + if self._is_ascii_latin_query(query_text): | |
| 81 | + return "en" | |
| 82 | + return self.language_detector.detect(query_text) | |
| 83 | +``` | |
| 84 | + | |
| 85 | +- **通用路径**:`LanguageDetector` 先按 Unicode 脚本返回明确语种(如汉字块即 `zh`),否则用 Lingua 在一大组语言中判别,见 `language_detector.py`。 | |
| 86 | + | |
| 87 | +检测最终结果写入 `context.store_intermediate_result('detected_language', ...)`(若提供 `context`)。 | |
| 88 | + | |
| 89 | +### 3. 按请求分词缓存与语言提示 | |
| 90 | + | |
| 91 | +每次 `parse` 会新建 `QueryTextAnalysisCache(tokenizer=self._tokenizer)`,并对**原始串、规范化串、改写后串**调用 `set_language_hint(..., detected_lang)`,使后续对同一文本的 `get_tokenizer_result` / `get_tokenized_text` 能按语言选择**是否调用 HanLP**。 | |
| 92 | + | |
| 93 | +### 4. HanLP 模型(与 `KeywordExtractor` 对齐) | |
| 94 | + | |
| 95 | +`QueryParser` 默认构建的 `self._tokenizer` 为 HanLP 预训练分词模型 **`FINE_ELECTRA_SMALL_ZH`**,并开启 `output_spans=True`,以便与关键词提取共用「带偏移的分词结果」。 | |
| 96 | + | |
| 97 | +```237:245:query/query_parser.py | |
| 98 | + def _build_tokenizer(self) -> Callable[[str], Any]: | |
| 99 | + """Build the tokenizer used by query parsing. No fallback path by design.""" | |
| 100 | + if hanlp is None: | |
| 101 | + raise RuntimeError("HanLP is required for QueryParser tokenization") | |
| 102 | + logger.info("Initializing HanLP tokenizer...") | |
| 103 | + tokenizer = hanlp.load(hanlp.pretrained.tok.FINE_ELECTRA_SMALL_ZH) | |
| 104 | + tokenizer.config.output_spans = True | |
| 105 | + logger.info("HanLP tokenizer initialized") | |
| 106 | + return tokenizer | |
| 107 | +``` | |
| 108 | + | |
| 109 | +`KeywordExtractor` 在未注入自定义 `tokenizer` 时同样加载 **`FINE_ELECTRA_SMALL_ZH`**,并额外加载 **`CTB9_POS_ELECTRA_SMALL`** 做词性标注;二者在「中文路径」上语义一致,便于复用 `tokenizer_result`。 | |
| 110 | + | |
| 111 | +### 5. 异步富集:翻译、文本向量、CLIP 文本向量 | |
| 112 | + | |
| 113 | +- 翻译目标:`translation_targets = normalized_targets` 中**去掉与检测源语言相同**的代码后的列表(例如源为 `en` 且索引语言为 `["en","zh"]` 时只翻 `zh`)。 | |
| 114 | +- 翻译模型名:由 `_pick_query_translation_model()` 根据「源语言是否在索引语言内」及 `zh↔en` 等分支从 `QueryConfig` 选取。 | |
| 115 | +- 当 `generate_vector` 为真且配置开启文本嵌入时,向线程池提交 `text_encoder.encode([query_text], ...)`;当配置了 `image_embedding_field` 时提交 `image_encoder.encode_clip_text(query_text, ...)`。 | |
| 116 | +- 线程池:`ThreadPoolExecutor`,`max_workers` 为 `min(任务数, 4)` 与至少 1。 | |
| 117 | +- **提交顺序**:先尽可能提交所有异步任务,再在主线程上做「与异步重叠」的轻量工作(见下一节),最后 `concurrent.futures.wait(..., timeout=budget_sec)`。超时未完成的任务会记 warning,并 `shutdown(wait=False)` 不阻塞关闭线程池。 | |
| 118 | + | |
| 119 | +等待预算(毫秒)来自 `QueryConfig`: | |
| 120 | + | |
| 121 | +- 源语言在索引语言内:`translation_embedding_wait_budget_ms_source_in_index` | |
| 122 | +- 否则:`translation_embedding_wait_budget_ms_source_not_in_index` | |
| 123 | + | |
| 124 | +完成每个 future 后打 `Async enrichment task finished` 日志(含 `elapsed_ms`,为从提交到完成的大致墙钟时间)。 | |
| 125 | + | |
| 126 | +### 6. 主查询分词与「base」关键词(与异步重叠) | |
| 127 | + | |
| 128 | +在异步任务已提交之后、`wait()` 之前,当前实现会: | |
| 129 | + | |
| 130 | +1. 通过 `text_analysis_cache.get_tokenizer_result(query_text)` 得到分词结果,再 `extract_token_strings` 得到 **`query_tokens`**; | |
| 131 | +2. 调用 `KeywordExtractor.extract_keywords(query_text, language_hint=detected_lang, tokenizer_result=...)` 得到 **`keywords_base_query`**(若失败则日志告警,base 关键词可能为空)。 | |
| 132 | + | |
| 133 | +这样主线程在等翻译/向量时,已并行完成源侧分词与源侧关键词的大部分工作。 | |
| 134 | + | |
| 135 | +### 7. 等待结束后的关键词汇总与检测器 | |
| 136 | + | |
| 137 | +`wait()` 返回后: | |
| 138 | + | |
| 139 | +- 若有翻译结果,写入 `context.store_intermediate_result("translations", translations)`,并对每条翻译 `text_analysis_cache.set_language_hint(result, lang)`,保证后续对该翻译串的分词/关键词走正确语言路径。 | |
| 140 | +- `collect_keywords_queries(...)` 合并 **`base`**(可传入已算好的 `base_keywords_query` 避免重复抽取)与各翻译语种的关键词,得到 **`keywords_queries`**;成功时 `context.store_intermediate_result("keywords_queries", keywords_queries)` 并打 `Keyword extraction completed` 日志。 | |
| 141 | +- 构造带 `_text_analysis_cache` 的 `ParsedQuery` 草稿,依次调用 `StyleIntentDetector.detect` 与 `ProductTitleExclusionDetector.detect`,再把完整 `ParsedQuery` 返回。 | |
| 142 | + | |
| 143 | +解析阶段会打聚合耗时日志 `Query parse stage timings`,字段含义为: | |
| 144 | + | |
| 145 | +- **`before_wait_ms`**:从解析开始计时点到进入 `wait()` 之前的主线程耗时(含规范化、改写、语言检测、提交异步任务、主查询分词、base 关键词等); | |
| 146 | +- **`async_wait_ms`**:`wait()` 阻塞时间; | |
| 147 | +- **`base_keywords_ms`**:base 关键词抽取耗时; | |
| 148 | +- **`keyword_tail_ms`**:`collect_keywords_queries` 及前后尾部逻辑中关键词相关部分的主要耗时; | |
| 149 | +- **`tail_sync_ms`**:`wait()` 之后整段同步尾巴(含关键词汇总、两检测器、写中间结果等)。 | |
| 150 | + | |
| 151 | +--- | |
| 152 | + | |
| 153 | +## 分词与 `QueryTextAnalysisCache` | |
| 154 | + | |
| 155 | +### `get_tokenizer_result`:何时走 HanLP,何时走轻量分词 | |
| 156 | + | |
| 157 | +- 若未配置模型 `tokenizer`,直接返回空列表路径的轻量结果(由上层避免依赖)。 | |
| 158 | +- 若根据**该文本的语言提示**与**是否含汉字**判断不需要模型:返回 `simple_tokenize_query` 的列表(字符串 token),**不调用 HanLP**。 | |
| 159 | +- 否则对该文本调用一次 `self.tokenizer(text)`(HanLP),结果按文本缓存,同一次 `parse` 内重复访问同一字符串不会重复推理。 | |
| 160 | + | |
| 161 | +核心判断在 `_should_use_model_tokenizer`:**语言提示为 `zh` 时,仅当文本含汉字才用模型**;非 `zh` 提示时,仅当文本含汉字才用模型。因此纯英文主查询在提示为 `en` 时走轻量分词;中文翻译串在 `set_language_hint(..., "zh")` 且含汉字时走 HanLP。 | |
| 162 | + | |
| 163 | +### `coarse_tokens` 与 `fine_tokens`:`TokenizedText` | |
| 164 | + | |
| 165 | +- **`fine_tokens`**:来自 `extract_token_strings(get_tokenizer_result(...))`,在中文路径上即 HanLP 分词后的词串(已按规范化键去重保序)。 | |
| 166 | +- **`coarse_tokens`**:由 `_build_coarse_tokens` 决定。若语言提示为 **`zh`**,或文本含汉字且已有 `tokenizer_tokens`,则 **粗粒度 token 与 fine 一致**(即采用模型分词粒度,而不用「整段 CJK 连成一项」的纯正则策略)。否则使用 `simple_tokenize_query`(适合拉丁词、数字、带连字符/撇号的英文词形)。 | |
| 167 | + | |
| 168 | +```92:103:query/tokenization.py | |
| 169 | +def _build_coarse_tokens( | |
| 170 | + text: str, | |
| 171 | + *, | |
| 172 | + language_hint: Optional[str], | |
| 173 | + tokenizer_tokens: Sequence[str], | |
| 174 | +) -> List[str]: | |
| 175 | + normalized_language = normalize_query_text(language_hint) | |
| 176 | + if normalized_language == "zh" or (contains_han_text(text) and tokenizer_tokens): | |
| 177 | + # Chinese coarse tokenization should follow the model tokenizer rather than a | |
| 178 | + # regex that collapses the whole sentence into one CJK span. | |
| 179 | + return list(_dedupe_preserve_order(tokenizer_tokens)) | |
| 180 | + return _dedupe_preserve_order(simple_tokenize_query(text)) | |
| 181 | +``` | |
| 182 | + | |
| 183 | +- **`candidates`**:在 fine、coarse、两类 n-gram 短语(上限由 `max_ngram` 控制)以及整句 `normalized_text` 上合并去重,供 `StyleIntentDetector`、`ProductTitleExclusionDetector` 等做子串/短语级匹配。 | |
| 184 | + | |
| 185 | +`tokenize_text()` 是对单次无缓存场景的薄封装:内部新建 `QueryTextAnalysisCache` 再 `get_tokenized_text`。 | |
| 186 | + | |
| 187 | +--- | |
| 188 | + | |
| 189 | +## 关键词提取:`KeywordExtractor` 与 `collect_keywords_queries` | |
| 190 | + | |
| 191 | +### 路由规则 | |
| 192 | + | |
| 193 | +`extract_keywords` 根据 `language_hint` 分支: | |
| 194 | + | |
| 195 | +- **`en`**:完全交给 `EnglishKeywordExtractor`(spaCy),**不使用** HanLP 分词结果做 POS 名词筛选(即使调用方传入 `tokenizer_result` 也会被忽略在该路径内)。 | |
| 196 | +- **`zh`**:使用 HanLP 分词结果(优先复用传入的 `tokenizer_result`),再对词序列跑 `CTB9_POS_ELECTRA_SMALL`,保留**长度 ≥ 2 且词性以 `N` 开头**的词;非连续名词之间插入空格拼接成一条字符串(与 ES 侧 `keywords_query` 的用法一致)。 | |
| 197 | +- **其它非空语言码**:当前实现返回空串,即**不为该语种生成关键词子句**(由调用方决定是否跳过)。 | |
| 198 | + | |
| 199 | +### `collect_keywords_queries` | |
| 200 | + | |
| 201 | +- 键 **`base`**:对应 `rewritten_query` 的关键词;若调用方已预先计算 `base_keywords_query` 则直接写入,避免重复抽取。 | |
| 202 | +- 其它键:与 `translations` 中每个非空语种一一对应,语言码归一化为小写。 | |
| 203 | +- 全程可传入 `text_analysis_cache`,以便 `get_tokenizer_result` 命中缓存并与检测器共享分词结果。 | |
| 204 | + | |
| 205 | +常量 `KEYWORDS_QUERY_BASE_KEY` 的值为字符串 **`"base"`**,与检索构建里读取的字段一致。 | |
| 206 | + | |
| 207 | +--- | |
| 208 | + | |
| 209 | +## 英文关键词:`EnglishKeywordExtractor` | |
| 210 | + | |
| 211 | +- 依赖 **spaCy** 模型 **`en_core_web_sm`**,加载时关闭 `ner`、`textcat` 以减轻开销;加载失败时记录 warning 并走基于 `simple_tokenize_query` 的回退策略。 | |
| 212 | +- 主路径用依存句法与名词块规则收集一小组「核心词」候选(如直接宾语名词、部分 ROOT 名词/专有名词、INTJ 结构下的宾语等),并处理价格/目的介词宾语降级、人口学名词(如 `women`)弱化、尺寸类 ROOT 与主语搭配等边界情况。 | |
| 213 | +- 使用 `_project_terms_to_query_tokens` 将 spaCy 词形映射回查询中的**表面分词**(例如复合词 `t-shirt`),避免在关键词串中出现被错误切断的片段。 | |
| 214 | + | |
| 215 | +最终返回**最多三个词**的空格连接字符串,用于检索侧第二层 `combined_fields` 的紧凑查询(见下节)。 | |
| 216 | + | |
| 217 | +--- | |
| 218 | + | |
| 219 | +## 与检索层的关系(消费方摘要) | |
| 220 | + | |
| 221 | +`ParsedQuery.keywords_queries` 由 `search/es_query_builder.py` 读取:在构建某一语言的 lexical 子句时,除主 `combined_fields`(完整 `query`)外,若存在非空的 `keywords_query` 且与主查询不同,会追加第二个 `combined_fields`,使用单独的 `minimum_should_match`(由 builder 的 `keywords_minimum_should_match` 配置)和较低 boost,从而在**不替代全文查询**的前提下加强核心词匹配。 | |
| 222 | + | |
| 223 | +`query_tokens` 在同文件中间接影响例如带文本向量时的 KNN 分支参数(按 token 数量选用长查询的 k / num_candidates 等)。具体字段与 boost 以 `ESQueryBuilder` 当前实现为准。 | |
| 224 | + | |
| 225 | +--- | |
| 226 | + | |
| 227 | +## 样式意图与标题排除(简要) | |
| 228 | + | |
| 229 | +- **`StyleIntentRegistry` / `StyleIntentDetector`**:从 `QueryConfig.style_intent_terms` 等加载意图定义;`detect` 时按中英变体取查询文本,经 `tokenize_text` 或缓存得到 `TokenizedText`,在 `candidates` 上与配置同义词表匹配,输出 `StyleIntentProfile`(含 `query_variants` 与命中意图列表)。 | |
| 230 | +- **`ProductTitleExclusionRegistry` / `ProductTitleExclusionDetector`**:从 `QueryConfig.product_title_exclusion_rules` 加载规则;对 `original_query`、`query_normalized`、`rewritten_query` 及所有 `translations` 去重后分词匹配触发词,输出 `ProductTitleExclusionProfile`。 | |
| 231 | + | |
| 232 | +二者均依赖 `tokenization` 与可选的 HanLP,启用与否由配置项控制。 | |
| 233 | + | |
| 234 | +--- | |
| 235 | + | |
| 236 | +## 可观测性与调试 | |
| 237 | + | |
| 238 | +当 `QueryParser.parse(..., context=...)` 传入请求上下文时,典型中间结果包括: | |
| 239 | + | |
| 240 | +- `query_normalized`、`rewritten_query`、`detected_language`、`query_tokens` | |
| 241 | +- `translation_{lang}`、`translations` | |
| 242 | +- `keywords_queries` | |
| 243 | +- `query_vector_shape`、`image_query_vector_shape` | |
| 244 | +- `style_intent_profile`、`product_title_exclusion_profile` | |
| 245 | + | |
| 246 | +搜索主流程在 `search/searcher.py` 中会把解析结果写入 `QueryAnalysisResult`(含 **`keywords_queries`**),并在 `debug=true` 时把 `query_analysis` 挂到响应的 `debug_info`;前端调试页在 `frontend/static/js/app.js` 中展示 **Translations** 与 **Keywords Queries** 等块,便于与翻译结果并列查看。 | |
| 247 | + | |
| 248 | +--- | |
| 249 | + | |
| 250 | +## 依赖与环境提示 | |
| 251 | + | |
| 252 | +- **HanLP**:分词与中文词性标注;模型名以本文与源码为准(`FINE_ELECTRA_SMALL_ZH` + `CTB9_POS_ELECTRA_SMALL`)。 | |
| 253 | +- **spaCy**:英文关键词路径需要可导入的 **`en_core_web_sm`**(若缺失则英文关键词退化为轻量规则)。 | |
| 254 | +- **Lingua**:通用语言检测(在英文 ASCII 快路径不适用时参与拉丁语系判别)。 | |
| 255 | + | |
| 256 | +运行与测试时请使用项目约定的虚拟环境(见仓库根目录 `CLAUDE.md` / `activate.sh`),避免系统 Python 缺少上述依赖。 | |
| 257 | + | |
| 258 | +--- | |
| 259 | + | |
| 260 | +## 扩展与测试 | |
| 261 | + | |
| 262 | +- 单元测试中与解析、分词、意图相关的用例分布在 `tests/test_query_parser_mixed_language.py`、`tests/test_tokenization.py`、`tests/test_style_intent.py`、`tests/test_product_title_exclusion.py` 等文件中;修改分词或关键词策略时应同步更新或新增测试,以保持与本文描述一致。 | |
| 263 | + | |
| 264 | +若新增语种或改写语言检测策略,应同步审视:`QueryParser._detect_query_language`、`QueryTextAnalysisCache._should_use_model_tokenizer`、`KeywordExtractor.extract_keywords` 中非 `zh`/`en` 分支,以及 ES 侧是否应为新语种生成 `keywords_query`。 | ... | ... |
docs/搜索API对接指南-01-搜索接口.md
| ... | ... | @@ -24,6 +24,76 @@ headers = { |
| 24 | 24 | response = requests.post(url, headers=headers, json={"query": "芭比娃娃"}) |
| 25 | 25 | ``` |
| 26 | 26 | |
| 27 | +**cURL 示例(复制即可试)** | |
| 28 | + | |
| 29 | +将 `BASE_URL`、`TENANT_ID` 换成你的环境与租户。本地开发默认后端为 `http://localhost:6002`(见 `scripts/start_backend.sh`)。 | |
| 30 | + | |
| 31 | +```bash | |
| 32 | +export BASE_URL="${BASE_URL:-http://localhost:6002}" | |
| 33 | +export TENANT_ID="${TENANT_ID:-162}" # 改成真实租户 | |
| 34 | +``` | |
| 35 | + | |
| 36 | +**1)基础检索**:关键词、分页、`language` 控制返回字段语言。 | |
| 37 | + | |
| 38 | +```bash | |
| 39 | +curl -sS "$BASE_URL/search/" \ | |
| 40 | + -H "Content-Type: application/json" \ | |
| 41 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 42 | + -d '{ | |
| 43 | + "query": "芭比娃娃", | |
| 44 | + "size": 20, | |
| 45 | + "from": 0, | |
| 46 | + "language": "zh" | |
| 47 | + }' | |
| 48 | +``` | |
| 49 | + | |
| 50 | +**2)过滤 + 价格区间 + 分面 + 排序**:联调最常见组合(品牌 OR、`min_price` 区间、类目与颜色分面、按最低价升序)。 | |
| 51 | + | |
| 52 | +```bash | |
| 53 | +curl -sS "$BASE_URL/search/" \ | |
| 54 | + -H "Content-Type: application/json" \ | |
| 55 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 56 | + -d '{ | |
| 57 | + "query": "手机", | |
| 58 | + "size": 20, | |
| 59 | + "from": 0, | |
| 60 | + "language": "zh", | |
| 61 | + "filters": { | |
| 62 | + "vendor.zh.keyword": ["品牌A", "品牌B"] | |
| 63 | + }, | |
| 64 | + "range_filters": { | |
| 65 | + "min_price": {"gte": 50, "lte": 200} | |
| 66 | + }, | |
| 67 | + "facets": [ | |
| 68 | + {"field": "category1_name", "size": 15, "type": "terms"}, | |
| 69 | + {"field": "specifications.color", "size": 20, "type": "terms"} | |
| 70 | + ], | |
| 71 | + "sort_by": "min_price", | |
| 72 | + "sort_order": "asc" | |
| 73 | + }' | |
| 74 | +``` | |
| 75 | + | |
| 76 | +**3)规格过滤 + SKU 维度 + 调试**:`specifications` 嵌套过滤、`sku_filter_dimension` 每组只保留一个 SKU;`debug: true` 便于对照 ES 与解析结果。 | |
| 77 | + | |
| 78 | +```bash | |
| 79 | +curl -sS "$BASE_URL/search/" \ | |
| 80 | + -H "Content-Type: application/json" \ | |
| 81 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 82 | + -d '{ | |
| 83 | + "query": "手机", | |
| 84 | + "size": 10, | |
| 85 | + "from": 0, | |
| 86 | + "language": "zh", | |
| 87 | + "filters": { | |
| 88 | + "specifications": {"name": "color", "value": "white"} | |
| 89 | + }, | |
| 90 | + "sku_filter_dimension": ["color"], | |
| 91 | + "debug": true | |
| 92 | + }' | |
| 93 | +``` | |
| 94 | + | |
| 95 | +需要美化 JSON 输出时,在命令末尾追加 ` | jq .`(需本机安装 [jq](https://jqlang.github.io/jq/))。 | |
| 96 | + | |
| 27 | 97 | ### 3.2 请求参数 |
| 28 | 98 | |
| 29 | 99 | #### 完整请求体结构 |
| ... | ... | @@ -897,5 +967,19 @@ response = requests.post(url, headers=headers, json={"query": "芭比娃娃"}) |
| 897 | 967 | } |
| 898 | 968 | ``` |
| 899 | 969 | |
| 970 | +**cURL(第 2 页,每页 20 条)**: | |
| 971 | + | |
| 972 | +```bash | |
| 973 | +curl -sS "${BASE_URL:-http://localhost:6002}/search/" \ | |
| 974 | + -H "Content-Type: application/json" \ | |
| 975 | + -H "X-Tenant-ID: ${TENANT_ID:-162}" \ | |
| 976 | + -d '{ | |
| 977 | + "query": "手机", | |
| 978 | + "size": 20, | |
| 979 | + "from": 20, | |
| 980 | + "language": "zh" | |
| 981 | + }' | |
| 982 | +``` | |
| 983 | + | |
| 900 | 984 | --- |
| 901 | 985 | ... | ... |
docs/搜索API对接指南-速查表.md
| 1 | 1 | # API 快速参考 (v3.0) |
| 2 | 2 | |
| 3 | +## 环境与 cURL | |
| 4 | + | |
| 5 | +- **默认地址**:`http://localhost:6002`(与仓库 `scripts/start_backend.sh` 一致)。 | |
| 6 | +- **租户**:必须使用请求头 **`X-Tenant-ID`**(推荐);一般不要依赖把 `tenant_id` 放在 URL 上。 | |
| 7 | +- 下列命令假设已设置: | |
| 8 | + | |
| 9 | +```bash | |
| 10 | +export BASE_URL="${BASE_URL:-http://localhost:6002}" | |
| 11 | +export TENANT_ID="${TENANT_ID:-163}" # 改成你的租户ID | |
| 12 | +``` | |
| 13 | + | |
| 3 | 14 | ## 基础搜索 |
| 4 | 15 | |
| 5 | 16 | ```bash |
| 6 | -POST /search/ | |
| 7 | -{ | |
| 8 | - "query": "芭比娃娃", | |
| 9 | - "size": 20 | |
| 10 | -} | |
| 17 | +curl -sS "$BASE_URL/search/" \ | |
| 18 | + -H "Content-Type: application/json" \ | |
| 19 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 20 | + -d '{ | |
| 21 | + "query": "芭比娃娃", | |
| 22 | + "size": 20, | |
| 23 | + "from": 0, | |
| 24 | + "language": "zh" | |
| 25 | + }' | |
| 11 | 26 | ``` |
| 12 | 27 | |
| 28 | +等同请求:`POST /search/`,请求体含 `query`、`size`、`from`、`language`。 | |
| 29 | + | |
| 13 | 30 | --- |
| 14 | 31 | |
| 15 | 32 | ## 精确匹配过滤 |
| 16 | 33 | |
| 34 | +在 `POST /search/` 的 JSON 里使用 `filters`(下面示例与「完整示例」可对照字段含义): | |
| 35 | + | |
| 17 | 36 | ```bash |
| 18 | -{ | |
| 19 | - "filters": { | |
| 20 | - "category_name": "手机", // 单值 | |
| 21 | - "category1_name": "服装", // 一级类目 | |
| 22 | - "vendor.zh.keyword": ["奇乐", "品牌A"], // 多值(OR) | |
| 23 | - "tags": "手机", // 标签 | |
| 24 | - // specifications 嵌套过滤 | |
| 25 | - "specifications": { | |
| 26 | - "name": "color", | |
| 27 | - "value": "white" | |
| 37 | +curl -sS "$BASE_URL/search/" \ | |
| 38 | + -H "Content-Type: application/json" \ | |
| 39 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 40 | + -d '{ | |
| 41 | + "query": "手机", | |
| 42 | + "size": 10, | |
| 43 | + "language": "zh", | |
| 44 | + "filters": { | |
| 45 | + "category_name": "手机", | |
| 46 | + "category1_name": "服装", | |
| 47 | + "vendor.zh.keyword": ["奇乐", "品牌A"], | |
| 48 | + "tags": "手机", | |
| 49 | + "specifications": {"name": "color", "value": "white"} | |
| 28 | 50 | } |
| 29 | - } | |
| 30 | -} | |
| 51 | + }' | |
| 31 | 52 | ``` |
| 32 | 53 | |
| 33 | 54 | ### Specifications 过滤 |
| 34 | 55 | |
| 35 | -**单个规格**: | |
| 56 | +**单个规格**: | |
| 57 | + | |
| 36 | 58 | ```bash |
| 37 | -{ | |
| 38 | - "filters": { | |
| 39 | - "specifications": {"name": "color", "value": "white"} | |
| 40 | - } | |
| 41 | -} | |
| 59 | +curl -sS "$BASE_URL/search/" \ | |
| 60 | + -H "Content-Type: application/json" \ | |
| 61 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 62 | + -d '{ | |
| 63 | + "query": "手机", | |
| 64 | + "size": 10, | |
| 65 | + "language": "zh", | |
| 66 | + "filters": { | |
| 67 | + "specifications": {"name": "color", "value": "white"} | |
| 68 | + } | |
| 69 | + }' | |
| 42 | 70 | ``` |
| 43 | 71 | |
| 44 | -**多个规格(按维度分组)**: | |
| 72 | +**多个规格(不同 name 为 AND)**: | |
| 73 | + | |
| 45 | 74 | ```bash |
| 46 | -{ | |
| 47 | - "filters": { | |
| 48 | - "specifications": [ | |
| 49 | - {"name": "color", "value": "white"}, | |
| 50 | - {"name": "size", "value": "256GB"} | |
| 51 | - ] | |
| 52 | - } | |
| 53 | -} | |
| 75 | +curl -sS "$BASE_URL/search/" \ | |
| 76 | + -H "Content-Type: application/json" \ | |
| 77 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 78 | + -d '{ | |
| 79 | + "query": "手机", | |
| 80 | + "size": 10, | |
| 81 | + "language": "zh", | |
| 82 | + "filters": { | |
| 83 | + "specifications": [ | |
| 84 | + {"name": "color", "value": "white"}, | |
| 85 | + {"name": "size", "value": "256GB"} | |
| 86 | + ] | |
| 87 | + } | |
| 88 | + }' | |
| 54 | 89 | ``` |
| 55 | 90 | 说明:不同维度(不同name)是AND关系,相同维度(相同name)的多个值是OR关系。 |
| 56 | 91 | |
| ... | ... | @@ -59,14 +94,17 @@ POST /search/ |
| 59 | 94 | ## 范围过滤 |
| 60 | 95 | |
| 61 | 96 | ```bash |
| 62 | -{ | |
| 63 | - "range_filters": { | |
| 64 | - "min_price": { | |
| 65 | - "gte": 50, // >= | |
| 66 | - "lte": 200 // <= | |
| 97 | +curl -sS "$BASE_URL/search/" \ | |
| 98 | + -H "Content-Type: application/json" \ | |
| 99 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 100 | + -d '{ | |
| 101 | + "query": "玩具", | |
| 102 | + "size": 20, | |
| 103 | + "language": "zh", | |
| 104 | + "range_filters": { | |
| 105 | + "min_price": {"gte": 50, "lte": 200} | |
| 67 | 106 | } |
| 68 | - } | |
| 69 | -} | |
| 107 | + }' | |
| 70 | 108 | ``` |
| 71 | 109 | |
| 72 | 110 | **操作符**: `gte` (>=), `gt` (>), `lte` (<=), `lt` (<) |
| ... | ... | @@ -78,47 +116,73 @@ POST /search/ |
| 78 | 116 | ### 简单模式 |
| 79 | 117 | |
| 80 | 118 | ```bash |
| 81 | -{ | |
| 82 | - "facets": ["category1_name", "category2_name", "category3_name", "specifications"] | |
| 83 | -} | |
| 119 | +curl -sS "$BASE_URL/search/" \ | |
| 120 | + -H "Content-Type: application/json" \ | |
| 121 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 122 | + -d '{ | |
| 123 | + "query": "玩具", | |
| 124 | + "size": 20, | |
| 125 | + "language": "zh", | |
| 126 | + "facets": ["category1_name", "category2_name", "category3_name", "specifications"] | |
| 127 | + }' | |
| 84 | 128 | ``` |
| 85 | 129 | |
| 86 | 130 | ### Specifications 分面 |
| 87 | 131 | |
| 88 | -**所有规格名称**: | |
| 132 | +**所有规格名称**: | |
| 133 | + | |
| 89 | 134 | ```bash |
| 90 | -{ | |
| 91 | - "facets": ["specifications"] // 返回所有name及其value列表 | |
| 92 | -} | |
| 135 | +curl -sS "$BASE_URL/search/" \ | |
| 136 | + -H "Content-Type: application/json" \ | |
| 137 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 138 | + -d '{ | |
| 139 | + "query": "手机", | |
| 140 | + "size": 10, | |
| 141 | + "language": "zh", | |
| 142 | + "facets": ["specifications"] | |
| 143 | + }' | |
| 93 | 144 | ``` |
| 94 | 145 | |
| 95 | -**指定规格名称**: | |
| 146 | +**指定规格名称**: | |
| 147 | + | |
| 96 | 148 | ```bash |
| 97 | -{ | |
| 98 | - "facets": ["specifications.color", "specifications.size", "specifications.material"] // 只返回指定name的value列表 | |
| 99 | -} | |
| 149 | +curl -sS "$BASE_URL/search/" \ | |
| 150 | + -H "Content-Type: application/json" \ | |
| 151 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 152 | + -d '{ | |
| 153 | + "query": "手机", | |
| 154 | + "size": 10, | |
| 155 | + "language": "zh", | |
| 156 | + "facets": ["specifications.color", "specifications.size", "specifications.material"] | |
| 157 | + }' | |
| 100 | 158 | ``` |
| 101 | 159 | |
| 102 | 160 | ### 高级模式 |
| 103 | 161 | |
| 104 | 162 | ```bash |
| 105 | -{ | |
| 106 | - "facets": [ | |
| 107 | - {"field": "category1_name", "size": 15}, | |
| 108 | - { | |
| 109 | - "field": "min_price", | |
| 110 | - "type": "range", | |
| 111 | - "ranges": [ | |
| 112 | - {"key": "0-50", "to": 50}, | |
| 113 | - {"key": "50-100", "from": 50, "to": 100} | |
| 114 | - ] | |
| 115 | - }, | |
| 116 | - "specifications", // 所有规格名称 | |
| 117 | - "specifications.color", // 指定规格名称 | |
| 118 | - "specifications.size", | |
| 119 | - "specifications.material" | |
| 120 | - ] | |
| 121 | -} | |
| 163 | +curl -sS "$BASE_URL/search/" \ | |
| 164 | + -H "Content-Type: application/json" \ | |
| 165 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 166 | + -d '{ | |
| 167 | + "query": "手机", | |
| 168 | + "size": 20, | |
| 169 | + "language": "zh", | |
| 170 | + "facets": [ | |
| 171 | + {"field": "category1_name", "size": 15}, | |
| 172 | + { | |
| 173 | + "field": "min_price", | |
| 174 | + "type": "range", | |
| 175 | + "ranges": [ | |
| 176 | + {"key": "0-50", "to": 50}, | |
| 177 | + {"key": "50-100", "from": 50, "to": 100} | |
| 178 | + ] | |
| 179 | + }, | |
| 180 | + "specifications", | |
| 181 | + "specifications.color", | |
| 182 | + "specifications.size", | |
| 183 | + "specifications.material" | |
| 184 | + ] | |
| 185 | + }' | |
| 122 | 186 | ``` |
| 123 | 187 | |
| 124 | 188 | --- |
| ... | ... | @@ -128,12 +192,19 @@ POST /search/ |
| 128 | 192 | **功能**: 按指定维度对每个SPU下的SKU进行分组,每组只返回第一个SKU。 |
| 129 | 193 | |
| 130 | 194 | ```bash |
| 131 | -{ | |
| 132 | - "query": "芭比娃娃", | |
| 133 | - "sku_filter_dimension": "color" // 按颜色筛选(假设option1_name="color") | |
| 134 | -} | |
| 195 | +curl -sS "$BASE_URL/search/" \ | |
| 196 | + -H "Content-Type: application/json" \ | |
| 197 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 198 | + -d '{ | |
| 199 | + "query": "芭比娃娃", | |
| 200 | + "size": 20, | |
| 201 | + "language": "zh", | |
| 202 | + "sku_filter_dimension": ["color"] | |
| 203 | + }' | |
| 135 | 204 | ``` |
| 136 | 205 | |
| 206 | +(`sku_filter_dimension` 为数组;按颜色筛选时需索引里 `option*_name` 与维度一致,见正文说明。) | |
| 207 | + | |
| 137 | 208 | **支持的维度值**: |
| 138 | 209 | - `option1`, `option2`, `option3`: 直接使用选项字段 |
| 139 | 210 | - 规格名称(如 `color`, `size`): 通过 `option1_name`、`option2_name`、`option3_name` 匹配 |
| ... | ... | @@ -157,10 +228,16 @@ POST /search/ |
| 157 | 228 | ## 排序 |
| 158 | 229 | |
| 159 | 230 | ```bash |
| 160 | -{ | |
| 161 | - "sort_by": "min_price", | |
| 162 | - "sort_order": "asc" // asc 或 desc | |
| 163 | -} | |
| 231 | +curl -sS "$BASE_URL/search/" \ | |
| 232 | + -H "Content-Type: application/json" \ | |
| 233 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 234 | + -d '{ | |
| 235 | + "query": "玩具", | |
| 236 | + "size": 20, | |
| 237 | + "language": "zh", | |
| 238 | + "sort_by": "min_price", | |
| 239 | + "sort_order": "asc" | |
| 240 | + }' | |
| 164 | 241 | ``` |
| 165 | 242 | |
| 166 | 243 | --- |
| ... | ... | @@ -168,46 +245,57 @@ POST /search/ |
| 168 | 245 | ## 分页 |
| 169 | 246 | |
| 170 | 247 | ```bash |
| 171 | -{ | |
| 172 | - "size": 20, // 每页数量 | |
| 173 | - "from": 0 // 偏移量(第1页=0,第2页=20) | |
| 174 | -} | |
| 248 | +curl -sS "$BASE_URL/search/" \ | |
| 249 | + -H "Content-Type: application/json" \ | |
| 250 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 251 | + -d '{ | |
| 252 | + "query": "手机", | |
| 253 | + "size": 20, | |
| 254 | + "from": 20, | |
| 255 | + "language": "zh" | |
| 256 | + }' | |
| 175 | 257 | ``` |
| 176 | 258 | |
| 259 | +(第 1 页 `from: 0`,第 2 页 `from: 20`,以此类推。) | |
| 260 | + | |
| 177 | 261 | --- |
| 178 | 262 | |
| 179 | 263 | ## 完整示例 |
| 180 | 264 | |
| 265 | +一键联调:过滤 + 区间 + 分面 + 排序 + SKU 维度(请按实际类目/规格改 `filters`)。 | |
| 266 | + | |
| 181 | 267 | ```bash |
| 182 | -POST /search/ | |
| 183 | -Headers: X-Tenant-ID: 162 | |
| 184 | -{ | |
| 185 | - "query": "手机", | |
| 186 | - "size": 20, | |
| 187 | - "from": 0, | |
| 188 | - "language": "zh", | |
| 189 | - "filters": { | |
| 190 | - "category_name": "手机", | |
| 191 | - "category1_name": "电子产品", | |
| 192 | - "specifications": {"name": "color", "value": "white"} | |
| 193 | - }, | |
| 194 | - "range_filters": { | |
| 195 | - "min_price": {"gte": 50, "lte": 200} | |
| 196 | - }, | |
| 197 | - "facets": [ | |
| 198 | - {"field": "category1_name", "size": 15}, | |
| 199 | - {"field": "category2_name", "size": 15}, | |
| 200 | - "specifications.color", | |
| 201 | - "specifications.size" | |
| 202 | - ], | |
| 203 | - "sort_by": "min_price", | |
| 204 | - "sort_order": "asc", | |
| 205 | - "sku_filter_dimension": "color" // 可选:按颜色筛选SKU | |
| 206 | -} | |
| 268 | +curl -sS "$BASE_URL/search/" \ | |
| 269 | + -H "Content-Type: application/json" \ | |
| 270 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 271 | + -d '{ | |
| 272 | + "query": "手机", | |
| 273 | + "size": 20, | |
| 274 | + "from": 0, | |
| 275 | + "language": "zh", | |
| 276 | + "filters": { | |
| 277 | + "category_name": "手机", | |
| 278 | + "category1_name": "电子产品", | |
| 279 | + "specifications": {"name": "color", "value": "white"} | |
| 280 | + }, | |
| 281 | + "range_filters": { | |
| 282 | + "min_price": {"gte": 50, "lte": 200} | |
| 283 | + }, | |
| 284 | + "facets": [ | |
| 285 | + {"field": "category1_name", "size": 15}, | |
| 286 | + {"field": "category2_name", "size": 15}, | |
| 287 | + "specifications.color", | |
| 288 | + "specifications.size" | |
| 289 | + ], | |
| 290 | + "sort_by": "min_price", | |
| 291 | + "sort_order": "asc", | |
| 292 | + "sku_filter_dimension": ["color"] | |
| 293 | + }' | |
| 207 | 294 | ``` |
| 208 | 295 | |
| 209 | 296 | --- |
| 210 | 297 | |
| 298 | + | |
| 211 | 299 | ## 响应格式 |
| 212 | 300 | |
| 213 | 301 | ```json |
| ... | ... | @@ -275,26 +363,60 @@ Headers: X-Tenant-ID: 162 |
| 275 | 363 | |
| 276 | 364 | ## 其他端点 |
| 277 | 365 | |
| 366 | +**图搜**(`POST /search/image`,与文本搜一样带 `X-Tenant-ID`): | |
| 367 | + | |
| 278 | 368 | ```bash |
| 279 | -POST /search/image | |
| 280 | -{ | |
| 281 | - "image_url": "https://example.com/image.jpg", | |
| 282 | - "size": 20 | |
| 283 | -} | |
| 369 | +curl -sS "$BASE_URL/search/image" \ | |
| 370 | + -H "Content-Type: application/json" \ | |
| 371 | + -H "X-Tenant-ID: $TENANT_ID" \ | |
| 372 | + -d '{ | |
| 373 | + "image_url": "https://example.com/product.jpg", | |
| 374 | + "size": 20, | |
| 375 | + "range_filters": { | |
| 376 | + "min_price": {"gte": 10, "lte": 500} | |
| 377 | + } | |
| 378 | + }' | |
| 379 | +``` | |
| 380 | + | |
| 381 | +(图搜请求体仅支持 `image_url`、`size`、可选的 `filters` / `range_filters`;与文本搜的 `language` / `from` 无关。) | |
| 382 | + | |
| 383 | +**搜索建议**: | |
| 384 | + | |
| 385 | +```bash | |
| 386 | +curl -sS -G "$BASE_URL/search/suggestions" \ | |
| 387 | + --data-urlencode "q=芭" \ | |
| 388 | + --data-urlencode "size=5" \ | |
| 389 | + --data-urlencode "language=zh" \ | |
| 390 | + -H "X-Tenant-ID: $TENANT_ID" | |
| 391 | +``` | |
| 284 | 392 | |
| 285 | -GET /search/suggestions?q=芭&size=5&language=zh | |
| 393 | +**即时搜索**(当前实现返回 501,勿用于生产): | |
| 286 | 394 | |
| 287 | -GET /search/instant?q=玩具&size=5 # 当前返回 501 Not Implemented | |
| 395 | +```bash | |
| 396 | +curl -sS -G "$BASE_URL/search/instant" \ | |
| 397 | + --data-urlencode "q=玩具" \ | |
| 398 | + --data-urlencode "size=5" \ | |
| 399 | + -H "X-Tenant-ID: $TENANT_ID" | |
| 400 | +``` | |
| 288 | 401 | |
| 289 | -GET /search/{doc_id} | |
| 402 | +**按文档 ID 取详情**(将 `YOUR_SPU_ID` 换成真实 `spu_id`): | |
| 290 | 403 | |
| 291 | -GET /admin/health | |
| 292 | -GET /admin/config | |
| 293 | -GET /admin/stats # 需 X-Tenant-ID 或 tenant_id | |
| 404 | +```bash | |
| 405 | +curl -sS "$BASE_URL/search/YOUR_SPU_ID" \ | |
| 406 | + -H "X-Tenant-ID: $TENANT_ID" | |
| 407 | +``` | |
| 408 | + | |
| 409 | +**运维**: | |
| 410 | + | |
| 411 | +```bash | |
| 412 | +curl -sS "$BASE_URL/admin/health" | |
| 413 | +curl -sS "$BASE_URL/admin/config" | |
| 414 | +curl -sS "$BASE_URL/admin/stats" -H "X-Tenant-ID: $TENANT_ID" | |
| 294 | 415 | ``` |
| 295 | 416 | |
| 296 | 417 | --- |
| 297 | 418 | |
| 419 | + | |
| 298 | 420 | ## Python 快速示例 |
| 299 | 421 | |
| 300 | 422 | ```python | ... | ... |
| ... | ... | @@ -0,0 +1,251 @@ |
| 1 | +# Query 模块说明 | |
| 2 | + | |
| 3 | +本目录实现搜索请求侧的**查询理解与解析**:在不做 Elasticsearch 语言计划拼装的前提下,产出可供检索层、重排层与调试界面消费的**结构化事实**(规范化文本、检测语言、可选翻译、文本与 CLIP 向量、分词与关键词、可选的样式意图与标题排除配置等)。下面按**当前实现**说明策略与数据流,便于与 `search/`、`context/`、`frontend/` 对照阅读。 | |
| 4 | + | |
| 5 | +--- | |
| 6 | + | |
| 7 | +## 包内文件与职责 | |
| 8 | + | |
| 9 | +| 文件 | 作用 | | |
| 10 | +|------|------| | |
| 11 | +| `query_parser.py` | 入口 `QueryParser`:编排规范化、改写、语言检测、异步翻译与向量、分词、关键词、意图与排除检测;定义 `ParsedQuery`。 | | |
| 12 | +| `tokenization.py` | 轻量分词、文本规范化、`TokenizedText` 与按请求复用的 `QueryTextAnalysisCache`(模型分词与语言提示、粗细分词策略)。 | | |
| 13 | +| `keyword_extractor.py` | `KeywordExtractor`:中文走 HanLP 分词 + 词性名词串;英文走 spaCy 核心词;`collect_keywords_queries` 汇总 `base` 与各翻译语种。 | | |
| 14 | +| `english_keyword_extractor.py` | `EnglishKeywordExtractor`:`en_core_web_sm` + 依存/名词块规则,产出短字符串供检索侧关键词子句使用。 | | |
| 15 | +| `language_detector.py` | 脚本优先 + Lingua 的通用语言检测(与 `QueryParser` 的英文 ASCII 快路径配合使用)。 | | |
| 16 | +| `query_rewriter.py` | 基于配置词典的查询改写与规范化。 | | |
| 17 | +| `style_intent.py` | 从配置加载样式意图词表,对查询变体做候选匹配,产出 `StyleIntentProfile`。 | | |
| 18 | +| `product_title_exclusion.py` | 从配置加载标题排除规则,对多路查询文本做触发词匹配,产出 `ProductTitleExclusionProfile`。 | | |
| 19 | + | |
| 20 | +公开符号见 `query/__init__.py`(`QueryParser`、`ParsedQuery`、`KEYWORDS_QUERY_BASE_KEY` 等)。 | |
| 21 | + | |
| 22 | +--- | |
| 23 | + | |
| 24 | +## 解析产物:`ParsedQuery` | |
| 25 | + | |
| 26 | +`ParsedQuery` 是单次 `parse()` 的权威结果容器,字段含义与下游约定如下。 | |
| 27 | + | |
| 28 | +- **`original_query` / `query_normalized` / `rewritten_query`**:分别为原始输入、规范化后、词典改写后的主查询文本;后续翻译、向量、默认分词与 `base` 关键词均以**改写后的 `rewritten_query`(在代码变量中常名为 `query_text`)**为基准。 | |
| 29 | +- **`detected_language`**:解析时认定的源语言代码;若检测为 `unknown` 或空,则回退到 `SearchConfig.query_config.default_language`。 | |
| 30 | +- **`translations`**:键为**目标语言代码**(如 `zh`、`en`),值为翻译服务返回的字符串;仅包含本次请求实际需要的目标语种(见下文翻译目标推导)。 | |
| 31 | +- **`query_vector` / `image_query_vector`**:分别为 BGE 类文本向量与 CLIP 文本向量(维度由各自编码服务决定);未生成或未在超时内完成则为 `None`。 | |
| 32 | +- **`query_tokens`**:对**改写后主查询**做分词后的字符串列表,供例如 KNN 参数按 token 数分支等逻辑使用;分词路径由 `QueryTextAnalysisCache` 决定(纯拉丁英文可走轻量分词,含汉字则走 HanLP)。 | |
| 33 | +- **`keywords_queries`**:与「主查询 + 各翻译变体」平行的**关键词子查询**映射:键 `base`(常量 `KEYWORDS_QUERY_BASE_KEY`)对应源语言侧关键词串,其它键与 `translations` 的语种键一致。空串或无法提取的条目**不会写入**字典。 | |
| 34 | +- **`style_intent_profile` / `product_title_exclusion_profile`**:可选的理解结果;是否生效完全由 `config.yaml` 中 `query_config` 的对应开关与词表/规则决定。 | |
| 35 | +- **`_text_analysis_cache`**:单次解析内的分词与语言提示缓存,**不参与序列化**,仅供同一次 `parse` 内各检测器复用,避免对同一文本重复调用 HanLP。 | |
| 36 | + | |
| 37 | +与重排相关的文本选择由独立函数 `rerank_query_text()` 完成:检测为 `zh` 或 `en` 时始终用原始查询;其它语言优先英译再中译,见 `query_parser.py` 中实现。 | |
| 38 | + | |
| 39 | +--- | |
| 40 | + | |
| 41 | +## `QueryParser.parse()` 的执行顺序与策略 | |
| 42 | + | |
| 43 | +解析主流程在 `QueryParser.parse()` 中实现。整体目标是:在**共享等待预算**下并行完成翻译与向量请求,同时尽量减少主线程上重复、昂贵的分词与 NLP 调用,并把结果写入可选的 `context`(请求上下文)供日志与 `debug_info` 使用。 | |
| 44 | + | |
| 45 | +### 1. 规范化与改写 | |
| 46 | + | |
| 47 | +- 使用 `QueryNormalizer` 得到 `query_normalized` 并可选写入 `context.store_intermediate_result('query_normalized', ...)`。 | |
| 48 | +- 若配置了改写词典,则用 `QueryRewriter` 可能更新 `query_text`;改写成功时记录 `rewritten_query` 与告警。 | |
| 49 | + | |
| 50 | +### 2. 语言检测:通用路径与英文 ASCII 快路径 | |
| 51 | + | |
| 52 | +- **快路径**:当「活跃语种集合」仅为 `en` 与 `zh` 的子集时(活跃集合取 `target_languages` 归一化结果,若为空则回退到 `query_config.supported_languages`),且当前查询为**纯 ASCII、含字母、不含汉字**,则**直接判定为 `en`**,不再调用 `LanguageDetector`(避免 Lingua 等开销)。逻辑见 `_detect_query_language()` 与 `_is_ascii_latin_query()`。 | |
| 53 | + | |
| 54 | +```303:317:query/query_parser.py | |
| 55 | + def _detect_query_language( | |
| 56 | + self, | |
| 57 | + query_text: str, | |
| 58 | + *, | |
| 59 | + target_languages: Optional[List[str]] = None, | |
| 60 | + ) -> str: | |
| 61 | + normalized_targets = self._normalize_language_codes(target_languages) | |
| 62 | + supported_languages = self._normalize_language_codes( | |
| 63 | + getattr(self.config.query_config, "supported_languages", None) | |
| 64 | + ) | |
| 65 | + active_languages = normalized_targets or supported_languages | |
| 66 | + if active_languages and set(active_languages).issubset({"en", "zh"}): | |
| 67 | + if self._is_ascii_latin_query(query_text): | |
| 68 | + return "en" | |
| 69 | + return self.language_detector.detect(query_text) | |
| 70 | +``` | |
| 71 | + | |
| 72 | +- **通用路径**:`LanguageDetector` 先按 Unicode 脚本返回明确语种(如汉字块即 `zh`),否则用 Lingua 在一大组语言中判别,见 `language_detector.py`。 | |
| 73 | + | |
| 74 | +检测最终结果写入 `context.store_intermediate_result('detected_language', ...)`(若提供 `context`)。 | |
| 75 | + | |
| 76 | +### 3. 按请求分词缓存与语言提示 | |
| 77 | + | |
| 78 | +每次 `parse` 会新建 `QueryTextAnalysisCache(tokenizer=self._tokenizer)`,并对**原始串、规范化串、改写后串**调用 `set_language_hint(..., detected_lang)`,使后续对同一文本的 `get_tokenizer_result` / `get_tokenized_text` 能按语言选择**是否调用 HanLP**。 | |
| 79 | + | |
| 80 | +### 4. HanLP 模型(与 `KeywordExtractor` 对齐) | |
| 81 | + | |
| 82 | +`QueryParser` 默认构建的 `self._tokenizer` 为 HanLP 预训练分词模型 **`FINE_ELECTRA_SMALL_ZH`**,并开启 `output_spans=True`,以便与关键词提取共用「带偏移的分词结果」。 | |
| 83 | + | |
| 84 | +```237:245:query/query_parser.py | |
| 85 | + def _build_tokenizer(self) -> Callable[[str], Any]: | |
| 86 | + """Build the tokenizer used by query parsing. No fallback path by design.""" | |
| 87 | + if hanlp is None: | |
| 88 | + raise RuntimeError("HanLP is required for QueryParser tokenization") | |
| 89 | + logger.info("Initializing HanLP tokenizer...") | |
| 90 | + tokenizer = hanlp.load(hanlp.pretrained.tok.FINE_ELECTRA_SMALL_ZH) | |
| 91 | + tokenizer.config.output_spans = True | |
| 92 | + logger.info("HanLP tokenizer initialized") | |
| 93 | + return tokenizer | |
| 94 | +``` | |
| 95 | + | |
| 96 | +`KeywordExtractor` 在未注入自定义 `tokenizer` 时同样加载 **`FINE_ELECTRA_SMALL_ZH`**,并额外加载 **`CTB9_POS_ELECTRA_SMALL`** 做词性标注;二者在「中文路径」上语义一致,便于复用 `tokenizer_result`。 | |
| 97 | + | |
| 98 | +### 5. 异步富集:翻译、文本向量、CLIP 文本向量 | |
| 99 | + | |
| 100 | +- 翻译目标:`translation_targets = normalized_targets` 中**去掉与检测源语言相同**的代码后的列表(例如源为 `en` 且索引语言为 `["en","zh"]` 时只翻 `zh`)。 | |
| 101 | +- 翻译模型名:由 `_pick_query_translation_model()` 根据「源语言是否在索引语言内」及 `zh↔en` 等分支从 `QueryConfig` 选取。 | |
| 102 | +- 当 `generate_vector` 为真且配置开启文本嵌入时,向线程池提交 `text_encoder.encode([query_text], ...)`;当配置了 `image_embedding_field` 时提交 `image_encoder.encode_clip_text(query_text, ...)`。 | |
| 103 | +- 线程池:`ThreadPoolExecutor`,`max_workers` 为 `min(任务数, 4)` 与至少 1。 | |
| 104 | +- **提交顺序**:先尽可能提交所有异步任务,再在主线程上做「与异步重叠」的轻量工作(见下一节),最后 `concurrent.futures.wait(..., timeout=budget_sec)`。超时未完成的任务会记 warning,并 `shutdown(wait=False)` 不阻塞关闭线程池。 | |
| 105 | + | |
| 106 | +等待预算(毫秒)来自 `QueryConfig`: | |
| 107 | + | |
| 108 | +- 源语言在索引语言内:`translation_embedding_wait_budget_ms_source_in_index` | |
| 109 | +- 否则:`translation_embedding_wait_budget_ms_source_not_in_index` | |
| 110 | + | |
| 111 | +完成每个 future 后打 `Async enrichment task finished` 日志(含 `elapsed_ms`,为从提交到完成的大致墙钟时间)。 | |
| 112 | + | |
| 113 | +### 6. 主查询分词与「base」关键词(与异步重叠) | |
| 114 | + | |
| 115 | +在异步任务已提交之后、`wait()` 之前,当前实现会: | |
| 116 | + | |
| 117 | +1. 通过 `text_analysis_cache.get_tokenizer_result(query_text)` 得到分词结果,再 `extract_token_strings` 得到 **`query_tokens`**; | |
| 118 | +2. 调用 `KeywordExtractor.extract_keywords(query_text, language_hint=detected_lang, tokenizer_result=...)` 得到 **`keywords_base_query`**(若失败则日志告警,base 关键词可能为空)。 | |
| 119 | + | |
| 120 | +这样主线程在等翻译/向量时,已并行完成源侧分词与源侧关键词的大部分工作。 | |
| 121 | + | |
| 122 | +### 7. 等待结束后的关键词汇总与检测器 | |
| 123 | + | |
| 124 | +`wait()` 返回后: | |
| 125 | + | |
| 126 | +- 若有翻译结果,写入 `context.store_intermediate_result("translations", translations)`,并对每条翻译 `text_analysis_cache.set_language_hint(result, lang)`,保证后续对该翻译串的分词/关键词走正确语言路径。 | |
| 127 | +- `collect_keywords_queries(...)` 合并 **`base`**(可传入已算好的 `base_keywords_query` 避免重复抽取)与各翻译语种的关键词,得到 **`keywords_queries`**;成功时 `context.store_intermediate_result("keywords_queries", keywords_queries)` 并打 `Keyword extraction completed` 日志。 | |
| 128 | +- 构造带 `_text_analysis_cache` 的 `ParsedQuery` 草稿,依次调用 `StyleIntentDetector.detect` 与 `ProductTitleExclusionDetector.detect`,再把完整 `ParsedQuery` 返回。 | |
| 129 | + | |
| 130 | +解析阶段会打聚合耗时日志 `Query parse stage timings`,字段含义为: | |
| 131 | + | |
| 132 | +- **`before_wait_ms`**:从解析开始计时点到进入 `wait()` 之前的主线程耗时(含规范化、改写、语言检测、提交异步任务、主查询分词、base 关键词等); | |
| 133 | +- **`async_wait_ms`**:`wait()` 阻塞时间; | |
| 134 | +- **`base_keywords_ms`**:base 关键词抽取耗时; | |
| 135 | +- **`keyword_tail_ms`**:`collect_keywords_queries` 及前后尾部逻辑中关键词相关部分的主要耗时; | |
| 136 | +- **`tail_sync_ms`**:`wait()` 之后整段同步尾巴(含关键词汇总、两检测器、写中间结果等)。 | |
| 137 | + | |
| 138 | +--- | |
| 139 | + | |
| 140 | +## 分词与 `QueryTextAnalysisCache` | |
| 141 | + | |
| 142 | +### `get_tokenizer_result`:何时走 HanLP,何时走轻量分词 | |
| 143 | + | |
| 144 | +- 若未配置模型 `tokenizer`,直接返回空列表路径的轻量结果(由上层避免依赖)。 | |
| 145 | +- 若根据**该文本的语言提示**与**是否含汉字**判断不需要模型:返回 `simple_tokenize_query` 的列表(字符串 token),**不调用 HanLP**。 | |
| 146 | +- 否则对该文本调用一次 `self.tokenizer(text)`(HanLP),结果按文本缓存,同一次 `parse` 内重复访问同一字符串不会重复推理。 | |
| 147 | + | |
| 148 | +核心判断在 `_should_use_model_tokenizer`:**语言提示为 `zh` 时,仅当文本含汉字才用模型**;非 `zh` 提示时,仅当文本含汉字才用模型。因此纯英文主查询在提示为 `en` 时走轻量分词;中文翻译串在 `set_language_hint(..., "zh")` 且含汉字时走 HanLP。 | |
| 149 | + | |
| 150 | +### `coarse_tokens` 与 `fine_tokens`:`TokenizedText` | |
| 151 | + | |
| 152 | +- **`fine_tokens`**:来自 `extract_token_strings(get_tokenizer_result(...))`,在中文路径上即 HanLP 分词后的词串(已按规范化键去重保序)。 | |
| 153 | +- **`coarse_tokens`**:由 `_build_coarse_tokens` 决定。若语言提示为 **`zh`**,或文本含汉字且已有 `tokenizer_tokens`,则 **粗粒度 token 与 fine 一致**(即采用模型分词粒度,而不用「整段 CJK 连成一项」的纯正则策略)。否则使用 `simple_tokenize_query`(适合拉丁词、数字、带连字符/撇号的英文词形)。 | |
| 154 | + | |
| 155 | +```92:103:query/tokenization.py | |
| 156 | +def _build_coarse_tokens( | |
| 157 | + text: str, | |
| 158 | + *, | |
| 159 | + language_hint: Optional[str], | |
| 160 | + tokenizer_tokens: Sequence[str], | |
| 161 | +) -> List[str]: | |
| 162 | + normalized_language = normalize_query_text(language_hint) | |
| 163 | + if normalized_language == "zh" or (contains_han_text(text) and tokenizer_tokens): | |
| 164 | + # Chinese coarse tokenization should follow the model tokenizer rather than a | |
| 165 | + # regex that collapses the whole sentence into one CJK span. | |
| 166 | + return list(_dedupe_preserve_order(tokenizer_tokens)) | |
| 167 | + return _dedupe_preserve_order(simple_tokenize_query(text)) | |
| 168 | +``` | |
| 169 | + | |
| 170 | +- **`candidates`**:在 fine、coarse、两类 n-gram 短语(上限由 `max_ngram` 控制)以及整句 `normalized_text` 上合并去重,供 `StyleIntentDetector`、`ProductTitleExclusionDetector` 等做子串/短语级匹配。 | |
| 171 | + | |
| 172 | +`tokenize_text()` 是对单次无缓存场景的薄封装:内部新建 `QueryTextAnalysisCache` 再 `get_tokenized_text`。 | |
| 173 | + | |
| 174 | +--- | |
| 175 | + | |
| 176 | +## 关键词提取:`KeywordExtractor` 与 `collect_keywords_queries` | |
| 177 | + | |
| 178 | +### 路由规则 | |
| 179 | + | |
| 180 | +`extract_keywords` 根据 `language_hint` 分支: | |
| 181 | + | |
| 182 | +- **`en`**:完全交给 `EnglishKeywordExtractor`(spaCy),**不使用** HanLP 分词结果做 POS 名词筛选(即使调用方传入 `tokenizer_result` 也会被忽略在该路径内)。 | |
| 183 | +- **`zh`**:使用 HanLP 分词结果(优先复用传入的 `tokenizer_result`),再对词序列跑 `CTB9_POS_ELECTRA_SMALL`,保留**长度 ≥ 2 且词性以 `N` 开头**的词;非连续名词之间插入空格拼接成一条字符串(与 ES 侧 `keywords_query` 的用法一致)。 | |
| 184 | +- **其它非空语言码**:当前实现返回空串,即**不为该语种生成关键词子句**(由调用方决定是否跳过)。 | |
| 185 | + | |
| 186 | +### `collect_keywords_queries` | |
| 187 | + | |
| 188 | +- 键 **`base`**:对应 `rewritten_query` 的关键词;若调用方已预先计算 `base_keywords_query` 则直接写入,避免重复抽取。 | |
| 189 | +- 其它键:与 `translations` 中每个非空语种一一对应,语言码归一化为小写。 | |
| 190 | +- 全程可传入 `text_analysis_cache`,以便 `get_tokenizer_result` 命中缓存并与检测器共享分词结果。 | |
| 191 | + | |
| 192 | +常量 `KEYWORDS_QUERY_BASE_KEY` 的值为字符串 **`"base"`**,与检索构建里读取的字段一致。 | |
| 193 | + | |
| 194 | +--- | |
| 195 | + | |
| 196 | +## 英文关键词:`EnglishKeywordExtractor` | |
| 197 | + | |
| 198 | +- 依赖 **spaCy** 模型 **`en_core_web_sm`**,加载时关闭 `ner`、`textcat` 以减轻开销;加载失败时记录 warning 并走基于 `simple_tokenize_query` 的回退策略。 | |
| 199 | +- 主路径用依存句法与名词块规则收集一小组「核心词」候选(如直接宾语名词、部分 ROOT 名词/专有名词、INTJ 结构下的宾语等),并处理价格/目的介词宾语降级、人口学名词(如 `women`)弱化、尺寸类 ROOT 与主语搭配等边界情况。 | |
| 200 | +- 使用 `_project_terms_to_query_tokens` 将 spaCy 词形映射回查询中的**表面分词**(例如复合词 `t-shirt`),避免在关键词串中出现被错误切断的片段。 | |
| 201 | + | |
| 202 | +最终返回**最多三个词**的空格连接字符串,用于检索侧第二层 `combined_fields` 的紧凑查询(见下节)。 | |
| 203 | + | |
| 204 | +--- | |
| 205 | + | |
| 206 | +## 与检索层的关系(消费方摘要) | |
| 207 | + | |
| 208 | +`ParsedQuery.keywords_queries` 由 `search/es_query_builder.py` 读取:在构建某一语言的 lexical 子句时,除主 `combined_fields`(完整 `query`)外,若存在非空的 `keywords_query` 且与主查询不同,会追加第二个 `combined_fields`,使用单独的 `minimum_should_match`(由 builder 的 `keywords_minimum_should_match` 配置)和较低 boost,从而在**不替代全文查询**的前提下加强核心词匹配。 | |
| 209 | + | |
| 210 | +`query_tokens` 在同文件中间接影响例如带文本向量时的 KNN 分支参数(按 token 数量选用长查询的 k / num_candidates 等)。具体字段与 boost 以 `ESQueryBuilder` 当前实现为准。 | |
| 211 | + | |
| 212 | +--- | |
| 213 | + | |
| 214 | +## 样式意图与标题排除(简要) | |
| 215 | + | |
| 216 | +- **`StyleIntentRegistry` / `StyleIntentDetector`**:从 `QueryConfig.style_intent_terms` 等加载意图定义;`detect` 时按中英变体取查询文本,经 `tokenize_text` 或缓存得到 `TokenizedText`,在 `candidates` 上与配置同义词表匹配,输出 `StyleIntentProfile`(含 `query_variants` 与命中意图列表)。 | |
| 217 | +- **`ProductTitleExclusionRegistry` / `ProductTitleExclusionDetector`**:从 `QueryConfig.product_title_exclusion_rules` 加载规则;对 `original_query`、`query_normalized`、`rewritten_query` 及所有 `translations` 去重后分词匹配触发词,输出 `ProductTitleExclusionProfile`。 | |
| 218 | + | |
| 219 | +二者均依赖 `tokenization` 与可选的 HanLP,启用与否由配置项控制。 | |
| 220 | + | |
| 221 | +--- | |
| 222 | + | |
| 223 | +## 可观测性与调试 | |
| 224 | + | |
| 225 | +当 `QueryParser.parse(..., context=...)` 传入请求上下文时,典型中间结果包括: | |
| 226 | + | |
| 227 | +- `query_normalized`、`rewritten_query`、`detected_language`、`query_tokens` | |
| 228 | +- `translation_{lang}`、`translations` | |
| 229 | +- `keywords_queries` | |
| 230 | +- `query_vector_shape`、`image_query_vector_shape` | |
| 231 | +- `style_intent_profile`、`product_title_exclusion_profile` | |
| 232 | + | |
| 233 | +搜索主流程在 `search/searcher.py` 中会把解析结果写入 `QueryAnalysisResult`(含 **`keywords_queries`**),并在 `debug=true` 时把 `query_analysis` 挂到响应的 `debug_info`;前端调试页在 `frontend/static/js/app.js` 中展示 **Translations** 与 **Keywords Queries** 等块,便于与翻译结果并列查看。 | |
| 234 | + | |
| 235 | +--- | |
| 236 | + | |
| 237 | +## 依赖与环境提示 | |
| 238 | + | |
| 239 | +- **HanLP**:分词与中文词性标注;模型名以本文与源码为准(`FINE_ELECTRA_SMALL_ZH` + `CTB9_POS_ELECTRA_SMALL`)。 | |
| 240 | +- **spaCy**:英文关键词路径需要可导入的 **`en_core_web_sm`**(若缺失则英文关键词退化为轻量规则)。 | |
| 241 | +- **Lingua**:通用语言检测(在英文 ASCII 快路径不适用时参与拉丁语系判别)。 | |
| 242 | + | |
| 243 | +运行与测试时请使用项目约定的虚拟环境(见仓库根目录 `CLAUDE.md` / `activate.sh`),避免系统 Python 缺少上述依赖。 | |
| 244 | + | |
| 245 | +--- | |
| 246 | + | |
| 247 | +## 扩展与测试 | |
| 248 | + | |
| 249 | +- 单元测试中与解析、分词、意图相关的用例分布在 `tests/test_query_parser_mixed_language.py`、`tests/test_tokenization.py`、`tests/test_style_intent.py`、`tests/test_product_title_exclusion.py` 等文件中;修改分词或关键词策略时应同步更新或新增测试,以保持与本文描述一致。 | |
| 250 | + | |
| 251 | +若新增语种或改写语言检测策略,应同步审视:`QueryParser._detect_query_language`、`QueryTextAnalysisCache._should_use_model_tokenizer`、`KeywordExtractor.extract_keywords` 中非 `zh`/`en` 分支,以及 ES 侧是否应为新语种生成 `keywords_query`。 | ... | ... |
scripts/es_debug_search.py renamed to scripts/evaluation/es_debug_search.py
scripts/eval_search_quality.py renamed to scripts/evaluation/eval_search_quality.py
| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | +- R3 完全相关:该结果的核心意图被满足,标题/副标题/类目/属性不违背意图。 | |
| 5 | +- R2 部分相关:同品类或相近用途,但规格/材质/年龄段/场景等维度的要求有偏差。 | |
| 6 | +- R1 不相关:品类/用途不符,或明显错误/违禁/空结果。 | |
| 7 | + | |
| 8 | + | |
| 9 | +## 指标说明 | |
| 10 | + | |
| 11 | +- **相关性**:1=低,2=中,3=高。 | |
| 12 | +- **「仅 3 相关」**:只把打分 3 视为相关;**「2_3 相关」**:把 2 和 3 都视为相关。 | |
| 13 | + | |
| 14 | +| 指标 | 含义 | | |
| 15 | +|------|------| | |
| 16 | +| **P@5, P@10, P@20, P@50** | 前 K 个结果中「仅 3 相关」的精确率 | | |
| 17 | +| **P@5_2_3 ~ P@50_2_3** | 前 K 个结果中「2 和 3 都算相关」的精确率 | | |
| 18 | +| **MAP_3** | 仅 3 相关时的 Average Precision(单 query) | | |
| 19 | +| **MAP_2_3** | 2 和 3 都相关时的 Average Precision | | |
| 20 | +| **AUC_3** | 仅 3 相关、1/2 不相关时,随机相关项排在随机不相关项前的概率 | | |
| 21 | +| **AUC_2_3** | 2 和 3 相关、1 不相关时的同上 AUC | | |
| 22 | + | ... | ... |
scripts/evaluation/queries/fashion_quries__high_quality.txt.v2.uniq
0 → 100644
| ... | ... | @@ -0,0 +1,1046 @@ |
| 1 | +abstract print top | |
| 2 | +acrylic beanie hat | |
| 3 | +acrylic knit hat | |
| 4 | +airport outfit | |
| 5 | +a-line dress | |
| 6 | +a-line skirt | |
| 7 | +all-season travel pants | |
| 8 | +all-season vest lightweight | |
| 9 | +ankle boots | |
| 10 | +ankle length dress | |
| 11 | +ankle length jeans | |
| 12 | +ankle length maxi dress | |
| 13 | +ankle pants | |
| 14 | +ankle pants wrinkle-free | |
| 15 | +ankle socks | |
| 16 | +ankle socks white | |
| 17 | +anorak jacket | |
| 18 | +anti-chafing body glide stick | |
| 19 | +anti-chafing gym shorts | |
| 20 | +anti-chafing pants | |
| 21 | +anti-chafing running shorts | |
| 22 | +anti-chafing seamless yoga shorts | |
| 23 | +anti-chafing women's running shorts | |
| 24 | +arm sleeves | |
| 25 | +asymmetrical top | |
| 26 | +athleisure compression leggings | |
| 27 | +athleisure sports bra | |
| 28 | +athleisure track suit | |
| 29 | +athletic fit compression shorts | |
| 30 | +athletic fit running shorts | |
| 31 | +athletic gear | |
| 32 | +athletic shorts | |
| 33 | +athletic socks | |
| 34 | +athletic socks merino wool | |
| 35 | +athletic tank top | |
| 36 | +athletic tee | |
| 37 | +autumn blazer tweed | |
| 38 | +autumn trench coat | |
| 39 | +baby blanket | |
| 40 | +baby onesie | |
| 41 | +backpack | |
| 42 | +baggy jeans | |
| 43 | +balloon sleeve | |
| 44 | +balloon sleeve blouse | |
| 45 | +bamboo socks | |
| 46 | +basic cotton t-shirt | |
| 47 | +basic tank top ribbed | |
| 48 | +bath robe | |
| 49 | +bathrobe | |
| 50 | +bathrobe women | |
| 51 | +bath towel | |
| 52 | +beach cover up | |
| 53 | +beach towel | |
| 54 | +beach umbrella | |
| 55 | +beach vacation cover-up | |
| 56 | +beach vacation outfit | |
| 57 | +beach vacation swimsuit | |
| 58 | +beach wedding dress | |
| 59 | +beanie hat | |
| 60 | +bed sheets | |
| 61 | +beige trench coat | |
| 62 | +bell bottoms | |
| 63 | +belt | |
| 64 | +belted coat | |
| 65 | +belted jumpsuit | |
| 66 | +belted jumpsuit wide leg | |
| 67 | +belted jumpsuit with pockets | |
| 68 | +belted kimono robe | |
| 69 | +belted linen blend maxi dress | |
| 70 | +belted moto jacket | |
| 71 | +belted trench coat with pockets | |
| 72 | +belted waist trainer | |
| 73 | +bikini bottom | |
| 74 | +bishop sleeve | |
| 75 | +bishop sleeve top | |
| 76 | +black boots | |
| 77 | +black mini dress | |
| 78 | +black tie dress | |
| 79 | +blazer | |
| 80 | +blue denim jacket | |
| 81 | +boat neck sweater | |
| 82 | +bodycon dress | |
| 83 | +bodysuit | |
| 84 | +bohemian maxi dress | |
| 85 | +bohemian tassel maxi skirt | |
| 86 | +bohemian tunic cotton | |
| 87 | +bootcut corduroy pants | |
| 88 | +bootcut denim pants for riding boots | |
| 89 | +bootcut jeans | |
| 90 | +bootcut jeans retro | |
| 91 | +bootcut pants | |
| 92 | +bootcut pants for cowboy boots | |
| 93 | +bootcut pants for winter boots | |
| 94 | +bootcut velvet pants | |
| 95 | +bootcut yoga pants | |
| 96 | +boxer briefs | |
| 97 | +boyfriend jeans | |
| 98 | +bracelet set | |
| 99 | +breathable shorts | |
| 100 | +bridesmaid dress | |
| 101 | +brown leather boots | |
| 102 | +bucket hat | |
| 103 | +burgundy dress | |
| 104 | +burgundy holiday dress | |
| 105 | +burnt orange pants | |
| 106 | +business casual blazer | |
| 107 | +business casual chinos | |
| 108 | +business casual loafers | |
| 109 | +business casual women | |
| 110 | +business suit | |
| 111 | +business travel suit | |
| 112 | +butterfly hair clip | |
| 113 | +butterfly top | |
| 114 | +button down shirt | |
| 115 | +button-down shirt | |
| 116 | +cable knit sweater | |
| 117 | +camisole | |
| 118 | +cami top | |
| 119 | +canvas tote | |
| 120 | +cap | |
| 121 | +cap sleeve | |
| 122 | +cap sleeve top | |
| 123 | +cardigan | |
| 124 | +cargo pants | |
| 125 | +cargo pants hidden pocket | |
| 126 | +cargo skirt | |
| 127 | +cashmere blend cardigan | |
| 128 | +cashmere cardigan | |
| 129 | +cashmere scarf | |
| 130 | +cashmere sweater | |
| 131 | +cashmere sweater beige | |
| 132 | +casual day romper | |
| 133 | +casual day tank top | |
| 134 | +casual friday polo shirt | |
| 135 | +casual pants | |
| 136 | +casual polo shirt | |
| 137 | +casual weekend outfit | |
| 138 | +cat bed | |
| 139 | +charcoal gray coat | |
| 140 | +checkered skirt | |
| 141 | +chelsea boots | |
| 142 | +chiffon blouse | |
| 143 | +chiffon blouse sheer | |
| 144 | +chiffon dress | |
| 145 | +chiffon scarf lightweight | |
| 146 | +chinos | |
| 147 | +chocolate brown boots | |
| 148 | +chunky knit sweater | |
| 149 | +chunky platform boots | |
| 150 | +classic fit blazer | |
| 151 | +classic fit polo shirt | |
| 152 | +classic trench coat tall | |
| 153 | +classic wool coat | |
| 154 | +cleaning solution | |
| 155 | +closed toe heels | |
| 156 | +clutch bag | |
| 157 | +coachella outfit | |
| 158 | +coat | |
| 159 | +cobalt blue dress | |
| 160 | +cocktail dress | |
| 161 | +cold-shoulder blouse | |
| 162 | +cold weather insulated boots | |
| 163 | +cold weather wool socks | |
| 164 | +colorblock hoodie | |
| 165 | +commuter outfits | |
| 166 | +compression anti-chafing running leggings | |
| 167 | +compression arm sleeves | |
| 168 | +compression athletic gear | |
| 169 | +compression bra | |
| 170 | +compression gym workout running shorts | |
| 171 | +compression knee brace | |
| 172 | +compression leggings | |
| 173 | +compression shorts | |
| 174 | +compression stockings | |
| 175 | +compression tights | |
| 176 | +compression tights winter | |
| 177 | +compression top | |
| 178 | +Compression Top Spandex | |
| 179 | +compression yoga sports bra | |
| 180 | +concert outfit | |
| 181 | +convertible backpack travel bag | |
| 182 | +convertible dress | |
| 183 | +convertible hiking pants | |
| 184 | +convertible laptop bag | |
| 185 | +convertible neck pillow blanket | |
| 186 | +convertible pants | |
| 187 | +convertible sleeves shirt | |
| 188 | +convertible sleeves UPF 50 shirt | |
| 189 | +convertible zip-off hiking pants | |
| 190 | +corduroy blazer | |
| 191 | +corduroy fall pants | |
| 192 | +corduroy jacket | |
| 193 | +corduroy pants | |
| 194 | +corduroy skirt | |
| 195 | +corduroy skirt mini | |
| 196 | +corset top | |
| 197 | +corset top black velvet | |
| 198 | +cottagecore floral maxi dress | |
| 199 | +cottagecore knit cardigan | |
| 200 | +cotton flannel shirt | |
| 201 | +cotton-padded jacket | |
| 202 | +cotton pajamas | |
| 203 | +cotton tank top | |
| 204 | +cotton t-shirt | |
| 205 | +cotton t-shirt white | |
| 206 | +cotton tunic | |
| 207 | +couple outfits | |
| 208 | +cover-up | |
| 209 | +cowboy boots | |
| 210 | +cream cardigan | |
| 211 | +cream knit sweater | |
| 212 | +cream sweater | |
| 213 | +crew neck sweatshirt | |
| 214 | +crew neck tee | |
| 215 | +crochet top | |
| 216 | +crop hoodie | |
| 217 | +crop length hoodie | |
| 218 | +crop length sports bra | |
| 219 | +crop top | |
| 220 | +crossbody leather handbag | |
| 221 | +cruise outfits | |
| 222 | +curvy fit belt | |
| 223 | +curvy fit high-waisted jeans | |
| 224 | +curvy fit maxi dress | |
| 225 | +curvy fit skinny jeans | |
| 226 | +curvy fit straight leg jeans | |
| 227 | +curvy fit stretch jeans | |
| 228 | +curvy fit stretch pencil skirt | |
| 229 | +curvy fit trousers | |
| 230 | +curvy fit wide leg jeans | |
| 231 | +cushion insert | |
| 232 | +cut-out dress | |
| 233 | +cycling shorts | |
| 234 | +dark academia blazer | |
| 235 | +dark academia plaid trousers | |
| 236 | +dark academia wool coat | |
| 237 | +date night bodycon dress | |
| 238 | +date night dress | |
| 239 | +date night mini dress | |
| 240 | +Dating outfit | |
| 241 | +denim jacket | |
| 242 | +denim jacket distressed | |
| 243 | +denim jacket oversized | |
| 244 | +denim jeans | |
| 245 | +denim pants | |
| 246 | +denim shirt | |
| 247 | +denim shorts | |
| 248 | +denim skirt | |
| 249 | +denim skirt midi | |
| 250 | +distressed denim jacket | |
| 251 | +distressed jeans | |
| 252 | +ditsy floral dress | |
| 253 | +dog blanket | |
| 254 | +dog coat | |
| 255 | +dog crate mat | |
| 256 | +dog harness | |
| 257 | +dog leash | |
| 258 | +dog towel | |
| 259 | +dog toy | |
| 260 | +dog treat | |
| 261 | +double breasted coat | |
| 262 | +down jacket | |
| 263 | +down parka | |
| 264 | +down vest | |
| 265 | +dress | |
| 266 | +dress sandals | |
| 267 | +dress shirt | |
| 268 | +duffle bag | |
| 269 | +dusty rose dress | |
| 270 | +dusty rose silk slip dress | |
| 271 | +dusty rose velvet cocktail dress | |
| 272 | +dusty rose velvet ribbon | |
| 273 | +dusty rose velvet scrunchie | |
| 274 | +dusty rose velvet silk dress | |
| 275 | +dusty rose velvet slip skirt | |
| 276 | +early autumn corduroy jacket | |
| 277 | +earthy tones olive | |
| 278 | +earthy tones outfit | |
| 279 | +electric blue mini skirt | |
| 280 | +embroidered blouse | |
| 281 | +emerald green top | |
| 282 | +ergonomic office chair | |
| 283 | +evening formal gown | |
| 284 | +evening formal heels | |
| 285 | +evening gown | |
| 286 | +face mask fashion | |
| 287 | +fall cardigan cozy | |
| 288 | +fall denim jacket | |
| 289 | +fall transitional trench coat | |
| 290 | +fanny pack | |
| 291 | +fathers day shirt | |
| 292 | +faux fur coat | |
| 293 | +faux leather clutch bag | |
| 294 | +faux leather jacket | |
| 295 | +faux leather jacket cropped | |
| 296 | +faux leather jacket moto style | |
| 297 | +faux leather leggings | |
| 298 | +faux leather leggings matte | |
| 299 | +faux leather moto jacket | |
| 300 | +faux leather moto jacket belted | |
| 301 | +faux leather pants | |
| 302 | +faux leather skirt | |
| 303 | +faux leather wallet | |
| 304 | +festival concert crop top | |
| 305 | +festival concert jumpsuit | |
| 306 | +festival outfit | |
| 307 | +field jacket | |
| 308 | +first date outfit | |
| 309 | +fisherman knit | |
| 310 | +fisherman knit sweater | |
| 311 | +fishing shirt | |
| 312 | +fishing shirt long sleeve | |
| 313 | +fishnet top | |
| 314 | +flannel shirt | |
| 315 | +flare blouse | |
| 316 | +flare jeans | |
| 317 | +flare pants | |
| 318 | +flare pants velvet | |
| 319 | +flare sleeved blouse | |
| 320 | +fleece jacket | |
| 321 | +fleece jacket winter | |
| 322 | +fleece lined leggings | |
| 323 | +fleece-lined leggings | |
| 324 | +fleece mid layer | |
| 325 | +fleece pullover | |
| 326 | +floral blouse | |
| 327 | +floral jacket | |
| 328 | +floral maxi dress | |
| 329 | +flowy chiffon dress | |
| 330 | +flutter sleeve | |
| 331 | +flutter sleeve top | |
| 332 | +formal attire | |
| 333 | +formal dress | |
| 334 | +formal gown | |
| 335 | +four-way stretch black yoga pants | |
| 336 | +four-way stretch denim jeans | |
| 337 | +four-way stretch elastic band | |
| 338 | +four-way stretch maternity leggings | |
| 339 | +four-way stretch tall slim fit pants | |
| 340 | +four-way stretch yoga pants | |
| 341 | +fringe bag | |
| 342 | +geometric pattern sweater | |
| 343 | +gingham dress | |
| 344 | +gingham pattern dress | |
| 345 | +gingham picnic dress | |
| 346 | +gingham summer dress | |
| 347 | +glasses frames | |
| 348 | +gold jewelry | |
| 349 | +gold necklace | |
| 350 | +gold sequin top | |
| 351 | +golf shirt | |
| 352 | +golf shirt uv protection | |
| 353 | +gothic black boots | |
| 354 | +gothic lace-up corset top | |
| 355 | +gothic velvet dress | |
| 356 | +graduation ceremony tailored suit | |
| 357 | +graduation dress | |
| 358 | +graphic hoodie | |
| 359 | +graphic tee | |
| 360 | +graphic t-shirt | |
| 361 | +graphic t-shirt vintage wash | |
| 362 | +gray hoodie | |
| 363 | +green cargo pants | |
| 364 | +grunge distressed jeans | |
| 365 | +grunge flannel shirt | |
| 366 | +gym bag | |
| 367 | +gym leggings with pocket | |
| 368 | +gym outfit | |
| 369 | +gym towel | |
| 370 | +gym workout compression top | |
| 371 | +gym workout running shorts | |
| 372 | +gym workout sports bra | |
| 373 | +hair towel | |
| 374 | +halloween costume ideas | |
| 375 | +halter neck top | |
| 376 | +hanger space saving | |
| 377 | +heels | |
| 378 | +hidden pocket fanny pack | |
| 379 | +hidden pocket infinity scarf | |
| 380 | +hidden pocket passport holder vest | |
| 381 | +hidden pocket travel belt | |
| 382 | +hidden pocket travel vest | |
| 383 | +hidden pocket women's travel vest | |
| 384 | +high heel compatible cocktail dress | |
| 385 | +high heel compatible dress sandals | |
| 386 | +high heel compatible formal dress | |
| 387 | +high heel compatible formal jumpsuit | |
| 388 | +high heel compatible shoe inserts | |
| 389 | +high neck dress | |
| 390 | +high rise leggings | |
| 391 | +high rise pants | |
| 392 | +high rise wide leg pants | |
| 393 | +high-waisted denim jeans | |
| 394 | +high-waisted denim shorts | |
| 395 | +high waisted jeans | |
| 396 | +high-waisted jeans | |
| 397 | +hiking boots | |
| 398 | +hiking outfit | |
| 399 | +hiking pants | |
| 400 | +hiking pants women | |
| 401 | +hiking shirt convertible sleeves | |
| 402 | +hiking shoes | |
| 403 | +hiking socks | |
| 404 | +hiking trail backpack | |
| 405 | +hiking trail raincoat | |
| 406 | +hippie bell bottoms | |
| 407 | +hippie tie-dye dress | |
| 408 | +holiday season formal gown | |
| 409 | +holiday season velvet dress | |
| 410 | +homecoming dress | |
| 411 | +homewear | |
| 412 | +hooded jacket | |
| 413 | +hoodie | |
| 414 | +houndstooth coat | |
| 415 | +instagram outfit | |
| 416 | +insulated base layer | |
| 417 | +insulated boots | |
| 418 | +insulated gloves | |
| 419 | +interview clothes | |
| 420 | +invisible socks | |
| 421 | +ivory dress | |
| 422 | +ivory wedding dress | |
| 423 | +jacket | |
| 424 | +jeans | |
| 425 | +jean shorts | |
| 426 | +jeggings | |
| 427 | +jewel tones dress | |
| 428 | +jewel tones emerald | |
| 429 | +jogger pants | |
| 430 | +jumpsuit | |
| 431 | +kawaii crop top | |
| 432 | +kawaii mini dress | |
| 433 | +keyhole top | |
| 434 | +khaki green backpack | |
| 435 | +khaki green canvas tote | |
| 436 | +khaki green cargo pants utility | |
| 437 | +khaki green military jacket | |
| 438 | +khaki green ripstop convertible pants | |
| 439 | +khaki green ripstop fabric trousers | |
| 440 | +kids cotton pajamas set | |
| 441 | +kids' cotton pajamas set | |
| 442 | +kimono robe | |
| 443 | +knee brace | |
| 444 | +knee high boots | |
| 445 | +knit cardigan | |
| 446 | +knit hat | |
| 447 | +knit scarf | |
| 448 | +knit sweater | |
| 449 | +knitwear | |
| 450 | +lace bralette | |
| 451 | +lace dress | |
| 452 | +lantern sleeve | |
| 453 | +lantern sleeve top | |
| 454 | +laptop backpack | |
| 455 | +laptop bag | |
| 456 | +layering hoodie | |
| 457 | +leather bag | |
| 458 | +leather handbag | |
| 459 | +leather jacket | |
| 460 | +leather sandals | |
| 461 | +leather shoes | |
| 462 | +leather skirt | |
| 463 | +leggings | |
| 464 | +leopard print blouse | |
| 465 | +light jacket | |
| 466 | +lightweight jacket | |
| 467 | +lightweight scarf | |
| 468 | +linen blend maxi dress | |
| 469 | +linen blend trousers | |
| 470 | +linen dress | |
| 471 | +linen fabric breathable | |
| 472 | +linen pants | |
| 473 | +linen shorts | |
| 474 | +linen short set | |
| 475 | +linen shorts summer | |
| 476 | +linen trousers | |
| 477 | +litter box | |
| 478 | +loafers | |
| 479 | +long blazer | |
| 480 | +long coat | |
| 481 | +long sleeve top | |
| 482 | +long tunic top | |
| 483 | +loose fit boyfriend jeans | |
| 484 | +loose fit cap | |
| 485 | +loose fit cardigan cashmere | |
| 486 | +loose fit graphic hoodie streetwear | |
| 487 | +loose fit graphic print t-shirt | |
| 488 | +loose fit graphic t-shirt | |
| 489 | +loose fit knit sweater | |
| 490 | +loose fit summer pure cotton t-shirt | |
| 491 | +loose fit tunic | |
| 492 | +lounge wear bodysuit | |
| 493 | +lounge wear pajamas set | |
| 494 | +low rise bikini bottom | |
| 495 | +low rise jeans | |
| 496 | +low rise shorts | |
| 497 | +maternity casual friday polo shirt | |
| 498 | +maternity dress | |
| 499 | +maternity nursing bra | |
| 500 | +maternity pillow | |
| 501 | +maternity robe silk | |
| 502 | +maternity support band leggings | |
| 503 | +maternity support leggings | |
| 504 | +maternity support leggings black | |
| 505 | +maternity swimsuit | |
| 506 | +maternity tall trousers | |
| 507 | +maternity trousers | |
| 508 | +maternity wear | |
| 509 | +maternity wide leg denim jeans | |
| 510 | +maternity yoga Pants | |
| 511 | +mauve pink cardigan | |
| 512 | +maxi dress | |
| 513 | +maxi skirt | |
| 514 | +men's athletic running shorts | |
| 515 | +men's dress shirt | |
| 516 | +men's running shorts | |
| 517 | +men's suit vest | |
| 518 | +merino wool base layer | |
| 519 | +merino wool glove liners | |
| 520 | +merino wool hiking socks | |
| 521 | +merino wool ski socks | |
| 522 | +merino wool socks | |
| 523 | +merino wool socks hiking | |
| 524 | +merino wool thermal base layer top | |
| 525 | +merino wool thermal leggings base layer | |
| 526 | +merino wool thermal underwear | |
| 527 | +mesh athletic shorts | |
| 528 | +mesh athletic tee | |
| 529 | +mesh shirt | |
| 530 | +mesh shoes | |
| 531 | +metallic leggings | |
| 532 | +Microfiber Robe | |
| 533 | +Microfiber Towel Quick-Dry | |
| 534 | +midi cocktail dress | |
| 535 | +midi dress | |
| 536 | +mid rise bootcut jeans | |
| 537 | +mid rise skirts | |
| 538 | +mid-season waterproof shell jacket | |
| 539 | +military field Jacket | |
| 540 | +military green jacket | |
| 541 | +military jacket | |
| 542 | +mini dress | |
| 543 | +minimalist high rise pants | |
| 544 | +minimalist linen dress | |
| 545 | +minimalist linen trousers | |
| 546 | +minimalist wallet | |
| 547 | +mini skirt | |
| 548 | +mock neck shirt | |
| 549 | +modest long sleeve top | |
| 550 | +modest swimsuit one-piece | |
| 551 | +moisture-wicking cycling jersey | |
| 552 | +moisture-wicking golf shirt | |
| 553 | +moisture-wicking golf shirt men's | |
| 554 | +moisture-wicking gym towel | |
| 555 | +moisture-wicking hiking trail t-shirt | |
| 556 | +mom outfit | |
| 557 | +monochrome outfit | |
| 558 | +mother bride dress | |
| 559 | +mothers day gift | |
| 560 | +moto jacket | |
| 561 | +mustard yellow top | |
| 562 | +nautical pea coat | |
| 563 | +nautical striped sweater | |
| 564 | +navy blazer | |
| 565 | +navy blue cashmere beanie | |
| 566 | +navy blue cashmere blanket | |
| 567 | +navy blue cashmere blend scarf | |
| 568 | +navy blue cashmere scarf | |
| 569 | +navy blue cashmere winter coat | |
| 570 | +neon pink crop top | |
| 571 | +neutral tones beige | |
| 572 | +neutral tones clothing | |
| 573 | +new mom clothes | |
| 574 | +new years eve dress | |
| 575 | +no show socks | |
| 576 | +notch lapel blazer | |
| 577 | +nurse scrubs | |
| 578 | +nursing bra | |
| 579 | +nursing dress | |
| 580 | +nylon raincoat waterproof | |
| 581 | +nylon running shorts | |
| 582 | +nylon shorts | |
| 583 | +odor control athletic headbands | |
| 584 | +odor control athletic socks | |
| 585 | +odor control men's athletic socks | |
| 586 | +odor control running shoes insoles | |
| 587 | +odor control shoe spray | |
| 588 | +odor control trash can | |
| 589 | +office wear | |
| 590 | +office wear pencil skirt | |
| 591 | +office wear sheath dress | |
| 592 | +off-shoulder dress | |
| 593 | +off shoulder top | |
| 594 | +off white sneakers | |
| 595 | +olive green jacket | |
| 596 | +olive jacket | |
| 597 | +olive utility jacket | |
| 598 | +one-piece swimsuit | |
| 599 | +one-shoulder top | |
| 600 | +organic cotton baby blanket | |
| 601 | +organic cotton baby wear | |
| 602 | +organic cotton crew neck tee | |
| 603 | +organic cotton crew neck tee vintage | |
| 604 | +organic cotton crew neck t-shirt | |
| 605 | +organic cotton t-shirt | |
| 606 | +organic cotton underwear | |
| 607 | +organic cotton washcloth | |
| 608 | +organic cotton white t-shirt | |
| 609 | +organza top | |
| 610 | +oversized cardigan | |
| 611 | +oversized hoodie | |
| 612 | +oversized knit sweater | |
| 613 | +oversized puffer vest | |
| 614 | +oversized pullover hoodie | |
| 615 | +oversized scarf | |
| 616 | +oversized t-shirt | |
| 617 | +oversized turtleneck sweater | |
| 618 | +oversized zip up hoodie | |
| 619 | +paisley print | |
| 620 | +paisley print boho | |
| 621 | +pajamas | |
| 622 | +pajamas set | |
| 623 | +palazzo pants | |
| 624 | +pants | |
| 625 | +parent-child matching outfits | |
| 626 | +party dress | |
| 627 | +pastel purple sweater | |
| 628 | +patterned jumpsuit | |
| 629 | +patterned scarf | |
| 630 | +pea coat | |
| 631 | +peak lapel formal | |
| 632 | +pencil skirt | |
| 633 | +pencil skirt stretch | |
| 634 | +pet camera | |
| 635 | +peter pan collar | |
| 636 | +petite a-line skirt | |
| 637 | +petite ankle boots | |
| 638 | +petite jeans | |
| 639 | +petite summer dress | |
| 640 | +petite summer linen shorts | |
| 641 | +petite tailored business suit | |
| 642 | +petite tailored suit jacket | |
| 643 | +petite tailored trousers wrinkle-free | |
| 644 | +petite wedding guest midi dress | |
| 645 | +photoshoot outfit | |
| 646 | +pink sweater | |
| 647 | +pink sweater aesthetic | |
| 648 | +plaid pants | |
| 649 | +plaid skirt | |
| 650 | +plaid trousers | |
| 651 | +platform boots | |
| 652 | +platform sneakers | |
| 653 | +play clothes | |
| 654 | +pleated tennis skirt | |
| 655 | +plus size blouse | |
| 656 | +plus size denim jeans | |
| 657 | +plus size petite jeans | |
| 658 | +plus size swim dress | |
| 659 | +plus size tunic tops | |
| 660 | +plus size tunic tops for leggings | |
| 661 | +plus size winter fleece lined leggings | |
| 662 | +plus size winter gloves | |
| 663 | +plus size winter hiking boots | |
| 664 | +plus size winter parka jacket | |
| 665 | +plus-size women's clothing | |
| 666 | +polarized sunglasses | |
| 667 | +polka dot dress | |
| 668 | +polo shirt | |
| 669 | +polyester fleece jacket | |
| 670 | +polyester running Shorts | |
| 671 | +polyester shorts | |
| 672 | +postpartum outfit | |
| 673 | +pregnancy pillow | |
| 674 | +preppy plaid skirt | |
| 675 | +preppy plaid tennis skirt | |
| 676 | +preppy sweater vest | |
| 677 | +prom dress | |
| 678 | +puffer jacket | |
| 679 | +puffer vest | |
| 680 | +puff sleeve blouse | |
| 681 | +puff sleeve dress | |
| 682 | +pullover hoodie | |
| 683 | +punk faux leather skirt | |
| 684 | +punk studded belt | |
| 685 | +pure cotton t-shirt | |
| 686 | +pure linen bath towel | |
| 687 | +pure linen short set | |
| 688 | +pure linen short set beach vacation | |
| 689 | +pure linen summer dress maxi | |
| 690 | +pure linen tablecloth | |
| 691 | +pure linen wide leg pants | |
| 692 | +quick-dry shirt | |
| 693 | +quick-dry towel | |
| 694 | +quilted jacket | |
| 695 | +rain boots | |
| 696 | +raincoat | |
| 697 | +rain jacket | |
| 698 | +rainy season anorak jacket | |
| 699 | +rainy season waterproof boots | |
| 700 | +rayon blouse | |
| 701 | +rayon blouse print | |
| 702 | +rayon jumpsuit | |
| 703 | +rayon jumpsuit summer | |
| 704 | +Recycled Fabric Hoodie | |
| 705 | +recycled fabric sneakers | |
| 706 | +recycled polyester bag | |
| 707 | +recycled polyester gym bag | |
| 708 | +recycled polyester laptop sleeve | |
| 709 | +recycled polyester reusable shopping bag | |
| 710 | +recycled polyester running jacket | |
| 711 | +recycled polyester swim shorts | |
| 712 | +recycled polyester waterproof shell jacket | |
| 713 | +red silk blouse | |
| 714 | +relaxed fit denim jacket | |
| 715 | +relaxed fit denim shorts | |
| 716 | +relaxed fit sweater | |
| 717 | +relaxed fit sweatpants | |
| 718 | +resort wear | |
| 719 | +retro flare jeans | |
| 720 | +retro patterned jumpsuit | |
| 721 | +ribbed tank top | |
| 722 | +riding boots | |
| 723 | +ripstop cargo trousers | |
| 724 | +robe | |
| 725 | +romantic chiffon blouse | |
| 726 | +romantic puff sleeve blouse | |
| 727 | +romantic silk blouse | |
| 728 | +romper | |
| 729 | +rose gold jewelry | |
| 730 | +round sunglasses | |
| 731 | +ruffle skirt | |
| 732 | +ruffle sleeve | |
| 733 | +ruffle sleeve top | |
| 734 | +running jacket | |
| 735 | +running leggings | |
| 736 | +running shorts | |
| 737 | +running shorts with lining | |
| 738 | +rust colored sweater | |
| 739 | +rust midi skirt | |
| 740 | +sage green lounge set | |
| 741 | +sandals | |
| 742 | +satin cami top | |
| 743 | +satin robe | |
| 744 | +scarf | |
| 745 | +scented candle | |
| 746 | +school uniform | |
| 747 | +school uniform blazer | |
| 748 | +school uniform polo shirt | |
| 749 | +scoop neck tank | |
| 750 | +scrunchie | |
| 751 | +seamless yoga leggings | |
| 752 | +semi-formal evening gown | |
| 753 | +sequin cocktail dress | |
| 754 | +shaving razor | |
| 755 | +shawl collar cardigan | |
| 756 | +sheath dress | |
| 757 | +sheer organza top | |
| 758 | +shell jacket | |
| 759 | +sherpa jacket | |
| 760 | +shimmering metallic crop top | |
| 761 | +shimmering metallic leggings | |
| 762 | +shimmering metallic leggings yoga | |
| 763 | +shimmering metallic party dress | |
| 764 | +shimmering metallic phone case | |
| 765 | +shirt | |
| 766 | +shoe inserts | |
| 767 | +shoe laces | |
| 768 | +shorts | |
| 769 | +short set | |
| 770 | +silk blouse | |
| 771 | +silk dress | |
| 772 | +silk pajamas | |
| 773 | +silk pajamas set | |
| 774 | +silk robe | |
| 775 | +silk slip dress | |
| 776 | +silver metallic heels | |
| 777 | +skiing fleece mid layer | |
| 778 | +skiing thermal underwear | |
| 779 | +skiing trip insulated base layer | |
| 780 | +skinny fit jeggings | |
| 781 | +skinny fit pants | |
| 782 | +skinny jeans | |
| 783 | +skinny pants | |
| 784 | +ski outfit | |
| 785 | +skirt | |
| 786 | +skirt suit | |
| 787 | +ski socks | |
| 788 | +sleeveless dress | |
| 789 | +sleeveless summer dress | |
| 790 | +slim fit ankle pants | |
| 791 | +slim fit blazer wool | |
| 792 | +slim fit button-down shirt | |
| 793 | +slim fit trousers | |
| 794 | +slip dress | |
| 795 | +slip on sneakers | |
| 796 | +smart casual men | |
| 797 | +snake print boots | |
| 798 | +sneaker matching ankle socks | |
| 799 | +sneaker matching crew socks | |
| 800 | +sneaker matching invisible socks | |
| 801 | +sneaker matching low cut socks | |
| 802 | +sneaker matching no show socks | |
| 803 | +sneaker matching shoe laces | |
| 804 | +sneakers | |
| 805 | +sock boots | |
| 806 | +sofa bed | |
| 807 | +soft robe | |
| 808 | +spaghetti strap cami | |
| 809 | +spaghetti strap dress | |
| 810 | +Spandex Compression Bra | |
| 811 | +spandex cycling shorts | |
| 812 | +sports bra | |
| 813 | +sports bra high impact | |
| 814 | +sports top | |
| 815 | +sportswear outfit | |
| 816 | +spring blouse floral | |
| 817 | +spring lightweight floral jacket | |
| 818 | +spring trench coat | |
| 819 | +square neck top | |
| 820 | +stain-resistant apron cooking | |
| 821 | +stain-resistant boys play pants | |
| 822 | +stain-resistant kids' play clothes | |
| 823 | +stain-resistant kids school uniform | |
| 824 | +stain-resistant placemats | |
| 825 | +stain-resistant white t-shirt | |
| 826 | +stiletto boots | |
| 827 | +stiletto heels | |
| 828 | +stiletto protectors | |
| 829 | +straight leg ankle pants | |
| 830 | +straight leg ankle pants office wear | |
| 831 | +straight leg ankle pants stretch | |
| 832 | +straight leg anti-chafing pants | |
| 833 | +straight leg jeans | |
| 834 | +straight leg pants | |
| 835 | +straight leg trousers work | |
| 836 | +straight leg trousers workwear | |
| 837 | +strapless dress | |
| 838 | +straw hat | |
| 839 | +streetwear graphic hoodie | |
| 840 | +streetwear hoodie men | |
| 841 | +streetwear oversized hoodie | |
| 842 | +streetwear zip-up hoodie | |
| 843 | +stretch jeans | |
| 844 | +stretch jeans straight leg | |
| 845 | +striped shirt | |
| 846 | +striped shirt navy | |
| 847 | +striped sweater | |
| 848 | +studded belt | |
| 849 | +suede ankle boots | |
| 850 | +suede boots | |
| 851 | +suede loafers | |
| 852 | +suede loafers casual | |
| 853 | +suit | |
| 854 | +suit jacket | |
| 855 | +suit men | |
| 856 | +summer quick-dry UPF 50 shirt | |
| 857 | +summer strapless dress | |
| 858 | +summer sundress floral | |
| 859 | +sundress | |
| 860 | +sun hat | |
| 861 | +sun hat wide brim | |
| 862 | +sun protection clothing | |
| 863 | +sun protective clothing | |
| 864 | +support belt | |
| 865 | +sweater | |
| 866 | +sweater dress | |
| 867 | +sweater vest | |
| 868 | +sweatpants | |
| 869 | +sweatshirt | |
| 870 | +sweatsuit | |
| 871 | +sweetheart neckline dress | |
| 872 | +swim dress | |
| 873 | +swim shorts | |
| 874 | +swimsuit | |
| 875 | +swim trunks | |
| 876 | +tailored shirt | |
| 877 | +tailored suit | |
| 878 | +tailored suit jacket | |
| 879 | +tailored suit slim fit | |
| 880 | +tall slim fit business casual blazer | |
| 881 | +tall slim fit men's dress shirt | |
| 882 | +tall slim fit men's linen shirt | |
| 883 | +tall slim fit men's suit vest | |
| 884 | +tall slim fit office wear blazer | |
| 885 | +tall slim fit tie | |
| 886 | +tall slim fit trousers | |
| 887 | +tall straight leg pants | |
| 888 | +tank top | |
| 889 | +tassel maxi skirt | |
| 890 | +teacher clothes | |
| 891 | +teal blouse | |
| 892 | +teal satin blouse | |
| 893 | +temperature regulating mattress pad | |
| 894 | +temperature regulating pajamas | |
| 895 | +temperature regulating pillow | |
| 896 | +temperature regulating silk pajamas | |
| 897 | +temperature regulating sleepwear | |
| 898 | +temperature regulating winter socks | |
| 899 | +tennis skirt | |
| 900 | +terracotta linen pants | |
| 901 | +terracotta pants | |
| 902 | +terry cloth bathrobe | |
| 903 | +terry cloth shorts | |
| 904 | +terry cloth sweatshirt | |
| 905 | +thermal base layer | |
| 906 | +thermal base layer top | |
| 907 | +thermal long sleeve top | |
| 908 | +thermal skiing jacket | |
| 909 | +thermal underwear | |
| 910 | +thigh shorts anti-chafing | |
| 911 | +tie | |
| 912 | +tie-dye bucket hat | |
| 913 | +tie-dye dress | |
| 914 | +tie-dye oversized knit sweater | |
| 915 | +tie-dye oversized sweatshirt | |
| 916 | +tie-dye socks pastel colors | |
| 917 | +tie-dye summer maxi dress | |
| 918 | +tie-dye water bottle | |
| 919 | +tie-waist pants | |
| 920 | +tiktok outfit | |
| 921 | +toddler crib | |
| 922 | +tote bag | |
| 923 | +track suit | |
| 924 | +track suit quick-dry | |
| 925 | +transitional light jacket | |
| 926 | +transitional sweater vest | |
| 927 | +Travel attire | |
| 928 | +travel blazer | |
| 929 | +travel blazer lightweight | |
| 930 | +travel clothes | |
| 931 | +travel lightweight scarf | |
| 932 | +travel pants | |
| 933 | +travel suit | |
| 934 | +travel vest | |
| 935 | +travel wrinkle-free pants | |
| 936 | +trench coat | |
| 937 | +tropical climate breathable shorts | |
| 938 | +tropical climate sandals | |
| 939 | +Tropical Climate Sandals Leather | |
| 940 | +tropical print shirt | |
| 941 | +trousers | |
| 942 | +trumpet sleeve | |
| 943 | +trumpet sleeve top | |
| 944 | +t-shirt | |
| 945 | +tube top | |
| 946 | +tulle overlay skirt | |
| 947 | +tulle skirt | |
| 948 | +tulle skirt party | |
| 949 | +tunic | |
| 950 | +tunic top | |
| 951 | +tunic tops | |
| 952 | +tunic tops loose fit | |
| 953 | +turtleneck sweater | |
| 954 | +tweed blazer | |
| 955 | +two piece set | |
| 956 | +ugly christmas sweater | |
| 957 | +umbrella | |
| 958 | +underwear | |
| 959 | +utility cargo pants | |
| 960 | +utility cargo skirt | |
| 961 | +UV protection face mask | |
| 962 | +UV protection fishing shirt | |
| 963 | +UV protection gardening hat | |
| 964 | +UV protection sun hat | |
| 965 | +UV protection women's sun gloves | |
| 966 | +vacation outfits | |
| 967 | +valentines day outfit | |
| 968 | +velvet cocktail party dress | |
| 969 | +velvet dress | |
| 970 | +velvet heels | |
| 971 | +velvet heels formal | |
| 972 | +velvet holiday dress | |
| 973 | +velvet midi skirt | |
| 974 | +velvet skirt | |
| 975 | +vest | |
| 976 | +vintage denim jacket | |
| 977 | +vintage distressed denim jacket | |
| 978 | +vintage leather skirt | |
| 979 | +viscose cami top | |
| 980 | +viscose scarf patterned | |
| 981 | +v-neck t-shirt | |
| 982 | +waffle knit henley | |
| 983 | +waist trainer | |
| 984 | +wallet | |
| 985 | +waterproof boots | |
| 986 | +waterproof hooded jacket | |
| 987 | +waterproof raincoat | |
| 988 | +waterproof shell jacket | |
| 989 | +waterproof spray | |
| 990 | +Wearing small clothes | |
| 991 | +wedding guest cocktail dress | |
| 992 | +wedding guest dress | |
| 993 | +wedding guest formal attire | |
| 994 | +wedding guest midi dress | |
| 995 | +wedding guest slip dress | |
| 996 | +western cowboy boots | |
| 997 | +western denim shirt | |
| 998 | +white cotton t-shirt | |
| 999 | +white linen pants | |
| 1000 | +wide brim hat | |
| 1001 | +wide leg jeans | |
| 1002 | +wide leg linen pants | |
| 1003 | +wide leg palazzo trousers | |
| 1004 | +wide leg pants | |
| 1005 | +wide leg trousers | |
| 1006 | +window film | |
| 1007 | +winter fleece-lined leggings | |
| 1008 | +winter gloves | |
| 1009 | +Winter outfits | |
| 1010 | +winter parka | |
| 1011 | +winter parka down | |
| 1012 | +winter puffer jacket | |
| 1013 | +women's summer maxi dress | |
| 1014 | +wool blazer | |
| 1015 | +wool blend long coat | |
| 1016 | +wool coat | |
| 1017 | +wool coat tall | |
| 1018 | +wool pea coat | |
| 1019 | +wool socks | |
| 1020 | +wool winter coat | |
| 1021 | +work from home | |
| 1022 | +workout clothes | |
| 1023 | +workwear trousers | |
| 1024 | +wrap dress | |
| 1025 | +wrinkle-free business shirt | |
| 1026 | +wrinkle-free business travel suit | |
| 1027 | +wrinkle-free fabric spray | |
| 1028 | +wrinkle-free pants | |
| 1029 | +wrinkle-free travel blazer | |
| 1030 | +wrinkle-free travel dress | |
| 1031 | +wrinkle-free travel pants | |
| 1032 | +wrinkle release spray | |
| 1033 | +y2k low-rise baggy jeans | |
| 1034 | +y2k low-rise cargo pants | |
| 1035 | +y2k low rise jeans | |
| 1036 | +y2k tube top | |
| 1037 | +yoga leggings | |
| 1038 | +yoga mat | |
| 1039 | +yoga outfit | |
| 1040 | +yoga pants | |
| 1041 | +yoga pants high-waisted | |
| 1042 | +yoga shorts | |
| 1043 | +zebra print pants | |
| 1044 | +zipper boots | |
| 1045 | +zip-up hoodie | |
| 1046 | +zoom shirt | ... | ... |
scripts/evaluation/queries/fashion_quries__high_quality.txt.v2.uniq.trans
0 → 100644
| ... | ... | @@ -0,0 +1,1232 @@ |
| 1 | +抽象打印顶部 | |
| 2 | +丙烯酸无檐便帽 | |
| 3 | +亚克力豆豆帽 | |
| 4 | +丙烯酸针织帽 | |
| 5 | +亚克力针织帽 | |
| 6 | +机场装备 | |
| 7 | +A字裙 | |
| 8 | +a字裙 | |
| 9 | +全季旅行裤 | |
| 10 | +四季旅行裤 | |
| 11 | +全季轻便背心 | |
| 12 | +踝靴 | |
| 13 | +及踝连衣裙 | |
| 14 | +及踝牛仔裤 | |
| 15 | +及膝牛仔裤 | |
| 16 | +及踝长裙 | |
| 17 | +齐踝长裙 | |
| 18 | +脚踝裤 | |
| 19 | +脚踝裤不起皱 | |
| 20 | +脚踝长裤无皱纹 | |
| 21 | +短袜 | |
| 22 | +白色脚踝袜 | |
| 23 | +防寒茄克衫 | |
| 24 | +防擦车身滑杆 | |
| 25 | +防擦伤运动短裤 | |
| 26 | +防擦伤裤 | |
| 27 | +防擦伤长裤 | |
| 28 | +防擦伤跑步短裤 | |
| 29 | +防摩擦无缝瑜伽短裤 | |
| 30 | +防擦伤女式跑步短裤 | |
| 31 | +臂袖 | |
| 32 | +不对称陀螺 | |
| 33 | +运动休闲紧身裤 | |
| 34 | +运动紧身裤 | |
| 35 | +运动休闲文胸 | |
| 36 | +运动休闲运动内衣 | |
| 37 | +运动休闲运动套装 | |
| 38 | +运动型紧身短裤 | |
| 39 | +运动型跑步短裤 | |
| 40 | +运动装备 | |
| 41 | +运动短裤 | |
| 42 | +运动袜 | |
| 43 | +美利奴羊毛运动袜 | |
| 44 | +运动背心 | |
| 45 | +运动T恤 | |
| 46 | +秋季花呢上衣 | |
| 47 | +秋季运动夹克 | |
| 48 | +秋季风衣 | |
| 49 | +婴儿毯 | |
| 50 | +婴儿连体衣 | |
| 51 | +背包 | |
| 52 | +宽松牛仔裤 | |
| 53 | +布袋牛仔裤 | |
| 54 | +气球袖 | |
| 55 | +气球袖衬衫 | |
| 56 | +竹制袜子 | |
| 57 | +竹袜 | |
| 58 | +基本棉t恤 | |
| 59 | +基本棉T恤 | |
| 60 | +基本油箱顶部带肋 | |
| 61 | +基本款背心罗纹 | |
| 62 | +浴袍 | |
| 63 | +女式浴袍 | |
| 64 | +浴巾 | |
| 65 | +海滩掩护 | |
| 66 | +海滩遮盖 | |
| 67 | +沙滩巾 | |
| 68 | +沙滩伞 | |
| 69 | +海滩度假掩盖 | |
| 70 | +海滩度假装 | |
| 71 | +海滩度假套装 | |
| 72 | +海滩度假泳装 | |
| 73 | +海滩度假泳衣 | |
| 74 | +沙滩婚纱 | |
| 75 | +毛线帽 | |
| 76 | +床单 | |
| 77 | +米色风衣 | |
| 78 | +喇叭裤 | |
| 79 | +腰带 | |
| 80 | +系带大衣 | |
| 81 | +皮带外套 | |
| 82 | +系带连身裤 | |
| 83 | +阔腿系带连身裤 | |
| 84 | +带口袋的系带连身裤 | |
| 85 | +腰带和服长袍 | |
| 86 | +系带亚麻混纺长裙 | |
| 87 | +系带摩托车夹克 | |
| 88 | +带腰带的摩托车夹克 | |
| 89 | +带口袋的系带风衣 | |
| 90 | +束腰训练器 | |
| 91 | +比基尼海滩 | |
| 92 | +主教袖 | |
| 93 | +主教袖上衣 | |
| 94 | +黑色靴子 | |
| 95 | +黑色迷你连衣裙 | |
| 96 | +黑色领带裙 | |
| 97 | +西装外套 | |
| 98 | +蓝色牛仔夹克 | |
| 99 | +船领毛衣 | |
| 100 | +紧身连衣裙 | |
| 101 | +连体衣 | |
| 102 | +波西米亚风格的长裙 | |
| 103 | +波西米亚长裙 | |
| 104 | +波西米亚流苏长裙 | |
| 105 | +波西米亚棉质束腰外衣 | |
| 106 | +波西米亚束腰棉 | |
| 107 | +短靴灯芯绒长裤 | |
| 108 | +马靴用短靴牛仔裤 | |
| 109 | +短靴牛仔裤 | |
| 110 | +复古短靴牛仔裤 | |
| 111 | +短靴裤 | |
| 112 | +开襟长裤 | |
| 113 | +牛仔靴开叉裤 | |
| 114 | +冬季靴子的短靴裤 | |
| 115 | +天鹅绒短靴裤 | |
| 116 | +短靴瑜伽裤 | |
| 117 | +四角裤 | |
| 118 | +平角内裤 | |
| 119 | +男朋友牛仔裤 | |
| 120 | +手镯套装 | |
| 121 | +透气短裤 | |
| 122 | +伴娘裙 | |
| 123 | +棕色皮靴 | |
| 124 | +渔夫帽 | |
| 125 | +酒红色连衣裙 | |
| 126 | +酒红色节日连衣裙 | |
| 127 | +勃艮第节日连衣裙 | |
| 128 | +焦橙色裤子 | |
| 129 | +烧焦的橙色裤子 | |
| 130 | +商务休闲夹克 | |
| 131 | +商务休闲斜纹棉布裤 | |
| 132 | +商务休闲奇诺 | |
| 133 | +商务休闲乐福鞋 | |
| 134 | +商务休闲女性 | |
| 135 | +商务套装 | |
| 136 | +商务旅行套装 | |
| 137 | +蝴蝶发夹 | |
| 138 | +蝶形上衣 | |
| 139 | +蝴蝶上衣 | |
| 140 | +纽扣衬衫 | |
| 141 | +针织毛衣 | |
| 142 | +女士背心 | |
| 143 | +吊带背心 | |
| 144 | +卡米上衣 | |
| 145 | +帆布手提包 | |
| 146 | +帽子 | |
| 147 | +帽 | |
| 148 | +帽袖 | |
| 149 | +帽套顶部 | |
| 150 | +帽袖上衣 | |
| 151 | +开襟毛衣 | |
| 152 | +工装裤 | |
| 153 | +工装裤隐藏口袋 | |
| 154 | +载货裙 | |
| 155 | +羊绒混纺开衫 | |
| 156 | +羊绒开衫 | |
| 157 | +羊绒围巾 | |
| 158 | +羊绒衫 | |
| 159 | +米色羊绒衫 | |
| 160 | +米色羊绒毛衣 | |
| 161 | +休闲日间连身裤 | |
| 162 | +休闲日服 | |
| 163 | +休闲日间背心 | |
| 164 | +休闲日背心 | |
| 165 | +休闲星期五马球衫 | |
| 166 | +休闲裤 | |
| 167 | +休闲马球衫 | |
| 168 | +休闲Polo衫 | |
| 169 | +休闲周末装 | |
| 170 | +猫床 | |
| 171 | +炭灰色大衣 | |
| 172 | +炭灰色涂层 | |
| 173 | +格子裙 | |
| 174 | +切尔西靴 | |
| 175 | +雪纺衬衫 | |
| 176 | +雪纺衬衫透明 | |
| 177 | +雪纺裙 | |
| 178 | +雪纺围巾轻盈 | |
| 179 | +斜纹棉布裤 | |
| 180 | +巧克力棕色靴子 | |
| 181 | +厚实针织毛衣 | |
| 182 | +厚底靴 | |
| 183 | +经典合身上衣 | |
| 184 | +经典合身运动夹克 | |
| 185 | +经典合身马球衫 | |
| 186 | +经典合身Polo衫 | |
| 187 | +经典风衣高 | |
| 188 | +经典羊毛大衣 | |
| 189 | +清洁溶液 | |
| 190 | +闭趾高跟鞋 | |
| 191 | +手拿包 | |
| 192 | +科切拉服装 | |
| 193 | +外套 | |
| 194 | +钴蓝色连衣裙 | |
| 195 | +鸡尾酒裙 | |
| 196 | +露肩上衣 | |
| 197 | +冷肩衬衫 | |
| 198 | +防寒保暖靴 | |
| 199 | +寒冷天气隔热靴 | |
| 200 | +寒冷天气羊毛袜 | |
| 201 | +拼色连帽衫 | |
| 202 | +通勤者服装 | |
| 203 | +紧身防擦伤跑步紧身裤 | |
| 204 | +压缩臂袖 | |
| 205 | +压缩式运动装备 | |
| 206 | +压缩运动装备 | |
| 207 | +压缩胸罩 | |
| 208 | +压缩健身房锻炼跑步短裤 | |
| 209 | +膝关节加压支架 | |
| 210 | +紧身裤 | |
| 211 | +紧身短裤 | |
| 212 | +压力袜 | |
| 213 | +紧身紧身裤 | |
| 214 | +冬季紧身裤 | |
| 215 | +压缩式上衣 | |
| 216 | +紧身上衣氨纶 | |
| 217 | +压缩瑜伽运动胸罩 | |
| 218 | +演唱会服装 | |
| 219 | +可转换背包旅行包 | |
| 220 | +敞篷连衣裙 | |
| 221 | +敞篷登山裤 | |
| 222 | +敞篷徒步裤 | |
| 223 | +可转换笔记本电脑包 | |
| 224 | +可转换颈枕毯 | |
| 225 | +敞篷裤 | |
| 226 | +可转换袖衬衫 | |
| 227 | +可转换袖UPF 50衬衫 | |
| 228 | +可转换拉链登山裤 | |
| 229 | +灯芯绒运动夹克 | |
| 230 | +灯芯绒秋裤 | |
| 231 | +灯芯绒秋季长裤 | |
| 232 | +条绒夹克 | |
| 233 | +灯芯绒长裤 | |
| 234 | +灯芯绒裙子 | |
| 235 | +灯芯绒迷你裙 | |
| 236 | +束身衣上衣 | |
| 237 | +黑色天鹅绒紧身胸衣上衣 | |
| 238 | +黑色天鹅绒紧身胸衣 | |
| 239 | +棉芯印花长裙 | |
| 240 | +Cottagecore印花长裙 | |
| 241 | +棉芯针织开衫 | |
| 242 | +Cottagecore针织开衫 | |
| 243 | +棉绒布衬衫 | |
| 244 | +棉袄 | |
| 245 | +棉睡衣 | |
| 246 | +棉质背心 | |
| 247 | +棉背心 | |
| 248 | +棉质t恤 | |
| 249 | +白色棉质t恤 | |
| 250 | +白色棉质T恤 | |
| 251 | +棉质上衣 | |
| 252 | +情侣装 | |
| 253 | +掩盖 | |
| 254 | +牛仔靴 | |
| 255 | +奶油色开衫 | |
| 256 | +奶油色针织毛衣 | |
| 257 | +奶油针织毛衣 | |
| 258 | +奶油色毛衣 | |
| 259 | +圆领运动衫 | |
| 260 | +圆领T恤 | |
| 261 | +钩针上衣 | |
| 262 | +露脐连帽衫 | |
| 263 | +露脐运动文胸 | |
| 264 | +及膝运动内衣 | |
| 265 | +露脐上衣 | |
| 266 | +斜身皮包 | |
| 267 | +斜挎包 | |
| 268 | +游轮服装 | |
| 269 | +曲线贴合腰带 | |
| 270 | +曲线合身高腰牛仔裤 | |
| 271 | +曲线合身的长裙 | |
| 272 | +修身长裙 | |
| 273 | +曲线合身的紧身牛仔裤 | |
| 274 | +修身牛仔裤 | |
| 275 | +曲线合身的直筒牛仔裤 | |
| 276 | +曲线合身的弹力牛仔裤 | |
| 277 | +曲线合身的弹力铅笔裙 | |
| 278 | +曲线合身的裤子 | |
| 279 | +修身长裤 | |
| 280 | +曲线合身阔腿牛仔裤 | |
| 281 | +修身宽腿牛仔裤 | |
| 282 | +衬垫衬垫 | |
| 283 | +镂空连衣裙 | |
| 284 | +骑行短裤 | |
| 285 | +深色学术运动夹克 | |
| 286 | +黑暗学院开拓者 | |
| 287 | +深色学术格子裤 | |
| 288 | +深色学院格子长裤 | |
| 289 | +深色学术羊毛大衣 | |
| 290 | +约会之夜紧身连衣裙 | |
| 291 | +约会晚礼服 | |
| 292 | +约会之夜迷你裙 | |
| 293 | +约会之夜迷你连衣裙 | |
| 294 | +约会装 | |
| 295 | +牛仔夹克 | |
| 296 | +破旧牛仔夹克 | |
| 297 | +牛仔夹克破旧 | |
| 298 | +大号牛仔夹克 | |
| 299 | +牛仔夹克超大 | |
| 300 | +牛仔牛仔裤 | |
| 301 | +牛仔裤 | |
| 302 | +牛仔衬衫 | |
| 303 | +牛仔短裤 | |
| 304 | +牛仔裙 | |
| 305 | +牛仔裙midi | |
| 306 | +牛仔裙Midi | |
| 307 | +破旧牛仔夹克 | |
| 308 | +破旧牛仔裤 | |
| 309 | +碎花连衣裙 | |
| 310 | +狗毯 | |
| 311 | +狗大衣 | |
| 312 | +狗笼垫 | |
| 313 | +狗用背带 | |
| 314 | +狗绳 | |
| 315 | +狗毛巾 | |
| 316 | +狗玩具 | |
| 317 | +狗食 | |
| 318 | +双排扣大衣 | |
| 319 | +双排扣外套 | |
| 320 | +羽绒服 | |
| 321 | +羽绒背心 | |
| 322 | +连衣裙 | |
| 323 | +连衣裙凉鞋 | |
| 324 | +正装凉鞋 | |
| 325 | +衬衫 | |
| 326 | +行李袋 | |
| 327 | +尘土飞扬的玫瑰色连衣裙 | |
| 328 | +玫瑰粉连衣裙 | |
| 329 | +尘土飞扬的玫瑰色丝绸吊带裙 | |
| 330 | +尘土飞扬的玫瑰色天鹅绒鸡尾酒会礼服 | |
| 331 | +尘土飞扬的玫瑰天鹅绒缎带 | |
| 332 | +尘土飞扬的玫瑰天鹅绒发髻 | |
| 333 | +尘土飞扬的玫瑰色天鹅绒丝绸连衣裙 | |
| 334 | +尘土飞扬的玫瑰色天鹅绒吊带裙 | |
| 335 | +初秋灯芯绒夹克 | |
| 336 | +泥土色调橄榄色 | |
| 337 | +土色调服装 | |
| 338 | +土色调套装 | |
| 339 | +电蓝色迷你裙 | |
| 340 | +绣花上衣 | |
| 341 | +祖母绿上衣 | |
| 342 | +翡翠绿上衣 | |
| 343 | +符合人体工程学的办公椅 | |
| 344 | +晚礼服 | |
| 345 | +晚礼服高跟鞋 | |
| 346 | +晚礼服 | |
| 347 | +口罩时尚 | |
| 348 | +秋季羊毛衫舒适 | |
| 349 | +秋季舒适开衫 | |
| 350 | +秋季牛仔夹克 | |
| 351 | +秋季过渡风衣 | |
| 352 | +腰包 | |
| 353 | +父亲节衬衫 | |
| 354 | +人造毛皮大衣 | |
| 355 | +人造革手包 | |
| 356 | +人造皮夹克 | |
| 357 | +短款人造皮夹克 | |
| 358 | +摩托风格人造皮夹克 | |
| 359 | +人造皮革紧身裤 | |
| 360 | +哑光人造皮革紧身裤 | |
| 361 | +仿皮哑光紧身裤 | |
| 362 | +人造皮革摩托车夹克 | |
| 363 | +人造皮革摩托车夹克,带腰带 | |
| 364 | +人造皮裤 | |
| 365 | +人造革长裤 | |
| 366 | +人造革裙子 | |
| 367 | +人造皮革钱包 | |
| 368 | +节日音乐会露顶 | |
| 369 | +节日音乐会Crop Top | |
| 370 | +节日音乐会连身裤 | |
| 371 | +节日音乐会连体衣 | |
| 372 | +节日服装 | |
| 373 | +野战夹克 | |
| 374 | +初次约会服装 | |
| 375 | +渔夫针织 | |
| 376 | +渔夫针织毛衣 | |
| 377 | +钓鱼衬衫 | |
| 378 | +长袖钓鱼衫 | |
| 379 | +钓鱼衫长袖 | |
| 380 | +渔网上衣 | |
| 381 | +法兰绒衬衫 | |
| 382 | +喇叭形上衣 | |
| 383 | +喇叭牛仔裤 | |
| 384 | +喇叭裤 | |
| 385 | +天鹅绒喇叭裤 | |
| 386 | +丝绒喇叭裤 | |
| 387 | +喇叭袖衬衫 | |
| 388 | +抓绒夹克 | |
| 389 | +冬季羊毛夹克 | |
| 390 | +羊毛夹克冬季 | |
| 391 | +羊毛衬里紧身裤 | |
| 392 | +羊毛中层 | |
| 393 | +羊毛套头衫 | |
| 394 | +花衬衫 | |
| 395 | +花夹克 | |
| 396 | +印花长裙 | |
| 397 | +花朵长裙 | |
| 398 | +飘逸雪纺连衣裙 | |
| 399 | +颤动袖 | |
| 400 | +颤振袖顶 | |
| 401 | +飘逸袖上衣 | |
| 402 | +礼服 | |
| 403 | +正装 | |
| 404 | +礼服 | |
| 405 | +四向弹力黑色瑜伽裤 | |
| 406 | +四向弹力牛仔牛仔裤 | |
| 407 | +四向拉伸弹性带 | |
| 408 | +四向弹力孕妇紧身裤 | |
| 409 | +四向弹力高修身长裤 | |
| 410 | +四向弹力瑜伽裤 | |
| 411 | +流苏包 | |
| 412 | +流苏袋 | |
| 413 | +几何图案毛衣 | |
| 414 | +条纹连衣裙 | |
| 415 | +金黄色连衣裙 | |
| 416 | +条纹野餐裙 | |
| 417 | +Gingham野餐裙 | |
| 418 | +条纹夏装 | |
| 419 | +金黄色连衣裙 | |
| 420 | +眼镜架 | |
| 421 | +黄金首饰 | |
| 422 | +黄金珠宝 | |
| 423 | +金项链 | |
| 424 | +金色亮片上衣 | |
| 425 | +高尔夫球衫 | |
| 426 | +高尔夫球衫紫外线防护 | |
| 427 | +哥特式黑色靴子 | |
| 428 | +哥特式系带紧身胸衣 | |
| 429 | +哥特式天鹅绒连衣裙 | |
| 430 | +毕业典礼定制西装 | |
| 431 | +毕业礼服 | |
| 432 | +图案连帽衫 | |
| 433 | +图案T恤 | |
| 434 | +图案t恤 | |
| 435 | +复古水洗图案t恤 | |
| 436 | +图案T恤复古水洗 | |
| 437 | +灰色连帽衫 | |
| 438 | +绿色工装裤 | |
| 439 | +破旧牛仔裤 | |
| 440 | +破烂牛仔裤 | |
| 441 | +脏兮兮的法兰绒衬衫 | |
| 442 | +粗面法兰绒衬衫 | |
| 443 | +健身包 | |
| 444 | +带口袋的健身紧身裤 | |
| 445 | +带口袋的健身房紧身裤 | |
| 446 | +运动服 | |
| 447 | +健身巾 | |
| 448 | +健身房毛巾 | |
| 449 | +健身房运动紧身上衣 | |
| 450 | +健身房运动短裤 | |
| 451 | +健身房锻炼跑步短裤 | |
| 452 | +健身房运动文胸 | |
| 453 | +健身房运动内衣 | |
| 454 | +发巾 | |
| 455 | +万圣节服装创意 | |
| 456 | +露背领上衣 | |
| 457 | +Halter领上衣 | |
| 458 | +节省衣架空间 | |
| 459 | +高跟鞋 | |
| 460 | +隐藏式口袋腰包 | |
| 461 | +隐藏式口袋无限围巾 | |
| 462 | +隐藏式口袋护照夹背心 | |
| 463 | +隐藏式口袋旅行带 | |
| 464 | +隐藏口袋旅行背心 | |
| 465 | +隐藏口袋女式旅行背心 | |
| 466 | +高跟兼容鸡尾酒会礼服 | |
| 467 | +高跟兼容连衣裙凉鞋 | |
| 468 | +高跟兼容的正式连衣裙 | |
| 469 | +与高跟鞋兼容的正式连身裤 | |
| 470 | +与高跟鞋兼容的鞋垫 | |
| 471 | +高领连衣裙 | |
| 472 | +高腰紧身裤 | |
| 473 | +高腰裤 | |
| 474 | +高腰阔腿裤 | |
| 475 | +高腰阔腿长裤 | |
| 476 | +高腰牛仔牛仔裤 | |
| 477 | +高腰牛仔短裤 | |
| 478 | +高腰牛仔裤 | |
| 479 | +登山靴 | |
| 480 | +徒步旅行装备 | |
| 481 | +登山裤 | |
| 482 | +徒步裤女士 | |
| 483 | +徒步女裤 | |
| 484 | +登山衫可转换袖 | |
| 485 | +徒步旅行衬衫可转换袖 | |
| 486 | +旅游鞋 | |
| 487 | +登山袜 | |
| 488 | +徒步旅行背包 | |
| 489 | +远足径雨衣 | |
| 490 | +嬉皮士喇叭裤 | |
| 491 | +嬉皮士扎染连衣裙 | |
| 492 | +节日礼服 | |
| 493 | +假日季正式礼服 | |
| 494 | +节日天鹅绒连衣裙 | |
| 495 | +假日天鹅绒连衣裙 | |
| 496 | +返校节礼服 | |
| 497 | +家居服 | |
| 498 | +外套 | |
| 499 | +连帽衫 | |
| 500 | +千鸟皮大衣 | |
| 501 | +Houndstooth大衣 | |
| 502 | +instagram套装 | |
| 503 | +隔热基层 | |
| 504 | +保温基层 | |
| 505 | +绝缘靴 | |
| 506 | +绝缘手套 | |
| 507 | +隔热手套 | |
| 508 | +面试服装 | |
| 509 | +隐形袜子 | |
| 510 | +象牙色连衣裙 | |
| 511 | +象牙色婚纱 | |
| 512 | +夹克 | |
| 513 | +牛仔裤 | |
| 514 | +牛仔短裤 | |
| 515 | +牛仔样式打底裤 | |
| 516 | +宝石色连衣裙 | |
| 517 | +珠宝色调连衣裙 | |
| 518 | +宝石色调祖母绿 | |
| 519 | +慢跑裤 | |
| 520 | +束脚裤 | |
| 521 | +连体衣 | |
| 522 | +kawaii作物顶部 | |
| 523 | +Kawaii作物顶部 | |
| 524 | +kawaii迷你连衣裙 | |
| 525 | +Kawaii迷你连衣裙 | |
| 526 | +钥匙孔顶部 | |
| 527 | +卡其色绿色背包 | |
| 528 | +卡其色绿色帆布手提包 | |
| 529 | +卡其色绿色工装裤实用 | |
| 530 | +卡其色绿色军用夹克 | |
| 531 | +卡其绿色防刮敞篷裤 | |
| 532 | +卡其绿色耐磨面料长裤 | |
| 533 | +儿童棉睡衣套装 | |
| 534 | +和服 | |
| 535 | +护膝 | |
| 536 | +过膝长靴 | |
| 537 | +针织开衫 | |
| 538 | +针织帽 | |
| 539 | +针织围巾 | |
| 540 | +针织毛衣 | |
| 541 | +针织品 | |
| 542 | +蕾丝文胸 | |
| 543 | +蕾丝连衣裙 | |
| 544 | +灯笼袖 | |
| 545 | +灯笼袖上衣 | |
| 546 | +笔记本电脑背包 | |
| 547 | +笔记本电脑包 | |
| 548 | +分层连帽衫 | |
| 549 | +皮革包 | |
| 550 | +皮包 | |
| 551 | +皮夹克 | |
| 552 | +皮凉鞋 | |
| 553 | +皮鞋 | |
| 554 | +皮裙 | |
| 555 | +紧身裤 | |
| 556 | +豹纹衬衫 | |
| 557 | +薄夹克衫 | |
| 558 | +轻便夹克 | |
| 559 | +轻便围巾 | |
| 560 | +亚麻混纺长裙 | |
| 561 | +亚麻混纺长裤 | |
| 562 | +亚麻连衣裙 | |
| 563 | +亚麻织物透气 | |
| 564 | +亚麻裤 | |
| 565 | +亚麻短裤 | |
| 566 | +亚麻短套装 | |
| 567 | +夏季亚麻短裤 | |
| 568 | +亚麻短裤夏季 | |
| 569 | +亚麻裤 | |
| 570 | +猫砂盆 | |
| 571 | +乐福鞋 | |
| 572 | +长款运动夹克 | |
| 573 | +长大衣 | |
| 574 | +长袖上衣 | |
| 575 | +长款束腰上衣 | |
| 576 | +宽松男朋友牛仔裤 | |
| 577 | +宽松的帽子 | |
| 578 | +宽松开衫羊绒 | |
| 579 | +宽松开襟羊毛衫 | |
| 580 | +宽松图案连帽衫街头装 | |
| 581 | +宽松图案印花t恤 | |
| 582 | +宽松图案t恤 | |
| 583 | +宽松针织毛衣 | |
| 584 | +宽松夏季纯棉t恤 | |
| 585 | +宽松束腰外衣 | |
| 586 | +休闲服连体衣 | |
| 587 | +休闲睡衣套装 | |
| 588 | +休闲服睡衣套装 | |
| 589 | +低腰比基尼下装 | |
| 590 | +低腰牛仔裤 | |
| 591 | +低腰短裤 | |
| 592 | +孕妇休闲周五马球衫 | |
| 593 | +孕妇装 | |
| 594 | +哺乳文胸 | |
| 595 | +孕妇枕头 | |
| 596 | +孕妇枕 | |
| 597 | +真丝孕妇袍 | |
| 598 | +丝绸孕妇服 | |
| 599 | +孕妇支撑带紧身裤 | |
| 600 | +孕妇支撑紧身裤 | |
| 601 | +黑色孕妇支撑紧身裤 | |
| 602 | +孕妇泳装 | |
| 603 | +孕妇长裤 | |
| 604 | +孕妇高长裤 | |
| 605 | +孕妇裤 | |
| 606 | +孕妇装 | |
| 607 | +孕妇阔腿牛仔牛仔裤 | |
| 608 | +孕妇瑜伽裤 | |
| 609 | +紫粉色开衫 | |
| 610 | +长裙 | |
| 611 | +超长连衣裙 | |
| 612 | +超长裙 | |
| 613 | +男式运动跑步短裤 | |
| 614 | +男士正装衬衫 | |
| 615 | +男士跑步短裤 | |
| 616 | +男式西装背心 | |
| 617 | +男士西装背心 | |
| 618 | +美利奴羊毛基层 | |
| 619 | +美利奴羊毛手套衬里 | |
| 620 | +美利奴羊毛登山袜 | |
| 621 | +美利奴羊毛徒步袜 | |
| 622 | +美利奴羊毛滑雪袜 | |
| 623 | +美利奴羊毛袜 | |
| 624 | +美利奴羊毛袜徒步旅行 | |
| 625 | +美利奴羊毛保暖基层顶层 | |
| 626 | +美利奴羊毛保暖紧身裤底层 | |
| 627 | +美利奴羊毛保暖内衣 | |
| 628 | +网眼运动短裤 | |
| 629 | +网眼运动T恤 | |
| 630 | +网眼衬衫 | |
| 631 | +网眼鞋 | |
| 632 | +金属紧身裤 | |
| 633 | +超细纤维长袍 | |
| 634 | +超细纤维毛巾快干 | |
| 635 | +midi鸡尾酒会连衣裙 | |
| 636 | +Midi鸡尾酒会礼服 | |
| 637 | +中长裙 | |
| 638 | +中腰短靴牛仔裤 | |
| 639 | +中号开襟牛仔裤 | |
| 640 | +中腰裙 | |
| 641 | +中季防水外套 | |
| 642 | +军用野战夹克 | |
| 643 | +军绿色夹克 | |
| 644 | +军用夹克 | |
| 645 | +迷你裙 | |
| 646 | +极简主义的高腰裤 | |
| 647 | +极简主义高帮长裤 | |
| 648 | +极简主义亚麻连衣裙 | |
| 649 | +极简主义亚麻长裤 | |
| 650 | +极简主义钱包 | |
| 651 | +迷你裙 | |
| 652 | +立领衬衫 | |
| 653 | +适中的长袖上衣 | |
| 654 | +朴素的连体泳衣 | |
| 655 | +一件式泳衣 | |
| 656 | +吸湿排汗的自行车运动衫 | |
| 657 | +吸湿高尔夫衬衫 | |
| 658 | +男式吸湿高尔夫衬衫 | |
| 659 | +吸湿运动毛巾 | |
| 660 | +吸湿排汗徒步旅行t恤 | |
| 661 | +妈妈装 | |
| 662 | +单色服装 | |
| 663 | +母亲新娘礼服 | |
| 664 | +母亲节礼物 | |
| 665 | +摩托车夹克 | |
| 666 | +芥末黄色上衣 | |
| 667 | +芥末黄上衣 | |
| 668 | +航海豌豆大衣 | |
| 669 | +航海泥炭大衣 | |
| 670 | +航海条纹毛衣 | |
| 671 | +海军蓝上衣 | |
| 672 | +海军运动夹克 | |
| 673 | +海军蓝羊绒无檐便帽 | |
| 674 | +海军蓝羊绒毯 | |
| 675 | +海军蓝羊绒混纺围巾 | |
| 676 | +海军蓝羊绒围巾 | |
| 677 | +海军蓝羊绒冬季大衣 | |
| 678 | +霓虹粉色露脐上衣 | |
| 679 | +中性色调米色 | |
| 680 | +中性色调服装 | |
| 681 | +新妈妈的衣服 | |
| 682 | +除夕夜礼服 | |
| 683 | +隐形袜 | |
| 684 | +无显示袜子 | |
| 685 | +凹口翻领上衣 | |
| 686 | +凹口翻领运动夹克 | |
| 687 | +护士擦洗 | |
| 688 | +哺乳胸罩 | |
| 689 | +护理服 | |
| 690 | +尼龙雨衣防水 | |
| 691 | +尼龙跑步短裤 | |
| 692 | +尼龙短裤 | |
| 693 | +气味控制运动头带 | |
| 694 | +气味控制运动袜 | |
| 695 | +气味控制男式运动袜 | |
| 696 | +气味控制跑鞋鞋垫 | |
| 697 | +气味控制鞋喷雾 | |
| 698 | +气味控制垃圾桶 | |
| 699 | +办公室服装 | |
| 700 | +办公室穿铅笔裙 | |
| 701 | +办公室穿紧身连衣裙 | |
| 702 | +露肩连衣裙 | |
| 703 | +露肩上衣 | |
| 704 | +米白色运动鞋 | |
| 705 | +橄榄绿夹克 | |
| 706 | +橄榄色夹克 | |
| 707 | +橄榄色多功能夹克 | |
| 708 | +橄榄色夹克 | |
| 709 | +连体式泳衣 | |
| 710 | +单肩上衣 | |
| 711 | +有机棉婴儿毯 | |
| 712 | +有机棉婴儿服 | |
| 713 | +有机棉圆领T恤 | |
| 714 | +有机棉圆领复古T恤 | |
| 715 | +有机棉圆领t恤 | |
| 716 | +有机棉t恤 | |
| 717 | +有机棉内衣 | |
| 718 | +有机棉毛巾 | |
| 719 | +有机棉白色t恤 | |
| 720 | +透明硬纱上衣 | |
| 721 | +超大开衫 | |
| 722 | +超大连帽衫 | |
| 723 | +超大针织毛衣 | |
| 724 | +超大羽绒背心 | |
| 725 | +超大套头衫连帽衫 | |
| 726 | +超大围巾 | |
| 727 | +超大t恤 | |
| 728 | +超大T恤 | |
| 729 | +超大高领毛衣 | |
| 730 | +超大拉链连帽衫 | |
| 731 | +佩斯利花纹 | |
| 732 | +佩斯利印花波西米亚风格 | |
| 733 | +睡衣 | |
| 734 | +睡衣套装 | |
| 735 | +阔腿裤 | |
| 736 | +裤子 | |
| 737 | +亲子配套服装 | |
| 738 | +派对礼服 | |
| 739 | +淡紫色毛衣 | |
| 740 | +图案连身衣 | |
| 741 | +图案围巾 | |
| 742 | +双排扣大衣 | |
| 743 | +翻领正装 | |
| 744 | +铅笔裙 | |
| 745 | +铅笔裙弹力 | |
| 746 | +宠物照相机 | |
| 747 | +小圆领 | |
| 748 | +娇小的a字裙 | |
| 749 | +娇小A字裙 | |
| 750 | +小脚靴 | |
| 751 | +小脚踝靴 | |
| 752 | +小脚牛仔裤 | |
| 753 | +娇小的夏装 | |
| 754 | +娇小的夏季亚麻短裤 | |
| 755 | +小巧的定制商务套装 | |
| 756 | +小巧的定制西装外套 | |
| 757 | +修身长裤,不起皱 | |
| 758 | +娇小的婚礼宾客中长裙 | |
| 759 | +摄影服装 | |
| 760 | +粉红色毛衣 | |
| 761 | +粉红色毛衣美学 | |
| 762 | +格子裤 | |
| 763 | +格子裙 | |
| 764 | +格子长裤 | |
| 765 | +厚底靴 | |
| 766 | +厚底运动鞋 | |
| 767 | +玩衣服 | |
| 768 | +打褶网球裙 | |
| 769 | +褶皱网球裙 | |
| 770 | +加大码衬衫 | |
| 771 | +加大码牛仔牛仔裤 | |
| 772 | +加大码小牛仔裤 | |
| 773 | +加大号泳装 | |
| 774 | +加大码泳装 | |
| 775 | +加大码束腰上衣 | |
| 776 | +加大码束腰外衣上衣搭配紧身裤 | |
| 777 | +加大码冬季羊毛衬里紧身裤 | |
| 778 | +加大码冬季手套 | |
| 779 | +加大码冬季登山靴 | |
| 780 | +加大码冬季大衣 | |
| 781 | +加大码女装 | |
| 782 | +偏光太阳镜 | |
| 783 | +圆点花纹服 | |
| 784 | +Polo衫 | |
| 785 | +涤纶羊毛夹克 | |
| 786 | +涤纶跑步短裤 | |
| 787 | +涤纶短裤 | |
| 788 | +产后装 | |
| 789 | +孕期枕头 | |
| 790 | +学院派格子裙 | |
| 791 | +Preppy格子裙 | |
| 792 | +预科生格子网裙 | |
| 793 | +预科生毛衣背心 | |
| 794 | +Preppy毛衣背心 | |
| 795 | +舞会礼服 | |
| 796 | +羽绒服 | |
| 797 | +羽绒背心 | |
| 798 | +蓬松袖衬衫 | |
| 799 | +泡泡袖衬衫 | |
| 800 | +蓬松袖连衣裙 | |
| 801 | +泡泡袖连衣裙 | |
| 802 | +连帽卫衣 | |
| 803 | +朋克仿皮裙 | |
| 804 | +朋克假皮革裙 | |
| 805 | +朋克风格的腰带 | |
| 806 | +朋克钉带 | |
| 807 | +纯棉t恤 | |
| 808 | +纯棉T恤 | |
| 809 | +纯亚麻浴巾 | |
| 810 | +纯亚麻短套装 | |
| 811 | +纯亚麻短套装海滩度假 | |
| 812 | +纯亚麻夏装长裙 | |
| 813 | +纯亚麻桌布 | |
| 814 | +纯亚麻阔腿裤 | |
| 815 | +快干衬衫 | |
| 816 | +快干毛巾 | |
| 817 | +绗缝夹克 | |
| 818 | +棉服 | |
| 819 | +雨靴 | |
| 820 | +雨衣 | |
| 821 | +防雨夹克 | |
| 822 | +雨季风衣 | |
| 823 | +雨季Anorak夹克 | |
| 824 | +雨季防水靴 | |
| 825 | +人造丝上衣 | |
| 826 | +人造丝衬衫印花 | |
| 827 | +人造丝连身衣 | |
| 828 | +夏季人造丝连身裤 | |
| 829 | +夏季人造丝连体衣 | |
| 830 | +再生纤维连帽衫 | |
| 831 | +再生纤维运动鞋 | |
| 832 | +再生聚酯袋 | |
| 833 | +再生聚酯健身袋 | |
| 834 | +再生聚酯笔记本电脑套 | |
| 835 | +再生聚酯可重复使用购物袋 | |
| 836 | +再生聚酯跑步夹克 | |
| 837 | +再生聚酯游泳短裤 | |
| 838 | +再生聚酯防水外套 | |
| 839 | +红色丝绸衬衫 | |
| 840 | +宽松的牛仔夹克 | |
| 841 | +宽松的牛仔短裤 | |
| 842 | +宽松版型牛仔短裤 | |
| 843 | +宽松版型毛衣 | |
| 844 | +宽松运动裤 | |
| 845 | +宽松版型运动裤 | |
| 846 | +度假服装 | |
| 847 | +复古喇叭牛仔裤 | |
| 848 | +复古喇叭裤 | |
| 849 | +复古图案连身裤 | |
| 850 | +肋状罐顶 | |
| 851 | +罗纹背心 | |
| 852 | +马靴 | |
| 853 | +防撕裂工装裤 | |
| 854 | +长袍 | |
| 855 | +浪漫雪纺衬衫 | |
| 856 | +浪漫的泡泡袖上衣 | |
| 857 | +浪漫的丝绸衬衫 | |
| 858 | +浪漫丝绸衬衫 | |
| 859 | +连体衣 | |
| 860 | +玫瑰金首饰 | |
| 861 | +圆形太阳镜 | |
| 862 | +皱边裙 | |
| 863 | +泡泡袖 | |
| 864 | +褶边袖上衣 | |
| 865 | +运动夹克 | |
| 866 | +跑步夹克 | |
| 867 | +跑步紧身裤 | |
| 868 | +运动短裤 | |
| 869 | +带衬里的运动短裤 | |
| 870 | +带衬里的跑步短裤 | |
| 871 | +铁锈色毛衣 | |
| 872 | +锈色中裙 | |
| 873 | +锈米裙 | |
| 874 | +鼠尾草绿色休息室套装 | |
| 875 | +Sage绿色休息室套装 | |
| 876 | +凉鞋 | |
| 877 | +缎面卡米上衣 | |
| 878 | +缎面背心 | |
| 879 | +缎面长袍 | |
| 880 | +围巾 | |
| 881 | +香薰蜡烛 | |
| 882 | +校服 | |
| 883 | +校服上衣 | |
| 884 | +校服运动夹克 | |
| 885 | +校服马球衫 | |
| 886 | +校服Polo衫 | |
| 887 | +勺颈油箱 | |
| 888 | +大圆领背心 | |
| 889 | +发圈 | |
| 890 | +无缝瑜伽紧身裤 | |
| 891 | +半正式晚礼服 | |
| 892 | +亮片鸡尾酒会连衣裙 | |
| 893 | +亮片鸡尾酒会礼服 | |
| 894 | +剃须刀 | |
| 895 | +青果领开衫 | |
| 896 | +紧身连衣裙 | |
| 897 | +透明硬纱上衣 | |
| 898 | +短外套 | |
| 899 | +夏尔巴夹克 | |
| 900 | +夏尔巴人夹克 | |
| 901 | +闪闪发光的金属露脐上衣 | |
| 902 | +闪闪发光的金属紧身裤 | |
| 903 | +闪闪发光的金属紧身裤瑜伽 | |
| 904 | +闪闪发光的金属色派对裙 | |
| 905 | +闪闪发光的金属手机壳 | |
| 906 | +衬衫 | |
| 907 | +鞋嵌件 | |
| 908 | +鞋套 | |
| 909 | +鞋带 | |
| 910 | +短裤 | |
| 911 | +短集 | |
| 912 | +丝绸衬衫 | |
| 913 | +丝绸连衣裙 | |
| 914 | +丝绸睡衣 | |
| 915 | +真丝睡衣 | |
| 916 | +丝绸睡衣套装 | |
| 917 | +丝绸长袍 | |
| 918 | +丝绸吊带裙 | |
| 919 | +银色金属高跟鞋 | |
| 920 | +滑雪羊毛中层 | |
| 921 | +滑雪保暖内衣 | |
| 922 | +滑雪旅行保温基层 | |
| 923 | +紧身牛仔裤 | |
| 924 | +紧身裤 | |
| 925 | +紧身长裤 | |
| 926 | +紧身牛仔裤 | |
| 927 | +紧身裤 | |
| 928 | +滑雪用具 | |
| 929 | +裙子 | |
| 930 | +裙装 | |
| 931 | +滑雪袜 | |
| 932 | +滑雪短袜 | |
| 933 | +无袖连衣裙 | |
| 934 | +无袖夏装 | |
| 935 | +修身及踝长裤 | |
| 936 | +修身脚踝长裤 | |
| 937 | +修身运动夹克羊毛 | |
| 938 | +修身运动衫羊毛 | |
| 939 | +修身纽扣衬衫 | |
| 940 | +修身长裤 | |
| 941 | +吊带裙 | |
| 942 | +穿上运动鞋 | |
| 943 | +防滑运动鞋 | |
| 944 | +时髦休闲男士 | |
| 945 | +蛇纹靴 | |
| 946 | +运动鞋搭配脚踝袜 | |
| 947 | +运动鞋搭配工装裤 | |
| 948 | +运动鞋搭配隐形袜子 | |
| 949 | +运动鞋搭配低帮袜子 | |
| 950 | +运动鞋搭配未露面的袜子 | |
| 951 | +运动鞋配套鞋带 | |
| 952 | +运动鞋 | |
| 953 | +袜子靴 | |
| 954 | +沙发床 | |
| 955 | +柔软的长袍 | |
| 956 | +意大利面条表带卡米 | |
| 957 | +意大利面条吊带背心 | |
| 958 | +细肩带连衣裙 | |
| 959 | +氨纶压缩文胸 | |
| 960 | +氨纶自行车短裤 | |
| 961 | +氨纶骑行短裤 | |
| 962 | +运动文胸 | |
| 963 | +运动文胸高冲击力 | |
| 964 | +运动内衣高冲击力 | |
| 965 | +运动上衣 | |
| 966 | +运动装 | |
| 967 | +春季花衬衫 | |
| 968 | +春季花朵衬衫 | |
| 969 | +春季轻质花夹克 | |
| 970 | +春季风衣少女 | |
| 971 | +春季风衣 | |
| 972 | +方领上衣 | |
| 973 | +防污围裙烹饪 | |
| 974 | +防污男孩运动裤 | |
| 975 | +防污儿童游戏服 | |
| 976 | +防污儿童校服 | |
| 977 | +防污餐垫 | |
| 978 | +防污白色t恤 | |
| 979 | +细高跟靴 | |
| 980 | +细高跟鞋 | |
| 981 | +细高跟鞋护具 | |
| 982 | +直筒裤 | |
| 983 | +直筒及踝长裤办公服 | |
| 984 | +直筒及踝弹力裤 | |
| 985 | +直筒防擦伤裤 | |
| 986 | +直筒牛仔裤 | |
| 987 | +直筒裤 | |
| 988 | +直筒裤工作 | |
| 989 | +直筒裤工作服 | |
| 990 | +无肩带连衣裙 | |
| 991 | +草帽 | |
| 992 | +街头服饰图案连帽衫 | |
| 993 | +街头风衣男子连帽衫 | |
| 994 | +街头装连帽衫男式 | |
| 995 | +街头装超大连帽衫 | |
| 996 | +街头装拉链连帽衫 | |
| 997 | +弹力牛仔裤 | |
| 998 | +直筒弹力牛仔裤 | |
| 999 | +弹力牛仔裤直筒 | |
| 1000 | +条纹衬衫 | |
| 1001 | +深蓝色条纹衬衫 | |
| 1002 | +条纹衬衫海军蓝 | |
| 1003 | +条纹毛衣 | |
| 1004 | +镶钉皮带 | |
| 1005 | +绒面短靴 | |
| 1006 | +绒面脚踝靴 | |
| 1007 | +绒面靴 | |
| 1008 | +绒面乐福鞋 | |
| 1009 | +绒面休闲乐福鞋 | |
| 1010 | +绒面乐福鞋休闲 | |
| 1011 | +套装 | |
| 1012 | +西装外套 | |
| 1013 | +西装男 | |
| 1014 | +男士西装 | |
| 1015 | +夏季快干UPF 50衬衫 | |
| 1016 | +夏季无肩带连衣裙 | |
| 1017 | +夏季太阳花连衣裙 | |
| 1018 | +夏季连衣裙花朵 | |
| 1019 | +太阳裙 | |
| 1020 | +遮阳帽 | |
| 1021 | +宽边太阳帽 | |
| 1022 | +防晒服 | |
| 1023 | +支撑带 | |
| 1024 | +毛衣 | |
| 1025 | +毛衣连衣裙 | |
| 1026 | +毛衣背心 | |
| 1027 | +运动裤 | |
| 1028 | +运动衫 | |
| 1029 | +运动服 | |
| 1030 | +甜心领口连衣裙 | |
| 1031 | +泳装 | |
| 1032 | +游泳短裤 | |
| 1033 | +泳衣 | |
| 1034 | +泳裤 | |
| 1035 | +定制衬衫 | |
| 1036 | +女式西服 | |
| 1037 | +定制西装夹克 | |
| 1038 | +定制西装修身 | |
| 1039 | +定制修身西装 | |
| 1040 | +高挑修身商务休闲夹克 | |
| 1041 | +高挑修身男式衬衫 | |
| 1042 | +高挑修身男式亚麻衬衫 | |
| 1043 | +高挑修身男式西装背心 | |
| 1044 | +高挑修身西装上衣 | |
| 1045 | +高挑修身领带 | |
| 1046 | +高挑修身长裤 | |
| 1047 | +高腰修身长裤 | |
| 1048 | +高直筒裤 | |
| 1049 | +背心 | |
| 1050 | +流苏长裙 | |
| 1051 | +教师服装 | |
| 1052 | +青色衬衫 | |
| 1053 | +青色缎面衬衫 | |
| 1054 | +温度调节床垫 | |
| 1055 | +调温睡衣 | |
| 1056 | +调温枕 | |
| 1057 | +调温丝绸睡衣 | |
| 1058 | +调温睡衣 | |
| 1059 | +调温冬袜 | |
| 1060 | +网球裙 | |
| 1061 | +赤土亚麻长裤 | |
| 1062 | +赤陶亚麻长裤 | |
| 1063 | +赤土裤 | |
| 1064 | +毛圈布浴袍 | |
| 1065 | +毛巾布浴袍 | |
| 1066 | +毛圈布短裤 | |
| 1067 | +毛圈布运动衫 | |
| 1068 | +Terry布运动衫 | |
| 1069 | +热基层 | |
| 1070 | +热基层顶部 | |
| 1071 | +保暖长袖上衣 | |
| 1072 | +保暖滑雪夹克 | |
| 1073 | +保暖内衣 | |
| 1074 | +防擦伤大腿短裤 | |
| 1075 | +大腿短裤防擦伤 | |
| 1076 | +系 | |
| 1077 | +领带 | |
| 1078 | +扎染桶帽 | |
| 1079 | +扎染连衣裙 | |
| 1080 | +扎染超大针织毛衣 | |
| 1081 | +扎染大号运动衫 | |
| 1082 | +扎染超大运动衫 | |
| 1083 | +扎染袜子颜色柔和 | |
| 1084 | +扎染夏季长裙 | |
| 1085 | +扎染水瓶 | |
| 1086 | +扎腰裤 | |
| 1087 | +系带长裤 | |
| 1088 | +tiktok服装 | |
| 1089 | +幼儿床 | |
| 1090 | +托特包 | |
| 1091 | +运动服 | |
| 1092 | +运动服快干 | |
| 1093 | +过渡轻便夹克 | |
| 1094 | +过渡性毛衣背心 | |
| 1095 | +过渡款毛衣背心 | |
| 1096 | +旅行服装 | |
| 1097 | +旅行夹克 | |
| 1098 | +轻便旅行夹克 | |
| 1099 | +旅行服装 | |
| 1100 | +旅行轻便围巾 | |
| 1101 | +旅行裤 | |
| 1102 | +旅行套装 | |
| 1103 | +旅行背心 | |
| 1104 | +旅行防皱裤 | |
| 1105 | +旅行无皱纹长裤 | |
| 1106 | +风衣 | |
| 1107 | +热带气候透气短裤 | |
| 1108 | +热带气候凉鞋 | |
| 1109 | +热带气候凉鞋皮革 | |
| 1110 | +热带印花衬衫 | |
| 1111 | +裤子 | |
| 1112 | +喇叭袖 | |
| 1113 | +喇叭袖上衣 | |
| 1114 | +小号袖上衣 | |
| 1115 | +T恤 | |
| 1116 | +抹胸上衣 | |
| 1117 | +薄纱覆面裙 | |
| 1118 | +薄纱裙 | |
| 1119 | +薄纱裙派对 | |
| 1120 | +束腰外衣 | |
| 1121 | +束腰上衣 | |
| 1122 | +宽松上衣 | |
| 1123 | +束腰上衣宽松版型 | |
| 1124 | +高领毛衣 | |
| 1125 | +粗花呢西装外套 | |
| 1126 | +两件套 | |
| 1127 | +难看的圣诞毛衣 | |
| 1128 | +伞 | |
| 1129 | +内衣 | |
| 1130 | +多功能工装裤 | |
| 1131 | +通用工装裤 | |
| 1132 | +实用货物裙 | |
| 1133 | +多功能货物裙 | |
| 1134 | +紫外线防护口罩 | |
| 1135 | +防紫外线钓鱼衫 | |
| 1136 | +防紫外线园艺帽 | |
| 1137 | +防紫外线太阳帽 | |
| 1138 | +防紫外线女式防晒手套 | |
| 1139 | +度假服装 | |
| 1140 | +情人节服装 | |
| 1141 | +天鹅绒鸡尾酒会礼服 | |
| 1142 | +天鹅绒连衣裙 | |
| 1143 | +天鹅绒高跟鞋 | |
| 1144 | +天鹅绒高跟鞋正式 | |
| 1145 | +天鹅绒高跟正式 | |
| 1146 | +天鹅绒节日连衣裙 | |
| 1147 | +天鹅绒中长裙 | |
| 1148 | +丝绒迷笛裙 | |
| 1149 | +天鹅绒裙子 | |
| 1150 | +背心 | |
| 1151 | +复古牛仔夹克 | |
| 1152 | +复古旧牛仔夹克 | |
| 1153 | +复古皮裙 | |
| 1154 | +粘胶卡米上衣 | |
| 1155 | +粘胶纤维背心 | |
| 1156 | +粘胶围巾图案 | |
| 1157 | +V领T恤 | |
| 1158 | +华夫饼针织亨利 | |
| 1159 | +华夫格针织亨利 | |
| 1160 | +束腰 | |
| 1161 | +钱包 | |
| 1162 | +防水靴 | |
| 1163 | +防水连帽夹克 | |
| 1164 | +防水雨衣 | |
| 1165 | +防水外壳 | |
| 1166 | +防水外壳外套 | |
| 1167 | +防水喷雾 | |
| 1168 | +穿着小衣服 | |
| 1169 | +婚礼宾客鸡尾酒会礼服 | |
| 1170 | +婚礼宾客礼服 | |
| 1171 | +婚礼宾客正装 | |
| 1172 | +婚礼宾客中长裙 | |
| 1173 | +婚礼宾客迷笛裙 | |
| 1174 | +婚礼宾客吊带裙 | |
| 1175 | +西部牛仔靴 | |
| 1176 | +西式牛仔衬衫 | |
| 1177 | +白色棉质t恤 | |
| 1178 | +白色亚麻长裤 | |
| 1179 | +宽边帽 | |
| 1180 | +售宽裤筒牛仔裤 | |
| 1181 | +阔腿亚麻裤 | |
| 1182 | +宽腿亚麻长裤 | |
| 1183 | +阔腿宫殿长裤 | |
| 1184 | +阔腿Palazzo长裤 | |
| 1185 | +阔腿裤 | |
| 1186 | +窗膜 | |
| 1187 | +冬季羊毛衬里紧身裤 | |
| 1188 | +冬季手套 | |
| 1189 | +冬季服装 | |
| 1190 | +冬季派克大衣 | |
| 1191 | +冬季公园 | |
| 1192 | +冬季大衣羽绒服 | |
| 1193 | +冬季帕卡羽绒服 | |
| 1194 | +冬季羽绒服 | |
| 1195 | +女式夏季长裙 | |
| 1196 | +羊毛外套 | |
| 1197 | +羊毛混纺长外套 | |
| 1198 | +羊毛大衣 | |
| 1199 | +羊毛大衣高 | |
| 1200 | +羊毛豌豆大衣 | |
| 1201 | +羊毛袜 | |
| 1202 | +羊毛冬衣 | |
| 1203 | +羊毛冬季大衣 | |
| 1204 | +居家办公 | |
| 1205 | +运动服 | |
| 1206 | +工作裤 | |
| 1207 | +工作服长裤 | |
| 1208 | +裹身裙 | |
| 1209 | +抗皱商务衬衫 | |
| 1210 | +抗皱商务旅行套装 | |
| 1211 | +抗皱织物喷雾 | |
| 1212 | +抗皱长裤 | |
| 1213 | +抗皱旅行夹克 | |
| 1214 | +抗皱旅行裙 | |
| 1215 | +抗皱旅行裤 | |
| 1216 | +抗皱喷雾 | |
| 1217 | +y2k低腰宽松牛仔裤 | |
| 1218 | +y2k低腰工装裤 | |
| 1219 | +y2k低腰牛仔裤 | |
| 1220 | +Y2K低腰牛仔裤 | |
| 1221 | +y2k管顶 | |
| 1222 | +Y2K管顶 | |
| 1223 | +瑜伽紧身裤 | |
| 1224 | +瑜伽垫 | |
| 1225 | +瑜伽服 | |
| 1226 | +瑜伽裤 | |
| 1227 | +高腰瑜伽裤 | |
| 1228 | +瑜伽短裤 | |
| 1229 | +斑马印花长裤 | |
| 1230 | +拉链靴 | |
| 1231 | +拉链连帽衫 | |
| 1232 | +变焦衬衫 | ... | ... |
tests/queries.txt renamed to scripts/evaluation/queries/queries.txt
前端分面配置说明.md deleted
| ... | ... | @@ -1,176 +0,0 @@ |
| 1 | -# 前端分面配置说明 | |
| 2 | - | |
| 3 | -## 问题描述 | |
| 4 | - | |
| 5 | -tenant_id=170 的分面返回为空,原因是: | |
| 6 | -1. `category1_name` 字段在数据中为 None(这是数据问题) | |
| 7 | -2. `specifications.name` 字段在数据中使用首字母大写(如 "Color"、"Size"),而前端查询时使用小写("color"、"size"),导致 ES term 查询匹配失败 | |
| 8 | - | |
| 9 | -## 解决方案 | |
| 10 | - | |
| 11 | -采用前端配置方案,根据不同的 `tenant_id` 配置不同的分面字段。配置包括: | |
| 12 | -- **字段名**(field):ES 中的实际字段名,如 `specifications.Color` | |
| 13 | -- **显示标签**(label):前端显示的名称,如 "颜色"、"尺寸" | |
| 14 | -- **容器ID**(containerId):HTML 中用于显示分面的容器 ID,如 `colorTags` | |
| 15 | -- **查询参数**:size、type、disjunctive 等 | |
| 16 | - | |
| 17 | -## 配置文件 | |
| 18 | - | |
| 19 | -配置文件位置:`frontend/static/js/tenant_facets_config.js` | |
| 20 | - | |
| 21 | -### 配置结构 | |
| 22 | - | |
| 23 | -```javascript | |
| 24 | -const TENANT_FACETS_CONFIG = { | |
| 25 | - "租户ID": { | |
| 26 | - specificationFields: [ | |
| 27 | - { | |
| 28 | - field: "specifications.字段名", // ES字段名(必须与实际数据匹配,包括大小写) | |
| 29 | - label: "显示标签", // 前端显示名称 | |
| 30 | - containerId: "容器ID", // HTML容器ID | |
| 31 | - size: 20, // 返回的分面值数量 | |
| 32 | - type: "terms", // 分面类型 | |
| 33 | - disjunctive: true // 是否支持多选 | |
| 34 | - } | |
| 35 | - ] | |
| 36 | - } | |
| 37 | -}; | |
| 38 | -``` | |
| 39 | - | |
| 40 | -### 示例配置 | |
| 41 | - | |
| 42 | -#### tenant_id=162(使用小写) | |
| 43 | -```javascript | |
| 44 | -"162": { | |
| 45 | - specificationFields: [ | |
| 46 | - { | |
| 47 | - field: "specifications.color", | |
| 48 | - label: "Color", | |
| 49 | - containerId: "colorTags", | |
| 50 | - size: 20, | |
| 51 | - type: "terms", | |
| 52 | - disjunctive: true | |
| 53 | - }, | |
| 54 | - { | |
| 55 | - field: "specifications.size", | |
| 56 | - label: "Size", | |
| 57 | - containerId: "sizeTags", | |
| 58 | - size: 15, | |
| 59 | - type: "terms", | |
| 60 | - disjunctive: true | |
| 61 | - }, | |
| 62 | - { | |
| 63 | - field: "specifications.material", | |
| 64 | - label: "Material", | |
| 65 | - containerId: "materialTags", | |
| 66 | - size: 10, | |
| 67 | - type: "terms", | |
| 68 | - disjunctive: true | |
| 69 | - } | |
| 70 | - ] | |
| 71 | -} | |
| 72 | -``` | |
| 73 | - | |
| 74 | -#### tenant_id=170(使用首字母大写,没有material) | |
| 75 | -```javascript | |
| 76 | -"170": { | |
| 77 | - specificationFields: [ | |
| 78 | - { | |
| 79 | - field: "specifications.Color", // 注意:首字母大写 | |
| 80 | - label: "Color", | |
| 81 | - containerId: "colorTags", | |
| 82 | - size: 20, | |
| 83 | - type: "terms", | |
| 84 | - disjunctive: true | |
| 85 | - }, | |
| 86 | - { | |
| 87 | - field: "specifications.Size", // 注意:首字母大写 | |
| 88 | - label: "Size", | |
| 89 | - containerId: "sizeTags", | |
| 90 | - size: 15, | |
| 91 | - type: "terms", | |
| 92 | - disjunctive: true | |
| 93 | - } | |
| 94 | - // 注意:170 没有 material 分面 | |
| 95 | - ] | |
| 96 | -} | |
| 97 | -``` | |
| 98 | - | |
| 99 | -#### 示例:添加新租户(包含其他规格字段,如重量、包装方式) | |
| 100 | -```javascript | |
| 101 | -"新租户ID": { | |
| 102 | - specificationFields: [ | |
| 103 | - { | |
| 104 | - field: "specifications.Weight", // 重量 | |
| 105 | - label: "Weight", | |
| 106 | - containerId: "weightTags", // 需要在HTML中添加此容器 | |
| 107 | - size: 15, | |
| 108 | - type: "terms", | |
| 109 | - disjunctive: true | |
| 110 | - }, | |
| 111 | - { | |
| 112 | - field: "specifications.PackageType", // 包装方式 | |
| 113 | - label: "Package Type", | |
| 114 | - containerId: "packageTags", // 需要在HTML中添加此容器 | |
| 115 | - size: 10, | |
| 116 | - type: "terms", | |
| 117 | - disjunctive: true | |
| 118 | - } | |
| 119 | - ] | |
| 120 | -} | |
| 121 | -``` | |
| 122 | - | |
| 123 | -## 添加新租户配置步骤 | |
| 124 | - | |
| 125 | -1. **确定 ES 数据中的实际字段名** | |
| 126 | - - 检查 ES 中 `specifications.name` 的实际值(注意大小写) | |
| 127 | - - 例如:`"Color"` 或 `"color"` 是不同的字段 | |
| 128 | - | |
| 129 | -2. **在配置文件中添加配置** | |
| 130 | - ```javascript | |
| 131 | - "新租户ID": { | |
| 132 | - specificationFields: [ | |
| 133 | - { | |
| 134 | - field: "specifications.实际字段名", | |
| 135 | - label: "显示名称", | |
| 136 | - containerId: "容器ID", | |
| 137 | - size: 20, | |
| 138 | - type: "terms", | |
| 139 | - disjunctive: true | |
| 140 | - } | |
| 141 | - ] | |
| 142 | - } | |
| 143 | - ``` | |
| 144 | - | |
| 145 | -3. **在 HTML 中添加容器**(如果需要新的容器) | |
| 146 | - 在 `frontend/index.html` 的 Filter Section 中添加: | |
| 147 | - ```html | |
| 148 | - <div class="filter-row"> | |
| 149 | - <div class="filter-label">显示名称:</div> | |
| 150 | - <div class="filter-tags" id="容器ID"></div> | |
| 151 | - </div> | |
| 152 | - ``` | |
| 153 | - | |
| 154 | -## 代码修改说明 | |
| 155 | - | |
| 156 | -### 1. 配置文件 (`tenant_facets_config.js`) | |
| 157 | -- 增加了 `label` 和 `containerId` 字段 | |
| 158 | -- 新增 `getFacetDisplayConfig()` 函数,根据字段名获取显示配置 | |
| 159 | - | |
| 160 | -### 2. 前端代码 (`app.js`) | |
| 161 | -- `performSearch()`: 使用配置文件获取分面查询参数 | |
| 162 | -- `displayFacets()`: 使用配置来匹配分面字段并找到对应的容器 | |
| 163 | - | |
| 164 | -### 3. HTML (`index.html`) | |
| 165 | -- 引入了配置文件 `tenant_facets_config.js` | |
| 166 | - | |
| 167 | -## 注意事项 | |
| 168 | - | |
| 169 | -1. **字段名必须完全匹配**:`field` 必须与 ES 中实际存储的 `specifications.name` 值完全匹配(包括大小写) | |
| 170 | -2. **容器ID必须存在**:`containerId` 必须在 HTML 中存在,否则分面无法显示 | |
| 171 | -3. **后端代码无需修改**:后端直接使用前端传入的字段名进行查询 | |
| 172 | -4. **分面信息是动态加载的**:只有在搜索返回后才显示,符合需求 | |
| 173 | - | |
| 174 | -## 数据问题说明 | |
| 175 | - | |
| 176 | -对于 tenant_id=170,`category1_name` 字段在 ES 数据中全部为 None,因此该类目分面会返回空。这需要在数据索引时修复,确保正确解析和填充 `category1_name` 字段。 |