Elasticsearch:如何基于字段值稀有度排序实现结果多样性
Elasticsearch实现搜索结果多样性排序(避免同品牌扎堆)
完全可以实现这个需求,核心思路是给同一品牌(store字段)的文档分组,让每组内得分最高的文档优先展示,同组内其他文档降权后置。下面给两种可行的实现方案:
方案一:窗口函数+排序(推荐,Elasticsearch 7.10+支持)
利用Elasticsearch的窗口函数row_number()给每个品牌的文档按原始得分编号,再通过排序让编号为1的文档(品牌内Top1)优先展示,后续编号的文档后置。
具体查询示例:
GET /your_index/_search { "query": { "match": { "name": "T-Shirt" } }, "runtime_mappings": { "store_rank": { "type": "long", "script": { "source": "emit(row_number() over (partition by doc['store'].value order by _score desc));" } } }, "sort": [ {"store_rank": "asc"}, {"_score": "desc"} ] }
逻辑说明:
runtime_mappings新增临时字段store_rank:按store分组,每组内文档按原始搜索得分降序编号(品牌内得分最高的文档编号为1)。- 排序时先按
store_rank升序,确保所有品牌的Top1文档排在最前面;再按原始得分降序,保证同优先级内的文档仍按相关性排序。 - 最终效果:Zara的Top文档和Bershka、Benetton的文档排在前列,其余Zara文档会被挤到后面,实现结果多样性。
方案二:函数得分脚本(兼容旧版本)
如果你的Elasticsearch版本不支持窗口函数,可以用函数得分查询结合脚本,对同一品牌的非首条文档降权。
具体查询示例:
GET /your_index/_search { "query": { "function_score": { "query": { "match": { "name": "T-Shirt" } }, "functions": [ { "script_score": { "script": { "source": """ def current_store = doc['store'].value; // 跟踪当前品牌,首次出现时得分不变,后续出现则降权 if (current_store == params.last_store) { params.counter++; return _score * 0.1; // 乘以0.1大幅降低排名权重 } else { params.last_store = current_store; params.counter = 1; return _score; } """, "params": { "last_store": "", "counter": 1 } } } } ], "boost_mode": "replace" } }, "sort": [{"_score": "desc"}] }
逻辑说明:
- 脚本通过
last_store参数跟踪上一条文档的品牌,counter记录同品牌出现次数。 - 首次遇到某品牌时,保留原始得分;后续遇到同品牌文档,将得分乘以0.1(可根据需求调整系数),大幅降低其排名。
- 注意:这种方式依赖文档的遍历顺序,建议先按原始得分排序后再应用脚本,确保品牌内得分最高的文档优先保留高权重。
内容的提问来源于stack exchange,提问作者pheeria
相关产品推荐
相关产品推荐

