07 Mar, 2026

1 commit


11 Feb, 2026

1 commit


06 Feb, 2026

1 commit

  • ---
    
     1. `search/es_query_builder.py`:`_all` 分支
    
    - **普通字段**(如 `tags_all`, `category1_name_all`):
      - 键以 `_all` 结尾时,先去掉后缀得到 ES 字段名。
      - 若值为**数组**:生成 `bool.must`,内含多个 `term`,即多值 **AND**。
      - 若值为**单值**:生成一个 `term`。
    - **specifications_all**:
      - 值为 `[{name, value}, ...]` 时,为每一项生成一个 nested 查询,全部放入同一个 `bool.must`,即列表内所有规格条件都要满足(AND)。
    
    原有逻辑不变:不带 `_all` 的字段,数组仍为 OR(`terms`),单值仍为 `term`。
    
     2. `api/models.py`:filters 说明
    
    - 在 `filters` 的 `description` 中补充:
      - 字段名加 `_all` 表示 AND(如 `tags_all: ['A','B']` 表示同时包含 A 和 B)。
      - `specifications_all` 表示列表内所有规格条件都要满足。
    
     3. `docs/搜索API对接指南.md`:文档
    
    - 在 3.3.1 开头说明:任意字段名可加 `_all` 后缀表示多值 AND。
    - 在格式示例中增加 `tags_all`、`category1_name_all` 示例。
    - 在「支持的值类型」中说明:数组在带 `_all` 时为 AND。
    - 新增小节「`*_all` 语义(多值 AND)」:说明用法及 `specifications_all` 行为。
    - 在「常用过滤字段」中补充:以上字段均可加 `_all` 后缀。
    
    ---
    
    **使用示例**
    
    ```json
    {
      "filters": {
        "tags": ["手机", "促销"],
        "tags_all": ["手机", "促销", "新品"]
      }
    }
    ```
    
    - `tags`:命中「手机」或「促销」或两者都有(OR)。
    - `tags_all`:必须同时包含「手机」「促销」「新品」三个标签(AND)。
    tangwang
     

05 Feb, 2026

2 commits

  • tangwang
     
  • - API:新增请求参数 ai_search,开启时在窗口内走重排流程
    - 配置:RerankConfig 移除 enabled/expression/description,仅保留 rerank_window 及
      service_url/timeout_sec/weight_es/weight_ai;默认超时 15s
    - 重排流程:ai_search 且 from+size<=rerank_window 时,ES 取前 rerank_window 条,
      调用外部 /rerank 服务,融合 ES 与重排分数后按 from/size 分页;否则不重排
    - search/rerank_client:新增模块,封装 build_docs、call_rerank_service、
      fuse_scores_and_resort、run_rerank;超时单独捕获并简短日志
    - search/searcher:移除 RerankEngine,enable_rerank=ai_search,使用 config.rerank 参数
    - 删除 search/rerank_engine.py(本地表达式重排),统一为外部服务一种实现
    - 文档:搜索 API 对接指南补充 ai_search 与 relevance_score 说明
    - 测试:conftest 中 rerank 配置改为新结构
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    tangwang
     

27 Jan, 2026

2 commits


06 Jan, 2026

