# 搜索引擎通用化开发进度 ## 项目概述 对后端搜索技术 做通用化。 通用化的本质 是 对于各种业务数据、各种检索需求,都可以 用少量定制+配置化 来实现效果。 **通用化的本质**:对于各种业务数据、各种检索需求,都可以用少量定制+配置化来实现效果。 --- ## 1. 原始数据层的约定 ### 1.1 店匠主表 所有租户共用以下主表: - `shoplazza_product_sku` - SKU级别商品数据 - `shoplazza_product_spu` - SPU级别商品数据 ### 1.2 索引结构(SPU维度) **统一索引架构**: - 所有客户共享同一个Elasticsearch索引:`search_products` - 索引粒度:SPU级别(每个文档代表一个SPU) - 数据隔离:通过`tenant_id`字段实现租户隔离 - 嵌套结构:每个SPU文档包含嵌套的`variants`数组(SKU变体) **索引文档结构**: ```json { "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/{customer_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配置) ```yaml 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_table`和`source_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`) #### 多语言字段映射 支持将不同语言的查询路由到对应的字段: ```yaml 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级别商品数据 **其他客户表**(customer1等): - 使用各自的数据源表和扩展表 ### 3.2 数据导入方式 **Pipeline层决定数据源**: - 数据导入流程是写死的脚本,不依赖配置 - 配置只关注ES搜索相关的内容 - 数据源映射逻辑写死在转换器代码中 #### Base配置数据导入(店匠通用) **脚本**:`scripts/ingest_shoplazza.py` **数据流程**: 1. **数据加载**:从MySQL读取`shoplazza_product_spu`和`shoplazza_product_sku`表 2. **数据转换**(`indexer/spu_transformer.py`): - 按`spu_id`和`tenant_id`关联SPU和SKU数据 - 将SKU数据聚合为嵌套的`variants`数组 - 计算扁平化价格字段(`min_price`, `max_price`, `compare_at_price`) - 字段映射(写死在代码中,不依赖配置) - 注入`tenant_id`字段 3. **索引创建**: - 根据配置生成ES mapping - 创建或更新`search_products`索引 4. **批量入库**: - 批量写入ES(默认每批500条) - 错误处理和重试机制 **命令行工具**: ```bash 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` 中配置查询改写规则: ```yaml 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) **实现情况**: #### 配置方式 ```yaml 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会用这个向量参与查询。 **实现情况**: #### 配置方式 ```yaml 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 #### 配置方式 ```yaml 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`),使用外部友好的格式: **响应结构**: ```json { "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": [] } ``` **主要变化**: - 结构化结果(`ProductResult`和`VariantResult`) - 嵌套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/customer1/config.yaml` --- ## 9. 相关文档 - `MULTILANG_FEATURE.md` - 多语言功能详细说明 - `QUICKSTART.md` - 快速开始指南 - `HighLevelDesign.md` - 高层设计文档 - `IMPLEMENTATION_SUMMARY.md` - 实现总结 - `商品数据源入ES配置规范.md` - 数据源配置规范