如何设计索引支持基于购买历史与产品属性的高效筛选?
针对用户购买历史+产品属性筛选的索引设计方案
方案一:单索引存储用户购买记录+产品属性
创建独立索引 user_product_purchases,每个文档对应一条用户购买记录,结构如下:
{ "user_id": "u_1001", "product_id": "p_2024", "purchase_date": "2024-05-20", "product_attrs": { "size": "L", "color": "navy", "category": "outerwear", "brand": "example" // 其他产品属性 } }
核心逻辑
直接在该索引内完成「用户身份匹配+产品属性筛选」的联合查询,示例查询语句:
{ "query": { "bool": { "must": [ {"term": {"user_id": "u_1001"}}, {"term": {"product_attrs.color": "navy"}}, {"term": {"product_attrs.size": "L"}} ] } } }
优缺点
- 优势:查询链路短,性能最优;支持任意产品属性的组合筛选,灵活度拉满;无需跨索引关联开销。
- 劣势:存在数据冗余,同一产品的属性会被所有购买过它的用户记录重复存储;若产品属性需要更新(如分类调整),需批量修改所有关联的购买记录文档,维护成本较高。
方案二:双索引关联查询(Terms Lookup模式)
保留原有的 products 索引存储产品属性,同时创建 user_purchases 索引按用户聚合购买记录,结构如下:
products索引(不变):存储所有在售产品的完整属性user_purchases索引:每个文档对应一个用户,存储其所有购买的产品ID数组{ "_id": "u_1001", // 用user_id作为文档ID "product_ids": ["p_2024", "p_1987", "p_3102"] }
核心逻辑
利用搜索引擎的Terms Lookup能力,在查询时自动从 user_purchases 索引拉取目标用户的产品ID列表,再在 products 索引中完成属性筛选。示例查询语句:
{ "query": { "bool": { "must": [ {"term": {"color": "navy"}}, {"term": {"size": "L"}}, { "terms": { "product_id": { "index": "user_purchases", "id": "u_1001", "path": "product_ids" } } } ] } } }
优缺点
- 优势:无数据冗余,产品属性仅在
products索引存储;产品属性更新只需修改单条产品文档,维护成本低;user_purchases索引体积极小(文档数等于用户数),存储成本可控。 - 劣势:查询需跨索引关联,性能略逊于单索引方案,但对于20k级产品+百万级用户的规模,完全在可接受范围内;若用户购买的产品数超过万级,Terms Lookup的性能会有所下降,可通过分页返回结果缓解。
选型建议
- 若产品属性基本稳定(如尺寸、颜色不会频繁变更),优先选择方案一,兼顾性能与查询灵活性。
- 若产品属性需频繁更新,或希望控制存储成本,选择方案二更合适。
额外优化技巧
- 对
user_purchases索引的product_ids字段启用doc_values,提升Terms Lookup的检索速度。 - 将
products索引中常用的筛选属性(如颜色、尺寸、类别)设置为keyword类型,优化倒排索引的查询效率。 - 针对高频查询场景(如用户常用的筛选组合),添加查询缓存,减少重复计算开销。
内容的提问来源于stack exchange,提问作者Jim Rubenstein
相关产品推荐
相关产品推荐