2 commits

  • tangwang
     
  • mappings/search_products.json:把原来的 title_zh/title_en/brief_zh/... 改成 按语言 key 的对象结构( /products/_doc/1 { "title": {"en":...} } )
    同时在这些字段下 预置了全部 analyzer 语言:
    arabic, armenian, basque, brazilian, bulgarian, catalan, chinese, cjk, czech, danish, dutch, english, finnish, french, galician, german, greek, hindi, hungarian, indonesian, italian, norwegian, persian, portuguese, romanian, russian, spanish, swedish, turkish, thai
    
    实现为 type: object + properties,同时满足“按语言灌入”和“按语言 analyzer”。
    索引灌入(全量/增量/transformer)已同步改完
    indexer/document_transformer.py:输出从 title_zh/title_en/... 改为:
    title: {<primary_lang>: 原文, en?: 翻译, zh?: 翻译}
    brief/description/vendor 同理
    category_path/category_name_text 也改为语言对象(避免查询侧继续依赖旧字段)
    indexer/incremental_service.py:embedding 取值从 title_en/title_zh 改为从 title 对象里优先取 en,否则取 zh,否则取任一可用语言。
    查询侧与配置、API/文档已同步
    search/es_query_builder.py:查询字段统一改成点路径:title.zh / title.en / vendor.zh / vendor.zh.keyword / category_name_text.zh 等。
    config/config.yaml:field boosts / indexes 里的字段名同步为新点路径。
    API & formatter:
    api/result_formatter.py 已支持新结构(并保留对旧 *_zh/_en 的兼容兜底)。
    api/models.py、相关 docs/examples 里的 vendor_zh.keyword 等已更新为 vendor.zh.keyword。
    文档/脚本:docs/、README.md、scripts/ 里所有旧字段名引用已批量替换为新结构。
    tangwang
     

31 Dec, 2025

1 commit


27 Dec, 2025

1 commit


25 Dec, 2025

1 commit

  • 问题:nested 聚合统计的是嵌套文档(specifications 条目)数量,而不是产品(父文档)数量。
    修复内容:
    在 es_query_builder.py 中:为 specifications 分面的聚合添加了 reverse_nested 子聚合,用于统计产品数量:
       "aggs": {       "product_count": {           "reverse_nested": {}       }   }
    在 result_formatter.py 中:更新读取逻辑,从 product_count.doc_count 读取产品数量,而不是直接使用 doc_count。
    修复效果:
    之前:分面显示 62(统计了 62 个嵌套文档/规格条目)
    现在:分面显示实际的产品数量(例如 2),与搜索结果数量一致
    tangwang
     

20 Dec, 2025

1 commit


18 Dec, 2025

1 commit

  • config/config_loader.py: 从 QueryConfig 类中删除 enable_translation 字段
    config/config.yaml: 删除 enable_translation: true 配置项
    config/config_loader.py: 从 to_dict() 方法中删除相关输出
    2. 索引阶段(离线)- 使用租户配置
    indexer/indexing_utils.py:
    根据 tenant_config.translate_to_en 和 translate_to_zh 决定是否初始化 translator
    只有任一方向开启时才创建 translator
    indexer/document_transformer.py:
    _fill_text_fields 从 tenant_config 读取 translate_to_en 和 translate_to_zh
    调用 translate_for_indexing 时传递这两个参数
    更新了文档注释
    3. 查询阶段(在线)- 使用租户配置
    query/query_parser.py:
    parse() 方法新增 tenant_id 参数
    根据租户配置决定翻译目标语言(translate_to_zh / translate_to_en)
    如果两个都是 false,跳过翻译阶段
    translator 属性不再依赖 enable_translation,总是可以初始化
    search/searcher.py:
    search() 方法中根据租户配置计算 enable_translation(用于日志和 metadata)
    调用 query_parser.parse() 时传递 tenant_id
    4. 翻译器方法更新
    query/translator.py:
    translate_for_indexing() 新增 translate_to_en 和 translate_to_zh 参数(默认 True 保持向后兼容)
    根据这两个参数决定翻译目标
    更新了文档注释
    tangwang
     

08 Dec, 2025

1 commit


05 Dec, 2025

2 commits

  • 将 must 子句改为 should 子句的多查询策略
    实现以下查询类型:
    base_query:主查询,使用 AND 操作符和 75% minimum_should_match
    翻译查询:跨语言查询,boost=0.4
    短语查询:短查询的精确短语匹配
    关键词查询:基于提取名词的查询,boost=0.1
    添加 _get_match_fields() 方法,支持中英文字段动态映射
    4. 关键改进点
    minimum_should_match 从 67% 提升到 75%
    添加 operator: "AND" 确保所有词都匹配
    使用 should 子句实现多策略融合
    支持短语查询和关键词查询的智能触发
    tangwang
     
  • 2. queries
    tangwang
     

04 Dec, 2025

