Tie*_*eme 6 aggregate elasticsearch
我正在尝试将旧MySQL数据库的一些查询迁移到我们新的Elasticsearch设置中.数据有点复杂,但归结为以下几点:
我有一个包含很多分数的索引.每个分数代表玩家在特定游戏中得分.
{
"userId": 2,
"scoreId": 3457,
"game": {
"id": 6,
"name": "scrabble"
},
"date": 1340047100,
"score": 56,
// and more game data
}
Run Code Online (Sandbox Code Playgroud)
scoreId是这个分数的唯一ID,game.id是该类型游戏的ID.
{
"userId": 6,
"gameId": 3479,
"game": {
"id": 5,
"name": "risk"
},
"date": "1380067200",
"score": 100,
// and more game data
}
Run Code Online (Sandbox Code Playgroud)
多年来,我们玩了许多不同的游戏,我想为每种类型的游戏排名最好的玩家.排名基于每位玩家的最佳6场比赛.因此,例如,如果玩家玩拼字游戏10次,则只有6个最佳分数计入其总分.
我想创建一个列表,如:
// Scrabble ranking:
# | user | total points
1 | 2 | 4500
2 | 6 | 3200
2 | 23 | 1500
Run Code Online (Sandbox Code Playgroud)
迁移的原因是旧的MySQL查询首先获得每个游戏的所有不同用户的列表,然后为每个用户执行另一个查询以获得其最佳的6个分数.我希望我可以使用弹性聚合在一个查询中完成所有操作,但到目前为止我无法使其工作.
问题是,经过几个小时的阅读弹性文档后,似乎我的问题比例子更复杂.也许如果有人能指出我正确的方向,我可以继续我的搜索.至少这不是让我在任何地方:
GET /my-index/scores/_search
{
"query": {
"bool": {
"filter": [
{"term": { "game.id": 6 }}
]
}
},
"aggs": {
"scores": {
"terms": {
"field": "userId"
}
},
"top_scores_user": {
"top_hits": {
"sort": [{
"score": {
"order": "desc"
}
}],
"size" : 6
}
}
},
"size": 0
}
Run Code Online (Sandbox Code Playgroud)
我正在使用弹性2.3,但如果真的有必要,我有机会升级.
使用top_hits不会让您实现所需的效果,因为您无法对顶部点击聚合中每个文档返回的字段进行操作。
解决这个问题的一种方法是对用户使用顶级terms聚合(就像您所做的那样),然后对每个用户使用另一个terms分数子聚合,您可以按降序排序,并只取 6 个最好的分数。最后,使用管道sum_bucket聚合,您可以总结每个用户的这 6 个分数。
POST /my-index/scores/_search
{
"size": 0,
"query": {
"bool": {
"filter": [
{
"term": {
"game.id": 6
}
}
]
}
},
"aggs": {
"users": {
"terms": { <--- segment by user
"field": "userId"
},
"aggs": {
"best_scores": {
"terms": { <--- 6 best scores for user
"field": "score",
"order": {
"_term": "desc"
},
"size": 6
},
"aggs": {
"total_score": {
"sum": {
"field": "score"
}
}
}
},
"total_points": { <--- total points for the user based on 6 best scores
"sum_bucket": {
"buckets_path": "best_scores > total_score"
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,此解决方案的一个缺点是,如果用户的分数完全相同,您将获得 7 个最佳分数,而不是 6 个最佳分数,并且该total_score值会太高。我们可以使用avg代替sum度量聚合,但如果我们这样做,我们将忽略其中一个分数出现,这也不好。
另请注意,根据用户的值对用户进行排序是理想的选择total_points,但不可能使用管道聚合进行排序(因为它们在归约阶段之后运行)。排序需要在客户端进行。