设计文档.md 16.6 KB

搜索引擎通用化开发进度

项目概述

对后端搜索技术 做通用化。 通用化的本质 是 对于各种业务数据、各种检索需求,都可以 用少量定制+配置化 来实现效果。

通用化的本质:对于各种业务数据、各种检索需求,都可以用少量定制+配置化来实现效果。


1. 原始数据层的约定

1.1 店匠主表

所有租户共用以下主表:

  • shoplazza_product_sku - SKU级别商品数据
  • shoplazza_product_spu - SPU级别商品数据

1.2 索引结构(SPU维度)

统一索引架构

  • 所有客户共享同一个Elasticsearch索引:search_products
  • 索引粒度:SPU级别(每个文档代表一个SPU)
  • 数据隔离:通过tenant_id字段实现租户隔离
  • 嵌套结构:每个SPU文档包含嵌套的variants数组(SKU变体)

索引文档结构

{
  "tenant_id": "1",
  "product_id": "123",
  "title": "蓝牙耳机",
  "variants": [
    {
      "variant_id": "456",
      "title": "黑色",
      "price": 199.99,
      "sku": "SKU-123-1",
      "stock": 50
    }
  ],
  "min_price": 199.99,
  "max_price": 299.99
}

1.3 配置化方案

配置分离原则

  • 搜索配置:只包含ES字段定义、查询域、排序规则等搜索相关配置
  • 数据源配置:不在搜索配置中,由Pipeline层(脚本)决定
  • 数据导入流程:写死的脚本,不依赖配置

统一通过配置文件定义:

  1. ES 字段定义(字段类型、分析器、boost等)
  2. ES mapping 结构生成
  3. 查询域配置(indexes)
  4. 排序和打分配置(function_score)

注意:配置中不包含以下内容:

  • mysql_config - MySQL数据库配置
  • main_table / extension_table - 数据表配置
  • source_table / source_column - 字段数据源映射

2. 配置系统实现

2.1 应用结构配置(字段定义)

配置文件位置config/schema/{tenant_id}_config.yaml

配置内容:定义了 ES 的输入数据有哪些字段、关联 MySQL 的哪些字段。

实现情况

字段类型支持

  • TEXT:文本字段,支持多语言分析器
  • KEYWORD:关键词字段,用于精确匹配和聚合
  • TEXT_EMBEDDING:文本向量字段(1024维,dot_product相似度)
  • IMAGE_EMBEDDING:图片向量字段(1024维,dot_product相似度)
  • INT/LONG:整数类型
  • FLOAT/DOUBLE:浮点数类型
  • DATE:日期类型
  • BOOLEAN:布尔类型

分析器支持

  • chinese_ecommerce:中文电商分词器(index_ansj/query_ansj)
  • english:英文分析器
  • russian:俄文分析器
  • arabic:阿拉伯文分析器
  • spanish:西班牙文分析器
  • japanese:日文分析器
  • standard:标准分析器
  • keyword:关键词分析器

字段配置示例(Base配置)

fields:
  # 租户隔离字段(必需)
  - name: "tenant_id"
    type: "KEYWORD"
    required: true
    index: true
    store: true

  # 商品标识字段
  - name: "product_id"
    type: "KEYWORD"
    required: true
    index: true
    store: true

  # 文本搜索字段
  - name: "title"
    type: "TEXT"
    analyzer: "chinese_ecommerce"
    boost: 3.0
    index: true
    store: true

  - name: "seo_keywords"
    type: "TEXT"
    analyzer: "chinese_ecommerce"
    boost: 2.0
    index: true
    store: true

  # 嵌套variants字段
  - name: "variants"
    type: "JSON"
    nested: true
    nested_properties:
      variant_id:
        type: "keyword"
      price:
        type: "float"
      sku:
        type: "keyword"

注意:配置中不包含source_tablesource_column,数据源映射由Pipeline层决定。

实现模块

  • config/config_loader.py - 配置加载器
  • config/field_types.py - 字段类型定义
  • indexer/mapping_generator.py - ES mapping 生成器
  • indexer/data_transformer.py - 数据转换器

2.2 索引结构配置(查询域配置)

配置内容:定义了 ES 的字段索引 mapping 配置,支持各个域的查询,包括默认域的查询。

实现情况

域(Domain)配置