1 commit

  • 核心功能:
    - 添加 multi_select 字段到 FacetConfig(默认为 true)
    - 实现 post_filter 支持 disjunctive faceting
    - 后端自动标记 facet 值的 selected 状态
    - 支持 specifications 和普通字段的 multi-select
    
    技术改进:
    - ESQueryBuilder: 分离 conjunctive/disjunctive filters
    - ResultFormatter: 根据 current_filters 标记 selected
    - Searcher: 传递 facet_configs 给 query builder
    
    文档更新:
    - 添加 multi_select_faceting.md 详细文档
    - 更新 API 对接指南,说明新功能
    - 添加测试脚本 test_multi_select_facet.py
    
    业界标准:
    - 遵循 Elasticsearch/Algolia/Amazon 的最佳实践
    - 提供探索式搜索体验
    - 前后端职责清晰分离
    tangwang
     

03 Dec, 2025

2 commits

  • {
      "facets": [
        {
          "field": "category1_name",
          "size": 15,
          "type": "terms"
        },
        "specifications.color",
        "specifications.size"
      ]
    }
    
    {
      "facets": [
        {"field": "category1_name", "size": 15, "type": "terms"},
        {"field": "specifications.color", "size": 10, "type": "terms"},
        {"field": "specifications.size", "size": 10, "type": "terms"}
      ]
    }
    
    之前是上面的接口形式,主要是考虑 属性的分面, 因为 款式都是有限的 不需要设定 "size": 10, "type": "terms" 这些参数。
    
    但是从接口设计层面,最好按下面这样,这样的话 specifications.color 和 category1_name 的组装格式 完全一样。前端不需要感知 属性分面 和 类别等其他字段分面的差异。
    tangwang
     
  • tangwang
     

02 Dec, 2025

