店匠是类似于shopify的独立站SAAS。我们要为基于店匠的独立站(一般为跨境电商独立站)做搜索SAAS。
商品数据 - 数据源 - mysql
店匠的商品结构如下:
数据库配置:
mysql数据源
host: 120.79.247.228 port: 3316 username: saas password: P89cZHS5d7dFyc9R
店匠主表
shoplazza_product_sku shoplazza_product_spu
主表sku表的结构为: id bigint(20) spu_id bigint(20) shop_id bigint(20) shoplazza_id varchar(64) shoplazza_product_id varchar(64) shoplazza_image_id varchar(64) title varchar(500) sku varchar(100) barcode varchar(100) position int(11) price decimal(10,2) compare_at_price decimal(10,2) cost_price decimal(10,2) option1 varchar(255) option2 varchar(255) option3 varchar(255) inventory_quantity int(11) weight decimal(10,2) weight_unit varchar(10) image_src varchar(500) wholesale_price json note text extend json shoplazza_created_at datetime shoplazza_updated_at datetime tenant_id bigint(20) creator varchar(64) create_time datetime updater varchar(64) update_time datetime deleted bit(1)
所有租户共用这个主表
每个租户的辅表
各个租户,有自己的扩展表。 入索引的时候,商品主表 shoplazza_product_sku 的 id + shopid,拼接租户自己单独的扩展表(比如可以放一些自己的属性体系、各种语言的商品名、品牌名、标签、分类等)
但是,各个租户,可能有不一样的业务数据,比如不同租户有不同的属性的体系、不同语言的商品标题(一般至少有中英文两种满足跨境的搜索需求),有不同的权重(提权)字段、业务过滤和聚合字段。 能够统一的 只能是 sku表 按照一套配置规范、做一个配置文件,按照配置文件建设ES mapping结构以及做数据的入库。
SearchEngine
IndexerConfig
@阿里opensearch电商行业.md, 有两套配置
- 应用结构配置 : 定义了ES的输入数据有哪些字段、关联mysql的哪些字段.
- 索引结构配置 : 定义了ES的字段,每个字段的索引mapping配置,支持各个域的查询,包括默认的域的查询。索引配置预定一号了一堆分析方式 由 @商品数据源入ES配置规范.md 定义。
测试数据灌入
灌入数据、mysql到ES的自动同步,不在本项目的范围内,另外有java项目负责。 但是,该项目 为了提供测试数据,需要 构造一个实例 customer1. 我们为他构造一套应用配置和索引配置。 灌入一批测试数据,可以些一个简单的 全量灌入的实现。 数据源地址在:data/customer1/goods_with_pic.5years_congku.csv.shuf.1w 请根据这里面的字段,建设辅助表(注意看哪些字段在主表有,哪些需要放到辅表) 然后写一个程序,将数据分别灌入主表和辅表。
queryParser
query分析,做以下几个事情:
- 查询改写。 配置词典的key是query,value是改写后的查询表达式,比如。比如品牌词 改写为在brand|query OR name|query,类别词、标签词等都可以放进去。纠错、规范化、查询改写等 都可以通过这个词典来配置。
- 翻译。配置需要得到的几种目标语言。 在customer1测试案例中,我们配置 zh en两种语言。先对query做语言检测,如果query是中文那么要翻译一下en,如果是en那么要翻译zh,如果两者都不是那么zh en都需要翻译。
- 如果配置打开了text_embedding查询,并且query 包含了default域的查询,那么要把default域的查询词转向量,后面searcher会用这个向量参与查询。 翻译代码参考: ``` import requests api_url = "https://api.deepl.com/v2/translate" headers = { "Authorization": "DeepL-Auth-Key YOUR_AUTH_KEY", "Content-Type": "application/json", } payload = { "text": ["要翻译的文本"], "target_lang": "ZH", # 中文 }
response = requests.post(api_url, headers=headers, json=payload, timeout=10)
if response.status_code == 200: data = response.json() translation = data["translations"][0]["text"] print(translation)
### searcher
支持多种检索表达式:
支持多种匹配方式,如AND、OR、RANK、NOTAND以及(),优先级从高到低为(),ANDNOT,AND,OR,RANK。
default域的相关性,是代码里面单独计算,是特定的深度定制优化的,暂时不做配置化。
暂时具体实现为 bm25()+0.2*text_embedding_relevence(也就是knn检索表达式的打分)
bm25打分(base_query):
"multi_match": {
"query": search_query,
"fields": match_fields,
"minimum_should_match": "67%",
"tie_breaker": 0.9,
"boost": 1.0, # Low boost for auxiliary keyword query
"_name": "base_query"
}
text_embedding_relevence:
knn_query = {
"knn": {
"field": text_embedding_field,
"query_vector": query_vector.tolist(),
"k": KNN_K,
"num_candidates": KNN_NUM_CANDIDATES
}
}
支持配置化的排序打分:
default域 支持配置的排序方式:
| 场景 | 表达式 | 含义 |
|------|--------|------|
| 电商 | `text_re()+general_score*2+timeliness(end_time)` | 文本分、宝贝综合分值、过期时间 |
有一个配置,是否按照spu聚合,如果打开spu聚合,那么 要配置spu_id的字段,检索表达上需要加上:
es_query["aggs"]["unique_count"] = {
"cardinality": {
"field": spu_id_field_name
}
}
es_query["collapse"]["inner_hits"] = {
"_source": False,
"name": "top_docs",
"size": INNER_HITS_SIZE
}
## 相关配置
ES_CONFIG = {
'host': 'http://localhost:9200',
'username': 'essa',
'password': '4hOaLaf41y2VuI8y'
}
REDIS_CONFIG = {
'host': 'localhost',
'port': 6479,
'password': 'BMfv5aI31kgHWtlx'
}
DEEPL_AUTH_KEY = "c9293ab4-ad25-479b-919f-ab4e63b429ed"