HighLevelDesign.md 6.05 KB

店匠是类似于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, 有两套配置

  1. 应用结构配置 : 定义了ES的输入数据有哪些字段、关联mysql的哪些字段.
  2. 索引结构配置 : 定义了ES的字段,每个字段的索引mapping配置,支持各个域的查询,包括默认的域的查询。索引配置预定一号了一堆分析方式 由 @商品数据源入ES配置规范.md 定义。

测试数据灌入

灌入数据、mysql到ES的自动同步,不在本项目的范围内,另外有java项目负责。 但是,该项目 为了提供测试数据,需要 构造一个实例 tenant1. 我们为他构造一套应用配置和索引配置。 灌入一批测试数据,可以些一个简单的 全量灌入的实现。 数据源地址在:data/tenant1/goods_with_pic.5years_congku.csv.shuf.1w 请根据这里面的字段,建设辅助表(注意看哪些字段在主表有,哪些需要放到辅表) 然后写一个程序,将数据分别灌入主表和辅表。

queryParser

query分析,做以下几个事情:

  1. 查询改写。 配置词典的key是query,value是改写后的查询表达式,比如。比如品牌词 改写为在brand|query OR name|query,类别词、标签词等都可以放进去。纠错、规范化、查询改写等 都可以通过这个词典来配置。
  2. 翻译。配置需要得到的几种目标语言。 在tenant1测试案例中,我们配置 zh en两种语言。先对query做语言检测,如果query是中文那么要翻译一下en,如果是en那么要翻译zh,如果两者都不是那么zh en都需要翻译。
  3. 如果配置打开了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() 包括多语言的打分:内部需要通过配置翻译为多种语言(配置几种目标语言 默认中文、英文,并且设置对应的检索域),然后分别到对应的字段搜索,中文字段到配置的中文title搜索,英文到对应的英文title搜索。
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"