Elasticsearch如何按用户分组取评分最高的TopN首歌曲(仅统计最新评分)
解决方案
原查询问题分析
你写的查询存在两个核心问题导致没有得到预期结果:
top_hits嵌套在timestamp子聚合中,仅能返回每首歌曲的最新1条评分,但没有对同一用户下的所有歌曲按照最新评分做全局排序track_id的terms分组默认按文档出现次数排序,没有按评分字段排序,自然拿不到评分最高的前N首
调整后兼容全版本的查询(示例取Top10,可自行修改size参数)
{ "size": 0, "aggs": { "group_by_user": { "terms": { "field": "user_id.keyword", "size": 10 // 按需调整需要返回的用户总数 }, "aggs": { "group_by_track": { "terms": { "field": "track_id.keyword", "size": 1000 // 建议设置为大于单个用户最多评分歌曲数,避免遗漏数据 }, "aggs": { // 取该用户对该歌曲的最新1条评分记录 "latest_rating": { "top_hits": { "sort": [{"timestamp": "desc"}], "size": 1, "_source": ["title", "artist", "score", "timestamp"] } }, // 提取最新评分的分数用于后续排序 "latest_score": { "max": { "field": "score" } } } }, // 对所有歌曲按最新评分倒序,取TopN "top_n_tracks": { "bucket_sort": { "buckets_path": "group_by_track>latest_score", "sort": [{"group_by_track>latest_score": "desc"}], "size": 10 // 这里就是你要的TopN的N值 } } } } } }
ES7.11+版本优化写法(性能更高)
高版本可以用top_metrics聚合替代top_hits,不需要额外提取排序字段,执行效率更高:
{ "size": 0, "aggs": { "group_by_user": { "terms": { "field": "user_id.keyword", "size": 10 }, "aggs": { "group_by_track": { "terms": { "field": "track_id.keyword", "size": 1000 }, "aggs": { "latest_record": { "top_metrics": { "metrics": [ {"field": "score"}, {"field": "title"}, {"field": "artist"} ], "sort": {"timestamp": "desc"} } } } }, "top_10_tracks": { "bucket_sort": { "buckets_path": "group_by_track>latest_record.score", "sort": [{"group_by_track>latest_record.score": "desc"}], "size": 10 } } } } } }
核心逻辑说明
- 先按用户ID分组,再按歌曲ID分组,保证每个用户的每首歌曲独立成桶
- 每个歌曲桶内按时间戳倒序取第一条记录,自动过滤掉同一用户同一首歌的历史评分,仅保留最新一条
- 用
bucket_sort聚合对同一用户下的所有歌曲桶按最新评分倒序排列,取前N条即可得到目标结果
内容的提问来源于stack exchange,提问作者Clayar91
相关产品推荐
相关产品推荐

