运营想圈出“有红色款、价格不超过 300 元”的鞋。一件商品有两个 SKU:红色款 399 元,蓝色款 199 元。它是否应该入选?
如果条件要求同一个 SKU 同时满足颜色和价格,这件商品就不应该出现。把颜色与价格分别收集到商品级数组后,两项条件却都能命中。后续再调分片数、缓存或查询超时,也不会修复这个结果。
动态圈品首先要确定筛选对象和条件之间的关系。下面按 Elasticsearch 8.19 的字段与查询语义讨论一种设计,示例价格采用人民币分,不代表现有系统的实际模型。
红色和低价必须属于同一个 SKU
品牌、类目、商品状态通常挂在商品上,颜色、规格和售价则跟着 SKU 变化。业务人员需要逐个确认字段归属。某些平台允许不同供货方使用各自品牌,同一个字段名在那里就可能属于 SKU。
查询要求同一个 SKU 满足多个条件时,把 SKU 数组建成 nested 字段。普通 object 数组会丢失对象之间的配对关系;nested 把联合判断限制在一个子对象内,同时增加文档数量和查询成本。参见 nested 官方说明。
设 status、category_id、skus.color 为 keyword,skus.price_cent 为 long,skus 为 nested。以下是搜索请求体示例:
{
"size": 50,
"query": {
"bool": {
"filter": [
{ "term": { "status": "ON_SALE" } },
{ "term": { "category_id": "shoes" } },
{
"nested": {
"path": "skus",
"score_mode": "none",
"query": {
"bool": {
"filter": [
{ "term": { "skus.color": "red" } },
{ "range": { "skus.price_cent": { "lte": 30000 } } }
]
}
}
}
}
]
}
},
"sort": [
{ "product_id": "asc" }
]
}
product_id 在此假设为可排序、唯一的 keyword 字段。这两个 SKU 条件必须出现在同一个 nested 查询内。拆成两个独立的 nested 查询后,它们仍可能分别命中两个 SKU。
调用方若要导出 SKU 清单,可以按 SKU 建文档。这样会重复商品字段,也会增加商品级计数、去重和更新传播的工作。产品经理应先定义结果代表商品还是 SKU,工程团队再计算存储成本。
长尾属性收进 flattened
商家每提交一个新属性,动态 mapping 都可能增加一个字段。属性积累后,团队还要处理类型冲突和字段上限。
价格、时间、库存需要明确的比较语义,应使用显式类型。查询少、只做精确匹配的长尾属性可以放进 flattened。这里的“查询少”取决于业务使用和变更频率,没有通用阈值。
flattened 会按类似 keyword 的方式索引叶子值。数字外观的字符串也不能当作普通数值字段来比较,范围与排序遵循字符串语义。需要“容量大于 256”这样的业务条件时,应建立数值字段,而不是依赖属性 JSON 的表面类型。flattened 文档明确说明了这些限制。
团队也可以把属性存成 [{key, value}] 的 nested 数组,但每项属性都会生成子文档,数字、日期和枚举还需要各自的值字段与验证逻辑。键值关联查询确有需求时再采用这种模型。
服务端把条件树编译成 DSL
圈品界面保存一份受控条件树,其中包含字段 ID、操作符、通过类型校验的值和分组关系。服务端据此生成 DSL,并加入租户隔离条件。前端无需提交任意 Elasticsearch 查询。
“品牌 A 或品牌 B”属于集合准入条件。生成 bool.should 时显式设定 minimum_should_match: 1,不要依赖默认值:同一个 bool 带有 must 或 filter 时,默认值为 0,两个品牌条件可能只参与评分而不限制入选范围。bool 查询文档说明了这个差异。
圈品一般不需要相关性评分,基础条件可以放在过滤上下文中。需要商品名称搜索时,再单独讨论分词和相关性;不要用“分数更高”解释商品是否符合一个明确的价格限制。
每次保存规则还应记录条件树版本、字段字典版本和单位。否则一个属性从字符串改成数值后,旧规则可能得到不同解释。可解释性应包含“哪个条件让商品入选”,也要能回答“它为什么被排除”。后者通常需要针对单个商品运行诊断求值,不能只看搜索命中的说明。
预览和执行使用各自的时间点
运营在页面上看到的是索引在某一时刻的匹配数量。数据库写入完成后,搜索索引仍可能保留旧值。业务若承诺保存后立刻可见,工程团队需要为这条路径安排刷新机制并评估吞吐代价。
如果圈品用于稍后发送优惠券,需要明确执行时重新求值,还是使用审批时的固定名单。前者会随着库存、价格变动改变结果;后者需要保存规则版本、生成时间和名单。导出期间也要固定读取视图,否则分批读取时商品变更可能造成重复或遗漏。本文不规定具体分页 API,实施时应按实际版本核对一致性和资源占用。
涉及资金或库存的执行环节,还要回到权威业务状态复核。索引适合寻找候选,不能单凭几分钟前的搜索结果承诺商品现在可售。
先指出哪条路径慢
“圈品慢”可能指首屏等待、精确计数,或者名单导出追不上更新。三条路径消耗不同资源,排查时应明确其中一条。
评估时应分别记录条件组合、命中规模、SKU 数量分布和更新频率。高选择性品牌筛选不能代表全站范围条件;没有 SKU 的商品、价格缺失、同一商品跨 SKU 命中等案例也要列入结果检查。应先比较结果集合是否正确,再讨论索引大小和响应时间。
接口契约应写明筛选对象、比较类型和名单时效。团队随后才能在同一份正确结果上比较索引大小与响应时间。