每个域定义了:

  • 域名称(如 default, title, category, brand
  • 域标签(中文描述)
  • 搜索字段列表
  • 默认分析器
  • 权重(boost)
  • 多语言字段映射language_field_mapping

多语言字段映射

支持将不同语言的查询路由到对应的字段:

indexes:
  - name: "default"
    label: "默认索引"
    fields:
      - "name"
      - "enSpuName"
      - "ruSkuName"
      - "categoryName"
      - "brandName"
    analyzer: "chinese_ecommerce"
    boost: 1.0
    language_field_mapping:
      zh:
        - "name"
        - "categoryName"
        - "brandName"
      en:
        - "enSpuName"
      ru:
        - "ruSkuName"

  - name: "title"
    label: "标题索引"
    fields:
      - "name"
      - "enSpuName"
      - "ruSkuName"
    analyzer: "chinese_ecommerce"
    boost: 2.0
    language_field_mapping:
      zh:
        - "name"
      en:
        - "enSpuName"
      ru:
        - "ruSkuName"

工作原理

  1. 检测查询语言(中文、英文、俄文等)
  2. 如果查询语言在 language_field_mapping 中,使用原始查询搜索对应语言的字段
  3. 将查询翻译到其他支持的语言,分别搜索对应语言的字段
  4. 组合多个语言查询的结果,提高召回率

实现模块

  • search/multilang_query_builder.py - 多语言查询构建器
  • query/query_parser.py - 查询解析器(支持语言检测和翻译)

3. 数据导入流程

3.1 数据源

店匠标准表(Base配置使用):

  • shoplazza_product_spu - SPU级别商品数据
  • shoplazza_product_sku - SKU级别商品数据

其他客户表(tenant1等):

  • 使用各自的数据源表和扩展表

3.2 数据导入方式

Pipeline层决定数据源

  • 数据导入流程是写死的脚本,不依赖配置
  • 配置只关注ES搜索相关的内容
  • 数据源映射逻辑写死在转换器代码中

Base配置数据导入(店匠通用)

脚本scripts/ingest_shoplazza.py

数据流程

  1. 数据加载:从MySQL读取shoplazza_product_spushoplazza_product_sku
  2. 数据转换indexer/spu_transformer.py):
    • spu_idtenant_id关联SPU和SKU数据
    • 将SKU数据聚合为嵌套的variants数组
    • 计算扁平化价格字段(min_price, max_price, compare_at_price
    • 字段映射(写死在代码中,不依赖配置)
    • 注入tenant_id字段
  3. 索引创建
    • 根据配置生成ES mapping
    • 创建或更新search_products索引
  4. 批量入库
    • 批量写入ES(默认每批500条)
    • 错误处理和重试机制

命令行工具

python scripts/ingest_shoplazza.py \
    --db-host localhost \
    --db-port 3306 \
    --db-database saas \
    --db-username root \
    --db-password password \
    --tenant-id "1" \
    --config base \
    --es-host http://localhost:9200 \
    --recreate \
    --batch-size 500

其他客户数据导入

  • 使用各自的数据转换器(如indexer/data_transformer.py
  • 数据源映射逻辑写死在各自的转换器中
  • 共享search_products索引,通过tenant_id隔离

实现模块

  • indexer/spu_transformer.py - SPU数据转换器(Base配置)
  • indexer/data_transformer.py - 通用数据转换器(其他客户)
  • indexer/bulk_indexer.py - 批量索引器
  • scripts/ingest_shoplazza.py - 店匠数据导入脚本

4. QueryParser 实现

4.1 查询改写(Query Rewriting)

配置词典的key是query,value是改写后的查询表达式,比如。比如品牌词 改写为在brand|query OR name|query,类别词、标签词等都可以放进去。纠错、规范化、查询改写等 都可以通过这个词典来配置。 实现情况

配置方式

query_config.rewrite_dictionary 中配置查询改写规则:

query_config:
  enable_query_rewrite: true
  rewrite_dictionary:
    "芭比": "brand:芭比 OR name:芭比娃娃"
    "玩具": "category:玩具"
    "消防": "category:消防 OR name:消防"

功能特性

  • 精确匹配:查询完全匹配词典 key 时,替换为 value
  • 部分匹配:查询包含词典 key 时,替换该部分
  • 支持布尔表达式:value 可以是复杂的布尔表达式(AND, OR, 域查询等)

实现模块

  • query/query_rewriter.py - 查询改写器
  • query/query_parser.py - 查询解析器(集成改写功能)

4.2 翻译(Translation)

实现情况

配置方式

query_config:
  supported_languages:
    - "zh"
    - "en"
    - "ru"
  default_language: "zh"
  enable_translation: true
  translation_service: "deepl"
  translation_api_key: null  # 通过环境变量设置

功能特性

  1. 语言检测:自动检测查询语言
  2. 智能翻译
    • 如果查询是中文,翻译为英文、俄文
    • 如果查询是英文,翻译为中文、俄文
    • 如果查询是其他语言,翻译为所有支持的语言
  3. 域感知翻译
    • 如果域有 language_field_mapping,只翻译到映射中存在的语言
    • 避免不必要的翻译,提高效率
  4. 翻译缓存:缓存翻译结果,避免重复调用 API

工作流程

查询输入 → 语言检测 → 确定目标语言 → 翻译 → 多语言查询构建

实现模块

  • query/language_detector.py - 语言检测器
  • query/translator.py - 翻译器(DeepL API)
  • query/query_parser.py - 查询解析器(集成翻译功能)

4.3 文本向量化(Text Embedding)

如果配置打开了text_embedding查询,并且query 包含了default域的查询,那么要把default域的查询词转向量,后面searcher会用这个向量参与查询。

实现情况

配置方式

query_config:
  enable_text_embedding: true

功能特性

  1. 条件生成
    • 仅当 enable_text_embedding=true 时生成向量
    • 仅对 default 域查询生成向量
  2. 向量模型:BGE-M3 模型(1024维向量)
  3. 用途:用于语义搜索(KNN 检索)

实现模块

  • embeddings/bge_encoder.py - BGE 文本编码器
  • query/query_parser.py - 查询解析器(集成向量生成)

5. Searcher 实现

参考opensearch,他们自己定义的一套索引结构配置、支持自定义的一套检索表达式、排序表达式,这是各个客户进行配置化的基础,包括索引结构配置、排序策略配置。 比如各种业务过滤策略 可以简单的通过表达式满足,比如brand|耐克 AND cate2|xxx。指定字段排序可以通过排序的表达式实现。

查询默认在default域,相也会对这个域的查询做一些相关性的重点优化,包括融合语义相关性、多语言相关性(可以基于配置 将查询翻译到指定语言并在对应的语言的字段进行查询)来弥补传统查询分析手段(比如查询改写 纠错 词权重等)的不足,也支持通过配置一些词表转为泛查询模式来优化相关性。

5.1 布尔表达式解析

实现情况

支持的运算符

  • AND:所有项必须匹配
  • OR:任意项匹配
  • RANK:排序增强(类似 OR 但影响排序)
  • ANDNOT:排除(第一项匹配,第二项不匹配)
  • ():括号分组

优先级(从高到低)

  1. () - 括号
  2. ANDNOT - 排除
  3. AND - 与
  4. OR - 或
  5. RANK - 排序

示例

laptop AND (gaming OR professional) ANDNOT cheap

实现模块

  • search/boolean_parser.py - 布尔表达式解析器
  • search/searcher.py - 搜索器(集成布尔解析)

5.2 多语言搜索

实现情况

工作原理

  1. 查询解析
    • 提取域(如 title:查询 → 域=title,查询=查询
    • 检测查询语言
    • 生成翻译
  2. 多语言查询构建
    • 如果域有 language_field_mapping
      • 使用检测到的语言查询对应字段(boost * 1.5)
      • 使用翻译后的查询搜索其他语言字段(boost * 1.0)
    • 如果域没有 language_field_mapping
      • 使用所有字段进行搜索
  3. 查询组合
    • 多个语言查询组合为 should 子句
    • 提高召回率

示例

查询: "芭比娃娃"
域: default
检测语言: zh

生成的查询:
- 中文查询 "芭比娃娃" → 搜索 name, categoryName, brandName (boost * 1.5)
- 英文翻译 "Barbie doll" → 搜索 enSpuName (boost * 1.0)
- 俄文翻译 "Кукла Барби" → 搜索 ruSkuName (boost * 1.0)

实现模块

  • search/multilang_query_builder.py - 多语言查询构建器
  • search/searcher.py - 搜索器(使用多语言构建器)

5.3 相关性计算(Ranking)

实现情况

当前实现

公式bm25() + 0.2 * text_embedding_relevance()

  • bm25():BM25 文本相关性得分
    • 包括多语言打分
    • 内部通过配置翻译为多种语言
    • 分别到对应的字段搜索
    • 中文字段使用中文分词器,英文字段使用英文分词器
  • text_embedding_relevance():文本向量相关性得分(KNN 检索的打分)
    • 权重:0.2

配置方式

ranking:
  expression: "bm25() + 0.2*text_embedding_relevance()"
  description: "BM25 text relevance combined with semantic embedding similarity"

扩展性

  • 支持表达式配置(未来可扩展)
  • 支持自定义函数(如 timeliness(), field_value()

实现模块

  • search/ranking_engine.py - 排序引擎
  • search/searcher.py - 搜索器(集成排序功能)

6. 已完成功能总结

6.1 配置系统

  • ✅ 字段定义配置(类型、分析器、来源表/列)
  • ✅ 索引域配置(多域查询、多语言映射)
  • ✅ 查询配置(改写词典、翻译配置)
  • ✅ 排序配置(表达式配置)
  • ✅ 配置验证(字段存在性、类型检查、分析器匹配)

6.2 数据索引

  • ✅ 数据转换(字段映射、类型转换)
  • ✅ 向量生成(文本向量、图片向量)
  • ✅ 向量缓存(避免重复计算)
  • ✅ 批量索引(错误处理、重试机制)
  • ✅ ES mapping 自动生成

6.3 查询处理

  • ✅ 查询改写(词典配置)
  • ✅ 语言检测
  • ✅ 多语言翻译(DeepL API)
  • ✅ 文本向量化(BGE-M3)
  • ✅ 域提取(支持 domain:query 语法)

6.4 搜索功能

  • ✅ 布尔表达式解析(AND, OR, RANK, ANDNOT, 括号)
  • ✅ 多语言查询构建(语言路由、字段映射)
  • ✅ 语义搜索(KNN 检索)
  • ✅ 相关性排序(BM25 + 向量相似度)
  • ✅ 结果聚合(Faceted Search)

6.5 API 服务

  • ✅ RESTful API(FastAPI)
  • ✅ 搜索接口(文本搜索、图片搜索)
  • ✅ 文档查询接口
  • ✅ 前端界面(HTML + JavaScript)
  • ✅ 租户隔离(tenant_id过滤)

6.6 Base配置(店匠通用)

  • ✅ SPU级别索引结构
  • ✅ 嵌套variants字段
  • ✅ 统一索引(search_products)
  • ✅ 租户隔离(tenant_id)
  • ✅ 配置简化(移除MySQL相关配置)

7. 技术栈

  • 后端:Python 3.6+
  • 搜索引擎:Elasticsearch
  • 数据库:MySQL(Shoplazza)
  • 向量模型:BGE-M3(文本)、CN-CLIP(图片)
  • 翻译服务:DeepL API
  • API 框架:FastAPI
  • 前端:HTML + JavaScript

8. API响应格式

8.1 外部友好格式

API返回格式不包含ES内部字段(_id, _score, _source),使用外部友好的格式:

响应结构

{
  "results": [
    {
      "product_id": "123",
      "title": "蓝牙耳机",
      "variants": [
        {
          "variant_id": "456",
          "price": 199.99,
          "sku": "SKU-123-1",
          "stock": 50
        }
      ],
      "relevance_score": 0.95
    }
  ],
  "total": 10,
  "facets": [...],
  "suggestions": [],
  "related_searches": []
}

主要变化

  • 结构化结果(ProductResultVariantResult
  • 嵌套variants数组
  • 无ES内部字段

8.2 租户隔离

所有API请求必须提供tenant_id

  • 请求头:X-Tenant-ID: 1
  • 或查询参数:?tenant_id=1

搜索时自动添加tenant_id过滤,确保数据隔离。

9. 配置文件示例

Base配置(店匠通用):config/schema/base/config.yaml

其他客户配置config/schema/tenant1/config.yaml


9. 相关文档

  • MULTILANG_FEATURE.md - 多语言功能详细说明
  • QUICKSTART.md - 快速开始指南
  • HighLevelDesign.md - 高层设计文档
  • IMPLEMENTATION_SUMMARY.md - 实现总结
  • 商品数据源入ES配置规范.md - 数据源配置规范