搜索引擎通用化开发进度
项目概述
对后端搜索技术 做通用化。 通用化的本质 是 对于各种业务数据、各种检索需求,都可以 用少量定制+配置化 来实现效果。
通用化的本质:对于各种业务数据、各种检索需求,都可以用少量定制+配置化来实现效果。
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层(脚本)决定
- 数据导入流程:写死的脚本,不依赖配置
统一通过配置文件定义:
- ES 字段定义(字段类型、分析器、boost等)
- ES mapping 结构生成
- 查询域配置(indexes)
- 排序和打分配置(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配置)
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)
多语言字段映射
支持将不同语言的查询路由到对应的字段:
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"
工作原理:
- 检测查询语言(中文、英文、俄文等)
- 如果查询语言在
language_field_mapping中,使用原始查询搜索对应语言的字段 - 将查询翻译到其他支持的语言,分别搜索对应语言的字段
- 组合多个语言查询的结果,提高召回率
实现模块:
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
数据流程:
- 数据加载:从MySQL读取
shoplazza_product_spu和shoplazza_product_sku表 - 数据转换(
indexer/spu_transformer.py):- 按
spu_id和tenant_id关联SPU和SKU数据 - 将SKU数据聚合为嵌套的
variants数组 - 计算扁平化价格字段(
min_price,max_price,compare_at_price) - 字段映射(写死在代码中,不依赖配置)
- 注入
tenant_id字段
- 按
- 索引创建:
- 根据配置生成ES mapping
- 创建或更新
search_products索引
- 批量入库:
- 批量写入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 # 通过环境变量设置
功能特性
- 语言检测:自动检测查询语言
- 智能翻译:
- 如果查询是中文,翻译为英文、俄文
- 如果查询是英文,翻译为中文、俄文
- 如果查询是其他语言,翻译为所有支持的语言
- 域感知翻译:
- 如果域有
language_field_mapping,只翻译到映射中存在的语言 - 避免不必要的翻译,提高效率
- 如果域有
- 翻译缓存:缓存翻译结果,避免重复调用 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
功能特性
- 条件生成:
- 仅当
enable_text_embedding=true时生成向量 - 仅对
default域查询生成向量
- 仅当
- 向量模型:BGE-M3 模型(1024维向量)
- 用途:用于语义搜索(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:排除(第一项匹配,第二项不匹配)
- ():括号分组
优先级(从高到低)
()- 括号ANDNOT- 排除AND- 与OR- 或RANK- 排序
示例
laptop AND (gaming OR professional) ANDNOT cheap
实现模块
search/boolean_parser.py- 布尔表达式解析器search/searcher.py- 搜索器(集成布尔解析)
5.2 多语言搜索
实现情况:
工作原理
- 查询解析:
- 提取域(如
title:查询→ 域=title,查询=查询) - 检测查询语言
- 生成翻译
- 提取域(如
- 多语言查询构建:
- 如果域有
language_field_mapping:- 使用检测到的语言查询对应字段(boost * 1.5)
- 使用翻译后的查询搜索其他语言字段(boost * 1.0)
- 如果域没有
language_field_mapping:- 使用所有字段进行搜索
- 如果域有
- 查询组合:
- 多个语言查询组合为
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": []
}
主要变化:
- 结构化结果(
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- 数据源配置规范