店匠是类似于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"