2 commits

  • 后端请求模型变更(api/models.py)
    SearchRequest.sku_filter_dimension 从 Optional[str] 改为 Optional[List[str]]。
    语义:列表表示一个或多个“维度标签”,例如:
    单维度:["color"]、["option1"]
    多维度:["color", "size"]、["option1", "option2"]
    描述更新为:对 维度组合进行分组,每个组合只保留一个 SKU。
    结果格式化与去重逻辑(api/result_formatter.py)
    ResultFormatter.format_search_results(..., sku_filter_dimension: Optional[List[str]] = None),调用处已同步更新。
    单维度旧逻辑升级为多维度逻辑:
    新方法:_filter_skus_by_dimensions(skus, dimensions, option1_name, option2_name, option3_name, specifications)。
    维度解析规则(按顺序处理,并去重):
    若维度是 option1 / option2 / option3 → 对应 option1_value / option2_value / option3_value。
    否则,将维度字符串转小写后,分别与 option1_name / option2_name / option3_name 对比,相等则映射到对应的 option*_value。
    未能映射到任何字段的维度会被忽略。
    对每个 SKU:
    按解析出的字段列表(例如 ["option1_value", "option2_value"])取值,组成 key,如 ("red", "L");None 用空串 ""。
    按 key 分组,每个 key 只保留遇到的第一个 SKU。
    若列表为空或所有维度都无法解析,则 不做过滤,返回原始 skus。
    Searcher 参数类型同步(search/searcher.py)
    Searcher.search(...) 中 sku_filter_dimension 参数类型从 Optional[str] 改为 Optional[List[str]]。
    传给 ResultFormatter.format_search_results 时,直接传该列表。
    前端参数格式调整(frontend/static/js/app.js)
    输入框 #skuFilterDimension 依旧是一个文本框,但解析方式改为:
    函数 getSkuFilterDimension():
    读取文本,如:"color" 或 "color,size" 或 "option1, color"。
    用逗号 , 拆分,trim() 后过滤空串,返回 字符串数组,例如:
    "color" → ["color"]
    "color,size" → ["color", "size"]
    若最终数组为空,则返回 null。
    搜索请求体中仍使用字段名 sku_filter_dimension,但现在值是 string[] 或 null:
        body: JSON.stringify({      // ...      sku_filter_dimension: skuFilterDimension,  // 例如 ["color", "size"]      debug: state.debug    })
    文档更新(docs/搜索API对接指南.md)
    请求体示例中的类型由:
    "sku_filter_dimension": "string"
    改为:
    "sku_filter_dimension": ["string"]
    参数表中:
    从 string 改为 array[string],说明为“维度列表,按组合分组,每个组合保留一个 SKU”。
    功能说明章节“SKU筛选维度 (sku_filter_dimension)”已调整为 列表语义 + 组合去重,并补充了示例:
    单维度:
          {        "query": "芭比娃娃",        "sku_filter_dimension": ["color"]      }
    多维度组合:
          {        "query": "芭比娃娃",        "sku_filter_dimension": ["color", "size"]      }
    使用方式总结
    单维度去重(保持旧行为的等价写法)
    旧:"sku_filter_dimension": "color"
    新:"sku_filter_dimension": ["color"]
    多维度组合去重(你新提的需求)
    例如希望“每个 SPU 下,同一颜色+尺码组合只保留一个 SKU”:
        {      "query": "芭比娃娃",      "sku_filter_dimension": ["color", "size"]    }
    tangwang
     
  • query config/ranking config优化
    tangwang
     

01 Dec, 2025

2 commits


27 Nov, 2025

3 commits

  • 1. 搜索API对接指南.md
    在“精确匹配过滤器”部分添加了 specifications 嵌套过滤说明
    支持单个规格过滤和多个规格过滤(OR 逻辑)
    在“分面配置”部分完善了 specifications 分面说明
    添加了两种分面模式:所有规格名称和指定规格名称
    在“常见场景示例”部分添加了场景5-8,包含规格过滤和分面的完整示例
    2. 搜索API速查表.md
    在“精确匹配过滤”部分添加了 specifications 过滤的快速参考
    在“分面搜索”部分添加了 specifications 分面的快速参考
    更新了完整示例,包含 specifications 的使用
    3. Search-API-Examples.md
    在“过滤器使用”部分添加了示例4-6,展示 specifications 过滤
    在“分面搜索”部分添加了示例2-3,展示 specifications 分面
    更新了 Python 和 JavaScript 完整示例,包含 specifications 的使用
    在“常见使用场景”部分添加了场景2.1,展示带规格过滤的搜索结果页
    4. 索引字段说明v2.md
    更新了 specifications 字段的查询示例,包含 API 格式和 ES 查询结构
    添加了两种分面模式的说明和示例
    更新了“分面字段”说明,明确支持指定规格名称的分面
    
    5. 补充参数
    参数说明:sku_filter_dimension 是可选参数,用于按指定维度过滤每个SPU下的SKU
    支持的维度:
    直接选项字段:option1、option2、option3
    规格名称:通过 option1_name、option2_name、option3_name 匹配(如 color、size)
    tangwang
     
  • 1. 搜索API对接指南.md
    在“精确匹配过滤器”部分添加了 specifications 嵌套过滤说明
    支持单个规格过滤和多个规格过滤(OR 逻辑)
    在“分面配置”部分完善了 specifications 分面说明
    添加了两种分面模式:所有规格名称和指定规格名称
    在“常见场景示例”部分添加了场景5-8,包含规格过滤和分面的完整示例
    2. 搜索API速查表.md
    在“精确匹配过滤”部分添加了 specifications 过滤的快速参考
    在“分面搜索”部分添加了 specifications 分面的快速参考
    更新了完整示例,包含 specifications 的使用
    3. Search-API-Examples.md
    在“过滤器使用”部分添加了示例4-6,展示 specifications 过滤
    在“分面搜索”部分添加了示例2-3,展示 specifications 分面
    更新了 Python 和 JavaScript 完整示例,包含 specifications 的使用
    在“常见使用场景”部分添加了场景2.1,展示带规格过滤的搜索结果页
    4. 索引字段说明v2.md
    更新了 specifications 字段的查询示例,包含 API 格式和 ES 查询结构
    添加了两种分面模式的说明和示例
    更新了“分面字段”说明,明确支持指定规格名称的分面
    tangwang
     
  • 1. 前端传递的过滤条件永远是要起作用的
    2. 然后召回模块包括文本相关性召回(中英文都是用)和向量召回,两者相互补充。
    3. 套用function_score以支持两种打分融合和各种提权字段
    4. 只需要build_query 这一层。
    
    实际操作:
    1. 架构简化
    移除了 MultiLanguageQueryBuilder 层级
    只保留单层的 ESQueryBuilder.build_query 方法
    2. 查询结构重构
    实现了 filters and (text_recall or embedding_recall) 结构:
    前端过滤条件:永远起作用(放在 filter 中)
    文本召回:同时搜索中英文字段(multi_match 覆盖 title_zh/en, brief_zh/en 等)
    向量召回:KNN 查询(独立参数,ES 会自动合并)
    Function_score:包装召回部分,支持提权字段配置
    3. 文本匹配字段更新
    在 DEFAULT_MATCH_FIELDS 中添加了中英文字段:
    中文:title_zh, brief_zh, description_zh, vendor_zh, category_path_zh, category_name_zh
    英文:title_en, brief_en, description_en, vendor_en, category_path_en, category_name_en
    语言无关:tags
    4. Function_score 框架保留
    保留了 function_score 配置框架(FUNCTION_SCORE_CONFIG)
    支持 filter_weight、field_value_factor、decay 等提权函数
    可以从配置中扩展提权字段
    5. 测试验证
    所有功能测试通过:
    基本文本搜索
    带过滤条件的搜索
    范围过滤
    分面搜索
    英文查询
    tangwang
     


25 Nov, 2025

1 commit

  • mappings/search_products.json - 完整的ES索引配置(settings + mappings)
    基于 docs/索引字段说明v2-mapping结构.md
    简化 mapping_generator.py
    移除所有config依赖
    直接使用 load_mapping() 从JSON文件加载
    保留工具函数:create_index_if_not_exists, delete_index_if_exists, update_mapping
    更新数据导入脚本
    scripts/ingest_shoplazza.py - 移除ConfigLoader依赖
    直接使用 load_mapping() 和 DEFAULT_INDEX_NAME
    更新indexer模块
    indexer/__init__.py - 更新导出
    indexer/bulk_indexer.py - 简化IndexingPipeline,移除config依赖
    创建查询配置常量
    search/query_config.py - 硬编码字段列表和配置项
    
    使用方式
    创建索引:
    from indexer.mapping_generator import load_mapping, create_index_if_not_existsfrom utils.es_client import ESClientes_client = ESClient(hosts=["http://localhost:9200"])mapping = load_mapping()create_index_if_not_exists(es_client, "search_products", mapping)
    数据导入:
    python scripts/ingest_shoplazza.py \    --db-host localhost \    --db-database saas \    --db-username root \    --db-password password \    --tenant-id "1" \    --es-host http://localhost:9200 \    --recreate
    
    注意事项
    修改mapping:直接编辑 mappings/search_products.json
    字段映射:spu_transformer.py 中硬编码,与mapping保持一致
    config目录:保留但不再使用,可后续清理
    search模块:仍依赖config
    tangwang
     

14 Nov, 2025

3 commits


13 Nov, 2025

3 commits

  • tangwang
     
  • tangwang
     
  • 主要变更:
    1. 去掉数据源应用结构配置化,我们只针对店匠的spu sku表设计索引,数据灌入流程是写死的(只是满足测试需求,后面外层应用负责数据全量+增量灌入)。搜索系统主要关注如何适配外部搜索需求
    目前有两个数据灌入脚本,一种是之前的,一种是现在的从两个店匠的表sku表+spu表读取并且以spu为单位组织doc。
       - 配置只关注ES搜索相关配置,提高可维护性
       - 创建base配置(店匠通用配置)
    
    2. 索引结构重构(SPU维度)
       - 所有客户共享search_products索引,通过tenant_id隔离
       - 支持嵌套variants字段(SKU变体数组)
       - 创建SPUTransformer用于SPU数据转换
    
    3. API响应格式优化
       - 约定一套搜索结果的格式,而不是直接暴露ES doc的结构(_id _score _source内的字段)
       - 添加ProductResult和VariantResult模型
       - 添加suggestions和related_searches字段 (预留接口,逻辑暂未实现)
    
    4. 数据导入流程
       - 创建店匠数据导入脚本(ingest_shoplazza.py)
       - Pipeline层决定数据源,配置不包含数据源信息
       - 创建测试数据生成和导入脚本
    
    5. 文档更新
       - 更新设计文档,反映新架构
       - 创建BASE_CONFIG_GUIDE.md使用指南
    tangwang
     

12 Nov, 2025

4 commits

  • tangwang
     
  • 核心改动:
    1. 配置化打分规则
       - 新增FunctionScoreConfig和RerankConfig配置类
       - 支持filter_weight、field_value_factor、decay三种ES原生function
       - 从代码中移除硬编码的打分逻辑
    
    2. 配置模型定义
       - FunctionScoreConfig: score_mode, boost_mode, functions
       - RerankConfig: enabled, expression(当前禁用)
       - 添加到CustomerConfig中
    
    3. 查询构建器改造
       - MultiLanguageQueryBuilder.init添加function_score_config引用
       - _build_score_functions从配置动态构建ES functions
       - 支持配置的score_mode和boost_mode
    
    4. 配置文件示例
       - 添加完整的function_score配置示例
       - 包含3种function类型的详细注释
       - 提供常见场景的配置模板
    
    5. ES原生能力支持
       - Filter+Weight: 条件匹配提权
       - Field Value Factor: 字段值映射打分
         * modifier支持: none, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt, reciprocal
       - Decay Functions: 衰减函数
         * 支持: gauss, exp, linear
    
    配置示例:
    - 7天新品提权(weight: 1.3)
    - 30天新品提权(weight: 1.15)
    - 有视频提权(weight: 1.05)
    - 销量因子(field_value_factor + log1p)
    - 时间衰减(gauss decay)
    
    优势:
    ✓ 配置化 - 客户自己调整,无需改代码
    ✓ 基于ES原生 - 性能最优,功能完整
    ✓ 灵活易用 - YAML格式,有示例和注释
    ✓ 统一约定 - function_score必需,简化设计
    
    参考:https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-function-score-query
    tangwang
     
  • 核心改动:
    1. 修复facets类型问题
       - 统一使用Pydantic模型(FacetResult, FacetValue)
       - SearchResult.facets改为List[FacetResult]
       - _standardize_facets直接构建Pydantic对象
    
    2. 修复RangeFilter支持日期时间
       - RangeFilter字段改为Union[float, str]
       - 支持数值范围和ISO日期时间字符串
       - 修复前端listing time筛选422错误
    
    3. 重构ES查询结构(核心)
       - 使用function_score包裹整个查询
       - 文本和KNN放入内层bool.should(minimum_should_match=1)
       - Filter在外层bool,同时作用于文本和KNN查询
       - 添加时效性加权函数(days_since_last_update<=30 weight:1.1)
    
    4. RankingEngine重构
       - 重命名为RerankEngine(语义更准确)
       - 默认禁用(enabled=False)
       - 优先使用ES的function_score打分
    
    5. 统一约定原则
       - 移除所有字典兼容代码
       - 全系统统一使用Pydantic模型
       - build_facets只接受str或FacetConfig
       - _build_filters直接接受RangeFilter模型
    
    修改文件:
    - search/multilang_query_builder.py: 重构查询构建逻辑
    - search/es_query_builder.py: 统一Pydantic模型支持
    - search/searcher.py: 使用RerankEngine,更新导入
    - search/rerank_engine.py: 新建(从ranking_engine.py重命名)
    - search/ranking_engine.py: 删除
    - search/__init__.py: 更新导出
    - api/models.py: RangeFilter支持Union[float, str]
    
    测试验证:
    ✓ Facets正常返回
    ✓ Filter同时作用于文本和KNN
    ✓ 日期时间范围过滤正常
    ✓ Function score时效性加权正常
    ✓ 所有测试通过
    
    架构原则:统一约定,不做兼容,保持简单
    tangwang
     
  • tangwang